Project - Differential Thermostat

We all know that hot air rises (and conversely, cold air sinks).
We are all sensitive to the relative temperature differences caused by draughts.
Most of us are sensitive to quite small temperature changes... even a 1 or 2 degree difference is noticeable.

So it might come as a surprise to know that the temperature difference between floor and ceiling of a non-draughty heated room can be 5 degrees or more.
Perhaps that shouldn't be so surprising if you consider the lifting power of a bag of hot air (see left).

So it is inevitable that all your heated air will shoot straight up to the ceiling.

If the ceiling is not high, the temperature difference can be noticeable on bare skin even when just changing position from sitting, laying or standing as your bare skin moves up or down through the temperature layers. If you've had a fire on, you might stand up and feel a warm flush of air on your face even though your feet still feel cold - and it's no coincidence that feet feel the cold... it's because they are usually stood in the colder bottom layer of air which can be several degrees colder than what the face feels.
Obviously the problem is due to all heated air rising straight up to the ceiling above you, leaving most of you still submerged in the colder lower layers.
This project offers a simple solution to ease the problem of different temperature layers, by using 2 sensors to compare the temperatures of the top and bottom and using the difference to control almost any fan capable of starting an air current circulating between floor and ceiling.

It may sound counter-intuitive to be turning a fan on to get warmer in winter, but it effectively reduces the overall temperature difference by causing the wasted upper warm air layer to circulate down and warm up the colder depths.
It does exactly the same on the hot summer days... sending the stifling hot upper air circulating down to the floor and stirring up a cooler circulation from below to the higher levels.

The Differential Thermostat doesn't care about absolute temperatures, it simply needs to know the relative difference between a pair of  temperature sensors. So we could use any sensors, but it seems a good excuse to use a couple of DS18B20 Dallas 1-wire (1 data wire) sensors.
We're not bothered which sensor goes where, we just need to know the temperature difference between them. 
It doesn't even matter if their readings don't agree, because we will do an initial comparison of both sensors next to each other to calculate a calibration offset, which we can then use to work out the true effective temperature difference between them, irrespective of their readings.
Another calibration can be done at any time, either from the no-frills web page, or from a hardware button.

Here's a link to a Dallas DS18B20 Data Sheet:

There are 2 types of DS18B20 available, a TO92 3-legged plastic transistor
type package, or a pre-wired water-proof metal cylider version.
The TO92 package is 'cheap as chips' but not convenient 'as is', whereas the waterproof type is only slightly more expensive and works just as well in free air, but can have pre-wired cable lengths from 1m to 10m, so is handier for routing between floor and ceiling of a room.
In the past I've had mixed results using multiple 1-wire sensors in 2-wire parasitic power mode, which was overcome by using the 3rd +V wire (and dropping the pullup resistor from the recommended 4.7K to 3K)... so the 3-core pre-wired waterproof version suits the task very nicely.

We only need 1 spare gpio available to connect the 1-wire sensors to, so I used an 'early' Sonoff TH Mains Relay Module costing a fiver.
It uses gpio12 for its relay, gpio13 for its green led, and has gpio14 available spare which can be used for the DS18B20 temperature sensors.

Sonoff now do TH10 (10 amp) and TH16 (16 amp) versions with built-in jack socket to plug different types of temperature sensors directly into, but the sellers listings are a ripoff minefield for the unwary, charging as much again for the sensor as for the module, effectively doubling the price.    

Electric fans might not be earthed, but it is advisable to connect a 'pass-through' earth wire between the mains input plug and the output socket.

How to Use
Do an initial calibration with the 2 sensors next to each other - the calculated offset value will be saved to file and reloaded next time.
The sensors can be re-calibrated at any time using the hardware button or web page, and the new offset value will again be saved for future use.
The required 'difference' threshhold setting is also saved to file each time it is changed, and that value will be reloaded until changed again.
The smaller the difference value, the more the fan will attempt to equalise the temperatures to your setting - the bigger the difference value then the more temperature difference is tolerated before the fan turns on - so too small and it won't turn off, and too large and it won't turn on.
The Sonoff green led normally shows relay status, but is also used to give 5 calibration confirmation flashes.

The best fan(s) location is a matter of trial and error, but a smokey candle helps to see what's happening and map the resulting air currents.

I've been using a clip-on 6" fan which gives me good results when blowing horizontally along the ceiling, but a 2nd fan at the opposite end at floor level would improve circulation. Rather than run Mains extension cable to a second fan, another unit could be remotely controlled using UDP.

title$ = "Differential Thermostat - configured for Sonoff"
preset = 2                                       'Preset switching threshhold
ledpin = 13                                      'Sonoff green LED (active low)
pin.mode ledpin, output
pin(ledpin) = 1
relaypin = 12                                    'Sonoff relay (active high)
pin.mode relaypin, output
pin(relaypin) = 0
buttonpin = 0                                    'Sonoff button (active low)
pin.mode buttonpin, input, pullup
interrupt buttonpin, pressed
start = 0
stop = 0
t = val(tempr$(14,1))                            'First sensor
temp1$ = str$(t,"%2.1f")                         'Formats to nn.n decimal digits
t = val(tempr$(14,2))                            'Second sensor
temp2$ = str$(t,"%2.1f")
offset = val(tempr$(14,1)) - val(tempr$(14,2))   'The out-of-balance offset between the 2 sensors
diff = val(temp1$) -  val(temp2$) + offset       'The adjusted effective difference in temp between the 2 sensors
indicator = 1                                    'Web page LED
filename$ = "/program/diffstat.ini"              'Name of saved settings file
gosub load
gosub paint
onhtmlreload paint
timer0 5000, readtemp                            'Reads sensors every 5 seconds

a$ = title$ + "<br><br>"
a$ = a$ + textbox$(temp1$,"tb") + " sensor 1<br>"
a$ = a$ + textbox$(temp2$,"tb") + " sensor 2<br>"
a$ = a$ + textbox$(offset,"tb") + " balance offset<br>"
a$ = a$ + button$("Calibrate", calib) + "<br>"
a$ = a$ + textbox$(diff,"tb") + " adjusted difference<br>"
a$ = a$ + led$(indicator) + "<br>"
a$ = a$ + textbox$(preset,"tb") + " switching threshhold<br>"
a$ = a$ + button$(" < ", down) + " " + button$(" > ", up) + "<br>"
a$ = a$ + cssid$("tb", "width:40;")
html a$

if pin(buttonpin) = 0 then gosub calib

' Loads settings from file
a$ = ""
if FILE.EXISTS(filename$) > 0 then
 a$ = FILE.READ$(filename$)
 if WORD.GETPARAM$(a$,"preset") <> ""  then preset = val(WORD.GETPARAM$(a$,"preset"))
 if WORD.GETPARAM$(a$,"offset") <> ""  then offset = val(WORD.GETPARAM$(a$,"offset"))

' Saves settings to file
a$ = ""
if FILE.EXISTS(filename$) > 0 then a$ = FILE.READ$(filename$)
WORD.SETPARAM  a$, "preset", str$(preset,"%2.1f")
WORD.SETPARAM  a$, "offset", str$(offset,"%2.1f")
FILE.SAVE filename$, a$

preset = preset + 1
gosub save

preset = preset - 1
if preset < 0 then preset = 0
gosub save

t = val(tempr$(14,1))
temp1$ = str$(t,"%2.1f") 
t = val(tempr$(14,2))
temp2$ = str$(t,"%2.1f")
offset = val(temp1$) - val(temp2$)
for c = 1 to 5
pin(ledpin) = 0
pause 400
pin(ledpin) = 1
pause 200
next c
pin(relaypin) = 0
gosub save

t = val(tempr$(14,1))
temp1$ = str$(t,"%2.1f")
t = val(tempr$(14,2))
temp2$ = str$(t,"%2.1f") 
diff = val(temp1$) -  val(temp2$) - offset
if diff >= preset or diff <= -preset then
 'turn fan on
 indicator = 0
 pin(ledpin) = 0
 pin(relaypin) = 1
 'turn fan off
 indicator = 1
 pin(ledpin) = 1
 pin(relaypin) = 0

'-------------------- End ---------------------

A Smart Socket project in another chapter uses a Sonoff S20 plug-in switched Mains socket which can be sent UDP commands to turn on or off.
This offers some alternative options using interactive remote control, because this Differential Thermostat need not necessarily be used as a self-contained Mains-switching unit, it could be used just as a low voltage sensor if preferred, sending instructions to one ormore remote plug-in S20's.
A low voltage version of the differential thermostat might use the Sonoff SV (Safe Voltage) module and could be useful for monitoring relative temperature difference for other applications, especially considering that the waterproof sensors can be used in liquids.
As a monitor, 1 sensor might act as a reference to monitor for relative temperature stability irrespective of changing ambient levels.

Margaret Baker,
Jan 16, 2018, 10:04 AM