rossharper.net

Decoding a Siemens RCR10/433 Thermostat Signal to Control a Boiler from a Raspberry Pi

This is a quick post in response to a question related to my boiler control Github project: https://github.com/rossharper/boilercontrol I intend to eventually perform a more comprehensive write-up of this whole project.

Background

I had been looking to start tinkering with my Raspberry Pi and do some home automation for a while. We have a Siemens RCR10/433 wireless thermostat programmer that controls our boiler and I wondered if it would be possible to hack the radio signal and take control of the boiler myself. Thus, I could create my own smart heating system inspired by products such as Nest and Hive. This post roughly outlines the process I used to sniff the signals sent between the thermostat and the receiver relay that controls the physical call for heat signal to the boiler.

Research

A quick Google search and I’d found some other people that had tried to do a similar thing with their boilers. First up, Steven Hale, who had a couple of great posts about Reverse Engineering a Worcester-Bosch DT10RF wireless thermostat in which he sniffs his thermostat’s signals and a further post with the detail of how he built a Soundcard Logic Analyser to do the sniffing. Secondly, I found a similar post (https://damn.technology/controlling-british-gas-wr1-receiver-arduino) detailing the same method of sniffing the thermostat’s radio signal. I identified that my thermostat uses the same radio frequency as those in the above posts: 433MHz (the clue is in the product name!). Perfect, I had something to go on. 433MHz transmitter and receiver ordered….  

Sniffing the Signals

433MHz transmitter and receiver

Once I had my 433MHz transmitter and receiver, I wired them up using a prototyping breadboard to my Raspberry Pi in a vain attempt to simply read the signals straight out of the air. The plan was to listen to the signal on the receiver’s data port whilst operating the thermostat to trigger its signals. No luck. The software I was using on the Raspberry Pi did not produce any results. Presumably the encoding used by my thermostat is non-standard. (I don’t remember the software off the top of my head. RC Switch, perhaps?)

So, the easy approach didn’t work — it was time to follow the posts mentioned above and build the ghetto-style logic analyser.

For this, I needed to buy the following:

I didn’t go to the extent that Steven Hale did, I just wired up one channel, and didn’t bother with shrink wrap to make it look professional. This is probably a one-off tool for me.

Ghetto Logic Analyser

Then:

Once I’d recorded some signals, I found them amongst the noise, and zoomed in to see them. But something was wrong…

First signal

This wasn’t the clear, decipherable, square-wave I was expecting.

When I looked at the levels, it was clear that I was only getting a very faint signal. I guessed it must be something to do with the line level on the Mac, and that the resistance of my analyser was pulling the signal too low. So I experimented…

I modified the analyser to add a second 18k resistor on the unused lead. This time in series, rather than across the data and ground leads.

Ghetto Logic Analyser V2

(NOTE: I was probably taking a bit of a risk here, that my soundcard might have been damaged by the higher voltages. But I figured “it’s 5V from a Raspberry Pi — what’s the worst that can happen?”. If you are following me, proceed with caution — I am no electronics engineer!)

Bingo. I had a signal. It appeared to be upside-down, so I flipped it in Audacity.

Here is the boiler ON signal when the noise either side is removed:

Tranmission received

(I later flipped HIGH/LOW back in my program, I just found the signal easier to read this way round)

There appear to be a number of packets. Zooming in to an individual packet, we can see the individual HIGH/LOW signals.

Zoomed in view of a packet

The signal is pulled LOW for a period before the packet begins. There are also two different lengths of pulse: one must represent a binary ‘1’ and the other a ‘0’. I guessed that a long pulse would be a ‘1’ and the short pulse a ‘0’. Then I got to work reading all the individual 1s and 0s in the packets. I discovered that the packets were repeating and that for the ON signal there were just two packets:

    100001000101111010000000100000001
    111110111010000101111111011111110

It seems that:

    100001000101111010000000100000000
    111110111010000101111111011111111

Now we can see that:

( * the ID is either hardcoded into the thermostat and learned by the receiver during pairing, or it is created and shared during pairing. I hope it is the former, as it would be a pain to have to keep sniffing a signal again if the receiver ever loses contact with the transmitter! I haven’t tested this yet.)

Now that I had the packet contents, there was one more thing left to determine: the timings of the pulses and the gaps between them. Using Audacity, I measured (in nanoseconds) the length of:

Most of these are constant values, except the delay between packets which appears to be random. However, I just recorded each of these and will replay the same values each time in my boiler control program. If you are following this yourself in order to hack an RCR10, you probably don’t need to measure the lengths. You probably just need to decode the packets and plug the 1s and 0s into my boilercontrol program(see Update, below).

Replaying the Signals — Boiler Control Program

Now that I had sniffed the signals, it was time to see if I could replay them through my 433MHz transmitter, connected to my Raspberry Pi, to turn my boiler on and off. So, I created my “boilercontrol” program.

It’s written in C++, based on stuff I’d read on various blogs and websites that I don’t remember, and uses the WiringPi library to control the pins on the Raspberry Pi in order to send data to the transmitter. It’s written in C++ for two reasons:

The packet values and pauses are all hardcoded into the program currently. If I was doing my day job, and following proper software engineering practices, this would be configurable, and there would be unit tests… but this is a home project hack, so I won’t bother with that for now.

I tried this on the Pi… and it failed. No click from the boiler relay 🙁

To debug it, I set up my sniffer again. This time it would listen to the signals I was sending, rather than the original thermostat signals. Comparing my signals to the originals, I could see that they all seemed to be out a little: a little too long. Damned non real-time operating system! I can’t remember what the value was off the top of my head, but I recall it was in the region of 90ns. In true bodger style, I simply subtracted that from my values in the program and tried again.

Eureka! It worked! The boiler clicked on as I issued the command:

    sudo ./callforheat 1

And off again with the command:

    sudo ./callforheat 0

(the sudo was needed in order to access the pins via WiringPi — I believe this is no longer needed in the latest version of Raspbian at the time of writing)

Now I have a command that I can use, and write software around, to control my boiler. 🙂

Update — 30th March 2016

Github user PatchworkBoy found when he sniffed the code for his set that the on/off signals are not the same, exactly, like mine appeared to be. For his units, he found that the on/off codes were slightly different in in the middle too (at the 10th bit):

    ON packet pair
    1 0001 1000 0100 1000 0000 0001 0000 0001
    1 1110 0111 1011 0111 1111 1110 1111 1110

    OFF packet pair
    1 0001 1000 1100 1000 0000 0001 0000 0000
    1 1110 0111 0011 0111 1111 1110 1111 1111

Furthermore, he found that the timings (the gaps between packets and pulses etc) were also different. Therefore, if you are following this blog post and attempting to decipher the code for your own units, you’ll probably have to read both the on/off codes and measure them fully, as it appears you may not be able to assume that these details will be the same as they were for me.