Photon MQTT temperature monitor for Home Assistant (using OneWire, DS18, and MQTT)

I’m relatively new to this and brand new to the use of MQTT.

I am working on a solution to prevent the basement freezer being left open by one of my four children once again…

I have the OneWire circuit created on a breadboard. I have deployed this basic code:

I can verify that the Photon event data is recording the temperature data correctly. From there I took the sample code in MQTT, added the IP, username, and password of my Home Assistant Mosquitto MQTT, blended the code from the OneWire circuit and this was the result… I am getting alternating values in MQTT Explorer jumping between the current temperature and and “32.000000”. I have yet to figure out why it is doing this… Any help and guidance you can offer is greatly appreciated.

// This #include statement was automatically added by the Particle IDE.
#include <OneWire.h>
#include <MQTT.h>
#include <DS18.h>

// Location of temp sensor 
DS18 sensor(D0);

void callback(char* topic, byte* payload, unsigned int length);

//MQTT server IP address
byte server[] = {192,168,1,40};

MQTT client(server, 1883, callback);

// recieve message
void callback(char* topic, byte* payload, unsigned int length) {
    char p[length + 1];
    memcpy(p, payload, length);
    p[length] = NULL;

    Serial.print( "Got a message : " );
    Serial.print( p );
}

// setup
void setup() {
    // connect to the server
    client.connect("sparkclient", "mosquitto", "^PqEPQN*derBs57QJmD*Z&TY56V5KY");

    // publish/subscribe
    if (client.isConnected()) {
        Serial.print("\nConnected");
        client.publish("MQTTTemp/temperature", String(sensor.fahrenheit()));
        client.subscribe("inTopic/message");
    }
}

// loop
void loop() {
    if (client.isConnected())
        client.loop();

    if (sensor.read()) {
    Particle.publish("temperature", String(sensor.fahrenheit()), PRIVATE);
    }
    
    // connect to the server
    client.connect("sparkclient", "mosquitto", "^PqEPQN*derBs57QJmD*Z&TY56V5KY");

    // publish/subscribe
    if (client.isConnected()) {
        Serial.print("\nConnected");
        client.publish("MQTTTemp/temperature", String(sensor.fahrenheit()));
        client.subscribe("inTopic/message");
    }
}
1 Like

Hi @richardacampbell -

Welcome to Particle forum!! I will do my best to assist. There are some really clever guys in here, I am sure you will be sorted out in no time :slight_smile:

May I ask, is there any particular reason you are using MQTT as apposed to Particle.publish(); for this particular case?

Is your concern here the six decimals, or is the value completely wrong??

Regards,
Friedl

Thank you Friedl, I am reporting data in degrees F so it is bouncing between 68 and 32 every time it takes a measurement.

I changed the code for my void loop as follows, I think I am heading in the right direction. I also added a 5 second delay, no need to take so many measurements. From what I can tell, my original code did not have the sensor.read before the client.publish so it was reporting 32.000000 for some reason, once I added that it is sending the right info to MQTT but my current code is no longer publishing data to particle.publish…

I’m using the data in Mosquitto MQTT on a PI for some home automation, in this case send me a text and set off a buzzer (a little surprise for the kids)…

void loop() {
    // 5 second delay
    delay(5000);
    
    if (client.isConnected())
    
        if (sensor.read()) {
        client.publish("freezer-temp/temperature", String(sensor.fahrenheit()));
        }
    
        client.loop();
    
    if (sensor.read()) {
    Particle.publish("temperature", String(sensor.fahrenheit()), PRIVATE);
    }
}

You are reading the sensor twice in a very short period of time which may be quicker than the sample rate of the sensor.

Why not have a single sensor.read() call and store the reading in a variable to be reused multiple times?

1 Like

That’s a great idea… Now I just need to figure out how to best do that… :slight_smile: Still learning.

@richardacampbell -

Ok it makes sense. Here is how I implemented the temperature readings utlizing the same sensor with Dallas Spark and OneWire libraries. I call the temperature function in void loop (); then.

void temperature() {
    sensors.requestTemperatures();

    Celsius = sensors.getTempCByIndex(0);

    Serial.print(Celsius);
    Serial.println(" *C ");
    Blynk.virtualWrite(V2, Celsius);  // Blynk Mobile app  - Monitor temp Data
    Particle.publish("Temp", String::format("{\"data\":%.3f}", Celsius), PRIVATE);  // Publish temp data to Particle Cloud
}

I did not use MQTT, but the readings sent to particle cloud is correct, so it is only a matter of sending to MQTT as apposed to publishing to Particle Cloud.

In your case and assuming you want to publish to Particle Cloud as well as MQTT, I would rather try something like this: (untested)

void loop() {
    // 5 second delay
    delay(5000);  // maybe try NON-BLOCKING delays instead of blocking delays 
    
    if (client.isConnected())
    
        if (sensor.read()) {
          client.publish("freezer-temp/temperature", String(sensor.fahrenheit()));
               Particle.publish("temperature", String(sensor.fahrenheit()), PRIVATE);
        }
        client.loop();
}

If you have not succeeded, I will try your code tomorrow, quite late here now :). Also, maybe have a look at the Maker Kit tutorial #4 here. It should get you going in the right direction. Just add your MQTT-stuff :slight_smile:

Regards,
Friedl.

I have it running now and the temperature value is not jumping around, what I determined was that in my void setup() I was publishing the temperature value but not ever reading the sensor prior to the publish. So, once I added the sensor read it is publishing a real temperature reading now rather than a default.

Here’s where I’m at with the code:

// This #include statement was automatically added by the Particle IDE.
#include <OneWire.h>
#include <MQTT.h>
#include <DS18.h>

// Location of temp sensor 
DS18 sensor(D0);

void callback(char* topic, byte* payload, unsigned int length);

//MQTT server IP address
byte server[] = {192,168,1,40};

MQTT client(server, 1883, callback);

// recieve message
void callback(char* topic, byte* payload, unsigned int length) {
    char p[length + 1];
    memcpy(p, payload, length);
    p[length] = NULL;

    Serial.print( "Got a message : " );
    Serial.print( p );
}

// setup
void setup() {
    // connect to the server
    client.connect("sparkclient", "mosquitto", "^PqEPQN*derBs57QJmD*Z&TY56V5KY");

    // publish/subscribe
    if (client.isConnected()) {
        if (sensor.read()) {
        Particle.publish("temperature", String(sensor.fahrenheit()), PRIVATE);
        Serial.print("\nConnected");
        client.publish("freezer-temp/temperature", String(sensor.fahrenheit()));
        client.subscribe("freezer-temp/message");
        }
    }
}

// loop
void loop() {
    // 5 second delay
    delay(5000);
    
    if (client.isConnected())
    
        if (sensor.read()) {
        
        Particle.publish("free memory", String(System.freeMemory()), PRIVATE);
        Particle.publish("temperature", String(sensor.fahrenheit()), PRIVATE);
        client.publish("freezer-temp/temperature", String(sensor.fahrenheit()));
        }
        
    client.loop();

}

The issue I am having now is after a few hours the Photon is still connected but it stops sending messages to paricle.publish and MQTT and I don’t know why… If I reboot everything is happy again for a while then it stops. Thoughts?

I’d first get rid of String and see whether things get better.

Additionally, I’d

  • get rid of delay(5000) but use a non-blocking delay
  • unify the publishes into a single event
  • call sensor.fahrenheit() only once and store its value in a float variable
  • take appropriate action when client.isConnected() returns false in order to reconnect
2 Likes

Thanks for the direction, I've changed the code a bit, I hope I am heading in the right direction. I'm fairly new to programming so any additional dialog you can offer as I learn is much appreciated.

// This #include statement was automatically added by the Particle IDE.
#include <OneWire.h>
#include <MQTT.h>
#include <DS18.h>

// Location of temp sensor 
DS18 sensor(D0);

void callback(char* topic, byte* payload, unsigned int length);

// Timer variables
int period = 10000; //10 seconds
unsigned long timeNow = 0;

// Temperature variable
float freezerTemp = 0.00;

// MQTT server IP address
byte server[] = {192,168,1,40};

MQTT client(server, 1883, callback);

// recieve message
void callback(char* topic, byte* payload, unsigned int length) {
    char p[length + 1];
    memcpy(p, payload, length);
    p[length] = NULL;

    Serial.print( "Got a message : " );
    Serial.print( p );
}

// setup
void setup() {
    Serial.begin(115200);
    // connect to the server
    client.connect("sparkclient", "mosquitto", "^PqEPQN*derBs57QJmD*Z&TY56V5KY");
    
        // publish/subscribe
    if (client.isConnected()) {
        Serial.print("\nConnected");
        }
    }

// loop
void loop() {
    
    sensor.read();
    freezerTemp = sensor.fahrenheit();
    
    if (millis() > timeNow + period){
        timeNow = millis();
        Serial.println("freezer-temp");
        //Particle.publish("free memory", String(System.freeMemory());
        Particle.publish("freezer-temp", String(freezerTemp));
        client.publish("freezer-temp/temperature", String(freezerTemp));
    }
    
    if (client.isConnected())
        client.loop();

}

Better, but I’d go with something like this

// keep your constants in one place for easier maintenance
const char* eventName  = "freezer-info";  
const char* mqttBroker = "mosquitto";
const char* mqttClient = "sparkclient";
const char* mqttKey    = "^PqEPQN*derBs57QJmD*Z&TY56V5KY";
const char* mqttTopic  = "freezer-temp/temperature";

// in order to trigger a (re)connect/(re)subscribe from multiple places
bool mqttConnect() {
  if (client.connect(mqttClient, mqttBroker, mqttKey)) {
    // optionally add subscriptions here
  }

  return client.isConnected();
}
...
void setup() {
  Serial.begin(115200);
  if (mqttConnect()) 
    Serial.println("Connected");
}

void loop() {
  static uint32_t msLastPub = 0;
  if (millis() - msLastPub < period) return;
  msLastPub = millis();

  sensor.read();
  freezerTemp = sensor.fahrenheit();
  
  char msg[64];                    // allows for multiple items in one string
  snprintf(msg, sizeof(msg), "%.1f (%d)", freezerTemp, System.freeMemory()); 
  Serial.printlnf("%s: %s", eventName, msg);
  Particle.publish(eventName, msg, PRIVATE);

  if (client.isConnected()) {      // when connected do MQTT stuff
    snprintf(msg, sizeof(msg), "%.1f", freezerTemp);
    client.publish(mqttTopic, msg);
    client.loop();
  }
  else {                           // when not connected 
    if(mqttConnect())              //   reconnect
      Serial.println("Reconnected");         
  }        
}
// This #include statement was automatically added by the Particle IDE.
#include <OneWire.h>
#include <MQTT.h>
#include <DS18.h>

// Location of temp sensor 
DS18 sensor(D0);

void callback(char* topic, byte* payload, unsigned int length);

// Timer variables
int period = 10000; //10 seconds

// Temperature variable
float freezerTemp = 0.00;

// MQTT server IP address
byte server[] = {192,168,1,40};
MQTT client(server, 1883, callback);

// keep your constants in one place for easier maintenance
const char* eventName  = "freezer-info";  
const char* mqttBroker = "mosquitto";
const char* mqttClient = "sparkclient";
const char* mqttKey    = "^PqEPQN*derBs57QJmD*Z&TY56V5KY";
const char* mqttTopic  = "freezer-temp/temperature";

// in order to trigger a (re)connect/(re)subscribe from multiple places
bool mqttConnect() {
  if (client.connect(mqttClient, mqttBroker, mqttKey)) {
    // optionally add subscriptions here
  }

  return client.isConnected();
}

// recieve message
void callback(char* topic, byte* payload, unsigned int length) {
    char p[length + 1];
    memcpy(p, payload, length);
    p[length] = NULL;
    Serial.print( "Got a message : " );
    Serial.print( p );
}

// setup
void setup() {
    Serial.begin(115200);
    if (mqttConnect()) 
    Serial.println("Connected");
    }

// loop
void loop() {
  static uint32_t msLastPub = 0;
  if (millis() - msLastPub < period) return;
  msLastPub = millis();

  sensor.read();
  freezerTemp = sensor.fahrenheit();
  
  char msg[64];                    // allows for multiple items in one string
  snprintf(msg, sizeof(msg), "%.1f (%d)", freezerTemp, System.freeMemory()); 
  Serial.printlnf("%s: %s", eventName, msg);
  Particle.publish(eventName, msg, PRIVATE);

  if (client.isConnected()) {      // when connected do MQTT stuff
    snprintf(msg, sizeof(msg), "%.1f", freezerTemp);
    client.publish(mqttTopic, msg);
    client.loop();
  }
  else {                           // when not connected 
    if(mqttConnect())              //   reconnect
      Serial.println("Reconnected");         
  }        
}

Working but I’m back to fluctuating values between temp and 32.0. Picture attached

I’m not using the same library for reading the DS18 but looking at one of your earlier posts where you had good readings, you could adapt my loop() this way to see if it helps

void loop() {
  static uint32_t msLastPub = 0;
  if (millis() - msLastPub < period || !sensor.read()) // bail out when it's not yet time or the reading is not OK  
    return;
  msLastPub = millis();
  freezerTemp = sensor.fahrenheit();
  ...
}

That got rid of the misread, appreciate the guidance!

1 Like

Have a look at my code in https://github.com/jakubczaplicki/aqua_iot
I am using Particle Photon to measure temperature in an aquarium. It sends value via MQTT to Home Assistant (local network), publishes measurement to Particle Cloud and presents measurement value on a screen.

1 Like