0

The following code reads the DHT22 temperature and humidity values, assuming the pin value as the sensor's host pin.

When the temperature drops below 0°C, this code returns inadequate values in the range from -3200 to -3300 (most often the values range around -3275). When using a popular Adafruit library, the values are also incorrect. The code was not written by me, it originates from here, but the comments in the source are in Russian.

The problem is quite common, but there was no adequate solution provided in any case. It was mentioned in the Adafruit library issues quite a few times (here, here and here (related)), and on other websites. People even reported this issue on DHT11 and DHT21 sensors.

// "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 ms 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;
  }
}

The only fix I found looks really silly and it does not work, the values don't drop below zero.

// The temperature is a 16 bit signed integer, 10 times the actual value in degrees Celsius
int16_t temperatureTimesTen = (int16_t)((receivedDHTData[2] << 8) | receivedDHTData[3]);
float temperature = (float)(temperatureTimesTen) * 0.1f;

// The maximum possible temperature for an DHT22 is 80 degrees Celsius
if (temperature > 80) temperature = temperature - 3276.7f;
4
  • That seems to be a bit of a silly way of combining bytes to form a 16 bit value if you ask me...
    – Majenko
    Oct 30, 2021 at 17:18
  • I've got the exactly same code in another library. I'll try to put my DHT22 into the freezer tomorrow and see what happens.
    – PMF
    Oct 30, 2021 at 21:12
  • @Majenko, at least this thing manages to work while the temperature is positive. I now wait until the temperature outside drops below zero, since my DHT22 is installed outside without a possibility to move it. Maybe, changing temperature *= -1; to temperature *= -1.0f; will help?
    – Starter
    Oct 31, 2021 at 4:55
  • That's unlikely to help, because temperature is already a float at that point.
    – PMF
    Oct 31, 2021 at 7:54

2 Answers 2

0

Looking at the data sheet (Page 3), the DHT22 uses, unlike the DHT11 and unlike the implementation in many libraries, a standard Base-2 format for the temperature. So converting the temperature should be as easy as:

// The temperature is a 16 bit signed integer, 10 times the actual value in degrees Celsius
int16_t temperatureTimesTen = (int16_t)((receivedDHTData[2] << 8) | receivedDHTData[3]);
float temperature = (float)(temperatureTimesTen) * 0.1;

I was not able to confirm this yet (couldn't get my sensor cold enough), but your observation that the wrong values report as being in the order of -3300 point to the same conclusion.

EDIT

I need to correct myself. The documentation is misleading, therefore my understanding of it was incorrect. After I finally got the possibility to really test my DHT22 in negative temperatures (it's snowing outside now), I found the correct formula to be:

int 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

EDIT 2

For this particular sensor, decoding seems to be as follows. It appears that on negative temperatures, the resolution is only 1 degree, not 0.1 degrees.

float temperature = 0;
int32_t temp = ((receivedDHTData[2]) << 8 | receivedDHTData[3]);
// if MSB = 1 we have negative temperature
if (temp & 0x8000)
{
    temp = temp | 0xFFFF0000; // sign-extend
    temperature = temp; // Convert to negative whole-degrees
}
else
{
    temperature = (float)temp / 10.0f;
}
29
  • I will try to implement this solution. I will get back to you shorty after getting any results.
    – Starter
    Nov 1, 2021 at 10:58
  • That didn't help, the -3275-like values are still reported.
    – Starter
    Nov 3, 2021 at 12:07
  • That is weird. Can you try to print out the values of receivedDHTData[2] and receivedDHTData[3]? The byte value at index 2 should be 255 (0xFF) if my assumption is right.
    – PMF
    Nov 3, 2021 at 12:22
  • indeed, it does: skr.sh/i/031121/…
    – Starter
    Nov 3, 2021 at 13:26
  • When I feed my code with exactly these values I get the correct result. Maybe this is also a compiler/board issue? I have tested on an AVR board (Arduino Nano)
    – PMF
    Nov 3, 2021 at 13:37
0

I ran into this issue of getting -3200 values. I swapped the sensor with another one from the same vendor (tenstar robot on ali express) and the values for the 2nd sensor were normal. I put a regular thermometer next to the sensor and the reading are within a couple of degree of each other at -15c. I am supplying my sensors with 3.3 volts which in some cases can also cause issues. Might be worth trying 5 volts to see it that makes any difference.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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