Код считывания датчика 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;

, 👍0

Обсуждение

Это кажется немного глупым способом объединения байтов для формирования 16-битного значения, если вы спросите меня..., @Majenko

У меня точно такой же код в другой библиотеке. Завтра я попробую положить свой DHT22 в морозилку и посмотрю, что произойдет., @PMF

@Majenko, по крайней мере, эта штука работает, пока температура положительная. Теперь я жду, пока температура снаружи опустится ниже нуля, так как мой DHT22 установлен снаружи без возможности его перемещать. Может быть, изменение температура *= -1; на "температура *= -1,0 f;" поможет?, @Starter

Это вряд ли поможет, потому что в этот момент температура уже плавает., @PMF


2 ответа


-1

Глядя на лист данных (стр. 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


0

Я столкнулся с проблемой получения значений -3200. Я поменял датчик на другой от того же производителя (tenstar robot на ali express) и значения второго датчика были в норме. Я положил рядом с датчиком обычный термометр, и показания оказались в пределах пары градусов при -15С. Я снабжаю свои датчики напряжением 3,3 В, что в некоторых случаях также может вызвать проблемы. Возможно, стоит попробовать 5 Вольт, чтобы увидеть разницу.

,