DIY humidity and temperature sensor

Jeremy Harris

Senior Member
I've seen a few approaches to measuring temperature and relative humidity here, and elsewhere, but none seemed to meet my needs for one reason or another. I wanted a 5 V sensor that would give temperature with 0.1 deg C resolution and 0.5 deg C accuracy and relative humidity to around 3 to 5 % accuracy. Some of the devices around almost do this, but either need 3 V supplies (like the HH10D from Hope RF, which works on 5V but is inaccurate when so run) or the DHT11/DHT22 which have interface issues with the Picaxe because of the signal timing.

After a fair bit of experimentation, I've come up with a combined temperature and relative humidity sensor that is relatively cheap (less that £15), accurate (tested accuracy is better than 3% RH) and easy to build. I used a Humirel HS1101 relative humidity sensor, which is available for around £2 if bought direct from China in small quantities (check out Sure Electronics, for example). This is the same sensor that's used in the Hope RF module. I used a CMOS 555 to convert the sesnor varying capacitance to frequency, and fed this to an 08M2. I also connected a DS18B20 to the O8M2. All the parts fit on a small circuit board that is about 20mm wide and 50mm long and which can be easily mounted inside a short piece of rectangular wiring conduit as a cheap housing.

The 08M2 does some simple arithmetic to convert the frequency and DS18B12 data into meaningful numbers and outputs this as serial data at 4800 baud. This seems to drive fairly long wires OK. The data can then be parsed and read by any device you like, I'm using these as sensors for an environmental data logger (using a 14M2) that stores the logged data to a microSD card, together with a date and time stamp.

The schematic, circuit board layout and code are below, if anyone is interested.

Before using the sensor it need to be calibrated, something that is fairly easy to do. Calibration requires a couple of clean jam jars, some insulating tape, a programming lead hooked up to the sensor board with thin wires, some anhydrous copper sulphate and some pure salt (sodium chloride). Anhydrous copper sulphate will need to be prepared, by baking the bright blue copper sulphate pentahydrate crystals (that are usually sold as plain "copper sulphate") in an oven at 150 deg C until they turn into a light grey powder. In this form copper sulphate is a powerful desiccant and will quickly start to absorb moisture from the air, so it needs to be transferred quickly to a sealed container.

To start the calibration process, edit the lines of code to comment out the normal data transmission line and uncomment the calibration line (these are marked by comments in the code below). The sensor module will now transmit temperature in deg C as ASCII and the frequency of the CMOS 555 in Hz, and these can be read using the terminal window in the Picaxe Programming Editor. Prepare a 0% RH chamber by sprinkling a few mm thickness of anhydrous copper sulphate into a jam jar, fitting the lid tightly and giving it a good shake. It may warm up slightly as moisture is absorbed, as the reaction is exothermic.

Next bang the jar on a hard surface to settle the powder at the bottom and quickly undo the lid, place the sensor module in the clear space at the top of the jar and close the lid, trapping the wires. Run a length of insulating tape around the lid/jar join to seal it and the wires. Next the jar needs to be left for a few hours at a constant temperature of around 20 deg C for all the moisture to be taken out of the air inside. This can be monitored by connecting the module to the terminal and watching the frequency change. It will increase, probably to somewhere around 8000 to 8400 Hz, as the air dries out. When the frequency has been reasonably stable (it will vary by a few Hz due to noise) for a time, note down the final 0% RH frequency, as you will need to enter it into the module code later. The module can now be removed from the jar and the jar re-sealed for use again if required.

To set the sensitivity of the module it needs to be calibrated at a known high relative humidity. Luckily, the air above a saturated salt will give a constant relative humidity over a modest temperature range. Ordinary pure table salt, when mixed with just enough water to form a thick slurry, will give a relative humidity of 75.7% at 20 deg C, and only vary by about 0.1% for around 5 or more degrees either side of that temperature. Using the second clean jam jar, place a thin layer of salt slurry in the base, place the sensor as before in the clear space and close and seal the lid with tape. Leave this for a few hours at as constant a room temperature as possible for the relative humidity to stabilise. As before, you can see when it has stabilised because the frequency will stop slowly changing. Note the frequency you end up with, it should be around 800 to 1000 Hz lower than the 0% RH frequency.

With both frequencies the sensitivity factor can be determined, using the formula: sensitivity = (75.7*4096)/(0% frequency - 75.7% frequency)

The module code can now be changed back to the final version by commenting out the calibration output line, un-commenting the normal output line and inserting the 0% RH frequency in the "LET offset = " line at the top of the code and the calculated sensitivity figure in the line beneath this. The module is now calibrated and will output data about once per second in the format "T", "sign_byte",whole_deg_byte,"decimal_deg_byte","H",relative_humidity_byte, where bytes in quotes are ASCII (for example, sign_byte is either "+" or "-", ASCII 43 and 45 respectively). The format of the output serial data stream can easily be changed; this format happened to work well with my logging application. I rename the code file to reflect the offset and sensitivity factors and also label the board with them, following final programme download.

I don't pretend that the code is either that tidy or well-optimised, but it does work OK and timing etc isn't at all critical in this application, so I'm happy with it. There are snippets in there that I've adapted from code others have generously shared and I acknowledge the help this has been, with thanks to the original authors.

There is more on the core project that this is a part of in this thread in the "Finished Projects" area: http://www.picaxeforum.co.uk/showthread.php?20551-Environmental-uSD-card-data-logger
 

Attachments

John West

Senior Member
"I'm using these as sensors for an environmental data logger (using a 14M2) that stores the logged data to a microSD card, together with a date and time stamp."

Is the info on this accessible somewhere?
 

John West

Senior Member
Sorry, I missed clicking on the right link. Too many information paths available these days and I get lost occasionally trying to recall where I've been. An 'old guy' problem.
 

g6ejd

Senior Member
For temp I use the 18B20 in 12-bit mode, here's my code for reading it:
Code:
display_temp:
	readtemp12 7,temp_word
	SignBit = temp_word / 256 / 128
      If SignBit = 1 Then
      	temp_word = temp_word ^ $ffff + 1 'Negative, so take two's comp
      Endif 
      TempC = temp_word * 6		' TC = value * 0.0625
      temp_word = temp_word * 25 / 100
      TempC = TempC + temp_word
      Htemp = TempC / 100
   	Ltemp = TempC % 100
	If SignBit = 0 Then SerOut LCD, BAUD,("  +",#Htemp) ELSE SerOut LCD, BAUD,("  -",#Htemp) ENDIF
   	Ltemp = Ltemp / 10
   	SerOut LCD, BAUD,(".",#Ltemp,223,"C") ' For the 20x4 LCD 223 is the degree symbol
   	Return
And I use the Humidity sensor sold by Rev-Ed the Honeywell HIH4000-001, here my code for that:
Code:
display_humidity:
	readadc C.2, ADVal 		 	' Read display_humidity value
	ADVal= ADVal - 42 * 100 / 157 	' Change to %RH
	SerOut LCD, BAUD,(254,Line3,#ADVal,"%Rh ") ' Display the result
	Return
Both devices work well at any supply voltage between 4...5 volts. and give accurate results compared with my Davis Vantage weather station which is widely accepted to be a benchmark for accuracy, furthermore they both track the many METAR stations around this area for readings and accuracy.

Posted for interest. BTW have you seen on eBay the cheap combined temeperature and humidity modules with an IC2 bus, about £3. They are generally used in low end weather stations.
 

Jeremy Harris

Senior Member
I tried one of the Honeywell HIH4000 sensors, but they are pretty expensive (around 12 times dearer than the HS1101) and it still needed calibrating to get better than maybe 6% accuracy when read by a Picaxe ADC (something I only found out by testing it and doing an accurate calibration), which put me off using them for this application. I did use the DS18B20, in 12 bit mode, in this module, as it seemed to be the best value, reasonably accurate, temperature sensor available.

One objective was to keep the module cost down, as one of the people interested in using these for an eco-house monitoring project wanted to use lots of sensors around a house. This whole module, including circuit board, all components etc, costs far less than a single Honeywell sensor, and it delivers a calibrated RH output that is at least as accurate (in fact more accurate unless the HIH4000 has been calibrated in circuit in a similar way to this).

Looking at the costs, all the parts, including the printed circuit board, connector and housing for my module cost about £11.10 including VAT but excluding postage. If I'd opted to use the HIH4000 sensor instead of a CMOS 555, two resistors and the HS1101, then the cost would have been about £32.55 plus postage. The accuracy would be the same, and both would require calibration to get the +/-3% RH level I was looking for.

When I looked at the Ebay sensors they weren't quite what they seemed, I found. I did buy 4 or 5 different RH sensors before working out that the cheap HS1101 offered remarkably good performance for the price (which is probably why Hope RF chose it).
 
Last edited:

g6ejd

Senior Member
I agree for cost sensitive applications the HS1101 is the right choice and yes I have calibrated mine to align the average of the stations around me and the results are quite linear, I suppose all readings are broadly relative as I don't compare to any standards. Out of the box though, the HIH4000-001 was very close, about 2% RH out from the average. 18B20 seems is very close.
 

westaust55

Moderator
@Jeremy,
Thank you for posting your work on setting up the DIY humidity sensor.
Some folks do seem to have problems with the HopeRF and other offerings so an alternate approach can be worthwhile.

This could be moved to the finished projects section if yor desire where it will not be lost (off the first page) within a day.
 

westaust55

Moderator
A suggestions for your code/reference:

You have the two SYMBOL declarations:
symbol RET = 13
symbol LFEED = 10

The Programming Editor already has the pre-defined alias names inbuilt that you can use:
cr or CR = 13 and
lf or LF = 10
 
Last edited:

John West

Senior Member
Well thought out, carefully constructed and well documented projects like this one (and the added follow-on comments and discussions) are one of this forum's (and the web's) greatest assets. This and projects from Wolfgang, Westy and several others around here just bring a smile to my face. They demonstrate the promise of the www when guided by the efforts of knowledgeable people who care.
 

westaust55

Moderator
For temp I use the 18B20 in 12-bit mode, here's my code for reading it:
Alhough it was a line of code I had also used long ago, the line
temp_word = temp_word ^ $ffff + 1 'Negative, so take two's comp​
can be replaced with
temp_word = -temp_word ; Negative, so take two's comp​


Also a relatively quick way the obtain the temperature for 4 decimal places is (add 2-comp part as above for negative values):
Code:
SYMBOL temp_integer = b5
SYMBOL temp_word   = w3
readtemp12 <pin>, temp_word
temp_integer = temp_word / 16
temp_word = temp_word * 4096 **10000
SERTXD (#temp_integer,".")
IF temp_word < 1000 THEN
SERTXD ("0")     ; need to insert a leading zero for xxx.[COLOR="#FF0000"]0[/COLOR]625
ENDIF
SERTXD (#temp_word,cr,lf)
for 1 decimal place:
Code:
SYMBOL temp_decimal = b4
SYMBOL temp_integer = b5
SYMBOL temp_word   = w3
readtemp12 <pin>, temp_word
temp_integer = temp_word / 16
temp_word = temp_word * 4096 **10000 [COLOR="#FF0000"]/ 1000[/COLOR]
SERTXD (#temp_integer,".", #temp_word,cr,lf)
 
Last edited:

John West

Senior Member
That's getting quite compact. Is there a tidy version for Fahrenheit conversion for those of us in more backwards lands?
 

Jeremy Harris

Senior Member
A suggestions for your code/reference:

You have the two SYMBOL declarations:
symbol RET = 13
symbol LFEED = 10

The Programming Editor already has the pre-defined alias names inbuilt that you can use:
cr or CR = 13 and
lf or LF = 10
Thanks, I'd guessed as much, but wasn't sure, as I accidentally typed LF instead of LFEED when I was developing the code and it still worked!

CR and LF seem more intuitive to me, so I'll make the change just to tidy things up a bit

PS: I'd already put it in the "Finished Projects" section, but thought the snippets about the sensor and driving a microSD card might be useful discussion topics, too.
 

Jeremy Harris

Senior Member
Well thought out, carefully constructed and well documented projects like this one (and the added follow-on comments and discussions) are one of this forum's (and the web's) greatest assets. This and projects from Wolfgang, Westy and several others around here just bring a smile to my face. They demonstrate the promise of the www when guided by the efforts of knowledgeable people who care.
Thanks for the kind words, but I'm not sure I'd put myself in the "knowledgeable people who care" category! This was my second finished Picaxe project. The first is simple "fuel gauge" for the lithium battery on my electric bike, using an 08M, that logs amp hours used since last charged and drives a small analogue LCD on the handlebars to display remaining battery capacity. That's been very useful, and proved to be at least as accurate as a car fuel gauge in use over the last 18 months or so.
 

westaust55

Moderator
That's getting quite compact. Is there a tidy version for Fahrenheit conversion for those of us in more backwards lands?
Unfortunately not quite as compact as hoped.
Just realised I needed to cover the situation where the temp is xx.0625 degrees and insert a '0' in the first decimal place for the 4 decimal place version.
Fixed above in post 11
 
Last edited:

Jeremy Harris

Senior Member
It also doesn't quite round correctly, as I've just found out by testing it in parallel with my approach. The challenge that caused me to use that rather lengthy bit of code was to correctly round up or down the 0.0625 increments, rather than discard the remainder. If anyone can see a neater way to convert the 0.0625 deg C increments to accurately rounded up or down 0.1 deg C increments I'd be grateful, as I'll freely admit that my approach is a bit of a brute force kludge.
 

westaust55

Moderator
It also doesn't quite round correctly, as I've just found out by testing it in parallel with my approach. The challenge that caused me to use that rather lengthy bit of code was to correctly round up or down the 0.0625 increments, rather than discard the remainder. If anyone can see a neater way to convert the 0.0625 deg C increments to accurately rounded up or down 0.1 deg C increments I'd be grateful, as I'll freely admit that my approach is a bit of a brute force kludge.
Presume you meant the 1 decimal place version.
If you wish to round rather than truncate, then try the line:

temp_word = temp_word * 4096 **10000 + 380 / 1000

since the steps are 0.0625, the addition of the 0.0380 degree is sufficient to round up. You could even use "375" if you wanted.
 

g6ejd

Senior Member
My observations for code reductions and simplications are that they need very good comment lines too, or for wider use and reference, the code is probably best left in a non-reduced form so that a wide range of people can understand them, applying their own reduction techniques if required.
 

Jeremy Harris

Senior Member
Presume you meant the 1 decimal place version.
If you wish to round rather than truncate, then try the line:

temp_word = temp_word * 4096 **10000 + 380 / 1000

since the steps are 0.0625, the addition of the 0.0380 degree is sufficient to round up. You could even use "375" if you wanted.
Thanks, that's a neater solution; if you don't mind I'll use it!
 

westaust55

Moderator
Thanks, that's a neater solution; if you don't mind I'll use it!
Feel free. The concept is one I used in my square root (to 2 decimal places) and logarithms (to 2 decimal places):
http://www.picaxeforum.co.uk/showthread.php?19714-Calculate-Square-Root-to-two-decimal-places
http://www.picaxeforum.co.uk/showthread.php?19695-Calculate-Logarithms-with-PICAXE-math

And, by way of some explanation for the line:
temp_word = temp_word * 4096 **10000

1. The decimal (fractional part) is the lowest 4 bits.
2. Multiplying by 4096 shifts those bits to the highest 4 bits in a word variable. (could use << 12 with the X1 and X2 parts but I stay with *4096 for more universal code)
3. After step 2, the most significant bit has a value of 0.5, the next bit 0.25, the third bit 0.125 and the 4th bit from the msb = 0.0625)
4. We then multiply by 10,000 so that the msb if set, it represents 0.5 and the result ==> 5000.
But since the actual msb bit has a binary value of 32768 we use the ** multiplication operator and collect the high word from a word x word multiplication.

By way of example, our 0.5 bit is represented by 32768, then multiplying by 10000 ==> 327,680,000 however by using the ** operator and taking the high byte we also in effect perfoming a hidden divide by 65536 so that instead of 327,680,000 we in fact have 327,680,000 / 65536 ==> 5000 which is exactly what was sought in step 4.
In the same way, the second bit from the left represents 0.25 but the actual value, if set, is 16384. Using the same math the end result is 2500.

In this way all of the four binary bits representing the decimal part are scaled up in a result from 0 to 9375 in increments of 0625.
 
Last edited:

marks

Senior Member
Although Ive only had a quick look !
Well done on your project looks impressive

heres some code you can try also

Code:
# Picaxe20x2

SYMBOL Ds18b20       = B.7
SYMBOL Temperature   = W1 
SYMBOL Sign          = b4
SYMBOL Rounding      = b5   SYMBOL Decimal       = b5         


  Main:

READTEMP12 Ds18b20,Temperature
    
  ConvertTemperature:
  
    Temperature=Temperature  * 25/4                                             '  (C)Celsius  
         
                                                                LET Sign = " "         '  Display + 
    IF Temperature > 39036 THEN : Temperature = - Temperature : LET Sign = "-" : ENDIF '  Display - 
                                                    
    LET Rounding = Temperature // 10 : IF Rounding > 4 THEN LET Temperature = Temperature + 10 : ENDIF 'rounding up 0.05 to 0.09
    
    Decimal=Temperature//100/10
    Temperature=Temperature/100
 
 SERTXD (CR, LF, "Temperature  ",Sign,#Temperature,".",#Decimal,"  Degrees C", CR, LF)
 
Last edited:

Jeremy Harris

Senior Member
Thanks. After a bit of playing about I've now changed the subroutine that converts raw DS18B20 temperature to "sign", "whole_degrees" and "decimal_degrees" to this one:

Code:
;convert 2's complement to decimal value and sign and round decimal degrees to nearest single decimal digit
;sets sign to ASCII 45 ("-") or space (" ", ASCII 32)

convert:
                              
  	LET sign = 32                                       			;set sign to ASCII 32 = " " (space character)
  	  	
  	IF temp_word > 64655 THEN                           			;-55 deg C = 64656
  	LET sign = 45                                       			;set sign to "-" for less than 0 deg C
	temp_word = - temp_word
	ENDIF                                
	
	whole_degrees = temp_word / 16
	decimal_degrees = temp_word * 4096 **10000 + 380 / 1000		;returns whole_degrees as integer and decimal_degrees as correctly rounded remainder to 0.1 deg C

	RETURN
which seems to work well, although I've needed to make some other changes to take account of it now sending the decimal_degrees part as a raw byte, rather than one of ten ASCII characters. Overall it has tidied the code up a bit so I shall go back and edit the files I previously posted on the "Finished Projects" thread. The above sub-routine accepts the raw DS18B20 reading in the word variable "temp_word".
 

mjy58

Member
Correction to round up correctly

Westaust55 - thanks for this excellent,concise piece of code.

However, I think the correction value is incorrect.

Presume you meant the 1 decimal place version.
If you wish to round rather than truncate, then try the line:

temp_word = temp_word * 4096 **10000 + 380 / 1000

since the steps are 0.0625, the addition of the 0.0380 degree is sufficient to round up. You could even use "375" if you wanted.
To get it round up correctly (particularly with the 0.25 and 0.75 values), I think the correction value needs to be between 500 and 624. So the line becomes:

temp_word = temp_word * 4096 **10000 + 500 / 1000
(or any value between 500 and 624)

Hopefully, I have understood it correctly?
 

westaust55

Moderator
Yes, if you want the rounding to 1 decimal place to be based on rounding up for second decimal place >= 5 (instead of > 5) then yes, the &#8220;380&#8221; will need to be changed to &#8220;500&#8221;

However, if we use the &#8220;Bankers Rule&#8221;, then when the fist digit being dropped is a &#8220;5&#8221; then the last retained is it is even or increased if it is odd.
In this case 0.250 would become 0.2, but 0.750 would become 0.8. The rationale for the &#8220;Bankers Rule&#8221; is that approximately half of the time the number will be rounded up and the other half of the time it will be rounded down.

It is all a case of which of several rules for rounding we wish to apply.
 

g6ejd

Senior Member
Observation that ' ... * 4096 **10000 + 500 / 1000' is a constant and so does not need to be worked out each time and can be replaced by "... * your calculator evaluated answer of the same" which can make for faster and more compact code.
 

HertzHog

Member
Be careful that if your power supply sags, e.g. If it is a battery. The peripheral chips may malfunction before the Picaxe. I got caught out and had to find a work around. HertzHog.
 

Jeremy Harris

Senior Member
Be careful that if your power supply sags, e.g. If it is a battery. The peripheral chips may malfunction before the Picaxe. I got caught out and had to find a work around. HertzHog.
Good point, as the calibration of the humidity sensor part is, to some degree, power supply dependent. The CMOS 555 astable is relatively free from frequency change due to supply voltage variation, but there will be some effect, enough to throw off the calibration by a few percent if the supply isn't held to within around +/- 0.1 V of the supply voltage used for calibration.

This isn't a problem if a 5 V regulated supply is used, as the tolerance on even a cheap 78L05 is far better than this. As I mentioned in the first post in this thread, one of my motives for going down this route was to have an RH sensor that worked at 5 V, rather than the 3.3 V that the HH10D needs.
 

westaust55

Moderator
Another alternative formula to:
temp_word = temp_word * 4096 **10000 + 500 / 1000
could be:
temp_word = temp_word AND $0F * 625 + 500 / 1000 ; calculate the fractional part to print after a "."
 

Timbertool

New Member
jeremy, I have constructed your temp & humidity project & have had a couple of problems
the first i have managed to fix myself.The temp & humidity board sends the humidity data
into pin c4 of the 08M2 but the software reads pin c2 which is unused,changing software fixed this.
I am completely baffled by the other problem which is the timestamp on the .csv file starts correct
but when we pass thorough midnight the date does not change.I wondered if you have any thoughts?.
 

Jeremy Harris

Senior Member
jeremy, I have constructed your temp & humidity project & have had a couple of problems
the first i have managed to fix myself.The temp & humidity board sends the humidity data
into pin c4 of the 08M2 but the software reads pin c2 which is unused,changing software fixed this.
I am completely baffled by the other problem which is the timestamp on the .csv file starts correct
but when we pass thorough midnight the date does not change.I wondered if you have any thoughts?.
Sorry for the delay, I've been away on holiday.

Sorry also for the pin error - I've made several versions and must have slipped up with the info I posted!

The date error is an odd one, as the three I've built so far have all rolled over the date OK at midnight. I suspect some sort of problem with the RTC, but TBH don't know quite what it might be.
 
Top