Sunday, January 26, 2014

From remote vacuum cleaner to my weather station

Story started quite differently than you may expect.
My initial plan was not to play with my weather station, but with remote control to my vacuum cleaner :) I have Miele brand vacuum cleaner with remote control buttons located on gripping part of pipe where you usually hold cleaning pipe.

I was trying to reverse engineer that. There were no markings on device about its working frequency so I use cheap DVB-T dongle supported by rtl_sdr libray as my spectrum analyzer and GNU Radio as a frontend. First step is of course to find working frequency. I assumed that it will work on 433MHz or 868MHz or 2.4GHz band. That was easy - I spotted nice signal on 433.82Mhz My initial step was to discover modulation used by this device.

Since this is only home appliance I was not expecting QAM256 :) I focused on trying to get some bit stream by experimenting with ASK,FSK,GMSK. I started with ASK and that was first good shot. In fact my vacum cleaner is using OOK (On Off Keying) modulation which is extreme type of ASK where you switch on/off carrier.

By using GNU Radio FFT and Scope sink with low pass filter I saw that I have regular pulses of something what was interferences from my perspective. You can see this as decaying line close to the right of main signal. It is hard to catch < 1 s pulse..
I knew that this must be some other devices in my vicinity. Since pulses were rare this must be something energy efficient. My first suspects were SMART Water meter with radio transmitter or heat logger mounted on my water powered heaters. Both are made by Ista and have radio interface. They are waiting for my free time to start hacking them :) After some walking over my house with laptop and DVB-T dongle I ruled out both of them, and then I remembered about my weather station since signal was strongest near part of house where it was located.

Some time ago I bought cheap 30 PLN (~7,5 EURO) weather station in Biedronka shop. For those who are not familiar with this name it is a shop network directly competing with Lidl on Polish market. They sell mostly grocery but they have some home cheap china electronic stuff also. Quality is mostly crap but you can find some rare oportunieties..

Remote unit model part number is WS-9941-M. As you can see there is a mark that it is working on  433 MHz band. Clearly it was my source of interference. Unfortunatelly I'm still learning GNURadio and I was able to see data on screen however I was unable to make setup to dump this data to file. I went to my neighbor radio shack shop AVT. It is really neat to have such well stocked electronics shop just 10 minutes bike drive. I bought  ZS-RR10-433MHZ  433.92 MHz radio reciever for 20 PLN (~5 EURO). This is really neat device - you just plug in to power, put some antena and you get demodulated signal on one of the pins.
After connecting Salae Logic Analyzer look what I got:

As you can see there is clearly pattern here. First what you see is sync packet contains three pulses and then delay ~ 9ms.  Then is pattern repeated 10 times. We have pattern here but what exacly "0" and "1" are represented ?

I search web page for any similar devices. Unfortunately there is not many such pages. I found Fred's webpage, and rc-switch Arduino library. I looked into source files and none protocol was similar to mine.

I dumped data from Salae to CSV file and started  write some python stuff that will try to get something from those data.
As you can see this signal have constant high pulse time so no duty modulation was possible. Period is changing and this was the clue. Like in every reverse hack you must start with some assumptions and check if they are wrong or not. I made one that bits are coded with time distance between two rising edges or two falling edges (since high state period is constant). With such assumption and data in csv file i wrote simple python code that will check time between two rising edges and if one is smaller than <VALUE_0> it is one bit, and second time period <VALUE_1> is zero bit. Of course you need some samples with knowledge what is expected to be inside, so I dumped packets for different temperature readings  on my weather display and this is what I got
Temperature (Celsius)Data
18,8 C101001101000000010111100111100000000
20,1 C101001101000000011001001111100000000
20,7 C101001101000000011010000111100000000
Definitely I have data here :) Encoding method assumption seems to be OK. First notice is weird data packet length - 36 Bits. I tought that my encoding is wrong, checked one manually. No errors. Some bits were never changed so I removed them for further analysis and focused on the ones which are changing. My another assumption - data should be contained or aligned within N x bytes. Yeah - maybe this cheap chinese stuff have 4bit MCU ..but lets start with some normal assumptions :) I converted this to decimal format and this is what I got:

Temperature (Celsius)Data
18,8 C188
20,1 C201
20,7 C207

Since such chip devices shouldn't be very efficient with float operations and temperature resolution was 0.1 deg I expected that station will multiply temp value by 10 and this should be value - and assumption was right.

I was happy and started to check my script until i measured first negative temperature. Since we have nice winter in Poland I put weather transmitter outside where it should be and started measurement. It was < -13 C deg and going down. Results were at least weird. Here is what I got with my previous method:
Temperature (Celsius)DataData decimal
-14.1 C101001101000111101110011111100000000115
-14.2 C101001101000111101110010111100000000114
-14.5 C101001101000111101101111111100000000111

I noticed that encoded number is going down along with temperature drop. So what is minimum temperature device can measure so all bits will be zeroes ? Simple extrapolation and result is -25,6 C deg. Now this is very nice and special number for binary system :) So 0 deg will be 256 or "11111111". Simple math and we have formula for negative temperature schema:

Negative TEMP = ( VALUE - 256)/10

Where is "-" mark ? The only difference between negative and positive data are next 4  most significant bits you see on bold in table.

This is python based algorythm for decoding my wireless station data:
def decode(data):
    # convert 36 bits to long integer
    # remove 12 least significant bits by shifting >> 12
    value=value >> 12
    # remove preambule - only 12 least significant bits are important
    # 0xfff is in binary 111111111111. All other bits will be ignored
    value=value & 0xfff
    # lets check do we have negative temperature by comparing bits 10-12
    # 0xf00 is 111100000000. We are checking value of 3 most significant bits
    # if set to 111 - negative temp. If set 000 - positive
    if (value & 0xe00) == 0xe00:
        # Negative algorythm
        return ((value & 0xff) - 256) / 10.0
        # Positive temp algorythm
        return (value & 0x1ff)/10.0

Now it is time to make some hardware to receive data from my unit and put them to PC via USB/UART, and put my temp data public. I have some STM32, PIC32, KL25Z,Atmega MCU's laying arround. But this is story for next part.

Jakub mention in comments that maybe my assuption about positive values is not perfect because that would mean max + temperature is 25.6 C deg. He was right. This is perfect example of working with not sufficient data samples :)
In fact temperature is encoded (probably)  in 12 bits signed format. I tested only up to ~ 40 C deg by warm up sensor above 25,6 C deg. In order to check if bits 10 and 11 also containg temp data I would have to warm up device above 51.2 C deg. :-)  Maximum temperature with 9 bits  would be 51,2 C deg and this comply with sensor range from specification  which is -25 - +50 C de, and I don't need higher values so example above decodes just 9 bits.
I updated python code