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;