Forty bits and a checksum
Five bytes come back: humidity, temperature and a checksum. The checksum proves the bytes arrived intact — and nothing at all about whether you decoded them right, which is why declaring the wrong sensor produces a confident wrong number rather than an error.
Five bytes
Every reading is forty bits, most significant bit first, in the same five bytes every time. Play it through, then switch what your sketch thinks it is talking to.
The first two bytes are the humidity, the next two the temperature, and the
fifth is the low eight bits of the sum of the other four. If that sum does not
match, the library throws the whole frame away and hands you nan.
How a bit gets its value
There is no clock line, so the length of a pulse is the data. Every bit opens with the sensor holding the line low for 50 µs; then it lets the line up, and how long it stays up is the value:
- about 26–28 µs high is a 0
- about 70 µs high is a 1
Adafruit's library does not actually measure microseconds. It counts loop iterations for the low half and for the high half and compares the two: if the high count is bigger than the low count, the bit is a 1. That is a neat trick, because it works identically on a 16 MHz Uno and a 240 MHz ESP32 without either being told its own clock speed — and it is also why interrupts have to be off while the counting happens.
The same bytes, two meanings
This is the part worth remembering, because it is silent when it goes wrong.
A DHT22 packs each number into two bytes as tenths. 51.2 % is 512, which is
0x02 0x00. 23.4 °C is 234, which is 0x00 0xEA.
A DHT11 puts whole units in the first byte and, on nearly every part, zero
in the second. 51 % is 0x33 0x00. 23 °C is 0x17 0x00.
Both frames pass their checksum. Both are exactly forty bits. Nothing on the wire says which sensor sent them. If your sketch declares the wrong one, the library reports success and prints a number that is wrong by two orders of magnitude — and if you are not watching, "the humidity is 1305 %" reads as a broken sensor rather than as a wrong constant.
What the checksum is actually worth
It catches the failure that this protocol is prone to: a bit whose pulse got stretched or clipped because something interrupted the counting. That happens, and when it does you want the frame thrown away rather than averaged in.
What it cannot catch is anything upstream of the bytes — the wrong sensor type, the wrong pin, a sensor reading its own warm board. Those all produce perfectly valid frames.
When it does not work
Then the bytes are fine and the decoder is wrong. A DHT22's five bytes read as a DHT11 come out near 2 % and 0 °C; a DHT11's read as a DHT22 come out in the hundreds. Change the second argument to the DHT constructor and nothing else.
The top bit of the third byte is the sign, and the rest of the two bytes is the magnitude in tenths. It is sign and magnitude, not two's complement, so −0.1 °C and +0.1 °C differ by one bit at the top and by nothing else. The library handles it; it matters if you ever decode the bytes yourself.
Most DHT11s send zero in both of them. The format has room for tenths and the library reads them, but the part's stated resolution is 1 °C and 1 % RH and that is what nearly all of them produce. It is not a bug and there is no setting for it.
The library already does — a frame that fails it is discarded and you get nan, with no way to see the bytes. If you want them, the library has a debug mode, or you can read the pin yourself with the timings in the datasheet. For ordinary use, nan is the checksum failing.
The sensor's floor, the library's cache, and why a tight loop shows a stale number.
How often you may ask →Edit this page — content/books/dht22/forty-bits-and-a-checksum.mdx
Questions about this product
See what other owners have asked, and read their solutions.
This page covers several products. Choose yours to see the right discussions.
Discuss this article
Ask about this page. The answer stays here, on the page it belongs to, for whoever hits the same wall next.