Reverse Engineering a Thierry Mugler Wireless Weather Station Protocol

Introduction

The awesome Action store sold cheap (6,99 eur) weather stations (branded `Thierry Mugler') with a wireless sensor. I bought two of them, one for normal usage and one for hacking. I tought that it would be neat to log temperature and humidity values from the sensor and plot it. In short, I extracted the receiver module from the base station, reverse engineered the protocol, and connected an Atmel Xmega micro controller to it which decodes temperature/humiditity values. These are fed into my thin client home server, plotted and shown on a web page. More details below.

Monitoring the Signal

I had no idea what to find inside the base station, the worst would be a PCB with an ASIC black blob and some wires to the buttons and LCD. What I found was actually the best: a main PCB, and a second smaller PCB which had to be the receiver board. The whole construction gave me the impression of being rather solid and high quality. The receiver board had four wires: vcc, gnd, data (text on the PCB!) and an antenna wire glued to the casing.

Having no oscilloscope, I connected the data and gnd wires to the line-in of my sound card, and started Audacity to monitor the running base station. Every minute I saw a quite large signal chunk (several seconds, iirc), surrounded by a flat line. Studying the chunk, most of it appeared random to me, with only a small part looking like data. When I recorded and compared some of these chunks, I started to loose confidence in the project, as there appeared to be no correlation between the chunks -- while the base station reported identical temperature / humidity values.

The next day I understood what was happening. I initially believed that the receiver was outputting zero when the transmitter was idle, and that all output from the receiver was actually broadcast by the transmitter. I was wrong. What actually happens is that once the base station receives a signal from the sensor, it syncs to it. Every minute, the base station powers on the receiver only when it expects data from the sensor (with some additional safety time). This is most likely done to save energy. The receiver then outputs first any noise it receives, next the sensor data, then additional noise, and finally it is turned off. I discovered this when I reset the base station while monitoring it: I was seeing a much larger data chunk now (noise) terminated by a signal that made sense (from the sensor).

Reverse Engineering the Signal

The signal in Audacity looks like this:

signal

First, I believed that I was looking at a Manchester-coded signal, but that was not the case. I recalled a forum post where someone reported a signal where the bits were pulse-length encoded. I recognized three different pulse lengths: two common pulses with a medium and short length, and an exotic long length pulse. For no good reason I called the medium pulse '1', the short one '0' and the long one 'S'. I manually decoded a part of the signal:

(noise)111111111111111S000001100111001110111101111101010101000011111111111111111S0000011001110011101111011111010101010000(noise)

The block of 1's on the start looked like a preamble, terminated with S. In the centre, this pattern appears again. After dropping the preambles and aligning the two S's, one gets:

S0000011001110011101111011111010101010000
S0000011001110011101111011111010101010000

Which looks a lot like the data is transmitted twice every minute. I started recording with Audacity for a longer period, while heating up the sensor in my pocket with my body warmth, writing down the values on the base station's display. I dumped the signal from Audacity to a .raw file and wrote a program to do the boring pulse-length decoding job for me. I got this table:

+25.8 @ 43% -- 0000111001011011001111011111111000000000
+25.9 @ 40% -- 0000011001011011111111011111100100100000
+26.2 @ 57% -- 0000101110011011000101011111111000000000
+31.9 @ 42% -- 0000011001110011101111011111010101010000

After staring for a while and swapping 0 and 1, I got this:

STt.d @ Hh%         dddd tttt TTT S hhhh HHHH      CCCCCCCC
+25.8 @ 43% -- 1111 0001 1010 010 0 1100 0010 0000 00011111 1111
+25.9 @ 40% -- 1111 1001 1010 010 0 0000 0010 0000 01101101 1111
+26.2 @ 57% -- 1111 0100 0110 010 0 1110 1010 0000 00011111 1111
+31.9 @ 42% -- 1111 1001 1000 110 0 0100 0010 0000 10101010 1111

S = temperature sign, T = first digit, t = second digit, d = decimal; H = humidity first digit, h = second digit. On the left the value on the base station is shown, on the right the encoding is shown. The numbers are BCD encoded, with the LSB on the left. The sign bit (S) is a guess. On the right the checksum is denoted by C. I was not yet able to reverse it. The 1111-nibbles on the start are fixed (and not part of the preamble), the 0000 between HHHH and CCCCCCCC is also fixed.

*Correction/update 2012: It's finally freezing here, and my assumption about the sign bit S was wrong, it is actually positioned right after the HHHH field. The correct encoding is:

1111 dddd tttt TTT 0 hhhh HHHH S 000 CCCCCCCC 1111

Building Hardware

As I wanted to log the sensor's data, I planned to use my low-power Linux thin-client-server to collect the data. The least work approach was to connect the receiver PCB to the server's sound card line-in, and make my .raw file reader work on a live input stream. That's what I thought at least, but for reasons unknown to me I was not able to capture the receiver's data with my server's mic-in. I ordered an xmega xplained development board, after I had read about it on hackaday. I could have used any uC, but it was cheap, and I already had some Atmel experience ages ago, back in the days when the AT1200 was new hotness.

Empirically, I found out that the voltage on the receiver board's data line was too low to trigger the Xmega's (3.3V) data pins. Therefore, I used the analogue comparator with a threshold I obtained experimentally. Using the Xmega's crazy event system, I made a timer that resets on changes in the analogue comparator's output. The Xmega has even an automatic pulse-width measurement timer setting (TC_EVACT_PW_gc), but it measured the wrong side of the signal and I could not find out how to invert the analogue comparator's output while routing it through the event system. Anyway, I needed to add only a single extra line in my interrupt routine to manually measure pulses so it was not a major problem.

After successfully measuring pulses with the Xmega, I added some code to it that translate pulse lengths into '0', '1', 'S' or invalid. Its output is forwarded to some additional codes that waits for the preamble, and reads the forty bits that follow it. After a successful reading (without invalids), the bits are sent to the UART in plain text, surrounded by square brackets, e.g.:

[0000011001110011101111011111010101010000]

To my surprise, things went rather smoothly and I received these strings via the USB-serial interface of the Xplained board on my Windows machine. Unfortunately, I discovered that this interface is completely broken on Linux. Next, I built a 3.3V logic to serial converter stripboard using the MAX3232, which connects to a USB-to-serial converter that is plugged in my Linux machine. Which actually works. Phew. On my Linux machine, a simple program is running that waits for a bracket-enclosed string, decodes it and writes it to text file. Graphs of it are produced using gnuplot, and shown on a web page. For example:

graph

Home

?

Creative Commons License
This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License.