Interfacing the DHT11/DHT22 Humidity/Temperature sensor
Posted: Thu Feb 18, 2016 11:52 am
The DHT11/DHT22 are well known Humidity and Temperature sensors. But I couldn't find any example of using these devices with the WiPy. So I developed my own interface drivers. The communication protocol is a sort of one wire protocol, but unique to the DHT11/DHT22 devices. The site http://www.uugear.com/portfolio/dht11-h ... or-module/ gives a good overview of the DHT11 and the communication protocol used. The DHT 22 uses the same protocol, but the interpretation of the result differs.
The protocol description indicates that we have to discriminate between high pulses of 28 us and 70 us. But if we use a loop to read an input pin and store its value:it costs around 20 us. So there is no time to do anything else in this loop. Therefore I decided to use a method where I first sample the input stream. Only after the end of the stream, I will convert the samples to bits and assemble the message.
In order to read the message from the DHT11/DHT22 we need an I/O pin with open drain, assuming an external pull-up resistor. In the examples I use 'GP4'. De code for the sample function is:Notice that we initiate the transfer by pulling the pin low for 20 ms. And notice that we disable interrupts while sampling the input. Otherwise we would not succeed in reading in a valid message. On the other hand the interrupt is disabled for around 6 ms.
We now have collected a list of sample values that we need to convert to bits. As indicated a 0-bit is encoded by a low followed by a short high, and a 1-bit as a low followed by a long high. This means we need to count the consecutive high samples (indicated by the value 1). But first we have to skip a start low pulse followed by a high pulse. The function that does this all is:Since we don't know if we collected enough samples, we use the try - except construct to catch errors in the index search we use next. We need to detect 40 bits, i.e. the length of 40 high values. We do this by searching first for the index of the first 1, and then searching for the index of the next 0. The difference is the number of 1's, and is a measure for the width of the pulse. For the time being we just store this count. We are ready when collected 40 counts for 5 bytes of 8 bits. If we run out of samples before that, an exception will occur and we return the impossible value of [0xff,0xf,0xff,0xff].
After counting the consecutive number of 1's for 40 bits we now need to determine the exact value of each bit. This is a simple operation. If the count is larger than 2, the bit value is 1, and otherwise the bit value is 0. The second part of the convert function does this:The conversion takes place in the double for-loop. Then a simple redundancy check follows. The sum mod 256 of the first 4 bytes should be equal to the last byte. If not then we have an error, probably due to a communication failure. Notice that we only have to return the first 4 bytes then.
Basically this is all we need to read humidity and temperature values from the DHT11 and DHT22 devices. But since the DHT22 gives a different meaning to the 4 byte result, we need to present the result differently, depending on the device. I therefore declared 2 additional functions:These functions are more or less self-explained. Notice that for the DHT22 the result should be interpreted as two 16 bit values giving humidity and temperature in a one decimal resolution, i.e. xx,x % RH and yy,y ⁰C.
The protocol description indicates that we have to discriminate between high pulses of 28 us and 70 us. But if we use a loop to read an input pin and store its value:
Code: Select all
for i in range(100):
dat[i] = pin()
In order to read the message from the DHT11/DHT22 we need an I/O pin with open drain, assuming an external pull-up resistor. In the examples I use 'GP4'. De code for the sample function is:
Code: Select all
from machine import Pin, enable_irq, disable_irq
dat=Pin('GP4', Pin.OPEN_DRAIN)
dat(1)
def getval(pin) :
ms = [1]*300
pin(0)
time.sleep_us(20000)
pin(1)
irqf = disable_irq()
for i in range(len(ms)):
ms[i] = pin() ## sample input and store value
enable_irq(irqf)
return ms
We now have collected a list of sample values that we need to convert to bits. As indicated a 0-bit is encoded by a low followed by a short high, and a 1-bit as a low followed by a long high. This means we need to count the consecutive high samples (indicated by the value 1). But first we have to skip a start low pulse followed by a high pulse. The function that does this all is:
Code: Select all
def decode( inp):
res= [0]*5
bits=[]
ix = 0
try:
if inp[0] == 1 : ix = inp.index(0, ix) ## skip to first 0
ix = inp.index(1,ix) ## skip first 0's to next 1
ix = inp.index(0,ix) ## skip first 1's to next 0
while len(bits) < len(res)*8 : ##need 5 * 8 bits :
ix = inp.index(1,ix) ## index of next 1
ie = inp.index(0,ix) ## nr of 1's = ie-ix
bits.append(ie-ix)
ix = ie
except:
return( [0xff,0xff,0xff,0xff])
## … second part of function
After counting the consecutive number of 1's for 40 bits we now need to determine the exact value of each bit. This is a simple operation. If the count is larger than 2, the bit value is 1, and otherwise the bit value is 0. The second part of the convert function does this:
Code: Select all
def decode( inp):
## … first part of the function
for i in range(len(res)) :
for v in bits[i*8 : (i+1)*8] : #process next 8 bit
res[i] = res[i]<<1 ##shift byte one place to left
if v > 2 : res[i] = res[i]+1 ##and add 1 if lsb is 1
if (res[0]+res[1]+res[2]+res[3])&0xff != res[4] : ##parity error!
print("Checksum Error")
res= [0xff,0xff,0xff,0xff]
return(res[0:4])
Basically this is all we need to read humidity and temperature values from the DHT11 and DHT22 devices. But since the DHT22 gives a different meaning to the 4 byte result, we need to present the result differently, depending on the device. I therefore declared 2 additional functions:
Code: Select all
def DHT11(pin) :
res = decode(getval(pin))
print('Humidity = {},{}'.format(res[0],res[1]))
print('Temperat = {},{}'.format(res[2],res[3]))
#
def DHT22(pin) :
res = decode(getval(pin))
hum = res[0]*256+res[1]
tem = res[2]*256 + res[3]
if ( tem > 0x7fff ):
tem = 0x8000 - tem
print('Humidity = {},{}'.format(hum//10,hum%10))
print('Temperat = {},{}'.format(tem//10,tem%10))