Building a Nest Thermostat With Arduino Part 1 - Control

The idea of a fully automated smart-home is an appealing one, and having a computer take care of repetitive tasks makes sense not only because it lets you be lazy, but also because a computer can make decisions based on data that you might not think or care about. One good example of a computer that can make smart decisions is the Nest Thermostat. Nest learns your schedule, how long it takes to heat up your home, and uses this data to decide when to turn your heating on or off. As a result, your home is heated to the temperature you want it to be, when you need it to be, without wasting energy.

Nest is the main motivation for my next Arduino project, which is to automate control of my appartment's central heating. In order to automate it, I need some way of controlling it, a way of turning my heating on and off programatically. The way that Nest does this is through a heat link device which connects directly to the boiler's thermostat cables. My existing thermostat is wireless, and I didn't want to dig around to find or play with thermostat cables, so I looked for an less intrusive option. I found a post on Steven Hale's blog which describes how he intercepted the on/off messages sent by his wireless thermostat to his boiler, and then re-transmitted those same messages to programatically control his central heating. I follow this same approach, but make use of a cheap USB TV tuner and Software Defined Radio to capture the control signals, instead of a soundcard based logic analyser.

RTL-SDR is a project for building a Software Defined Radio using the RTL2832U chip found in most USB TV tuners. The basic setup will set you back less than £10, and allows you view and record radio signals in the frequency range of 20 MHz to 1.7 GHz. This range includes AM/FM/DAB radio, digital TV, GSM channels used by mobile phones, and most importantly for this project, the un-licensed (at least in the UK) 433 MHz channel for short range devices such as central heating thermostats.

Capturing the Messages

Using SDRSharp, I was able to set the listening frequency to 433 MHz and watch radio traffic stream in real-time. To check that I had tuned into the same frequency as that used by my thermostat, I turned up the temperature setting on the thermostat so that it would transmit an ON signal to the boiler. I noticed three spikes show up in SDRSharp in short succession, and similarly when forcing the corresponding OFF signal. To capture the data being sent, I futher tuned the frequency in SDRSharp to that at which I saw the spikes, and clicked record.

Analysing the Messages

I opend up the recorded waveforms in Audacity, an audio editor that happens to work well for analysing radio signals. Here we can see three spikes, where the ON signal is repeated three times.

Waveform

Zooming in a bit closer, we can see that the radio signal is composed of on/off bursts of varying lengths. This is known as on-off keying, where a digital 1 bit is represented by a high signal of a given duration, and a 0 bit is represented by a high signal of a different duration. In this example, we can see two pulse lengths, one which is ~ 1000 microseconds long and the other ~ 500 microseconds, and the gap between bits is ~ 400 microseconds. If you think of the long pulse as representing a digital bit 1, and the short pulse representing the digital bit 0, then this signal represents the 31 bit binary string 0000001000100001000010000100000.

Close-up

Re-transmitting the messages

To replicate the signal sent by my thermostat to the boiler, I attached a cheap 433 MHz radio transmitter (with a make-shift antenna) to my Arduino Nano, and had it send out pulses of the same duration as measured in Audacity.

Arduino Transmitter

The standard Arduino delay(x) method pauses the application for x milliseconds, which doesn't quite give us enough resolution to reproduce the signal, as the durations we require are on the order of 100s of microseconds. Instead I use the delayMicroseconds() method, which accurately pauses the application for a given number of microseconds. With constants defined for the durations of a 1 bit and a 0 bit pulse, as well as the gap between bits, the code for replicating the signal essentially consists of a sequence of calls to sendOne() and sendZero(), where sendOne() is simply:

void sendOne() {}
    digitalWrite(TRANSMITTER_PIN, HIGH);
    delayMicroseconds(DATA_ONE);
    digitalWrite(TRANSMITTER_PIN, LOW);
    delayMicroseconds(GAP);
}

The code did take a fair bit of tweaking however, with the main issue being that the gap between bits isn't the same for the whole signal, but once I had accounted for that it worked!

Next Steps

Now that I can control my central-heating programatically, the next steps toward building this project into something useful will include adding a temperature probe, and building a web interface for monitoring and configuring it. I could port the code to the ESP8266 WiFi platform, and have that run the web server as well as send the control signals. I should then be able to track data such as how long it takes to heat the house, have it learn my schedule, and eventually make it fully autonomous.