PDA

View Full Version : 16 bit conversion - AM2302



Scampy
- 10th April 2015, 15:31
Hi guys,

Following on from the thread when using a DHT11 sensor for humidity readings I've received my SHT11 / AM2302 sensor from E-bay.

My current routine for reading the DHT11 is



read_dht:

TRISA.1 = 0 '
high dht_data
low dht_data : pause 18' send 18ms low
high dht_data : pauseus 30 ' send 30us hi
TRISA.1 = 0
PulsIn PORTA.1, 1, dht
if dht < 9 then goto badresponse
for x = 31 to 0 step-1
PulsIn PORTA.1, 1, dht[x] ' 1
next x
For x = 31 to 0 step-1
if dht(x)>=9 and dht(x)<=21 then dht(x)=0 'if pulsewidth between 20 and 40uS then read as '0'
if dht(x)>=29 and dht(x)<=41 then dht(x)=1 'if pulsewidth between 60 and 80uS then read as '1'
next x
hum=dht[31]*128+dht[30]*64+dht[29]*32+dht[28]*16+dht[27]*8+dht[26]*4+dht[25]*2+dht[24]*1
return


The sensor uses the same 40 bit data stream as the DHT, but has twice the resolution. The data sheet (http://www.adafruit.com/datasheets/Digital%20humidity%20and%20temperature%20sensor%20 AM2302.pdf) suggests that you convert the 16 bit decimal to decimal and then divide that by 10 to get the reading. As the sensor sends RH then Temp then checksum, what would be the simplest way of reading the first 16 bits rather then doing the multiply by 128, 64, 32, 16 etc

Tabsoft
- 10th April 2015, 16:21
You could possibly do something like this...



hum var word
j var byte
x var byte

read_dht:

TRISA.1 = 0 '
high dht_data
low dht_data : pause 18' send 18ms low
high dht_data : pauseus 30 ' send 30us hi
TRISA.1 = 0
PulsIn PORTA.1, 1, dht
if dht < 9 then goto badresponse
for x = 31 to 0 step-1
PulsIn PORTA.1, 1, dht[x] ' 1
next x
'For x = 31 to 0 step-1
' if dht(x)>=9 and dht(x)<=21 then dht(x)=0 'if pulsewidth between 20 and 40uS then read as '0'
' if dht(x)>=29 and dht(x)<=41 then dht(x)=1 'if pulsewidth between 60 and 80uS then read as '1'
'next x

'Stuff values into hum word variable based on pulsewidth value in dht byte arrary
j = 15
for x = 31 to 16 step - 1
if dht(x)>=9 and dht(x)<=21 then hum.0[j] = 0
if dht(x)>=29 and dht(x)<=41 then hum.0[j] = 1
j = j-1
next x

Scampy
- 10th April 2015, 16:52
Thanks Tabsoft,

I'll give that a try. I did stumble across a similar thread (http://www.picbasic.co.uk/forum/showthread.php?t=17867) and amended the code to



read_dht:

TRISA.1 = 0 '
high dht_data
low dht_data : pause 18' send 18ms low
high dht_data : pauseus 30 ' send 30us hi
TRISA.1 = 0
PulsIn PORTA.1, 1, dht
if dht < 9 then goto badresponse
for x = 0 to 31
PulsIn PORTA.1, 1, dht(x) ' 1
next x
For x = 0 to 31
if dht(x)>=9 and dht(x)<=21 then dht(x)=0 'if pulsewidth between 20 and 40uS then read as '0'
if dht(x)>=29 and dht(x)<=41 then dht(x)=1 'if pulsewidth between 60 and 80uS then read as '1'
next x
hum=32768*dht(1)+16384*dht(2)+8192*dht(3)+4096*dht (4)+2048*dht(5)+1024*dht(6)+512*dht(7)+256*dht(8)+ 128*dht(9)+64*dht(10)+32*dht(11)+16*dht(12)+8*dht( 13)+4*dht(14)+2*dht(15)+1*dht(16)

return


with an amended display line, but get random digits all over the range from 6453% to 17% !!

I'll give your example a try a and will get back

Scampy
- 10th April 2015, 17:06
tabsoft,

Using your code and the line
LCDOut $FE,$80+9,"RH ",dec hum The LCD is displaying 6473, 6730, 6729, 6473, 6729 so I would assume that there is the odd misread now and again.

What divider should I use of the variable hum to get a realistic figure, and is there any tweaks to the values in the for/next loop to clearly define the 1's and 0's to make the reading of the sensor more stable. I'm using an 18F4580 running at 40 Mhz (10mhz crystal with the HS_ppl option enabled)

EDIT:

For info using
LCDOut $FE,$80+9,"RH ",#hum the values jump around 18714, 18970,18970,18714,

Scampy
- 10th April 2015, 17:19
I'm assuming that for 100% humidity the value of hum would be 65535 and 0% would be 0 ?

If so then using DEC hum/256 gives a reading of 73, 74 on the LCD - which seems a tad to high for the living room. The cheap simple dial hydrometer in the reptile enclosures read 37% and the sensor built into the weather station which is 18" away from the development board displaying 42%

Scampy
- 10th April 2015, 19:09
OK, just to check things I got my Arduino out and downloaded a sketch and library file. here's the results

7771

which is close enough to the readings of both hydrometers in the reptile tanks.

Now I don't understand the C style of these arduino sketches, but the link to the website is http://www.electroschematics.com/11291/arduino-dht22-am2302-tutorial-library/ - maybe it might help in working out why this is such a pain to do in PBP

Tabsoft
- 10th April 2015, 21:47
Give this a try.



hum var word
j var byte
x var byte


read_dht:

TRISA.1 = 0 ' Make pin an input
high dht_data '250ms High
pause 250

low dht_data '20ms Low
pause 20' send 20ms low

high dht_data '40us High
pauseus 40

PulsIn PORTA.1, 1, dht
if dht < 9 then goto badresponse
for x = 31 to 0 step-1
PulsIn PORTA.1, 1, dht[x] ' 1
next x

hum = 0 'Clear hum variable
'Stuff values into hum word variable based on pulsewidth value in dht byte arrary
j = 15
for x = 31 to 16 step - 1
for x = 31 to 16 step - 1
if dht(x)>=35 then hum.0[j] = 1
j = j - 1
next x
return


and then to display try this.



LCDOut $FE,$80+9,"RH ",dec hum/10, "." dec hum //10

Scampy
- 10th April 2015, 22:16
Excellent !!

I took a reading with the arduino code and it was giving me a value of 39.7 - 40.2 over a few minutes. Tried your suggestions and the LCD displayed 39.7 - 40.1 - so it's bang on and confirmed with the hydrometers I have.

Ok now as I want to learn, can you explain what is happening in the code so I may learn from these examples.

Scampy
- 10th April 2015, 22:47
Left it too long to edit the above post.. but here's my understanding - Please correct me if I'm wrong



read_dht:

TRISA.1 = 0 'make pin input
high dht_data : pause 250 'take pin high for 250ms

low dht_data : pause 20 'take pin low for 20ms
high dht_data : pauseus 40 'send 40us pulse

PulsIn PORTA.1, 1, dht 'measure incomming high pulse and store in variable dht
if dht < 9 then goto badresponse 'if pulse width is less than 9 got bad responce. 10mhz xtal should give 4us resolution
for x = 31 to 0 step -1 'loop to read pulse widths from bits 31 - 0
PulsIn PORTA.1, 1, dht[x] ' 1 'measure incoming pulse width and add it to the array variable
next x 'go round the loop until 32 bits have been read

hum=0 'clear variable
j = 15
for x = 31 to 16 step - 1 'loop round the last 16 bits
if dht(x)>=35 then hum.0[j] = 1 'if any value of dht(31 to 16) is equal or over 35 then ?????
j = j-1 'J is reduced by 1
next x 'go round until x = 16

return

badresponse:
LCDOut $FE,$80+9,"RH N/C"
return


I'm stuck on the IF THEN section of the last for next loop

Tabsoft
- 11th April 2015, 01:12
I'm glad it worked for you.

There were 3 areas I looked at to try to make it work.
1. The sensor will send a "0" as a High pulse between 26us to 28us long, and a "1" as a High for 70us long.

The original code was designed for a 20MHz PIC, so the PulsIn would return the pulsewidth in 2us increments.
Your PIC 18F4580 was running at 40MHz so we needed to adjust the dht value test to accommodate the fact that PulsIn command was now returning the pulsewidth in 1us increments.
The PulsIn command uses the PICs frequency to set the resolution level recorded into the variable.
The way this works is with this formula: Resolution (us) = (1/(Fosc/4)) * 10
For a 40Mhz OSC this would be: Res = (1/(40000000/4)) * 10 = (1/10000000) * 10 = (.0000001) * 10 = .000001 or 1us.
So this means that the value in the PulsIn variable will be a count of 1us increments.
The original code was testing for a zero by checking if the dht value was =>9 and <=21.
With a 20MHz osc, this would would mean the "9" would be "9*2us = 18us" and the "21" would be "21*2us=42us", which would be close. (=>18 and <=42 us)
With your 40MHz osc, it would be testing for 9*1us and 21*1us, which would not be correct (=>9 and <=21 us).

2. With item 1 above and the fact that I "Zeroed" the "hum" variable before loading the bits into it from "dht", I simply tested for a "1" value from the sensor.
"if dht(x)>=35 then hum.0[j] = 1"
I chose "35us" since it is a value greater than a "0" value from the sensor (26-28us) and it is 1/2 of a "1" value from the sensor (70us).

3. The sensor's initialization sequence that is defined in the datasheet you attached plus the DHT C library pushed me to make changes to the initialization sequence in the PBP program.


I would also change the following line of code from "if dht < 9 then goto badresponse" to "if dht < 20 then goto badresponse"., given the 40MHz Osc.

I commented the code below to help describe what is happening.



hum var word 'Holds final 16bit Humidity value (integral and decimal)
j var byte
x var byte


read_dht:

TRISA.1 = 0 ' Make pin an input

'**Intialization sequence to sensor**
high dht_data '250ms High on data pin to sensor
pause 250

low dht_data '20ms Low on data pin to sensor
pause 20 ' send 20ms low

high dht_data '40us High
pauseus 40

'Read the sensor's acknowledgment of init sequence
PulsIn PORTA.1, 1, dht 'Read a High pulse from the sensor data line
if dht < 20 then goto badresponse 'Not a good signal, maybe noise? Change this to < 20

'**End of Initialization sequence"

'**Load the data from the sensor into the dht byte array
for x = 31 to 0 step-1
PulsIn PORTA.1, 1, dht[x] 'Read 32 High pulses (bits) from the sensor and store them as pulsewidth counts in individual bytes in the dht byte array.
next x
'**End of Load Data**

hum = 0 'Clear hum word variable which holds the final 16bit humidity value

'**Convert the dht byte array to a 16bit word variable
'Stuff values into hum word variable based on the pulsewidth value in dht byte array
'Use "hum.0(j)" to select a specific bit position in the hum word variable.
'0.[j] is treated as an bit-offset into the hum variable. E.g. 0.[0] = hum.0, 0.[1] = hum.0+1, 0.[2] = hum.0+2, etc

j = 15 'Used to select the correct bit in the hum word variable
for x = 31 to 16 step - 1 'Read the 16 bytes for humidity in the dht byte array (humidity pulsewidth values)
if dht(x)>=35 then hum.0[j] = 1 'If the pulsewidth is => 35us then the value is a "1".
'otherwise, the value is a "0", so we do not need to do anything, all bits in "hum" were initialized to "0" with "hum = 0"
j = j - 1
next x

'**End Conversion**

'**Display the result**
'Send the ascii code that is the decimal representation of the humidity, "dec" modifier.
'The 16bit hum variable includes both the integral and 1 digit decimal for the humidity (e.g. hum = 311 = 31.1 %RH).
'So we need to display the value correctly by dividing the value by 10 (integral) and then displaying the decimal portion using the modulo "//" function.

LCDOut $FE,$80+9,"RH ",dec hum/10, "." dec hum //10


Hope this helps.

Tabsoft
- 11th April 2015, 02:19
Scampy,

If It were me, I might make modification like the following.




dht_data var PORTA.1

dht var byte[40] 'Holds pulsewidth counts of sensor data
fHum var byte 'Final humidity value integral and decimal place
fTemp var byte 'Final temperature vaule, integral and decimal place degC
checksum var byte 'Checksum of payload data from sensor
chkError var bit 'Flag for checksum error
j var byte 'Generic counter
k var byte 'Generic counter

chkError = 0

main:
gosub read_dht
if !chkError then
'Print out sensor values
LCDOut $FE,$80,"T ",dec fTemp/10, "." dec fTemp //10
LCDOut $FE,$80+9,"RH ",dec fHum/10, "." dec fHum //10

else
'Print out checksum error
endif
pause 2000
goto main

read_dht:
'*** NOTE: You should disable interrputs during this subroutine.
'*** Sensor communications are timing sensitive

TRISA.1 = 0 ' Make the sensor data pin an input

'**Intialization sequence to sensor**
high dht_data '250ms High on data pin to sensor
pause 250

low dht_data '20ms Low on data pin to sensor
pause 20 ' send 20ms low

high dht_data '40us High
pauseus 40

'Read the sensor's acknowledgment of init sequence
PulsIn PORTA.1, 1, dht[0] 'Read a High pulse from the sensor data line
if dht < 20 then goto badresponse 'Not a good signal, maybe noise? Changed this to < 20
'**End of Initialization sequence"

'**Load the data from the sensor into the dht byte array
for j = 0 to 39 step 1
PulsIn PORTA.1, 1, dht[j] 'Read data High pulses (bits) from the sensor and store them as pulsewidth counts in individual bytes in the dht byte array.
next j
'**End of Load Data**


'**Convert the Humidity dht byte array to a 16bit word variable
'Stuff values into hum word variable based on the pulsewidth value in dht byte array
'Use "fHum.0(j)" to select a specific bit position in the fHum word variable.
'0.[j] is treated as an bit-offset into the hum variable. E.g. 0.[0] = fHum.0, 0.[1] = fHum.0+1, 0.[2] = fHum.0+2, etc

fHum = 0 'Clear fHum word variable which holds the final 16bit humidity value

k = 0 'Used to select the correct bit in the fHum word variable
for j = 0 to 15 step 1 'Read the 16 bytes for humidity in the dht byte array (humidity pulsewidth values)
if dht(j)>=35 then fHum.0[j] = 1 'If the pulsewidth is => 35us then the value is a "1".
'otherwise, the value is a "0", so we do not need to do anything, all bits in "hum" were initialized to "0" with "hum = 0"
k = k + 1
next j
'**End Conversion**

'**Convert the Temperature dht byte array to a 16bit word variable
fTemp = 0 'Clear fTemp word variable which holds the final 16bit temperature value

k = 16 'Used to select the correct bit in the fTemp word variable
for j = 16 to 31 step 1 'Read the 16 bytes for temperature in the dht byte array (temperature pulsewidth values)
if dht(j)>=35 then fTemp.0[j] = 1 'If the pulsewidth is => 35us then the value is a "1".
'otherwise, the value is a "0", so we do not need to do anything, all bits in "hum" were initialized to "0" with "hum = 0"
k = k + 1
next j
'**End Conversion**

'**Convert the Checksum dht byte array to a 16bit word variable
checksum = 0 'Clear Checksum word variable which holds the final 8bit checksum value

k = 32 'Used to select the correct bit in the fTemp word variable
for j = 32 to 39 step 1 'Read the 8 bytes for checksum in the dht byte array (checksum pulsewidth values)
if dht(j)>=35 then checksum.0[j] = 1 'If the pulsewidth is => 35us then the value is a "1".
'otherwise, the value is a "0", so we do not need to do anything, all bits in "hum" were initialized to "0" with "hum = 0"
k = k + 1
next j
'**End Conversion**

'Test the checksum
chkError = 0 'Reset checksum error flag
if checksum <> fHum + fTemp then
chkError = 1
endif

return


badresponse:
LCDOut $FE,$80+9,"RH N/C"
return

Scampy
- 11th April 2015, 11:33
tabsoft thanks for the detailed explanation of how the section works, and for the modified code to include the temperature readings as well.

The project that I'm developing uses DT's interrupts and DS18B20 include files to control the PID routines for the thermostats. Using the temperature element in the AM2302 would be better as it saves having multiple sensor probes going to the snakes enclosures. I'll try out your suggested code so that it works with the existing variables to see how it works. My main concern is that as the code uses DT's interrupts it continues to drive the heaters whilst the pic is doing other things such as entering the menu options.

Thanks once again fo your help, and for explaining what you did in such detail. It makes the learning process a lot simpler :-)

Tabsoft
- 12th April 2015, 00:34
Noticed I made a small mistake on using the correct variable (used j instead of k) during the Humidity/Temperature Pulsewidth-to-Bit value conversions.




dht_data var PORTA.1

dht var byte[40] 'Holds pulsewidth counts of sensor data
fHum var byte 'Final humidity value integral and decimal place
fTemp var byte 'Final temperature value, integral and decimal place degC
checksum var byte 'Checksum of payload data from sensor
chkError var bit 'Flag for checksum error
j var byte 'Generic counter
k var byte 'Generic counter

chkError = 0

main:
gosub read_dht
if !chkError then
'Print out sensor values
LCDOut $FE,$80,"T ",dec fTemp/10, "." dec fTemp //10
LCDOut $FE,$80+9,"RH ",dec fHum/10, "." dec fHum //10

else
'Print out checksum error
endif
pause 2000
goto main

read_dht:
'*** NOTE: You should disable interrputs during this subroutine.
'*** Sensor communications are timing sensitive

TRISA.1 = 0 ' Make the sensor data pin an input

'**Intialization sequence to sensor**
high dht_data '250ms High on data pin to sensor
pause 250

low dht_data '20ms Low on data pin to sensor
pause 20 ' send 20ms low

high dht_data '40us High
pauseus 40

'Read the sensor's acknowledgment of init sequence
PulsIn PORTA.1, 1, dht[0] 'Read a High pulse from the sensor data line
if dht < 20 then goto badresponse 'Not a good signal, maybe noise? Changed this to < 20
'**End of Initialization sequence"

'**Load the data from the sensor into the dht byte array
for j = 0 to 39 step 1 'Record the bits in correct order (not reverse order)
PulsIn PORTA.1, 1, dht[j] 'Read data High pulses (bits) from the sensor and store them as pulsewidth counts in individual bytes in the dht byte array.
next j
'**End of Load Data**


'**Convert the Humidity dht byte array to a 16bit word variable
'Stuff values into hum word variable based on the pulsewidth value in dht byte array
'Use "fHum.0(j)" to select a specific bit position in the fHum word variable.
'0.[j] is treated as an bit-offset into the hum variable. E.g. 0.[0] = fHum.0, 0.[1] = fHum.0+1, 0.[2] = fHum.0+2, etc

fHum = 0 'Clear fHum word variable which holds the final 16bit humidity value

k = 0 'Used to select the correct bit in the fHum word variable
for j = 0 to 15 step 1 'Read the 16 bytes for humidity in the dht byte array (humidity pulsewidth values)
if dht(j)>=35 then fHum.0[k] = 1 'If the pulsewidth is => 35us then the value is a "1".
'otherwise, the value is a "0", so we do not need to do anything, all bits in "hum" were initialized to "0" with "hum = 0"
k = k + 1
next j
'**End Conversion**

'**Convert the Temperature dht byte array to a 16bit word variable
fTemp = 0 'Clear fTemp word variable which holds the final 16bit temperature value

k = 16 'Used to select the correct bit in the fTemp word variable
for j = 16 to 31 step 1 'Read the 16 bytes for temperature in the dht byte array (temperature pulsewidth values)
if dht(j)>=35 then fTemp.0[k] = 1 'If the pulsewidth is => 35us then the value is a "1".
'otherwise, the value is a "0", so we do not need to do anything, all bits in "hum" were initialized to "0" with "hum = 0"
k = k + 1
next j
'**End Conversion**

'**Convert the Checksum dht byte array to a 16bit word variable
checksum = 0 'Clear Checksum word variable which holds the final 8bit checksum value

k = 32 'Used to select the correct bit in the fTemp word variable
for j = 32 to 39 step 1 'Read the 8 bytes for checksum in the dht byte array (checksum pulsewidth values)
if dht(j)>=35 then checksum.0[k] = 1 'If the pulsewidth is => 35us then the value is a "1".
'otherwise, the value is a "0", so we do not need to do anything, all bits in "hum" were initialized to "0" with "hum = 0"
k = k + 1
next j
'**End Conversion**

'Test the checksum
chkError = 0 'Reset checksum error flag
if checksum <> fHum + fTemp then
chkError = 1
endif

return


badresponse:
LCDOut $FE,$80+9,"RH N/C"
return