Код считывания датчика DHT22 странно интерпретирует отрицательные значения (распространенная проблема)
Следующий код считывает значения температуры и влажности DHT22, принимая значение pin
в качестве основного вывода датчика. Когда температура падает ниже 0°C, этот код возвращает неадекватные значения в диапазоне от -3200 до -3300 (чаще всего значения находятся в пределах -3275). При использовании популярной библиотеки Adafruitзначения также неверны. Код был написан не мной, он происходит отсюда, но комментарии в источнике на русском языке.
Проблема довольно распространенная, но адекватного решения в любом случае не было. Это упоминалось в выпусках библиотеки Adafruit довольно много раз (здесь, здесь и здесь (по теме)), а также на других сайтах. Люди даже сообщали об этой проблеме на датчиках DHT11 и DHT21.
// "DHTLif.h"
#pragma once
#include <Arduino.h>
#include <Firmata.h>
uint64_t readingTime;
void DHTt(const uint8_t pin) {
if ((millis() - readingTime) > 2000) {
unsigned char receivedDHTData[5];
float temperature, humidity;
#define DHT_PORT PORTD
#define DHT_DDR DDRD
#define DHT_PIN PIND
#define DHT_BIT pin
int count = 32;
unsigned char i, j;
ReLoad: //Restarting point for error handling
//=============MCU send START
DHT_DDR |= (1 << DHT_BIT); //exit
DHT_PORT &= ~(1 << DHT_BIT); //the pin's level is low, set it to high and wake up the sensor
delay(18); //18 ms delay from the docs
DHT_PORT |= (1 << DHT_BIT); //set the pin's level to low
DHT_DDR &= ~(1 << DHT_BIT); //the pin as exit
//=============Initialize DHT
delayMicroseconds(50); //delay from the docs
if (DHT_PIN & (1 << DHT_BIT)) { //DHT must return 0
if (count != 0) {
goto ReLoad;
count--;
} else {
//send initialization error message
Firmata.sendString("ERR;DHTt;INIT");
return;
}
}
delayMicroseconds(80);
if (!(DHT_PIN & (1 << DHT_BIT))) { //the bus must be set to 0 after 80 ms
if (count != 0) {
goto ReLoad;
count--;
} else {
//send weird behaviour error message
Firmata.sendString("ERR;DHTt;BHVR");
return;
}
}
//===============Receive 40 bits of data
while (DHT_PIN & (1 << DHT_BIT)); //wait until the bus is set to 1
for (j = 0; j < 5; j++) { //loop for 0-4 bytes
receivedDHTData[j] = 0;
for (i = 0; i < 8; i++) { //receive bits and pack them into bytes
while (!(DHT_PIN & (1 << DHT_BIT))); //wait until the bus is set to 0
delayMicroseconds(30); //30 mcs delay from the docs
if (DHT_PIN & (1 << DHT_BIT)) //if the pin is 1 after the delay,
receivedDHTData[j] |= 1 << (7 - i); //set the i'th bit to 1
while (DHT_PIN & (1 << DHT_BIT)); //wait until the bus is set to 0
}
}
if ((unsigned char)(receivedDHTData[0] + receivedDHTData[1] +
receivedDHTData[2] + receivedDHTData[3]) != receivedDHTData[4]) { //checksum
Firmata.sendString("ERR;DHTt;CHSM");
return;
}
temperature = (receivedDHTData[3] * 0.1) + ((receivedDHTData[2] & 0b01111111) * 25.6); //calculating temperature for DHT22
if (receivedDHTData[2] & 0b10000000) temperature *= -1; //if the temperature is negative
humidity = (receivedDHTData[1] * 0.1) + (receivedDHTData[0] * 25.6); //calculating humidity for DHT22
//form the final message
char result[32], catres[8];
readingTime = millis();
strcpy(result, "OK;DHTt;");
dtostrf(temperature, 5, 2, catres);
strcat(result, catres);
strcat(result, ";");
dtostrf(humidity, 5, 2, catres);
strcat(result, catres);
Firmata.sendString(result);
return;
}
}
Единственное исправление, которое я нашел, выглядит действительно глупо, и оно не работает, значения не опускаются ниже нуля.
//Температура представляет собой 16 - разрядное целое число со знаком, в 10 раз превышающее фактическое значение в градусах Цельсия
int16_t temperatureTimesTen = (int16_t)((receivedDHTData[2] << 8) | receivedDHTData[3]);
float temperature = (float)(temperatureTimesTen) * 0.1f;
//Максимально возможная температура для DHT22 составляет 80 градусов Цельсия
if (temperature > 80) temperature = temperature - 3276.7f;
@Starter, 👍0
Обсуждение2 ответа
Глядя на лист данных (стр. 3), DHT22 использует, в отличие от DHT11 и в отличие от реализации во многих библиотеках, стандартный формат Base-2 для температуры. Таким образом, преобразование температуры должно быть таким же простым, как:
// Температура представляет собой 16 - разрядное целое число со знаком, в 10 раз превышающее фактическое значение в градусах Цельсия
int16_t temperatureTimesTen = (int16_t)((receivedDHTData[2] << 8) | receivedDHTData[3]);
float temperature = (float)(temperatureTimesTen) * 0.1;
Я еще не смог это подтвердить (не смог достаточно остудить свой датчик), но ваше наблюдение о том, что неправильные значения указывают на то, что они находятся в пределах -3300, указывает на тот же вывод.
Обновление
Мне нужно исправиться. Документация вводит в заблуждение, поэтому мое понимание ее было неверным. После того, как я, наконец, получил возможность по-настоящему протестировать свой DHT22 при отрицательных температурах (сейчас на улице идет снег), я нашел правильную формулу:
var temp = ((receivedDHTData[2] & 0x7F) << 8 | receivedDHTData[3]) * 0.1;
// if MSB = 1 we have negative temperature
temp = ((receivedDHTData[2] & 0x80) == 0 ? temp : -temp);
return temp; // in degrees celsius
Я постараюсь реализовать это решение. Я свяжусь с тобой, коротышка, после получения каких-либо результатов., @Starter
Это не помогло, значения, подобные-3275, все еще сообщаются., @Starter
Это странно. Можете ли вы попытаться распечатать значения " receivedDHTData[2] и
receivedDHTData[3]"? Значение байта в индексе 2 должно быть 255 (0xFF), если мое предположение верно., @PMF
действительно, это так: https://skr.sh/i/031121/YWJ4uO4E.jpg?download=1&name=Скриншот%2003-11-2021%2016:26:01.jpg, @Starter
Когда я вводлю в свой код именно эти значения, я получаю правильный результат. Может быть, это также проблема компилятора/платы? Я протестировал на плате AVR (Arduino Nano), @PMF
моя плата также является Arduino Uno на базе AVR. Я использую этот простой скетч: https://github.com/StarterCraft/Hyrex_AsQamm/blob/6eb87320997e0b2cf4a714d13f210cb1359f640e/AsQammArduino/DHTTest/DHTTest.ino, @Starter
и это последовательная версия кода, преобразованная в.println, о которой идет речь: https://github.com/StarterCraft/Hyrex_AsQamm/blob/6eb87320997e0b2cf4a714d13f210cb1359f640e/AsQammArduino/Features/DHTLifo.h, @Starter
Пожалуйста, используйте код, который я опубликовал выше, точно так, как он есть, заменив строки 71 и 72. Не сжимайте его в одну строку (два приведения в одной строке могут сбить с толку некоторых компиляторов)., @PMF
заменил линии, пожалуйста, проверьте., @Starter
ничего не изменилось после изменения этого утверждения на две строки., @Starter
Давайте [продолжим это обсуждение в чате](https://chat.stackexchange.com/rooms/131137/discussion-between-pmf-and-starter)., @PMF
Я только что видел https://stackoverflow.com/questions/69870078/visual-studio-c-has-different-comparison-results-for-uint16-and-uint32?noredirect=1#comment123516299_69870078 Может быть, это как-то связано. Может быть, вам стоит попробовать продлить регистрацию вручную?, @PMF
Не могли бы вы подтвердить свою информацию, заморозив датчик?, @Starter
Нет, у меня не было времени, чтобы проверить это еще раз. Я серьезно удивляюсь, почему вы получаете другие результаты, чем я. Я попробую еще раз., @PMF
@Starter Мне жаль, что это заняло так много времени, но я думаю, что приведенная выше формула теперь должна сработать. Данные отличались от того, что я ожидал получить из документации., @PMF
https://skr.sh/i/051221/kWFdQYc1.jpg?download=1&name=Скриншот%2005-12-2021%2014:27:41.jpg но это не помогло :<, @Starter
@Starter: Это похоже на то, что я изначально ожидал, но мое наблюдение показало другую бинарную модель для отрицательных температур. Вы уверены, что у вас есть DHT22? Это датчики с белым корпусом., @PMF
Да, я совершенно уверен. Это карточка товара от продавца Aliexpress, у которого я купил датчик. Я использую вариант Модуль DHT22
. Кроме того, не могли бы вы поделиться своими двоичными данными? https://github.com/StarterCraft/Hyrex_AsQamm/blob/29eb8f825140b98287feeb3cac1f1e4cc3ada88c/AsQammArduino/Features/DHTLifo.h#L72 является ли код производящим мой вывод, @Starter
Мои данные были [2] = 0x80 и [3] = 0x04 для -0.4°C. Ваш выглядит как закодированная база-2. Можете ли вы подтвердить, что правильная температура -2,5°C?, @PMF
Прямо сейчас, по данным местного поставщика прогнозов погоды (https://skr.sh/i/061221/qEcPFcRN.jpg), температура составляет около -9 градусов Цельсия, и это текущие двоичные данные: https://skr.sh/i/061221/fYC1gHnH.jpg, @Starter
Также, пожалуйста, исправьте свой синтаксис: в C++ нет "var", есть "auto"., @Starter
1111 1111 1111 0111-это -9 в стандартной кодировке base-2. Насколько мне известно, нет варианта DHT, использующего эту кодировку, тем более что все варианты обеспечивают температуру в десятых долях градуса. Есть ли у вас возможность получить набор данных с положительным измерением?, @PMF
можете ли вы предложить мне способ расшифровать это правильно?, @Starter
Боюсь, я не могу этого сделать, мой датчик установлен статически снаружи, @Starter
Я думаю, что должно быть сделано следующее: int32_t temperature = ((receivedDHTData[2] << 8) | receivedDHTData[3]); if (temperature & 0x8000) temperature |= 0xFFFF0000;
Результат тогда в целых градусах., @PMF
https://skr.sh/i/071221/bF9R2Yb2.jpg?download=1&name=Скриншот%2007-12-2021%2009:31:24.jpg Положительное чтение, @Starter
Каждый раз это становится все более запутанным :-( Как вы показываете, это декодируется правильно, а 100101b-это 37 десятичных знаков, которые можно (как указано) правильно разделить на 10, чтобы получить 3,7°. (Я предполагаю, что снаружи не 37°) У меня все больше и больше складывается впечатление, что у вас там какой-то китайский клон, который не соответствует официальным спецификациям. Я добавлю предложенный код для этого открытия выше, но это должно остаться догадкой., @PMF
https://skr.sh/i/141221/775SSTjY.jpg?download=1&name=Скриншот%2014-12-2021%2008:36:51.jpg умф, это довольно холодно. Я думаю, что мне нужно умножить его на 0,1, @Starter
Да, очевидно. Тогда, по крайней мере, это было бы последовательно. До сих пор не понимаю, почему мое первое предложение тогда не совпало. Тем не менее, я счастлив, что это, наконец, работает для вас., @PMF
Я столкнулся с проблемой получения значений -3200. Я поменял датчик на другой от того же производителя (tenstar robot на ali express) и значения второго датчика были в норме. Я положил рядом с датчиком обычный термометр, и показания оказались в пределах пары градусов при -15С. Я снабжаю свои датчики напряжением 3,3 В, что в некоторых случаях также может вызвать проблемы. Возможно, стоит попробовать 5 Вольт, чтобы увидеть разницу.
- Сокращение времени считывания показаний датчиков температуры DS18B20.
- DHT11 аналоговый или цифровой?
- Отправка аналоговых входных данных из последовательного порта в Google Таблицы
- Объединение кода для нескольких датчиков в одной программе
- Быстрые случайные логические значения
- Датчики Dht 11 дают нулевое значение
- Запуск прерываний с помощью DHT-11
- Шестнадцатеричный массив не складывается, как ожидалось
Это кажется немного глупым способом объединения байтов для формирования 16-битного значения, если вы спросите меня..., @Majenko
У меня точно такой же код в другой библиотеке. Завтра я попробую положить свой DHT22 в морозилку и посмотрю, что произойдет., @PMF
@Majenko, по крайней мере, эта штука работает, пока температура положительная. Теперь я жду, пока температура снаружи опустится ниже нуля, так как мой DHT22 установлен снаружи без возможности его перемещать. Может быть, изменение
температура *= -1;
на "температура *= -1,0 f;" поможет?, @StarterЭто вряд ли поможет, потому что в этот момент температура уже плавает., @PMF