**This is an old revision of the document!**
Table of Contents
DHT22 Protocol
Introduction
The DHT22 module is a low-cost digital temperature and humidity sensor. It utilizes a capacitive humidity sensor and a thermistor to measure the surrounding air and outputs a digital signal via a single data pin. It's known for its wider measurement range and higher accuracy compared to its predecessor, the DHT11.
Key features include:
- Measures both temperature (from -40°C to 80°C with an accuracy of ±0.5°C) and humidity (from 0% to 99.9%RH with an accuracy of ±2-5%).
- Digital output via a single-wire interface, simplifying communication with microcontrollers like Arduino and Raspberry Pi.
- Operates on a voltage range of 3.3V to 6V.
- Has a relatively low power consumption.
- Offers good long-term stability.
- Typically comes on a small PCB with necessary pull-up resistors, making it easy to integrate into projects.
- Has a sampling rate of no more than 0.5 Hz (once every 2 seconds).
- Suitable for indoor environmental monitoring, weather stations, and HVAC systems.
SDA
The DHT22 communicates using a proprietary single-wire serial protocol. Here's a breakdown of the process:
- Start Signal from Microcontroller: The microcontroller initiates communication by pulling the data line low for a specific duration (typically > 800µs to 1ms). This signals the DHT22 to wake up and prepare to send data.
- Response from DHT22: After the start signal, the DHT22 pulls the data line low for about 80µs and then high for another 80µs. This serves as a response signal indicating that the sensor is ready to transmit data.
- Data Transmission (40 bits): The DHT22 transmits 40 bits of data representing humidity and temperature readings, followed by a checksum bit. Each bit is transmitted as follows: * The data line is first pulled low for approximately 50µs. * Then, it's pulled high for a varying duration: * 26-28µs high indicates a “0” bit. * 70µs high indicates a “1” bit.
- Data Format (5 bytes): The 40 bits of data are structured into five 8-bit bytes: * Byte 1: Humidity integer part (0-99) * Byte 2: Humidity decimal part (always 0 for DHT22) * Byte 3: Temperature integer part (-40 to +80) * Byte 4: Temperature decimal part (always 0 for DHT22) * Byte 5: Checksum (sum of the first four bytes, modulo 256)
- End of Transmission: After transmitting all 40 bits, the DHT22 releases the data line, and it's pulled high by an external pull-up resistor, returning to the idle state.
The microcontroller needs to carefully time these pulses to correctly interpret the data sent by the DHT22. Libraries for popular platforms like Arduino and Raspberry Pi handle these timing-critical operations.
am2302-en-aosong.pdf This Aosong document has the correct data, although it is a creepy English translation.
Implementation
Testing with a Logic Analyser (PulsView)
Pin P1.0 is bidirectional. It sends the start pulse and then receives the 40 data bits from the sensor module.
The code includes test pulses on pin P1.6 to check the timing. You can remove those.
The time-critical part is assembler code. The surrounding code was written in noForth. It uses tools.f
and noforth_m_asm
from the noForth library. These must be compiled into noForth on the MSP430G2553 beforehand.
Sensor modules vary in their output speed due to manufacturing and temperature conditions. 1/4 of the response pulse is approximately the waiting time to read the bit. Therefore, the code adapts to the observed speed. A 'while' loop is used for this, but its execution takes about twice as long as the wait loop for reading the bit. The waiting time from 'moon' is therefore simply doubled to make it fit.
Pseudocode
Function: Pio ( -- ) \ set port I/O function. SDA --> P1.0 Function: wakeup ( -- ) \ notify sensor to prepare the data Function: @data ( -- sun moon hum tmp chk chksum ) \ get sensor data get response time of sensor, store in register 'sun' (just for testing) get startpulse duration, store in register 'moon' 40 loop read bit using 'moon' based delay lshift bit into array xx yy zz 08 loop lshift array \ adjust xx yy zz --> hum tmp chk calculate chksum Function: (dht22) ( -- sun moon hum tmp chk chksum ) \ wake up and read sensor Function: dht22 ( -- ) \ print temperatur and humidity Function: test ( n -- ) \ multiple readings n loop dht22
Source Code
DHT22 with Launchpad MSP430G2553
\ Read DHT22. (bitbang) \ TI MSP430G2553 Launchpad with noForth mv 2553 240101 (* To do: timeout History V0065 - Temperature in tenths of a degree. mk 20250424 V0064 - Loops named with ( A ) ... ( D ). Variants of loops B and D studied. This loop variation of B and D work well. Comments revised. mk 20250423 05:25 V0063 - Cleaned up the code. Data output reduced to the essentials. mk 20250421 02:04 V0062 - Works well, output rounded to whole digits. mk 20250419 21:51 Previous versions: Successive approximation to the solution. Patch millisecond to 100 microseconds. See: config2553.f 00c7 10B2 rom! hex 10B2 DMP After COLD this value is active. addr acronym registername 020 P1IN Input 021 P1OUT Output 022 P1DIR Direction 023 P1IFG Interupt Flag 024 P1IES Interrupt Edge Select 025 P1IE Interrupt Enable 026 P1SEL Port Select 041 P1SEL2 Port Select2 027 P1REN Resistor Enable *) asm\ hex \ helper code cls ( -- ) s0 # sp mov next end-code \ clear stack code p1H #1 021 & .b bis next end-code \ set lines code p1L #1 021 & .b bic next end-code code p6H 40 # 021 & .b bis next end-code code p6L 40 # 021 & .b bic next end-code \ Assign DHT22 data line to pin p1.0, controll pin to p1.6 : Pio ( -- ) \ set I/O function 41 027 *bic \ p1ren off 41 025 *bic \ p1ie disable interrupt 41 026 *bic \ P1SEL I/0 41 041 *bic \ P1SEL2 I/0 41 022 *bis \ set out for P1.0, P1.6 ; : wakeup ( -- ) \ notify sensor to prepare the data pio p1H p6H 1 ms p1L 10 ms ( p6L ) p1H ; \ 1 ms == 100┬Ás ! Code @data ( -- sun moon hum tmp chk chksum ) \ read sensor data \ get response time #1 022 & .b bic \ p1,0 IN #0 sun mov #1 024 & .b bis \ FALLING EDGE\__ #1 023 & .b bic \ clear P1IFG 40 # 021 & .b bic \ p6L\___ (for logic analyser) begin, ( A ) \ wait for edge #1 023 & .b bit \ test bit \ Z: Set if result is zero, reset otherwise \ C: Set if result is not zero, reset otherwise (.NOT. Zero) cc? while, #1 sun add repeat, 40 # 021 & .b bis \ ___/p6H (for logic analyser) tos sp -) mov sun tos mov ( -- sun ) \ get startpulse time #0 moon mov #1 024 & .b bic \ ___/ RISING EDGE #1 023 & .b bic \ clear P1IFG 40 # 021 & .b bic \ p6L\___ (for logic analyser) begin, ( B ) \ wait for edge #1 023 & .b bit \ test bit \ Z: Set if result is zero, reset otherwise \ C: Set if result is not zero, reset otherwise (.NOT. Zero) cc? while, #1 moon add repeat, 40 # 021 & .b bis \ ___/p6H (for logic analyser) tos sp -) mov moon tos mov ( -- moon ) \ read 40 bits to array #0 xx mov \ init array #0 yy mov #0 zz mov dm 40 # day mov begin, ( C ) #1 023 & .b bic \ clear P1IFG begin, \ wait for edge #1 023 & .b bit \ test IFG \ Z: Set if result is zero, reset otherwise \ C: Set if result is not zero, reset otherwise (.NOT. Zero) cs? until, \ wait to read bit moon w mov w w add \ 2*moon, Loop D is about twice as fast as loop B. 40 # 021 & .b bic \ p6L\___ (for logic analyser) begin, ( D ) #1 w sub 0=? until, \ read bit 40 # 021 & .b bis \ __/p6H (for logic analyser) 020 & w .b mov \ read data line: bit H or L #1 w bia \ get bit \ tos sp -) mov \ w tos mov ( ... -- ... bit ) \ test \ #1 sr bic \ clear carry bit \ shift left all bits zz zz add yy yy addc xx xx addc w zz bix \ = xor : write data bit #1 day sub 0=? until, \ 40 bits are read \ adjust bits 8 # day mov \ lshift 40 bits up 8x -> xx=hum yy=tmp begin, zz zz add yy yy addc xx xx addc #1 day sub 0=? until, \ push data to stack tos sp -) mov xx tos mov ( -- xx ) tos sp -) mov yy tos mov ( -- yy ) zz swpb \ get upper byte tos sp -) mov zz tos mov ( -- zz.b ) \ calculate checksumme #0 w mov xx w .b add xx swpb xx w .b add yy w .b add yy swpb yy w .b add tos sp -) mov w tos mov next end-code \ display decimal : (dht22) ( -- sun moon hum tmp chk chksum ) cls wakeup @data ; : decemit ( c -- ) \ print c from DEC special character set hx 1B emit [char] ( emit [char] 0 emit emit hx 1B emit [char] ( emit [char] B emit \ back to normal output ; : .tmp ( tmp -- ) 10 /mod 3 .r [char] , emit . [char] f decemit [char] C emit space ; \ ja, geht. : .hum ( hum -- ) \ rounded to whole percent 10 / . ." %rel" space ; : dht22 ( -- ) \ print temperatur and humidity cr (dht22) ( 2 + ) \ add 2 for 'else' part = if ." chk " else ." chksum error" cls exit then .tmp space space .hum space space \ ( sun moon -- ) drop drop ( sun moon -- ) . . \ testing ; (* The DHT22 starts its next measurement AFTER the query and saves the values. These are output with the next query, so they are old. For up-to-date values, query twice. There must be more than 2 seconds between each query, otherwise the DHT22 will not respond again. *) : test ( n -- ) >r cr ." discard old values..." (dht22) cls cr ." reading current data:" r> 0 do dm 40000 ms \ wait until the sensor is ready again. dht22 loop ; shield nn\ freeze ( finis)
More information on the MSP430G2553
- MSP430G2553 datasheet SLAS735J.PDF, port data on page 49ff
Source code editor
I like Notepad Next for Linux, a cross-platform, reimplementation of Notepad++. https://github.com/dail8859/NotepadNext
Terminal
To work with the embedded noForth system I use e4thcom by Manfred Mahlow. https://wiki.forth-ev.de/doku.php/projects:e4thcom
Logic Analyser
AZDelivery Logic Analyzer 8 CH, 24MHz. (Amazon) PulseView is a Qt-based logic analyzer and oscilloscope GUI for sigrok.