NuHeat floor thermostats are great but have a couple of drawbacks: they cost a lot and they fail from electricity spikes. After two replacements, I decided that a cost of a NuHeat thermostat is sufficient budget for making something cool while not paying NuHeat for their extortive designs.
Description
It’s a Raspberry PI-based GFCI-protected 16A thermostat with 7 inch dimmable touch screen. User interface is web-based, accessible over WiFi or the touchscreen.
User Interface
Web page running on Chromium browser in kiosk mode on a 7-inch touchscreen:
Mobile User Interface
Same web page, on a mobile phone:
Hardware
Bill of materials
Thermostat Base: (~$44.21) – I could’ve used just this, with user interface accessed using my mobile phone.
- GFCI Outlet, 15A – $18.72
- Relay 230V 16A – $2.98
- Power supply – $7.95
- Raspberry PI Zero W – $10.00
- Transistor – $0.49
- Resistor 1kOhm x2
- Diode – $0.44
- Analog-to-Digital Converter – $2.68
- USB micro B plug – $0.95
Screen: ($98) – Nice addition with touch control and weather display
- 7-inch touchscreen – $47.50
- HDMI plug – $6.50
- Mini HDMI Plug – $6.50
- Ribbon cable – $2.75
- HDMI touch screen driver – $29.95
- Driver extender cable – $4.50
- USB micro B plug x2 – $0.95
- Basic White 4×6 Photo Frame from Aaron Brothers – $5.00
Dimmer: ($6.95) – without it, the screen is bright enough to fully light the bathroom at night
- Light sensor – $6.95
Total: ~ $149.16 plus shipping. Pretty close.
Connections
I had to chisel back of the frame to widen it a bit to fit the screen. The components are attached to the back of the screen with small patches of 3M VHB double-sided sticky foam tape.
All connections are from Raspberry PI GPIO pins to sensor pads named correspondingly:
Light Sensor
- 1 – +3.3V
- 3 – I2C SDA
- 5 – I2C SCL
- 6 – GND
Dimmer PWM control to HDMI Driver
- 12 – PWM (HDMI Driver backlight control pad)
- 6 – GND (hm, looks like 14 would have been a better choice)
Touchscreen
- USB and HDMI connections
Note: USB cable, both ends being micro-B plugs, requires a jumper between pins 4 & 5 to indicate master device on Pi side
Also, I soldered all the brightness jumpers on HDMI driver board for maximum brightness.
Floor Sensor ADC
Relay Control
Power Supply and GFCI Outlet
Power supply is removed from its original bulky case and placed into a custom housing made by two diagonally cut pieces of a small plastic box.
Both relay and power supply are connected to the output terminal of the GFCI outlet – terminals protected by ground fault interruptor (the other pair of terminals are AC line inputs). Couple of nice benefits of connecting power supply after the interruptor: Raspberry can be reset by outlet’s “Reset” button or turned off altogether by the “Test” button.
Again, both power supply and relay are attached to the outlet with 3M VHB tape.
Software
Most appreciated feature turned out to be the current weather display and weather forecast from National Weather Service.
Base Thermostat
holdtemp.py – python script reading temperature from ADC, then turning relay on or off based on the temperature threshold in /run/lock/set-temp.txt. Writes current temp to /run/lock/current-temp.txt
restart-holdtemp.sh – shell script to kill holdtemp if it hangs (and does not update current-temp.txt for 2 minutes)
settemp.sh – shell script that sets set-temp.txt – runs from cron. Ignores the setting if the file /run/lock/schedule-status.txt is not present, this is used to disable scheduled operation while leaving schedule settings in place
Web-based Controls
/var/www/thermostat – contains php scripts for reading and writing crontable and current-temp, as well as html, css, fonts and javascript to display web ui. The UI can be used from any browser, e.g. from a phone
Screen
runkiosk.sh – script to launch X11 GUI, with Chromium browser in kiosk mode pointing to the web-based thermostat user interface URL (http://localhost)
Dimmer
dimmer.py – script that reads light sensor and sets PWM pulse width that adjusts screen backlight
Requires pigpiod to be installed and running
Setting Up
Web server config
apt install nginx php-fpm
sudo mkdir /var/www/cache; sudo chown www-data.www-data /var/www/cache
sudo mkdir /var/www/thermostat; sudo chown www-data.www-data /var/www/thermostat
unpacked website folder from github into /var/www/thermostat and updated dependencies using npm and bower (actually, just unpacked a zip file)
added nginx config file:
sudo vi /etc/nginx/sites-enabled/bathroom
with contents
proxy_cache_path /var/www/cache levels=1:2 keys_zone=my_cache:1m max_size=1g inactive=60m use_temp_path=off;
server {
listen 80 default_server;
listen [::]:80 default_server;
root /var/www/thermostat/app;
index index.php index.html index.htm;
server_name bathroom;
location / {
try_files $uri $uri/ =404;
}
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php7.0-fpm.sock;
}
location /weather_current {
proxy_cache my_cache;
proxy_pass https://api.weather.gov/stations/KSFO/observations?limit=1;
proxy_cache_use_stale error timeout http_500 http_502 http_503 http_504;
proxy_ignore_headers Cache-Control;
proxy_cache_valid any 60m;
add_header X-Cache-Status $upstream_cache_status;
}
location /weather_forecast {
proxy_cache my_cache;
proxy_pass https://api.weather.gov/gridpoints/MTR/91,115/forecast;
proxy_cache_use_stale error timeout http_500 http_502 http_503 http_504;
proxy_ignore_headers Cache-Control;
proxy_cache_valid any 60m;
add_header X-Cache-Status $upstream_cache_status;
}
}
The two cached URLs above are location specific, if I lived not in SF Bay Area, I would have used URLs with other coordinates in parameters.
Set webserver to auto-start:
sudo systemctl enable nginx
It will need to be started in rc.local ac described below, though, as it fails to resolve weather service URLs above on start 🙁
Screen config
edited config.txt
sudo vi /boot/config.txt
added to force 800×480 mode and to set overscan margins where screen is hidden behind frame edges:
overscan_left=43
overscan_right=21
hdmi_group=2
hdmi_mode=1
hdmi_mode=87
hdmi_cvt 800 480 60 6 0 0 0
Other related configs
I installed package for running chromium in a kiosk mode:
apt install matchbox-window-manager nginx php-fpm
Enabled pigpio daemon:
sudo systemctl enable pigpio
edited crontab:
crontab -e
added temperature and relay control restart script
*/5 * * * * /var/www/thermostat/scripts/holdtemp_restart.sh
edited rc.local:
sudo vi rc.local
added (needed to make manual start of nginx because is needs resolver to be connected to the internet and working properly before it can start with proxy directives in the configuration):
sudo chmod 0660 /dev/tty*
sudo service nginx start
sudo -u pi xinit /var/www/thermostat/scripts/runkiosk.sh&
sudo -u pi /var/www/thermostat/scripts/dimmer.py&
Notes
Why ADC is read in differential mode with one input hanging in the air? Why ACD is read multiple times over several seconds and averaged?
It turned out that the thermistor cable picks up all sorts of noise, most of it being AC current. Having one ADC input in the air helps to alleviate this a bit, the rest is handled by averaging: when several full wavelengths of samples are captured, the phase of start and finish matters less.
Looking back any pitfalls I could have avoided?
Light sensor should have been placed on top of the frame. At the bottom it’s covered by hand when using touch screen, which then promptly dims.
Very cool. I’m going to try something like this. Might go headless though and see about integrating this straight in to Home Assistant as I have 6 different pads and 6 controllers at present.
Perhaps a 2 gang box so there’s enough room for the relay/ADC/Pi zero/GFCI plug, just blind off the one Decora. Perhaps I can mate a small display to a Decora hole (even 3D print something) so there can be a local temp indicator.
Thoughts on that integration?
Thanks. Headless is the way to go, unless you require controls and display at the point of use, as Chromium on PI zero pretty sluggish. If you can put a double box into wall, all the better! (I could not)