Почему dtostrf() не работает для этого значения?
Я строю метеостанцию, используя ESP32 и BME280. Я передаю данные через MQTT и Python в базу данных, которая затем используется для создания удобной информационной панели для данных.
Сообщение MQTT должно быть символом, а показания датчика — числами с плавающей запятой, поэтому я использую dtostrf()
для их преобразования. Это отлично работает для показаний температуры и давления, но по какой-то причине не работает для показаний влажности.
Я объявляю свои переменные:
float temp;
float hum;
float pres;
char mqttTemp[6];
char mqttHum[6];
char mqttPres[5];
Затем я получаю показания:
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);
Это возвращает:
Темп: 25,57 Уровень шума: 40,15 Pres: 97684,39
Затем я пропускаю результаты через функцию:
dtostrf(temp,4,2,mqttTemp);
dtostrf(hum,4,2,mqttHum);
dtostrf(pres,5,0,mqttPres);
Затем я вывожу их в Serial:
Serial.print("Temperature: ");
Serial.print(mqttTemp);
Serial.print("Humidity: ");
Serial.print(mqttHum);
Serial.print("Pressure: ");
Serial.print(mqttPres);
Что дает мне:
Temperature: 25.57
------------------------------
Humidity:
------------------------------
Pressure: 97684
Сообщение о влажности через MQTT также не отправляется, но для двух других показаний оно отлично работает. Что дает?
@user36735, 👍1
Обсуждение1 ответ
Лучший ответ:
Происходит следующее: переменные, которые вы объявили для mqttTemp и т. д., хранятся в памяти примерно так
variable memory address
mqttPres @ 1000-1004 (5 bytes)
mqttHum @ 1005-1010 (6 bytes)
mqttTemp @ 1011-1015 (6 bytes)
Для начала вам хватит места (с обязательным нулевым байтовым ограничителем строки) только для 4, 5 и 5 символов
При преобразовании давления NUL-байт помещается в качестве первого байта в mqttHum, поэтому при печати mqttHum вы получаете на выходе пустую строку (поскольку первый байт равен NUL)
Чтобы понять, что происходит, вот код, иллюстрирующий проблему
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);
// поместите сюда свой код установки для однократного запуска:
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);
// поместите сюда свой основной код для многократного запуска:
}
Приведенное выше выводит следующее:
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
Самое простое решение — объявить переменные правильного размера.
char mqttTemp[7]; // потому что -10.00 занимает 6 символов + 1 для NULL
char mqttHum[7]; // потому что 100.00 занимает 6 символов + 1 для NULL
char mqttPres[6]; // потому что 97684 принимает 5 символов + 1 для NULL - если давление может достигать 100000, то здесь также используйте 7
а затем используйте dtostrf
правильно, т. е. второй аргумент — это общая ширина, включая .
и возможный -
dtostrf(temp,6,2,mqttTemp);
dtostrf(hum,6,2,mqttHum);
dtostrf(pres,5,0,mqttPres);
Спасибо за очень подробный ответ! Я подозревал, что что-то фундаментально не понимаю (не в последнюю очередь потому, что это мое состояние по умолчанию). Теперь он работает отлично., @user36735
Это минимальная ширина. Почему бы не сделать массивы размером 20 вместо того, чтобы ждать следующей проблемы, когда текст окажется длиннее, чем ожидалось?, @Jot
если температура выше 999 или ниже -99 - есть о чем беспокоиться @Jot - хотя согласен, но с минимальными размерами в приведенном выше коде не может быть никаких проблем, учитывая данные, @Jaromanda X
@JaromandaX Я должен не согласиться. Я даже вынужден очень сильно не согласиться. Ошибка в программном обеспечении не должна быть причиной лавины ошибок по всему коду. Поэтому при использовании dtostrf буфер должен быть достаточно большим, чтобы вместить все возможные значения с плавающей запятой. Предпочтительно буфер в стеке. Если значение с плавающей запятой ограничено, это ограничение должно быть в коде с операторами if и разделом комментариев, объясняющим, почему возможен буфер меньшего размера. Возможно, это полезно для Attiny, но уж точно не для ESP32., @Jot
@Jot - я понимаю, о чем вы говорите, но BME280 не выдаст вам никаких значений за пределами своего диапазона измерения, а dtostrf «запишет» не более 7, 7 и 6 байтов, включая завершение NUL в этом коде - там невозможно **в данном случае** лавины, о которой вы говорите, @Jaromanda X
- Проблема с датчиком температуры и влажности DHT11
- Получение ошибки ets 8 января 2013,rst cause:4,boot mode(1,6) wdt reset
- Проблемы с подключением I2C на ESP8266 — 12F, какие контакты использовать?
- Данные DHT11 из Arduino UNO в Firebase через ESP8266
- Отправка данных из ESP8266 в PHP
- Могут ли ESP8266 и HC-SR04 дружить?
- Проблема «Не найдена плата PN53x» Считыватель карт Arduino NFC
- Как определить наличие воды с помощью всего двух проводов
Используете esp32 и пытаетесь сжать массив до самого последнего байта и далее? Это бессмысленно. Можете ли вы сделать массивы символов по 16 байтов каждый (или 20 или 100 байтов). Размер dtostrf — это **минимальный** размер, плюс, возможно, отрицательный знак, плюс нулевой терминатор, плюс еще немного для безопасности, что намного больше, чем просто 6 или 5. Что, если датчик отключен и есть некоторые значения ошибок? возвращаются и эти ошибки преобразуются в текст?, @Jot
две основные ошибки: 1) вы не объявили переменные с достаточным пространством для данных, которые они должны содержать, и 2) вы неправильно используете
dtostrf
(ширина температуры **минимум** 5, или 6, если вы можете получить температуру -10,00 или ниже, а ширина влажности не менее 6, поскольку100,00
— это 6 символов., @Jaromanda X