How to create/format MQTT message

Hello everyone,

MQTT is working fine on multiple devices in my home network. Now I'm trying to get the below sensorValue (using modified code from AReResearch for an MQ-7 CO sensor connected to an ESP32) to be published over MQTT. Serial output works fine.

I presume that I'm using "snprintf" wrong.. help would be much appreciated! Thanks!

#include <Arduino.h>
#include <analogWrite.h>
#include <WiFi.h>
#include <PubSubClient.h>

const char* ssid = "****";
const char* password =  "****";
const char* mqttServer = "****";
const int mqttPort = 1883;
const char* mqttUser = "****";
const char* mqttPassword = "****";

char msg[50];

WiFiClient espClient;
PubSubClient client(espClient);

int step = 1;
int brightness = 0;
 
 int sensorPin = A0;  // select the input pin for the CO sensor  
 int sensorValue = 0; // variable to store the value coming from the sensor  
 // Initial setup  
 void setup() {  
  // initialize digital pin LED_BUILTIN as an output  
  pinMode(LED_BUILTIN, OUTPUT);  
  // initialize the serial port  
  Serial.begin(9600);  
  WiFi.begin(ssid, password);

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.println("Connecting to WiFi...");
  }
    Serial.println("");
    Serial.println("WiFi connected");
    Serial.print("IP address: ");
    Serial.println(WiFi.localIP());
    long rssi = WiFi.RSSI();
    Serial.print("RSSI: ");
    Serial.println(rssi);

    client.setServer(mqttServer, mqttPort);

    while (!client.connected()) {
      Serial.println("Connecting to MQTT...");

      if (client.connect("ESP32Client", mqttUser, mqttPassword )) {

        Serial.println("Broker connected");

      } else {
      Serial.print("failed with state ");
      Serial.print(client.state());
      delay(2000);
      }
   }
}  
 
 void loop() {  
  analogWrite(LED_BUILTIN, 255);  // turn the heater fully on  
  delay(60000);                   // heat for 60 second  
 // now reduce the heating power  
  analogWrite(LED_BUILTIN, 72);   // turn the heater to approx 1,4V  
  delay(90000);                   // wait for 90 seconds  
 // we need to read the sensor at 5V, but must not let it heat up. So hurry!  
  digitalWrite(LED_BUILTIN, HIGH);  
  delay (50); //don't know how long to wait without heating up too much. Getting an analog read apparently takes 100uSec  
   // read the value from the sensor:  
  int sensorValue = analogRead(sensorPin);
  sensorValue;
  Serial.println(sensorValue);
  
  // MQTT publish
  snprintf (msg, sizeof(msg), "%ld", sensorValue);
      client.publish("toaster/co/",  msg);
      Serial.println(msg);        // debug
      
 }  

Try using sprintf().

sprintf (msg, "%ld", sensorValue);
client.publish("toaster/co/",  msg);
Serial.println(msg);        // debug

sizeof(msg) will always return 50 in your code because msg is a char array. If you want the number of elements in the array, use:
sizeof(msg) / sizeof(msg[0])

For simple conversions, like an integer to string array, sprintf() is safe.

sizeof(msg) / sizeof(msg[0])

will also always return 50 because of

char msg[50];

As sensorValue is an int you might just as well use

itoa(sensorValue, msg, 10);

to create the message

Thank you @SteveMann @UKHeliBob !

Both options result in printing the sensorValue, which ist great! But the MQTT message still does not get published... after some more debugging I found that the long delays (60+90 seconds) in the loop don't seem to work for MQTT. Reducing delays to 6+9 seconds everything works as expected, the message gets published.

Is this a known issue with MQTT? Am I missing something? Different post..? Thanks again :wink:

Sort of. client.loop() must be called in loop() periodically or the broker link is dropped. I do not know how long.

The delay(eternity) statements in your code threw up a red flag for me. In general any delay() of more than a few ms is strongly discouraged in the loop() function. I suspect that you are losing connection to the MQTT broker.

Here is a good introduction to using millis() to avoid using the delay() statement: Gammon Forum : Electronics : Microprocessors : How to do multiple things at once ... like cook bacon and eggs

In loop you need to check that the client is still connected. I missed that on my first glance at your code.

If you want to see how I do it, look at:

I like to use tabs in my projects, but you can open any of the ino files to get them all into your IDE.

It's not an issue per se, that's how MQTT works. There is a keep alive that checks whether you're still listening and closes your session if you don't respond. The timeout for it is often 60 seconds.

Thanks. I didn't recall how long before the connection is dropped. The OP has 150 seconds of delay() in the loop().

Seems like a backward step for code that works as is
snprintf is a safer option see C static code analysis
All that needs to be added is a check on the return value

if (snprintf (msg, sizeof(msg), "%ld", sensorValue) > sizeof(msg)) {
  Serial.println(F("msg buffer too small"));
}

Well not really if you don't get the buffer size correct.
itoa has similar safety issues (possible buffer overflow)
snprintf() is the save option.

To sort out the delays() see
How to write Timers and Delays in Arduino and
Multi-tasking in Arduino
and move your 'publish' code to a task
multitaskingDiagramSmall
But as noted in a previous post you should check you are still connected after a loooong pause.

1 Like

I probably should start using the snprintf() format. Typically my buffers are overkill. Inefficient, but safe.

Thanks for claryfing that 'snprintf' is good/better practice and how to implement it properly! So it seems the inital code was not all bad but for the long delay periods.

Interestingly the sensor readings don't seem to differ much with the much shorter heating periods. Anyways the MQ-7 is prone to influence from all kinds of exterior factors (room temperature, window open/closed) which makes it hard to figure out a baseline to build upon. Just a side note :slight_smile:

I'll look into it, thanks for sharing! Great project btw :smiley:

This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.