DHT22 on the cheap
To be honest, I’m not a fan of the DHT family of sensors. That’s probably why I don’t think there has ever been anything here about this sensor. And I have the impression that these sensors are still “flying around” somewhere in the DIY world. Wherever you look, they are in various starter kits, various tutorial sites are full of examples of DHT use.
After thinking about it for a while, I see one and only one advantage of DHT over sensors like SHT, BME, BMP and others. The specific communication protocol on only one pin allows it to be plugged into systems with missing free IO. Admittedly, if you are already using I2C in your design this is no longer an advantage, as you can easily add some temperature/humidity sensor to the bus.
Well, but let’s say that using e.g. ESP01s which has only 2 IOs (well, unless TX/RX you give up then there are 4) using DHT might make sense.
The second reason why DHT was on my radar was that a so-called KOPAJ! bag from our Chinese broker was lying on my desk and DHT was also in it…. What is this bag? The broker sometimes sends us various samples and they are often functional copies of various sensors. One time the sticker on the pouch was with a typo that the sensor from COPAY CHIP. And that’s how it stayed – the COPAY in us are generally clones.
As I mentioned a DHT22 clone has been lying there for some time. It is supposed to be functionally and size identical, but the price is more favorable. Briefly done tests show that indeed, it works similarly and maybe sometimes even better (O tempora, o mores! clones are better?).
Results comparison

To start with, I put 4 DHT22s on a contact board – two originals, two clones, then I added an SHT31 as a “reference” sensor. The whole thing presents itself as above in the photo. Without further ado – examples of the results:




As you can see, the results generally converge and are reproducible. This is no elaborate test, the similarity is sufficient. That’s why it’s been out for a few days now – the DHT22 clone module.
How to connect to Arduino/ESP
Connecting and using the DHT22 is quite simple. Connection – as I mentioned the advantage of DHTxx is the use of only one data pin. You just need to connect it to any digital IO.

The shot from the back shows clearly how to connect – the blue jumper wires connect ground and the green ones connect power. The JW-75 wires connect data signals to selected digital pins. On top lies the I2C to NAM cable, with SHT31 soldered on. He is connected to the I2C bus – (temperature measurements using SHT in this article I do not describe).
Here’s a little note about voltages. A long time ago (in 2012) we wrote an article about connecting devices operating at different voltage levels (on Polish blog). Basically, it’s still valid, because the principles haven’t changed, but remember – you can supply the DHT22 module with a voltage from 3 to 5V. The signals at the output of the module signaling 1 will be equal to the supply voltage. If you have a microcontroller operating at 3.3V whose inputs are sensitive to 5V remember to supply the DHT22 also with 3.3V, there will be no problem.
If you look closely at the previous photo, you’ll see that I made a mistake by connecting the 5V power supply to the sensors. This means that the 5V voltage was transmitted to the ESP8266 IO when DHT was sending 1 on data bus. According to the ESP8266 datasheet, the maximum input voltage on the IO pin is 3.6V. As practice shows, the ESP8266 usually withstands 5V (this Wemos still works), but just because it usually works, doesn’t mean it will always work. When designing something on the ESP8266, remember this!
Libraries and reading data from DHT22 on Arduino/ESP
When we already have the sensor connected, it remains to use the appropriate library to get the data. There are quite a few libraries, I used the ones from Adafruit for benchmark testing. Why the plural? Because Adafruit has made a lot of libraries available for Arduino under an open source license, and to make it easier/standardized, it will divide it into several levels of abstraction. This makes part of the code common to all libraries, but in practice it means that you have to install:
lib_deps =
adafruit/Adafruit Unified Sensor@^1.1.15
adafruit/DHT sensor library@^1.4.6
adafruit/Adafruit DHT Unified@^1.0.0
This snippet is from platformio.ini
, because as blog readers probably know well, this is the environment I use on a daily basis. If you use the Arduino IDE, you will need to manually indicate which libraries to install.
Test code I wrote on the fly, now I’m looking at these libraries this way and it’s possible that the adafruit/DHT sensor library alone will suffice, because although the description mentions a dependency on Adafruit Unified, as I’ve reviewed the code using just DHT.h
I guess is without having to install Unified.
I connected those aforementioned 4 sensors to a Wemos D1 mini, each to a separate pin. In code it looks like this:
#include <DHT_U.h>
DHT_Unified dht_or_1(D2, DHT22);
DHT_Unified dht_or_2(D3, DHT22);
DHT_Unified dht_cl_1(D6, DHT22);
DHT_Unified dht_cl_2(D5, DHT22);
The naming of the variables is probably clear – or
are the original DHT22, cl
are the clones. The first argument is the pin number to which the DHT is connected. When using ESP remember to use Dx
labels because the numeric value denotes the GPIO no. of ESP8266, if you are using Arduino then just give the pin numbers. That is, D2 for ESP8266, simply 2 for Arduino.
The second argument is the type of sensor. Because in addition to DHT22, we have DHT11 and even rarer varieties like DHT21.
In the setup
function, you need to start working with the sensor by calling begin
:
dht_cl_1.begin();
dht_cl_2.begin();
dht_or_1.begin();
dht_or_2.begin();
Now we will come across the effects of this creation by Adafruit of common abstractions in sensor handling. “Normally” one reads a value by some read and here we have a slightly more complicated procedure. But only a little.
Abstract introduces a so-called event (event). Wanting to read the temperature, you need to draw the event for the temperature. Since in my case I had 4 sensors, I wrote a helper function that takes as arguments an object of type DHT_Unified
and a structure in which to store the results.
void readSensorVal(DHT_Unified dht, Readings &s) {
sensors_event_t event;
bool valid = true;
dht.temperature().getEvent(&event);
if (isnan(event.temperature)) {
valid = false;
} else {
s.t = event.temperature;
}
// Get humidity event and print its value.
dht.humidity().getEvent(&event);
if (isnan(event.relative_humidity)) {
valid = false;
} else {
s.h = event.relative_humidity;
}
s.valid = valid;
}
That is, event
is an auxiliary variable that stores the data of each event (temperature or humidity reading. The temperature is read by dht.temperature().getEvent(&event)
. By calling my function readSensorVal
sequentially as the first argument, I pass dht_cl_1
, dht_cl_2
, and so on. This way, the function reads data from subsequent sensors.
Abstraction from Adafruit assumes that a “generic” sensor has different types and can return different data. Therefore, for temperature we have dht.temperature().getEvent(&event)
and to download humidity data dht.humidity().getEvent(&event)
. When called, we can check if the value read is a number (isnan
returns true
if the argument is not a number – IS Not A Number) – if not, the entire reading is marked as invalid.
Readings
is a structure that has two float fields for temperature and humidity and a flag whether the reading is correct.
typedef struct {
float t;
float h;
bool valid;
} Readings;
I won’t post the entire program here, which cycles the results into my working Influx, but the actual construction of the string to send to the database looks like this:
if (millis() - last_send >= INTERVAL) {
String postData;
postData = String(MEASUREMENT_NAME) + String(F(",host=esp8266-")) + String(ESP.getChipId()) + F(" ");
readSensorVal(dht_cl_1, s);
if (s.valid)
postData += F("cl1_t=") + String(s.t) + F(",cl1_h=") + String(s.h);
readSensorVal(dht_cl_2, s);
if (s.valid)
postData += F(",cl2_t=") + String(s.t) + F(",cl2_h=") + String(s.h);
readSensorVal(dht_or_1, s);
if (s.valid)
postData += F(",or1_t=") + String(s.t) + F(",or1_h=") + String(s.h);
readSensorVal(dht_or_2, s);
if (s.valid)
postData += F(",or2_t=") + String(s.t) + F(",or2_h=") + String(s.h);
sendToDB(postData);
last_send = millis();
}
Just one more note – in the definition of the readSensorVal
function:
void readSensorVal(DHT_Unified dht, Readings &s) {....}
The second argument is passed by reference (character &) – this ensures that the data written to this structure inside the function is available upon return.
About one sensor to rule them all, that is, Unified Sensor from AF
I am tempted to rewrite this code using only the AF/DHT library without this abstraction of a “single” sensor. I feel that for such simple tasks this is a better solution and the code is more readable even for a beginner.