1

I'm building a weather station using an ESP32 and BME280. I'm feeding the data via MQTT and Python into a database that is then used to make a nice dashboard for the data.

The MQTT message needs to be a char, and the sensor readings are floats, so I'm using dtostrf() to convert them. This works fine for the temperature and pressure readings, but for some reason not for the humidity reading.

I declare my variables:

float temp;
float hum;
float pres;

char mqttTemp[6];
char mqttHum[6];
char mqttPres[5];

Then I get my readings:

temp = mySensor.readTempC();
Serial.print("Temp: ");
Serial.print(temp);
hum = mySensor.readFloatHumidity();
Serial.print("Hum: ");
Serial.print(hum);
pres = mySensor.readFloatPressure();
Serial.print("Pres: ");
Serial.print(pres);

This returns:

Temp: 25.57 Hum: 40.15 Pres: 97684.39

Then I run the results through the function:

dtostrf(temp,4,2,mqttTemp);
dtostrf(hum,4,2,mqttHum);
dtostrf(pres,5,0,mqttPres);

Then I output these to Serial:

Serial.print("Temperature: ");
Serial.print(mqttTemp);
Serial.print("Humidity: ");
Serial.print(mqttHum);
Serial.print("Pressure: ");
Serial.print(mqttPres);

Which gives me:

Temperature: 25.57
------------------------------
Humidity: 
------------------------------
Pressure: 97684

There is also no message sent via MQTT for humidity, but it works perfectly for the other two readings. What gives?

  • 1
    Using a esp32 and trying to squeeze an array to the very last byte and beyond? That makes no sense. Can you make the char arrays 16 bytes each (or 20 or 100 bytes). The size for dtostrf is the minimum size, plus perhaps a negative sign, plus zero terminator, plus some extra for safety, that makes a lot more than just 6 or 5. What if the sensor is disconnected and some error values are returned and those errors are converted to text? – Jot Jan 27 at 22:28
  • two basic errors - 1) you haven't declared the variables with enough room for the data they need to contain, and 2) you're using dtostrf incorrectly (the width of temperature is at least 5, or 6 if you can get temperatures at -10.00 or less, and the width of humidity is at least 6, since 100.00 is 6 characters – Jaromanda X Jan 28 at 3:58
1

What is happening is that the variables you declared for mqttTemp etc are stored in memory something like this

variable   memory address
mqttPres @ 1000-1004 (5 bytes)
mqttHum  @ 1005-1010 (6 bytes)
mqttTemp @ 1011-1015 (6 bytes)

For a start, you only have enough room (with the mandatory NUL byte string terminator) for 4, 5 and 5 characters

When you convert pressure, the NUL byte is being placed as the first byte in mqttHum, therefore, when printing mqttHum you get an empty string output (since the first byte is NUL)

To really see what's happening, here's some code that illustrates the issue

char mqttTemp[6] = "TTTTT";
char mqttHum[6] = "HHHHH";
char mqttPres[5] = "PPPP";
void setup() {
  float temp = 25.57;
  float hum = 40.15;
  float pres = 97684.39;


  Serial.begin(74880);
  // put your setup code here, to run once:
  delay(100);
  Serial.println("before");
  printThem();
  dtostrf(temp, 4, 2, mqttTemp);
  Serial.println("converted temp");
  printThem();
  dtostrf(hum, 4, 2, mqttHum);
  Serial.println("converted hum");
  printThem();
  dtostrf(pres, 5, 0, mqttPres);
  Serial.println("converted press");
  printThem();
}
void printThem()
{
  Serial.print("mqttPres ");
  Serial.print((unsigned long) &mqttPres, DEC);
  Serial.print(": ");
  for (int i = 0; i < 5; i++) {
    Serial.print((int) mqttPres[i], DEC);
    Serial.print(' ');
  }
  Serial.println();

  Serial.print("mqttHum  ");
  Serial.print((unsigned long) &mqttHum, DEC);
  Serial.print(": ");
  for (int i = 0; i < 6; i++) {
    Serial.print((int) mqttHum[i], DEC);
    Serial.print(' ');
  }
  Serial.println();

  Serial.print("mqttTemp ");
  Serial.print((unsigned long) &mqttTemp, DEC);
  Serial.print(": ");
  for (int i = 0; i < 6; i++) {
    Serial.print((int) mqttTemp[i], DEC);
    Serial.print(' ');
  }
  Serial.println();
}

void loop() {
  delay(50);
  // put your main code here, to run repeatedly:

}

The above outputs the following:

before
mqttPres 1073644748: 80 80 80 80 0 
mqttHum  1073644753: 72 72 72 72 72 0 
mqttTemp 1073644759: 84 84 84 84 84 0 
converted temp
mqttPres 1073644748: 80 80 80 80 0 
mqttHum  1073644753: 72 72 72 72 72 0 
mqttTemp 1073644759: 50 53 46 53 55 0 
converted hum
mqttPres 1073644748: 80 80 80 80 0 
mqttHum  1073644753: 52 48 46 49 53 0 
mqttTemp 1073644759: 50 53 46 53 55 0 
converted press
mqttPres 1073644748: 57 55 54 56 52 
mqttHum  1073644753: 0 48 46 49 53 0 
                     ^ ======== note the leading 0
mqttTemp 1073644759: 50 53 46 53 55 0 

The simplest fix, of course, is to declare your variables with the correct size

char mqttTemp[7]; // because -10.00 takes 6 characters + 1 for NULL
char mqttHum[7];  // because 100.00 takes 6 characters + 1 for NULL
char mqttPres[6]; // because 97684 takes 5 characters + 1 for NULL - if pressure can reach 100000 then use 7 here as well

and then use dtostrf correctly, i.e. the second argument is the total width, including . and a possible -

dtostrf(temp,6,2,mqttTemp);
dtostrf(hum,6,2,mqttHum);
dtostrf(pres,5,0,mqttPres);
  • Thanks for a very thorough answer! I suspected I was fundamentally misunderstanding something (not least because it's my default state of being). It now works perfectly. – user36735 Jan 28 at 7:36
  • It is the minimal width. Why not make the arrays 20 in size instead of waiting for the next problem when the text turns out to be longer than expected? – Jot Jan 28 at 11:00
  • if the temperature is above 999 or below -99 - there's a lot more to worry about @Jot - agreed though, but with the minimal sizes in the code above there can't be any issue given the data – Jaromanda X Jan 28 at 11:09
  • @JaromandaX I have to disagree. I even have to disagree very strong. A bug in software should not be the cause of an avalanche of errors throughout the code. Therefor when using dtostrf, the buffer should be large enough to hold every possible float value. Preferably a buffer on the stack. In case the float value is limited, that limit should be in the code with if-statements and with a comment section to explain why a smaller buffer is possible. Perhaps that is useful in a attiny, but certainly not in a ESP32. – Jot Feb 2 at 17:38
  • @Jot - I understand what you're saying, but a BME280 will not give you any values outside its measuring range, and dtostrf will "write" at most 7, 7 and 6 bytes including the NUL termination in this code - there is no possibility, in this case of the avalanche you speak of – Jaromanda X Feb 2 at 22:55

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.