Using the PIO to Interface with the DHT11 and DHT22 sensors
The DHT11 and DHT22 are popular sensors for measuring temperature and humidity. While connecting these sensors to a microcontroller is very simple, communicating with them demands generating and measuring times of the order of microseconds. Let's see how the RP2040's PIO can help us.
How the DHT11 and DHT22 Communicates
Communication between these sensors and a microcontroller uses a single bi-directional signal. There is an internal pull-up resistor that forces this signal high when it is not driven by the microcontroller or the sensor. The image bellow shows the data pin when a reading is done:
![]() |
| Left: full reading, Right: zoom on the first byte |
To request a reading, the microcontroller must drives the pin low for at least 18ms (DHT11) or 1ms (DHT22). After that, it reconfigure the pin for input to get the sensor response,
- 2 bytes for humidity
- 2 bytes for temperature
- 1 byte for error check (it should be the sum of the previous four bytes)
The encoding of the values is different for the two models.
The DHT11 can read temperatures form 0 to 50C, with a 2 degree precision and send the values as separated integer and decimal parts (the decimal part for temperature should be zero):
- If the two bytes for humidity are 86 and 2, the humidity is 86.2%
- If the two bytes for temperature are 19 and 0, the temperature is 19.0C
- If the two bytes for humidity are 2 and 86, , the humidity is (2*256+86)*0.1 = 59.8%
- If the two bytes for temperature are 1 and 8, the temperature is (1*256+8)*0.1 = 33.6ºC.
- If the two bytes for temperature are 129 e o 8, the temperature is -(1*256+8)*0.1 = -33.6ºC.
- Put the pin in output mode
- Wait for the processor to write a value
- Keep the pin low by the time defined by this value
- Change the pin for input
- Wait for the acknoledge
- Read bits
- Send back each byte received
- Set a clock of 1.4MHz (so 1 cycle is about 0,7 usec)
- Select the pin connected to the sensor for input, set and jump condition
- Configures the PIO
- Starts the PIO
- Writes the initial pulse value (969 if DHT11 or 54 if DHT22)
- Reads 5 bytes from the PIO
- Stops the PIO
import utime
import rp2
from rp2 import PIO, asm_pio
from machine import Pin
# PIO program
# automatically pushes each 8 bits of received data in the FIFO
@asm_pio(set_init=(PIO.OUT_HIGH),autopush=True, push_thresh=8)
def DHT_PIO():
# wait for a request
pull()
# keeps pin in 0 for the requested time
set(pindirs,1) # set pin to output
set(pins,0) # set pin low
mov (x,osr)
label ('waitx')
nop() [25]
jmp(x_dec,'waitx') # wait time*26/clock=x
# reads the acknoledge
set(pindirs,0) # change pin to input
wait(1,pin,0) # wait for high level
wait(0,pin,0) # wait for initial pulse
wait(1,pin,0)
wait(0,pin,0) # wait for first bit
# read the bits
label('readdata')
wait(1,pin,0) # wait for pin to go high
set(x,20) # register x is the timeout for low
label('countdown')
jmp(pin,'continue') # count while pin is high
# pin went low before timeout -> bit 0
set(y,0)
in_(y, 1) # puts a zero in result
jmp('readdata') # read next bit
# pin is still high
label('continue')
jmp(x_dec,'countdown') # decrement count
# timeout -> bit 1
set(y,1)
in_(y, 1) # puts a one in result
wait(0,pin,0) # wait for pin get back to low
jmp('readdata') # ler o próximo bit
DHT11 = 0
DHT22 = 1
class DHT:
# Construtor
# dataPin: data pin
# model: DHT11 ou DHT22
# smID: state machine id
def __init__(self, dataPin, model, smID=0):
self.dataPin = dataPin
self.model = model
self.smID = smID
self.sm = rp2.StateMachine(self.smID)
self.lastreading = 0
self.data=[]
# read sensor
def read(self):
data=[]
self.sm.init(DHT_PIO,freq=1400000,set_base=self.dataPin,
in_base=self.dataPin,jmp_pin=self.dataPin)
self.sm.active(1)
if self.model == DHT11:
self.sm.put(969) # wait 18 miliseconds
else:
self.sm.put(54) # wait 1 milisecond
for i in range(5): # read 5 bytes
data.append(self.sm.get())
self.sm.active(0)
total=0
for i in range(4):
total=total+data[i]
if data[4] == (total & 0xFF):
# checksum ok, salvar os dados
self.data = data
self.lastreading = utime.ticks_ms()
return True
else:
return False
# get data by reading or using stored data
def getData(self):
# make sure we have some data
while len(self.data) == 0:
if not self.read():
utime.sleep_ms(2000)
# only read if more than 2 seconds since previous reading
agora = utime.ticks_ms()
if self.lastreading > agora:
self.lastreading = agora # counter wraped up
if (self.lastreading+2000) < agora:
self.read()
# returns the humidity
def umidade(self):
self.getData()
if self.model == DHT11:
return self.data[0] + self.data[1]*0.1
else:
return ((self.data[0] << 8) + self.data[1]) * 0.1
# returns temperature
def temperature(self):
self.getData()
if self.model == DHT11:
return self.data[2] + self.data[3]*0.1
else:
s = 1
if (self.data[2] & 0x80) == 1:
s = -1
return s * (((self.data[2] & 0x7F) << 8) + self.data[3]) * 0.1
#main program
dht_data = Pin(16, Pin.IN, Pin.PULL_UP)
dht = DHT(dht_data, DHT22, 0)
while True:
print("Humidity: %.1f%%, Temperature: %.1fC" %
(dht.umidade(), dht.temperature()))
utime.sleep_ms(3000)


Comments
Post a Comment