Temperature + Humidity Measurement with DHT11 Sensor and 08M2

Long story short, I had reason to look at a simple and cheap way to create a multi-sensor temperature and humidity control system. I came across the DHT11 sensor, which looked ideal. But subsequently it became apparent that it wasn’t a good match for the Picaxe, which is the only microcontroller I have any (very limited) experience of, as it's data output was too fast. These forums have documented a few ways around this, but they tend to need external components or high end microcontrollers.

It occurred to me that whilst a lower end Picaxe may not be quick enough to catch every DHT11 data pulse, it might be able to catch every other pulse. So if I took two readings from the DHT11, one catching all the odd pulses, the other catching the even ones, I could re-construct the entire data stream and decode the readings. As long as the values didn’t change between readings!

So that’s what I did, running a 08M2 at 32MHz, with no external pre-processing components. Essentially, the code initialises the DHT11, then on one pass executes a Pauseus 0 command. Just running this command introduces a slight delay, which is enough for the following Pulsin to miss the first edge of the 80uS start pulse put out by the DHT11. Hence, that Pulsin picks up the next pulse, which is the 1st data pulse. It’s not quick enough to process the next pulse (2nd data pulse), so misses this and picks up the 3rd and so on, picking up all the odd data pulses. These are stored in sequential memory with @bptrinc.

The full data stream is then processed, but this is irrelevant when it’s just started up as there hasn’t been a second pass yet.

So we go round for the second pass and the DHT11 is initialised again. But no Pauseus 0 command this time. Without it, the next Pulsin picks up the 80uS start pulse (which is ignored later). It misses the next pulse (1st data) and picks up the 2nd, 4th, 6th etc. data pulses, ie all the even data pulses.

So you end up with 40 pulse width measurements in memory, if out of sequence! The subsequent code unscrambles them and extracts the 5 DHT11 data bytes.

The checksum value of the data is calculated. If it matches the checksum byte (byte 5), the temp and humid values are converted to ASCII and sent out to a 20x4 OLED screen (AXE134Y). If not, the existing values remain on the display and round we go again.

Next time, we acquire the odd pulses again and use these and the previous even bits to extract the 5 data byes. Then we go round again acquiring the even bits and combine them with the previous odd bits, and so on.

By way of checking how many times the data fails the checksum test, there’s an alternative bit of output code (gosub testout) where the calculated checksum and the checksum byte values are displayed, along with two counters indicating how many times the measurement succeeded or failed.

Overall, it seems to succeed 50%-60% of the time sitting in my office, which means a valid reading every few seconds or so. The main culprit for failure seems to be the jittery temperature decimal part. But it could be a noisy electrical environment, the fact that the DHT11 isn’t enclosed or even that the 08M2 fails to catch an edge every now and again. But actually, with the display propped up next to a couple of other temp / humid meters, it reads and responds as they do. If you have fast changing values, then this probably isn’t the solution for you. But then, neither is the DHT11! Essentially, it has to get two identical readings in a row to display a valid result – maybe not a bad thing?

The code is sloppy and won't win any awards, but that’s coz I’m not a programmer! I’m sure someone more skilled could make it slicker. I just wanted to see if I could make it work.
 

Attachments

papaof2

Senior Member
I haven't looked at the code, but the concept is very creative. Most of the time, People using a PICAXE aren't looking for instant responses so the process of two good reads to get an answer is a good option.
 
I suppose I could just paste it in....!

symbol dht = c.4 ;dht11 sensor.
symbol oled=c.0 ;OLED display.
symbol bitctr=b2
symbol memaddr=b3
symbol memdata=b4
symbol outaddr=b5
symbol decval=b6
symbol weight=b7
symbol tempint=b8
symbol tempdec=b9
symbol humidint=b10
symbol humiddec=b11
symbol chksum=b12
symbol chkdata=b13
symbol dhtpass=b14
symbol goodctr=b15
symbol badctr=b16

symbol pauseval=10000
goodctr=0
badctr=0

'Setup OLED display.
setfreq m4
serout oled,N2400,(254,1)
pause 5000
serout oled,N2400,(254,212)
serout oled,N2400,("DHT11 Monitor ")
setfreq m32
pause 5000

dhtpass=0

do
if dhtpass=0 then ;Alternate between aquiring odd or even data pulses.

bptr=28 ;Point to first memory location to store incoming odd pulse widths.
high dht ;Set the sensor data line pin high for a while.
pause 200
low dht ;Set sensor data line low for 18mS.
pause 130
high dht ;Then high.
pauseus 0 ;Just enough delay to skip first dht11 80uS sync pulse.

;Measure pulse width of 1st data pulse. Next pulsin command not ready in time for 2nd data pulse, so picks up the 3rd etc.
;This continues, picking up every other pulse (odd ones) and saving width to memory.
pulsin dht,1,@bptrinc
pulsin dht,1,@bptrinc
pulsin dht,1,@bptrinc
pulsin dht,1,@bptrinc
pulsin dht,1,@bptrinc
pulsin dht,1,@bptrinc
pulsin dht,1,@bptrinc
pulsin dht,1,@bptrinc
pulsin dht,1,@bptrinc
pulsin dht,1,@bptrinc
pulsin dht,1,@bptrinc
pulsin dht,1,@bptrinc
pulsin dht,1,@bptrinc
pulsin dht,1,@bptrinc
pulsin dht,1,@bptrinc
pulsin dht,1,@bptrinc
pulsin dht,1,@bptrinc
pulsin dht,1,@bptrinc
pulsin dht,1,@bptrinc
pulsin dht,1,@bptrinc
dhtpass=1

else

;Acquire data from dht11 again, this time starting without a slight delay, so it picks up the 18ms sync pulse, misses
;the 1st data pulse then picks up the 2nd and all even pulse widths. Store in memory directly after first pass.
bptr=48
high dht
pause 200
low dht
pause 130
high dht

pulsin dht,1,@bptrinc
pulsin dht,1,@bptrinc
pulsin dht,1,@bptrinc
pulsin dht,1,@bptrinc
pulsin dht,1,@bptrinc
pulsin dht,1,@bptrinc
pulsin dht,1,@bptrinc
pulsin dht,1,@bptrinc
pulsin dht,1,@bptrinc
pulsin dht,1,@bptrinc
pulsin dht,1,@bptrinc
pulsin dht,1,@bptrinc
pulsin dht,1,@bptrinc
pulsin dht,1,@bptrinc
pulsin dht,1,@bptrinc
pulsin dht,1,@bptrinc
pulsin dht,1,@bptrinc
pulsin dht,1,@bptrinc
pulsin dht,1,@bptrinc
pulsin dht,1,@bptrinc
pulsin dht,1,@bptrinc
dhtpass=0

endif


;Go thru pulse width data saved in memory, alternately accessing odd and even pulse data.
;If pulse width evaluates to a '1', add decimal value to cumulative variable, based on which bit in the
;data byte it represents. When the byte is complete, save the final decimal value.
bitctr=7
memaddr=28
outaddr=69
decval=0

do
peek memaddr,memdata

if memdata>50 then
gosub getweight
decval=decval+weight
endif

if bitctr<>0 then
bitctr=bitctr-1
else
bitctr=7
poke outaddr,decval
outaddr=outaddr+1
decval=0
endif

if memaddr>48 then
memaddr=memaddr-20
else
memaddr=memaddr+21
endif

loop until memaddr=69

peek 69,humidint
peek 70,humiddec
peek 71,tempint
peek 72,tempdec
peek 73,chksum

chkdata=humidint+humiddec+tempint+tempdec

gosub dispout
;gosub testout

pause pauseval

loop

end

getweight:
if bitctr=7 then
weight=128
elseif bitctr=6 then
weight=64
elseif bitctr=5 then
weight=32
elseif bitctr=4 then
weight=16
elseif bitctr=3 then
weight=8
elseif bitctr=2 then
weight=4
elseif bitctr=1 then
weight=2
else weight=1
endif
return

testout:

setfreq m4

serout oled,N2400,(254,128) ;Select first line.
bintoascii tempint,b27,b26,b25 ;Convert tempint to ascii.
bintoascii tempdec,b24,b23,b22 ;Convert tempdec to ascii.
serout oled,N2400,("Temperature ",b26,b25,".",b22,210,"C ") ;Send temp to display.
serout oled,N2400,(254,192) ;Same for humidity.
bintoascii humidint,b27,b26,b25
bintoascii humiddec,b24,b23,b22
serout oled,N2400,("Humidity ",b26,b25,".",b22," % ")

if chkdata=chksum then
goodctr=goodctr+1 ;Increment good or bad reading counters.
else
badctr=badctr+1
endif

;Reset good / bad counters before overflow.
if goodctr=255 or badctr=255 then
goodctr=0
badctr=0
endif

;Display checksum data, good / bad counters etc, for testing.
serout oled,N2400,(254,148)
bintoascii chksum,b27,b26,b25
bintoascii chkdata,b24,b23,b22
serout oled,N2400,("Cksum ",b26,b25," ","Ckdata ",b23,b22," ")
bintoascii goodctr,b27,b26,b25
bintoascii badctr,b24,b23,b22
serout oled,N2400,(254,212)
serout oled,N2400,("Good ",b27,b26,b25," Bad ",b24,b23,b22," ")
setfreq m32

return

dispout:

if chksum=chkdata then

setfreq m4

serout oled,N2400,(254,128) ;Select first line.
bintoascii tempint,b27,b26,b25 ;Convert tempint to ascii.
bintoascii tempdec,b24,b23,b22 ;Convert tempdec to ascii.
serout oled,N2400,("Temperature ",b26,b25,".",b22,210,"C ") ;Send temp to display.
serout oled,N2400,(254,192) ;Same for humidity.
bintoascii humidint,b27,b26,b25
bintoascii humiddec,b24,b23,b22
serout oled,N2400,("Humidity ",b26,b25,".",b22," % ")

setfreq m32

endif

return
 

hippy

Technical Support
Staff member
To put code inline use the [code]...[/code] tags. But there's no problem with adding code as an attachment. Many prefer that, and it's often best when it's more than a few lines long.

That's an excellent trick used there, absolutely brilliant.

I guess it's on the boundary of working or not working which has it fail about half the time, or there are small changes between readings which causes checksum mismatch. You could build up your combined data bytes then twiddle the data lsb's until you get a match - or can't, use that as a kind of error correction. You might only have to try twiddling the temperature.

Untested, but another trick you might want to try is a long sequence of '@bPtrInc = pinC.4". That will copy the raw signal straight into RAM. You would then have to look for consecutive highs, determine how long those were. That might be something better suited to a PICAXE with Scratchpad RAM because there's more of it, and you may have to still make two passes, but you never know.
 
To put code inline use the [code]...[/code] tags. But there's no problem with adding code as an attachment. Many prefer that, and it's often best when it's more than a few lines long.

That's an excellent trick used there, absolutely brilliant.

I guess it's on the boundary of working or not working which has it fail about half the time, or there are small changes between readings which causes checksum mismatch. You could build up your combined data bytes then twiddle the data lsb's until you get a match - or can't, use that as a kind of error correction. You might only have to try twiddling the temperature.

Untested, but another trick you might want to try is a long sequence of '@bPtrInc = pinC.4". That will copy the raw signal straight into RAM. You would then have to look for consecutive highs, determine how long those were. That might be something better suited to a PICAXE with Scratchpad RAM because there's more of it, and you may have to still make two passes, but you never know.
I did look at the higher end PICAXE chips, which look like they would make the coding easier with their extended command set and array capability, but I really wanted to do it with the cheapest, lowest performance PICAXE.
 

Goeytex

Senior Member
Nice workaround for a really cheap but somewhat dodgy sensor. When I was building high end egg incubators I tried both DHT-11 and DHT-22 sensors (AM2302). Testing showed the variance between individual sensors to be as high as 10% RH. This was with the temperature at 35.5 C stable to within 1/2 degree. So don't expect much in the way of accuracy, especially with DHT-11. These may be OK for applications where "cheap" is the primary specification, but not where accuracy, repeatability and reliability are required.
 

inglewoodpete

Senior Member
Nice workaround for a really cheap but somewhat dodgy sensor. When I was building high end egg incubators I tried both DHT-11 and DHT-22 sensors (AM2302). Testing showed the variance between individual sensors to be as high as 10% RH. This was with the temperature at 35.5 C stable to within 1/2 degree. So don't expect much in the way of accuracy, especially with DHT-11. These may be OK for applications where "cheap" is the primary specification, but not where accuracy, repeatability and reliability are required.
When you read some manufacturer's datasheets for the DHT-22, they mention all sorts of things that can affect the accuracy of the sensors - even humidity can affect their performance!
 
Nice workaround for a really cheap but somewhat dodgy sensor. When I was building high end egg incubators I tried both DHT-11 and DHT-22 sensors (AM2302). Testing showed the variance between individual sensors to be as high as 10% RH. This was with the temperature at 35.5 C stable to within 1/2 degree. So don't expect much in the way of accuracy, especially with DHT-11. These may be OK for applications where "cheap" is the primary specification, but not where accuracy, repeatability and reliability are required.
The application I had in mind will use 16 temp/humidity sensors, would you believe, via a multiplexing arrangement. So price is a small issue. I bought four to experiment with. One was a bit wayward, but the other three were consistent with other instruments and each other. At their price point, you can afford to select the good ones and discard the bad. Ironically, your fairly positive review of the DHT11 in the forum back in 2011 was one of the factors in me choosing it, for which I am grateful (and to other past contributors).
 

erco

Senior Member
THANK YOU for the most interesting post here in ages. Your solution is very creative and warrants the high praise from both hippy and goeytex. Well done Bitznbytes!
 

papaof2

Senior Member
Many humidity sensors are spec'd no better than +/-5% and some aren't that close. I have several temp/hum monitors from Banggood, all bought at the same time so potentially from the same production lot - no two of them agree on temp or hum :-( Many home dehumidifiers can only be set in 5% increments so you know the sensor used can do no better than that.
 
Spec on DHT11 is +-5%, so two examples can quite possibly be 10% apart. Even the HTU21D 'Digital High Accuracy RH/T Sensor' is only spec'd to +-3%. For my 16 sensors, I had intended to select them against one that I determine as a 'gold standard'. I did think about having provision for a calibration factor in the code, but that throws up other issues. For my application, selection is fine.
 
Last edited:

papaof2

Senior Member
Calibration needs one variable and a table that's accessed by the sensor number - something you'd already have in a variable. If selection isn't an adequate matching method, there's probably someone here who's done that.
 

AllyCat

Senior Member
Hi,

Personally, I would use a "better" sensor such as the Sensirion SHT30 (low-cost version of the well-respected SHT31), if only because the data sheets for the DHT11 (e.g. Aosong) appear to be so "incomplete". Maybe it's around half the price of the Sensirion and is adequate for some "Air Conditioning" (e.g. indoor) type applications, but not for "meteorological" applications, or where negative or non-integer temperature resolution is required. The similar DHT22 appears somewhat better (at a higher price) but its data sheet is still uninformative about how the negative temperatures and "decimal" values are coded. It appears that the "8-bit decimal" fields are actually either all-zeros (DHT11) or a single BCD value (i.e. 0 to 9) stored in a full byte?

However, if you do want to read data from a DHT11/22 then the OP does appear to have devised a clever (and perhaps the best) method to read the relatively high bit-rate data into a PICaxe. The program could be more compact and there is a (minor) error in the coding, because the Bus drivers are intended to be "Open Collector/Drain" or "Tri-State", so the "High DHT" commands should ideally be either an INPUT DHT , REVERSE DHT or the equivalent DIRS variable command:
Code:
high dht       ; Pull the sensor data line pin high for a while ***
pause 200
low dht         ; Pull sensor data line low for 18mS.
pause 130
high dht        ; Then high [Start command]. ***
pauseus 0      ; Just enough delay to skip first dht11 80uS sync pulse.
      ;   Continue, picking up every other pulse (odd ones) and saving width to memory.
pulsin dht,1,@bptrinc     ; etc..
The first HIGH DHT appears to be unnecessary (and potentially undesirable), because the Bus is already High (PICaxe pins floating), but if it were Low then the rising edge could represent a Start command, so the DHT11 would begin pulling the Bus low to transmit data (without the PICaxe releasing the Bus). It's not clear if this "short-circuit" current is a significant issue because the data sheet indicates only that the DHT11 is capable of pulling current through a 5k resistor (i.e. 1 mA), but like the PICaxe, it's probably much stronger. The second HIGH DHT ideally should be an INPUT or REVERSE DHT , but in this case the pin is automatically soon set as Input/TriState by the subsequent PULSIN DHT... command(s) anyway.

I don't have a DHTxx to test, but suspect that after an 18ms or a 1ms Low "start" period (depending on which data sheet you read) then simply a PULSIN DHT , DHTPASS , @bptrinc could be used to replace the above code for both the Odd and Even phases. This command should synchronise (initiate) the data read after the trailing edge of the first (high) or second (low) 80 us run-in pulse, such that the first "Even" pulse (@bptr=0) would give a validity/sanity check for the first 50 us low period. Then all subsequent PULSIN , DHT , 0 , @bptrinc instructions would store the 0/1 data pulse widths, like the existing program.

Personally, (unless using a SLEEP in the main loop), I would operate the PICaxe at 4 MHz except during the essential data capture phases, although local self-heating by the controller is far less of an issue than with, say, an Ard..... . Also, probably make 4 consecutive measurements (i.e. two even and two odd sets of samples) to give 4 opportunities to match the data in odd and even fields. I suspect that there's considerable risk of "dither" in the values between consecutive measurements in devices which have an active "decimal" value (such as the DHT22) that could give a high proportion of "bad" data.

Untested, but another trick you might want to try is a long sequence of '@bPtrInc = pinC.4".
Yes, a "sampling" method is the first that I would have considered ... until I did the calculations. ;) Ideally you need to sample every High and Low period at least once, which requires a maximum 25 us sampling period to grab all the "0" (positive) pulses. But the full data packet can be up to 40 (bytes) * 120 us ("1" data bit) = 4800 us; thus almost 200 bytes of data would need to be recorded, about twice that available in the 08M2. With a sampling period of just under 50 us, it might be possible to infer/guess the location of any "skipped" 27 us (data "0") pulses, but I believe the @bPtrInc = pinC.4 command actually takes more than 60 us to execute, even in an 08M2 at 32 MHz (and longer in the 14/20M2 and X2s, etc.).

It' should be possible to use the Serial UART or SPI "silicon" Hardware in any of the PICaxe chips (including the 08M2), to sample a complete single data packet into full bytes of 8 consecutive bit samples (at about 40 kbaud) via PEEKSFR commands, but that's straying into quite Advanced programming territory.

Cheers, Alan.
 
Thanks AllyCat, some very useful comments.

I looked at a few I2C sensors, but I’m only just figuring out PICAXE programming, let alone I2C! And I wondered if, with 16 sensors, addressing might be a bit fruity. That’s as far as I went with it. Maybe a challenge for the future. The 8-bit humidity decimal byte on the DHT11 is all zeros, but the temperature one is BCD. It appears to be the instability of this value that causes most of the reading-to-reading mismatches.

I never gave any thought to negative temperatures, I must admit. The DHT11 claims to go to -20 degrees, but I can’t find any mention of how that is represented.

As suggested, I removed the initial ‘high dht’ and ‘pause 200’ and it works fine. On my original code the second ‘high dht’ was required because it was followed by the delaying ‘pauseus 0’, which needed to happen after the 18mS period. However, I was able to remove it with subsequent mods described below. The clock speed now only runs at 32MHz during the acquisition phase.

I was interested in the idea of PULSIN DHT, DHTPASS, @bptrinc and I tried what I thought was being suggested:

for dhtpass=0 to 1

bptr=21*dhtpass+28
low dht
pause 130

pulsin dht,dhtpass,@bptrinc
pulsin dht,1,@bptrinc
pulsin dht,1,@bptrinc
pulsin dht,1,@bptrinc
pulsin dht,1,@bptrinc
pulsin dht,1,@bptrinc
pulsin dht,1,@bptrinc
pulsin dht,1,@bptrinc
pulsin dht,1,@bptrinc
pulsin dht,1,@bptrinc
pulsin dht,1,@bptrinc
pulsin dht,1,@bptrinc
pulsin dht,1,@bptrinc
pulsin dht,1,@bptrinc
pulsin dht,1,@bptrinc
pulsin dht,1,@bptrinc
pulsin dht,1,@bptrinc
pulsin dht,1,@bptrinc
pulsin dht,1,@bptrinc
pulsin dht,1,@bptrinc

next dhtpass

Had this worked, it would have been a very elegant modification. Alas, it didn’t and I think it’s because the 20-40uS after the initial18mS isn’t long enough for pulsin in to be ready to catch the first negative edge of the following 80uS period, when it has to resolve the value of dhtpass first. Previous experience taught me that there can be no computation other than the raw pulsin command, otherwise it will miss pulses.

However, it does work by doing the same thing but with two batches of pulsin measurements similar to the original method. Because there’s no dhtpass to resolve, that first negative edge is caught (I assume, coz it works!). Using pulsin dht,0,@bptrinc at the start of first batch of pulsin commands has the same net effect as the old pauseus 0 delay, catching the odd pulses, but is a slicker way of doing it.

I’ve endeavoured to illustrate what I think is going on with the attached pictures. But frankly I was going boss-eyed by the time I’d finished, so they may or may not be right!

Full modified code:

symbol dht = c.4 ;dht11 sensor.
symbol oled=c.0 ;OLED display.
symbol bitctr=b2
symbol memaddr=b3
symbol memdata=b4
symbol outaddr=b5
symbol decval=b6
symbol weight=b7
symbol tempint=b8
symbol tempdec=b9
symbol humidint=b10
symbol humiddec=b11
symbol chksum=b12
symbol chkdata=b13
symbol dhtpass=b14
symbol goodctr=b15
symbol badctr=b16

symbol pauseval=10000
goodctr=0
badctr=0

'Setup OLED display.
setfreq m4
serout oled,N2400,(254,1)
pause 5000
serout oled,N2400,(254,212)
serout oled,N2400,("DHT11 Monitor ")

setfreq m32
pause 100

dhtpass=0

do
if dhtpass=0 then
bptr=28 ;Point to first memory location to store incoming odd pulse widths.
low dht ;Set sensor data line low for 18mS.
pause 130

;Measure negative pulse width of 1st 80uS pulse. Next pulsin command misses the following positive 80uS pulse and picks up 1st data pulse.
;The next pulsin not ready in time for 2nd data pulse, so picks up the 3rd etc.
;This continues, picking up every other pulse (odd ones) and saving width to memory.
pulsin dht,0,@bptrinc
pulsin dht,1,@bptrinc
pulsin dht,1,@bptrinc
pulsin dht,1,@bptrinc
pulsin dht,1,@bptrinc
pulsin dht,1,@bptrinc
pulsin dht,1,@bptrinc
pulsin dht,1,@bptrinc
pulsin dht,1,@bptrinc
pulsin dht,1,@bptrinc
pulsin dht,1,@bptrinc
pulsin dht,1,@bptrinc
pulsin dht,1,@bptrinc
pulsin dht,1,@bptrinc
pulsin dht,1,@bptrinc
pulsin dht,1,@bptrinc
pulsin dht,1,@bptrinc
pulsin dht,1,@bptrinc
pulsin dht,1,@bptrinc
pulsin dht,1,@bptrinc
dhtpass=1

else

;Acquire data from dht11 again, this picking up the 18ms positive pulse first. It then misses
;the 1st data pulse then picks up the 2nd and all even pulse widths. Stored in memory directly after first pass.
bptr=49
low dht
pause 130

pulsin dht,1,@bptrinc
pulsin dht,1,@bptrinc
pulsin dht,1,@bptrinc
pulsin dht,1,@bptrinc
pulsin dht,1,@bptrinc
pulsin dht,1,@bptrinc
pulsin dht,1,@bptrinc
pulsin dht,1,@bptrinc
pulsin dht,1,@bptrinc
pulsin dht,1,@bptrinc
pulsin dht,1,@bptrinc
pulsin dht,1,@bptrinc
pulsin dht,1,@bptrinc
pulsin dht,1,@bptrinc
pulsin dht,1,@bptrinc
pulsin dht,1,@bptrinc
pulsin dht,1,@bptrinc
pulsin dht,1,@bptrinc
pulsin dht,1,@bptrinc
pulsin dht,1,@bptrinc
pulsin dht,1,@bptrinc
dhtpass=0

endif

setfreq m4

;Go thru pulse width data saved in memory, alternately accessing odd and even pulse data.
;If pulse width evaluates to a '1', add decimal value to cumulative variable, based on which bit in the
;data byte it represents. When the byte is complete, save the final decimal value.
bitctr=7
memaddr=29
outaddr=70
decval=0

do
peek memaddr,memdata

if memdata>50 then
gosub getweight
decval=decval+weight
endif

if bitctr<>0 then
bitctr=bitctr-1
else
bitctr=7
poke outaddr,decval
outaddr=outaddr+1
decval=0
endif

if memaddr>49 then
memaddr=memaddr-20
else
memaddr=memaddr+21
endif

loop until memaddr=70

peek 70,humidint
peek 71,humiddec
peek 72,tempint
peek 73,tempdec
peek 74,chksum

chkdata=humidint+humiddec+tempint+tempdec

gosub dispout
;gosub testout

setfreq m32
pause pauseval

loop

end

getweight:
if bitctr=7 then
weight=128
elseif bitctr=6 then
weight=64
elseif bitctr=5 then
weight=32
elseif bitctr=4 then
weight=16
elseif bitctr=3 then
weight=8
elseif bitctr=2 then
weight=4
elseif bitctr=1 then
weight=2
else weight=1
endif
return

testout:

setfreq m4

serout oled,N2400,(254,128) ;Select first line.
bintoascii tempint,b27,b26,b25 ;Convert tempint to ascii.
bintoascii tempdec,b24,b23,b22 ;Convert tempdec to ascii.
serout oled,N2400,("Temperature ",b26,b25,".",b22,210,"C ") ;Send temp to display.
serout oled,N2400,(254,192) ;Same for humidity.
bintoascii humidint,b27,b26,b25
bintoascii humiddec,b24,b23,b22
serout oled,N2400,("Humidity ",b26,b25,".",b22," % ")

if chkdata=chksum then
goodctr=goodctr+1 ;Increment good or bad reading counters.
else
badctr=badctr+1
endif

;Reset good / bad counters before overflow.
if goodctr=255 or badctr=255 then
goodctr=0
badctr=0
endif

;Display checksum data, good / bad counters etc, for testing.
serout oled,N2400,(254,148)
bintoascii chksum,b27,b26,b25
bintoascii chkdata,b24,b23,b22
serout oled,N2400,("Cksum ",b26,b25," ","Ckdata ",b23,b22," ")
bintoascii goodctr,b27,b26,b25
bintoascii badctr,b24,b23,b22
serout oled,N2400,(254,212)
serout oled,N2400,("Good ",b27,b26,b25," Bad ",b24,b23,b22," ")

return

dispout:

if chksum=chkdata then

setfreq m4

serout oled,N2400,(254,128) ;Select first line.
bintoascii tempint,b27,b26,b25 ;Convert tempint to ascii.
bintoascii tempdec,b24,b23,b22 ;Convert tempdec to ascii.
serout oled,N2400,("Temperature ",b26,b25,".",b22,210,"C ") ;Send temp to display.
serout oled,N2400,(254,192) ;Same for humidity.
bintoascii humidint,b27,b26,b25
bintoascii humiddec,b24,b23,b22
serout oled,N2400,("Humidity ",b26,b25,".",b22," % ")

endif

return
 

Attachments

hippy

Technical Support
Staff member
I never gave any thought to negative temperatures, I must admit. The DHT11 claims to go to -20 degrees, but I can’t find any mention of how that is represented.
I hadn't given it any thought either, but am not sure I have ever used a DHT11., though I am sure I have something in a 'blue perforated plastic package' somewhere which perhaps is.

Given the DHT11 and DHT22 are such commonly used devices I found it incredible that there's no definitive information in the datasheets or out there. Every 'How To' and example program seems to disagree with the others, as to what the data values can be, how they are formatted, how they should be decoded.

I would guess that whatever you are doing is right or you have got incredibly lucky that incorrect processing is delivering results which match those of other displays.

From what I could find about negatives, the DHT11 won't report temperatures below 0C. The DHT22 will and uses the msb as a sign bit, with the rest of the bits as an unsigned value rather than two's complement. So -1C would be '10000001' rather than '11111111'.

But, at the moment, the information conflicts so much I wouldn't like to say what's correct and what isn't.

Doing a Google Image Search of sensors which have had their internal circuitry exposed shows some very different designs for what are claimed to be "DHT" and equivalent modules. All of them seem to use a microcontroller to deliver their output data. It could be they don't all generate exactly the same data, and what one gets is however it was implemented for that module, not necessarily the same as for any other modules.

That could explain why very different programs and decodings work for different users.
 

AllyCat

Senior Member
Hi,

Thanks for your detailed update which is quite encouraging. To be honest, I wasn't even sure that the pulsin dht , 0 , @bptrinc would give the correct delay/phase. Often in PICaxe Basic, Variables and Constants are largely interchangeable in function/timing terms but, with hindsight, not in this case. I don't even know if it would be better to try to synchronise to the falling pulse-edge response from the DHTxx, or to simply rely on its specification of being between 20 and 40 us after the initial rising edge.

Although in the past I have measured the execution times of most PICaxe Basic commands, PULSIN is an exception because it's dependent on an "external" event (i.e. an input pulse) and also there will be a complex sequence of delays involving: 1. Decoding the OpCode, 2. Switching the pin from (a potential) Output to Input mode, 3. Selecting (and waiting for) the target Edge, 4. Measuring the pulse Width, 5. Storing the value and finally 6. Exiting. I assumed the (minimum) execution time would be similar to the PULSOUT command (that incidentally has a very similar period to PAUSEUS) which is relatively "slow" at about 700 PIC Instruction Cycles (~90 us at 32 MHz) plus the pulse/pause delays.

Hippy has described above, the hazards of a "generic" device based on custom microcontroller software, but at least the negative temperature ("sign" flag or 2's complement) probably can be inferred from the bit6 value (because it has a weight of -64 degrees). However, the "decimal" byte is a real can of worms which could represent one or two nibbles (4 bits) coded in BCD or "fractional binary", etc.. One of the DHT22 specifications claimed to use (instead of an NTC/Thermistor) an internal 18B20 temperature sensor (which I suspect would be only a Chinese clone), that does employ (4 bits of) fractional binary notation. But one thing that the decimal byte could NOT represent is three ASCII digits decoded by BINTOASCII, because that has a maximum value of 0.255 ! ;)

It might be worth trying an alternative pulsin dht , bit24 , @bptrinc (where dhtpass has been symbolised as b3 or bit 24) , but I think there might be another solution, by jumping to (or calling) a single (sub-) routine with two different entry points (i.e. labels). One path might (need to) contain an additional pulsin dht , 1 .... , but if that is left "hanging", waiting for a 41st data pulse, then the (automatic) timeout feature of the command should recover within 100 ms if necessary:
Code:
; This continues, picking up every other pulse (odd ones) and saving width to memory.
getodd:
        pulsin dht,0,@bptrinc       ; Or perhaps some other form of delay/triggering?
geteven:   
        pulsin dht,1,@bptrinc
        pulsin dht,1,@bptrinc    
;    +18 More ...
Incidentally, the need for the "getweight" subroutine could be eliminated by simply writing the bits into the LSB of the target register and then Left-Shifting by multiplying the register value by 2 (or I usually add to itself, being slightly faster).

The I2C Bus can be extremely useful and is well-supported natively in PICaxe Basic (no need to pull in Library routines, etc).. It's far better standardised than say SPI, which IMHO is hardly a "Bus" at all. But I2C is such a powerful concept that there can be quite a few "gotchas" when learning to use it. Most I2C sensors (like the SHT3x) do have two (or occasionally more) different Slave addresses, but rarely as many as 16 ! One solution is to include an (analogue) multiplexer in one (or both) of the Bus lines, or if speed is not an issue, then (PICaxe-available) I2C Emulation software routines can be applied to any of the I/O pins. However, most sensors of this type consume so little power that the easiest "Disabling" method is to simply disconnect their Earth Rail via a PICaxe Output pin. It's the Earth line which needs to be switched because I2C employs "Idle High" Data and Clock lines, but it might be necessary to use (pull-down) isolating diodes or FETs from the Bus lines, to prevent the Data and Clock lines becoming bridged together.

Cheers, Alan.
 
Last edited:

hippy

Technical Support
Staff member
I don't even know if it would be better to try to synchronise to the falling pulse-edge response from the DHTxx, or to simply rely on its specification of being between 20 and 40 us after the initial rising edge.
This is where it gets tricky and hard to tell, or even figure out what needs to be done, and how to do it. I was quite surprised it even worked at all as the short pulse and gap periods had me thinking one might never be able to guarantee staying in sync, reading the bits one wants to.

I am not sure which would be best but it only really matters that the PULSIN is ready to receive the particular pulse it is expecting to get. The best solution might only come from trying it and seeing.

I would probably create something, probably not PICAXE based, which would generate a worse case 0101010... signal for the first and a 0010101... signal for the second, check that all bits read were one's.. Then it's a case of seeing which receiving techniques actually work, which give fewest errors., making the signal incrementally worse to see which survives the best.
 
Top