Arduino Thermostat Project
Now! With Android app
Nest aint got nothin' on me!
When my "smart" thermostat died last year, I went to the store to
replace it. The thermostat market has changed since I last bought one.
There used to be only four tiers of thermostats:
-
"Dumb" ones, full manual control of the temperature.
seven days of the week. Priced below $20 (All prices US $).
-
"One day" programmable ones: assumes every day has the same schedule.
Priced below $30.
-
"Five/Two day" programmable ones: assumes that the weekend and weekdays are
on two different schedules, but the weekdays are all the same and
Saturday and Sunday are the same. Priced below $50.
-
"Seven day" programmable ones: Allows seven different programs for the
seven days of the week. Priced between $50 and $100.
Now, there's a fifth tier: "internet" and "phone controlled" thermostats,
which run an eye-watering $250. I thought "that's stupid, I can make
a better one than that, and it'll be waaaaay cooler than any of these."
Several months of hacking and coding later, I have succeeded. I finished
the project just in time for the heating season to end last year, but
winter 2014 heating season is under the control of my own thermostats.
I decided on the following specifications/features:
-
The thermostat would be on my network and would be controllable via the
web.
-
It would get the time via NTP, and so handle Daylight Savings Time, which
may be stupid, but that's what we've got, so I deal with it. But at least
I won't have to dick around with the thermostat's time twice a year.
-
The thermostat would control 3 of the four zones of my home heating system.
The fourth zone would remain under the control of an ordinary "2 day/5 day"
thermostat, thus acting as a backup in case the Arduino packs it in. That
way I won't come home to frozen pipes if the main thermostat breaks and
I'm away for a weekend during a cold spell.
-
The thermostat would have "one day" programmability, as I'm retired so
I don't give a crap about "work days" vs "weekends" anymore.
-
I would try to have ONE Arduino control all three zones, and the two
remote display units.
-
The remote display units would be at least as capable as any ordinary
"one day" thermostat, in that one would be able to turn the heat on and
off, adjust the temperature, and fiddle with the programming for that
zone from the zone display unit.
-
The main display unit would be able to control the programs for all
three zones. This way you don't have to go running around the house
to change all three programs; you can do it from the main unit or
from the web interface.
-
Finally, I invented my "killer" feature: "Vacation Mode", which allows
one to tell the system how many weeks,days,hours one will be out of
the house, and the system will turn the temperatures down to 60°
in all three zones, and keep them there until the vacation timer
counts down to 0, at which time the temperature returns to 70°
I read up on Arduino thermostat projects, and found
this one
to my liking. (The web site is desert-home.com so I will call the author
"DSG" for "desert home guy" since he doesn't give his name.) So this
is the basis for my code, and you'll find similarities between my code
to his code, though I re-wrote most of it to fit my own situation.
Like DSG, I'm using this Serial LCD controller to make all my LCDs
accessable via a serial port. I'm using the Arduino Mega 2560 which has
four hardware serial ports, so I have one for programming the Arduino
and three to talk to the three LCDs.
Instead of the LM335 Temp sensors DSG is using, I went with
DHT22 Temperature/Humidity sensors. The humidity is a nice thing to be
able to display on the LCDs in each room. I get these from random Ebay
vendors.
I use a DS1302 Real Time Clock as a backup clock, in case the network
goes down, this will keep the thermostat system time.
I use little 5v relays to interface with the 24VAC control inputs to
the heating system.
The remote display units are "almost" dumb: they contain the LCD with serial
"backpack", a DHT22 Temperature/Humidity sensor, a relay, and a voltage
regulator to drop the "raw" 9v to 5v for the other parts. This needs
8 wires (relax, 2 more are called out below), so I use RJ45 connectors
and ordinary Cat5 cable to interconnect the remote display units with
the main display unit.
I say "almost" because the serial LCD controller has a PIC micro on
it, and it has three unused GPIO pins that the vendor has made available
as three digital outputs controllable via serial commands. I use this to
scan a 2 x 3 "keypad", where the three columns are driven by the GPIO pins
on the PIC, and the 2 row lines are sent back to the main unit on the
Cat5 cable and from there into 2 of the Arduino Mega 2560's digital inputs.
Unlike DSG, I don't try to use the 24VAC from the heating system to power
my thermostat, I just have a 9v DC wall wart mounted in the basement and
feed its output up the wall with the rest of the wires and into the
back of the main unit. I felt that if I tried to convert the 24VAC to
5V, I would be drawing too much from the 24VAC, causing it to always
think I was calling for heat.
Notes and Caveats in no particular order:
-
You will need to filter your temperature readings to prevent the odd
spurious/noise reading from causing the system to send a "ON-OFF spike"
to the heating system, which cannot be good for it.
I use infinite impulse response filters (IIR) because they
don't require much computation for the amount of filtering you get.
I classify inputs as "close to current reading" and "outlier". For
the "close" values the filter is:
new-value = old-value + (reading / 4.0) - (old-value / 4.0)
and since I'm using floating point arithmetic, this works reasonably
well.
For the "outlier" values I have a much slower IIR filter.
new-value = old-value + (reading / 16.0) - (old-value / 16.0)
In this way, if the temperature really does suddenly jump 20 degrees,
the system will eventually catch up to this change.
The real value is that if a sensor becomes unplugged (or fails) the
temperature will drop to some stupid number, like -200 degrees, and
then when I fix the problem, the temperature will recover back to
the actual reading. I want the display to show a failure if there
is one, but I don't want it reacting quickly to the odd spurious
reading caused by noise. I'm driving the serial line to the DHT22's
over 25 to 50 feet of wire, so noise is likely.
-
The relays draw enough that when they are on, there isn't quite enough
voltage making it to the remote units and the LCD units lose contrast.
This doesn't seem to hurt anything, and so I haven't bothered to try
to raise the supply voltage by one more volt.
-
Yes, using RJ45's for non-ethernet will disturb some people. I clearly
label the jacks on the back of the units "NOT ETHERNET", so it doesn't
bother me.
-
I use strong magnets (taken from old disk drives) to attach the units
to the wall. I glue the magnets to the back of the boxes, and a small
piece of steel to the wall, and between that and the chunky wire going
into the hole in the wall, the units are stable and yet easy to remove
for servicing/hacking.
-
You can do quite a lot of multitasking in an Arduino project by cleverly
using timers to schedule "events". What this means is that instead of
doing something like:
// read Foo once a second:
digitalWrite(FOOENABLE, LOW);
delay(500);
Foovalue = digitalRead(FOOPIN);
delay(500);
digitalWrite(FOOENABLE, HIGH);
You have a milliseconds counter running in a timer interrupt service
routine, which looks like this:
inside TimerISR(), which runs every millisecond:
fooTimer++
. . .
and then inside loop():
if(fooTimer >= 1000) {
fooTimer = 0;
digitalWrite(FOOENABLE, LOW);
}
if(fooTimer = 500) {
Foovalue = digitalRead(FOOPIN);
digitalWrite(FOOENABLE, HIGH);
}
-
My code profiler was
invaluable in figuring out where the time was being spent, and in fact
I wrote the profiler so I could debug the thermostat "real-time". It's
hard to juggle all this: the scan rate of the three keyboards, the scan
rate of the temperature sensors and the low pass filtering of same, the
web ui responsiveness, and the rest of the housekeeping, like actually
monitoring the temperature and controlling the heating system.
-
I have the IP address hard coded in the code, but I also have a "hidden"
button that, if pressed during boot, causes the code to use DHCP to
get an address from the DHCP server. So if I change my network class
C for some reason, I can get the thermostat working again simply by
rebooting it with the "use DHCP" button held down. Successful aquisition
of an IP address is reported on the LCD and held there until the button
is released. Finally, an LED on the side of the box illuminates if the
thermostat successfully finds an NTP server, so that I know networking
is working and the box is connected.
-
Those illuminated displays are really bright at night. I had to turn
the duty cycle almost to zero in order to be able to sleep at night,
otherwise the bedroom LCD backlight lit up the whole room. I do have
a timer for each display, so that the LCD backlight gets brighter when
one touches the keyboard, and dims again after several minutes of
inactivity.
-
Of course, the settings are stored in the internal EEPROM of the Arduino.
-
It's worthwhile learning about PROGMEM. In this code, the HTML template
for the web page is stored in PROGMEM so that it doesn't use up valuable
RAM. Anytime you have constant data of any appreciable size, it should
be stored in PROGMEM. For an Arduino UNO, "appreciable" might be as
little as 100 bytes, as the UNO has so little RAM.
Link to the code. Last updated 20210117.
Link to a copy of the DHT library.
Link to a copy of the DS1302 library.
Note: this code compiles with the Arduino version 1.5.4 IDE.
Note 2: the code has been updated to my latest version, which has the network parameters (ip address, gateway, etc) stored in EEPROM.
Note 3: the code has been updated to my latest version, which includes the MQTT feature and fixes a bug with the web page.
Pictures of the boxes. No pictures of the insides, it looks like
a dog's breakfast in there.
The main thermostat box in the living room. You can see that I spared no expense on the front panel labeling.
The bedroom control unit (with camera flash).
The bedroom control unit (without camera flash).
The kitchen control unit.
The rear of the kitchen control unit showing the mounting magnets and
the RJ45 connector and the control pair going to the heating unit.
Screenshot of the web interface.
A schematic would be a lot of work, showing all the interconnects and
cabling, and is specific to my situation, so instead, you get this
wiring list.
The User Manual.
New feature added much later: The thermostat now "does" MQTT. It broadcasts temperature and humidity readings and how much "vacation" time is left. One can also set the zone temp set points and vacation times via MQTT. You will have to edit your MQTT user and password into thermonew.ino. If you don't have MQTT, you will need to "comment out" the MQTT code. This is left as an exercise for the reader.
The Android application to view/change
the current temperature of the three zones. The app scrapes the web
page for the current setpoint and current temp, so if you change the
format of the web page in the Arduino code, you will have to rebuild
the Android app. Source for that here.
It's built with MIT App
Inventor 2.
Track Back
hackaday.com
William Dudley is a retired electrical engineer and computer programmer who spent many years programming embedded systems, on everything from 8048's to 68HC11's. He spent many more years writing code for MSDOS and then Unix systems, and is glad to be retired. He appreciates the Arduino for it's accessible development environment and wealth of libraries supporting every kind of hardware imaginable.
|