Anonymous - 2014-04-02

Hello all,

Here's something you might find useful. The DHT22 is pretty mysterious first off because it uses pulse code modulation to send and receive, employs a non-standard way of representing negative temperatures, and lastly because the numeric format is not explained in the poorly translated data sheet.

So, I've put together an include file that takes all the misery and mystery out of it. This is my first attempt at an include file, so I would appreciate any comments or suggestions. It's pretty much self-documented and heavily annotated. Observe that it now returns relative humidity, Celsius and Fahrenheit properly rounded and accurate to the nearest tenth. I'm not sure how attach files here, so will simply list it. Below the file is a sample program using the include.

DHT22.H Include File

~~~~

;DHT22 Humidity/Temperature Sensor Library for Great Cow Basic

;Thomas Henry
;Version 1.0 -- 4/2/2014

;There is only one subroutine here. When called, it will
;return the relative humidity, Celsius and Faharenheit
;temperatures to one decimal place. The three numbers are
;treated as signed integers, scaled up by ten. For example,
;a return value of 657 for the relative humidity would be
;interpreted as 65.7%.

;Also returned is an error condition (a byte):
;0 = no error
;1 = no response from the sensor
;2 = bad checksum from the sensor

;Nine local bytes are consumed in the computations here,
;and seven bytes used for the output parameters. That's a
;grand total of sixteen bytes required when invoking this.

;The only constant required in the main calling program
;is that which sets the single data pin, e.g.,

;#define DHT22_pin Port A.0

;Note that whichever pin is employed, it must be capable
;of both input and output operation and must have a
;10k pull-up resistor.

;For reference, the pinout of the DHT22 is, looking from
;the front, (grill side):

;Pin 1: +5V
;Pin 2: DHT22_pin
;Pin 3: no connection
;Pin 4: ground

;This sensor transmits data via pulse code modulation at a
;fairly zippy rate, so the microcontroller clock should be
;at least 8 MHz. Observe that the DHT22 can not be
;polled more frequently than once every two seconds.

;----- Variables

dim DHT22_values(5)
dim DHT22_counter, DHT22_i, DHT22_byte as byte

;reuse a couple variables to save memory
dim DHT22_chksum alias DHT22_counter
dim DHT22_abs alias DHT22_counter

;----- Subroutine

sub readDHT22(out DHT22_rh as integer, out DHT22_cels as integer, out DHT22_fahr as integer, out DHT22_error as byte)

;----- request an update

dir DHT22_pin out ;port is output now
set DHT22_pin off ;go low
wait 18 mS ;for 18 milliseconds
set DHT22_pin on ;then go high
wait 40 uS ;for 40 microseconds

;----- wait for an acknowledgment

dir DHT22_pin in ;port is input now

DHT22_counter = 0 ;count by tens of microseconds
do while DHT22_pin = off ;until pin goes high
wait 1 10uS
DHT22_counter++
if DHT22_counter > 9 then ;should take 80 microseconds
goto DHT22_noResponse ;so must be a dud
end if
loop

DHT22_counter = 0 ;repeat, looking for a low now
do while DHT22_pin = on
wait 1 10uS
DHT22_counter++
if DHT22_counter > 9 then ;should take 80 microseconds
goto DHT22_noResponse ;so must be a dud
end if
loop

;----- start of transmission

for DHT22_i = 1 to 5 ;five bytes to collect
DHT22_byte = 0 ;build up DHT22_byte here
repeat 8 ;8 bits to a byte
DHT22_byte = 2 * DHT22_byte ;shift left one place
do while DHT22_pin = off ;wait for start of pulse
loop

  DHT22_counter = 0                 ;measure incoming pulse
  do while DHT22_pin = on
    wait 1 10uS                     ;count by tens of microseconds
    DHT22_counter++
  loop

  if DHT22_counter > 4 then         ;long pulse is a 1
    DHT22_byte++                    ;factor the bit in
  end if
end repeat                          ;next bit

DHT22_values(DHT22_i) = DHT22_byte  ;store complete byte

next DHT22_i

;----- construct output values

DHT22_chksum = DHT22_values(1) ;compute the checksum
for DHT22_i = 2 to 4
DHT22_chksum = DHT22_chksum + DHT22_values(DHT22_i)
next DHT22_i

if DHT22_chksum <> DHT22_values(5) then
DHT22_error = 2 ;Error 2 means bad checksum
goto DHT22_conclude
else
DHT22_error = 0 ;Error 0 means all okay

;compute relative humidity scaled up by 10
DHT22_rh = [integer]256 * DHT22_values(1) + DHT22_values(2)

;save the absolute value of the Celsius temperature
DHT22_abs = DHT22_values(3) & 0x7F

;compute Celsius temperature scaled up by 10
DHT22_cels = [integer]256 * DHT22_abs + DHT22_values(4)

;factor in the sign if needed
if DHT22_values(3).7 = on then      ;a negative temp
  DHT22_cels = -DHT22_cels          ;make a true 2's complement
end if

DHT22_fahr = [integer]10*DHT22_cels       ;scaled up by 100 now
DHT22_fahr = [integer]DHT22_fahr*9/5+3205 ;force rounding in the 100ths
DHT22_fahr = [integer]DHT22_fahr/10       ;this is 10*Fahrenheit, rounded

goto DHT22_conclude                 ;all done!

end if

DHT22_noResponse:
DHT22_error = 1 ;Error 1 means no response
DHT22_conclude:
end sub


And here's a sample program using it to show how easy it is now.


;Humidity/Temperature Sensor demo using DHT22.h include file.
;Thomas Henry
;This revision: 4/2/2014

include <dht22.h> ;sensor include file

;----- Settings

chip 16F88, 8 ;PIC16F88 running at 8 MHz

config mclr=off ;reset handled internally

config osc=int ;use internal clock

;----- Constants

define DHT22_Pin PortA.0 ;RH/Temp sensor on pin 17

define LCD_IO 4 ;4-bit mode

define LCD_RS PortB.2 ;LCD Register Select on pin 6

define LCD_Enable PortB.3 ;LCD Enable on pin 7

define LCD_DB4 PortB.4 ;DB4 on pin 8

define LCD_DB5 PortB.5 ;DB5 on pin 9

define LCD_DB6 PortB.6 ;DB6 on pin 10

define LCD_DB7 PortB.7 ;DB7 on pin 11

define LCD_NO_RW 1 ;ground the RW line on LCD

define degree 223 ;ASCII code for degree mark

define period 2 S ;update period

;----- Variables

dim msg, whole, tenths as byte
dim rh, cels, fahr as integer

;----- Main Program

dir portB 0b00000000 ;Port B is all output

cls
print "Initializing..."
wait period ;let unit stabilize

do
readDHT22(rh, cels, fahr, msg) ;get current values
cls
select case msg
case 0: ;all okay, so proceed
print "Humidity: " ;print relative humidity
printResult(rh)
print "%"

  locate 1,0                    ;print temperature in Celsius
  print "C:"
  printResult(cels)
  LCDWriteChar degree           ;print degree mark
  print " "

  print "F:"                    ;print temperature in Fahrenheit
  printResult(fahr)
  LCDWriteChar degree           ;print degree mark
case 1:                         ;unit not responding
  print "No response..."
case 2:                         ;checksum error
  print "Bad checksum..."

end select
wait period ;2 seconds min between readings
loop ;repeat in perpetuity

;----- Subroutines

sub printResult(in param as integer)
if param.15 = on then ;if it's negative
param = -param ;make non-negative
print "-" ;print a minus sign
end if

whole = param / 10 ;whole number part
tenths = param % 10 ;and the tenths

print whole ;integer part
print "." ;print decimal point
print tenths ;and the tenths
end sub