Smart Thermostat for Raspberry Pi and eInk Display
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
John Mertz 19d8074c22 Fix links in 5 months ago
bin Initial commit of simple-server 5 months ago
include Initial commit of simple-server 5 months ago
src Initial commit of simple-server 5 months ago
LICENSE Initial commit 6 months ago
Makefile Initial commit of simple-server 5 months ago Fix links in 5 months ago


Smart Thermostat for Raspberry Pi and eInk Display


Currently under development with no working release.


This project strives to create a control server and graphical client for the Raspberry Pi (specifically the Zero W) to provide smart thremostat options to a standard 2-wire mercury thermostat or 3-wire digital thermostat.


The goal is to create a server with a socket that allows for commands to be given, logic to determine the correct state of the furnace control given the commands and current conditions, and a configuration management system to store schedules and other preferences.

The server should also be readable with details provided for the current state and existing configuration settings.


The built-in client will provide a GUI for an eInk display with multi-button interface via the Pi's GPIO pins. Optional clients can also be created, such as a Web UI to send commands to the server and display current data.


See HARDWARE.pdf for detailed build documentation.

  • Raspberry Pi (I will be using a Pi Zero W)
  • 1.54” Tri-color WaveShare e-Paper Display (commonly available online for ~$30 CAD)
  • DHT-22 Temperature and Humidity Sensor
  • G3MB-202P DC 5V Solid State Relay
  • Momentary Buttons x3
  • NPN Transistor (2N2222 or similar)
  • Case (optional)

Integration with existing thermostat

Integrating a smart device into either type of thermostat is quite simple. We simply need our device to be capable of completing the curcuit using two of the wires available within the existing thermostat. In either case, the existing thermostat should be left with all the wires where they are, but with an extra leads coming from the positive and negative lines. These will allow us to complete the curcuit using our relay, regardless of whether it is being completed by the existing thermostat. The control on the existing thermostat should be set as low as possible, or at least to the minimum “safe” temperature so that it will only kick in if the relay fails to complete the curcuit (ie. if the smart device is not functioning as expected). The furnace will be run if either or both thermostats complete the curcuit, so this will allow for a backup minimum temperature without interfering with the smart thermostats normal operations.

2-wire mercury thermostat

The interface with a standard 2-pin mercury thermostat is quite simple. In these devices, the mercury balances within a glass tube. When the room is at or above the desired temperature the mercury has expanded such that it's balance tips to the vacant end of the tube. When the room cools, it contracts, allowing the balance of the tube to shift, causing the mercury to slide to the other end of the tube. This end of the tube contains the positive and negative lead for the thermostat control and since mercury is conductive, it is able to bridge the gap between these and complete the curcuit.

Thermostats in North America traditionally run on 28V DC, so we simply need a couple of lead wires to a relay that is then controlled by one of the 3.3v GPIO pins on the Raspberry Pi.

Note that the hardware setup described here works correctly. I can modify the display using the WaveShare sample code and when I command heat via the Relay using the raspi-gpio command I successfully get heat. So few, if any changes are expected to the hardware design.

3-wire digital thermostat

Similar to the mercury thermostat, the 28V circuit simply needs to be completed. The difference with these devices is that the third wire provides a constant voltage so that the digital display may remain powered even when this circuit is incomplete. Simply identify the control and ground wires and leave the constant line alone. As above, change your digital thermostat settings to a backup temperature below that which you expect to be reached during normal operation so that it will only override the Pi in the event that it fails.

Personal notes

Exact implementation of the hardware is up to your own personal design decisions, but here are a few comments on my design:

  • I used a Pi Zero without the GPIO headers, opting instead to solder the wires directly to the board. This was done because there is very little room in the case I chose once the display has been added.
  • I used the UniPi Case Zero with a hole cut in the top for the display. As mentioned, it provides very little extra space, but was just large enough and has nice flat bezels on each side for easy face mounting.
  • (Don't try this at home) My thermostat is directly above a light switch but miles from a standard outlet, so I took a small 5v wall adapter and placed it behind the light switch. The switch is a 3-way curcuit, so one pole of the switch is always live meaning that the Pi is powered regardless of the switch position. The Pi will lose power briefly whenever the switch is flipped, however this switch controls a fan that I rarely use. Frequent on/off cycles risks corruption to the SD card, so I keep a backup. It is highly recommended that you use a proper outlet. Note that if you have a 3-wire thermostat, you could simply draw power from the thermostat wires instead as long as you limit the voltage first (you will not be able to connect the 120/240V adapter to 28V and get 5V out).
  • I used 6x6x9mm momantary micro switch buttons. 9mm was an abundance of caution because I didn't want them to be too short to penetrate the case. 6x6x6 is likely sufficient.


Heating Modes

The following heating modes will be targetted for an initial release:

  • Default - If no other heating commands or schedules are active, maintain this heat. [Exactly 1 at any time]
  • Hold - Set a temperature to maintain permanently, until the next scheduled event, or for a fixed period of time. [Max 1]
  • Schedule - Set a target temperature at a specific time. This will continue until the next scheduled event or until it is overriden by a hold. [Unlimited]
  • Offset - Modify the existing temperature by a certain amount, positive or negative, permanently, until the next scheduled event or for a fixed period of time. The same offset will apply even when the primary temperature changes. For example, if the scheduled temperature is 20 and the offset is +2, then 22 will be the current target. If a new scheduled event then updates to 18 and the offset is set to continue, then the new target will be 20. [Max 1]


The following preferences will be targetted for an initial release:

  • Celcius or Fahrenheit display.
  • Minimum and Maximum run cycles. Minimum has priority over upper variance.
  • Minumum rest. Priority over lower variance.
  • Allowed variance. ie. To prevent frequent on/off cycles, allow the temperature to fall a certain amount before commanding heat and heat a certain amount beyond the target before turning off.


The following API functions will be targetted for an initial release:

  • JSON input and output
  • Read current state and settings
  • Get next event and time
  • Command each of the heating modes
  • Update each preference


The following Server functionality will be targetted (required) for an initial release:

  • IPC socket (read/write)
  • Validate and parse JSON
  • JSON return codes for all input
  • Init stript with SystemD Unit
  • Persistent settings storage
  • Control of GPIO
  • Reading from thermostat via GPIO
  • Evaluating state, conditions and commands to determine current and next likely action
  • Broadcast actions to all connected clients
  • Broadcast any temperature change of 0.1 degrees to all connected clients


In order to allow for other clients, a simple wrapper library will be created for each function of the API. This will provide easier reference material for other programmers to make their own clients in a high-level programming language. I'll probably do this in JavaScript since that will likely be my first target for a client, but it could easily be Perl or Python depending on how I feel at the time.


Development will be targetted at Debian Bullseye but the aim will be for Posix compliance. Will also aim for minimum dependencies, included in source or fetched during install where possible.

The bcm2835 C library has been selected for interfacing with the GPIO pins. This should be included with Raspbian (Raspberry Pi OS).

JSMN JSON parser is likely to be used.


Instructions will be available in once a production release is available.


I'd be very interested in contributions for other localizations, heating modes, libraries and so on. Note that nothing is anywhere near being decided upon, so all aspects of the architecture, API, libraries, etc. are subject to change, so I'm open to all suggestions early on but cannot promise not to break anything if you do not communicate. Please open issues and pull requests through my Gitea (GitHub authentication is supported).