**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.
