Reverse Engineering a 433MHz Motorised Blind RF Protocol

I’ve been doing a fair bit of DIY home automation hacking lately across many different devices - mostly interested in adding DIY homekit integrations. A couple of months ago, my dad purchased a bulk order of RAEX 433MHz RF motorised blinds to install around the house, replacing our existing manual roller blinds.

RAEX Motorised Blind

Note: If you are based in Australia, you can purchase these in bulk or individually via www.raexaustralia.com (Full disclosure – my father runs the site).

The blinds are a fantastic addition to the house, and allow me to be super lazy opening/closing my windows, however in order to control them you need to purchase the RAEX brand remotes. RAEX manufacture many different types of remotes, of which, I have access to two of the types, depicted below:

R Type Remote

R Type Remote (YRL2016)

X Type Remote

X Type Remote (YR3144)

Having a remote in every room of the house isn’t feasible, since many channels would be unused on these remotes and thus a waste of $$$ purchasing all the remotes. Instead, multiple rooms are programmed onto the same remote. Unfortunately due to this, remotes are highly contended for.

An alternate solution to using the RAEX remotes is to use a piece of hardware called the RM Pro. This allows you to control the remotes via your smartphone using their app

RM Pro Home Screen
RM Pro Blind Control Screen

The app is slow, buggy and for me, doesn’t fit well into the home-automation ecosystem. I want my roller blinds to be accessible via Apple Homekit.

In order to control these blinds, I knew I’d need to either:

  1. Reverse engineer how the RM Pro App communicated with the RM Pro and piggy-back onto this
  2. Reverse engineer the RF protocol the remotes used to communicate with the blinds.

I attempted option 1 for a little while, but ruled it out as I was unable to intercept the traffic used to communicate between the iPhone and the hub. Therefore, I began my adventure to reverse engineer the RF protocol.

I purchased a 433MHz transmitter/receiver pair for Arduino on Ebay. In case that link stops working, try searching Ebay for 433Mhz RF transmitter receiver link kit for Arduino.

Initial Research

A handful of Google searches didn’t yield many results for finding a technical specification of the protocol RAEX were using.

  • I could not find any technical specification of the protocol via FCC or patent lookup
  • Emailed RM Pro to obtain technical specification; they did not understand my English.
  • Emailed RAEX to obtain technical specification; they would not release without confidentiality agreement.
  • I did find that RFXTRX was able to control the blind via their BlindsT4 mode, which appears to also work for Outlook Motion Blinds.
  • After opening one of the remotes and identifying the micro-controllers in use, I was unable to find any documentation explaining a generic RF encoding scheme being used.
  • It may have been possible to reverse engineer the firmware on a remote by taking an I2C dump of the ROM chip. It seems similar remotes allow dumping at any point after boot

Capturing the data

Once my package had arrived I hooked up the receiver to an Arduino and began searching for an Arduino sketch that could capture the data being transmitted. I tried many things that all failed, however eventually found one that appeared to capture the data.

Once I captured what I deemed to be enough data, I began analysing it. It was really difficult to make any sense of this data, and I didn’t even know if what had been captured was correct.

I did some further reading and read a few RF reverse engineering write-ups. A lot of them experimented with the idea of using Audacity to capture the signal via the receiver plugged into the microphone port of the computer. I thought, why not, and began working on this.

The RF capturing setup

Audacity capture

This captures a lot of data. I captured 4 different R type remotes, along with 2 different X type remotes, and to make things even more fun, 8 different devices pairings from the Broadlink RM Pro (B type).

From this, I was able to determine a few things

  1. The transmissions did not have a rolling code. Therefore, I could simply replay captured signals and make the blind do the exact same thing each time. This would be the worst-case scenario if I could not reverse engineer the protocol.
  2. The transmissions were repeated at least 3 times (changed depending on the remote type being used)

Zooming into the waveform, we can see the different parts of a captured transmission. This example below is the capture of Remote 1, Channel 1, for the pairing action:

R1, CH1 PAIR capture

Zooming in:

Zoomed R1, CH1 PAIR capture

In the zoomed image you can see that the transmission begins with a oscillating 0101 AGC pattern, followed by a further double width preamble pattern, followed by a longer header pattern, and then by data.

This preamble, header and data is repeated 3 times for R type remotes (The AGC pattern is only sent once at the beginning of transmission). This can be seen in the first image.

Looking at this data won’t be too useful. I need a way to turn it digital and analyse the bits and determine some patterns between different remotes, channels and actions.

Decoding the waveform.

We need to determine how the waveform is encoded. It’s very common for these kinds of hardware applications to use one of the following:

By doing some research, I was able to determine that the encoding used was most likely manchester encoding. Let’s keep this in mind for later.

Digitising the data

I began processing the data as the raw scheme outlined above (even though I believed it was manchester). The reason for this is that if it happened to not be manchester, I could try decode it again with another scheme. (Also writing out raw by hand was easier than doing manchester decoding in my head).

I wrote out each capture into a Google Sheets spreadsheet. It took about 5 minutes to write out each action for each channel, and there were 6 channels per remote. I began to think this would take a while to actually get enough data to analyse. (Considering I had 160 captures to digitise)

I stopped once I collected all actions from 8 different channels across 2 remotes. This gave me 32 captures to play with. From this much data, I was able to infer a few things about the raw bits:

  • Some bits changed per channel
  • Some bits changed per remote.
  • Some bits changed seemingly randomly for each channel/remote/action combination.
    • Could this be some sort of checksum?

I still needed more data, but I had way too many captures to decode by hand. In order to get anywhere with this, I needed a script to process WAV files I captured via Audacity. I wrote a script that detected headers and extracted data as its raw encoding equivalent (as I had been doing by hand). This script produced output in JSON so I could add additional metadata and cross-check the captures with the waveform:

[
  {
    "filename": "/Users/nickw/Dropbox/RF_Blinds/Export_Audio2/tracks2/R1_CH1.wav",
    "captures": [
      {
        "data": "01100101100110011001100101101001011010010110011010011010101010101010101010011001101010101010101010101010101",
        "header_pos": 15751,
        "preamble_pos": 15071
      },
      {
        "data": "01100101100110011001100101101001011010010110011010100110101010101001101010011001101010101010101010101010101",
        "header_pos": 46307,
        "preamble_pos": 45628
      },
      {
        "data": "01100101100110011001100101101001011010010110011010010110101010101010011010011001101010101010101010101010101",
        "header_pos": 73514,
        "preamble_pos": 72836
      },
      {
        "data": "01100101100110011001100101101001011010010110011010101010101010100101010101101001011010101010101010101010101",
        "header_pos": 103575,
        "preamble_pos": 102895
      }
    ]
  }
]

Once verified, I tabulated this data and inserted it into my spreadsheet for further processing. Unfortunately there was too many bits per capture to keep myself sane:

Raw captures inside a spreadsheet

I decided it would be best if I decoded this as manchester. To do this, I wrote a script that processes the raw capture data into manchester (or other encoding types). Migrating this data into my spreadsheet, it begins to make a lot more sense.

Manchester captures inside a spreadsheet

Looking at this data we can immediately see some relationship between the bits and their purpose:

  • 6 bits for channel (C)
  • 2 bits for action (A)
  • 6 bits for some checksum, appears to be a function of action and channel. F(A, C)
    • Changes when action changes
    • Changes when channel changes.
    • Cannot be certain it changes across remotes, since no channels are equal.
  • 1 bit appears to be a function of Action F(A)
  • 1 bit appears to be a function of F(A), thus, G(F(A)). It changes depending on F(A)’s value, sometimes 1-1 mapping, sometimes inverse mapping.

After some further investigation, I determined that for the same remote and channel, for each different action, the F(A, C) increased by 1. (if you consider the bits to be big-endian.).

Encoded value increasing per different action

Looking a bit more into this, I also determined that for adjacent channels, the bits associated with C (Channel) count upwards/backwards (X type remotes count upwards, R type remotes count backward). Additionally F(C) also increases/decreases together. Pay attention to the C column.

Encoded value increasing with adjacent channels

From this, I can confirm a relationship between F(A, C) and C, such that F(A, C) = F(PAIR, C0) == F(PAIR, C1) ± 1. After this discovery, I also determine that there’s another mathematical relationship between F(A, C) and A (Action).

Making More Data

From the information we’ve now gathered, it seems plausible that we can create new remotes by changing 6 bits of channel data, and mutating the checksum accordingly, following the mathematical relationship we found above. This means we can generate 64 channels from a single seed channel. This many channels is enough to control all the blinds in the house, however I really wanted to fully decode the checksum field and in turn, be able to generate an (almost) infinite amount of remotes.

I wrote a tool to output all channels for a seed capture:

./remote-gen generate 01000110110100100001010110111111111010101
...

My reasoning behind generating more data was that maybe we could determine how the checksum is formed if we can view different remotes on the same channel. I.e. R0CH0, R1CH0, X1CH0, etc…

Essentially what I wanted to do was solve the following equation’s function G:

F(ACTION_PAIR, CH0) == G(F(ACTION_PAIR, CH0))

However, looking at all Channel 0’s PAIR captures, the checksum still appeared to be totally jumbled/random:

Identical channels / action jumbled checksums

Whilst looking at this data, however, another pattern stands out. G(F(A)) sits an entire byte offset (8 bits) away from F(A). Additionally the first 2 bits of F(A, C) sit at the byte boundary and also align with A (Action). As Action increases, so does F(A, C). Lets line up all the bits at their byte boundaries and see what prevails:

Identified Boundaries
Colours denoting byte boundaries
Aligned byte boundaries
Aligned boundaries

From here, we need to determine some function that produces the known checksum based on the first 4 bytes. Initially I try to do XOR across the bytes:

Attempt to find checksum function via XOR

Not so successful. The output appears random and XOR’ing the output with the checksum does not produce a constant key. Therefore, I deduce the checksum isn’t produced via XOR. How about mathematical addition? We’ve already seen some addition/subtraction relationship above.

Attempt to find checksum function via addition

This appeared to be more promising - there was a constant difference between channels for identical type remotes. Could this constant be different across different type remotes because my generation program had a bug? Were we not wrapping the correct number of bits or using the wrong byte boundaries when mutating the channel or checksum?

It turns out that this was the reason 😑.

Solving the Checksum

Looking at the original captures, and performing the same modulo additions, we determine the checksum is computed by adding the leading 4 bytes and adding 3. I can’t determine why a 3 is used here, other than RAEX wanting to make decoding their checksum more difficult or to ensure a correct transmission pattern.

I refactored my application to handle the boundaries we had just identified:

type RemoteCode struct {
    LeadingBit uint // Single bit
    Channel    uint8
    Remote     uint16
    Action     uint8
    Checksum   uint8
}

Looking at the data like this began to make more sense. It turns out that F(A) wasn’t a function of A (Action), it was actually part of the action data being transmitted:

type BlindAction struct {
    Name  string
    Value uint8
}

var validActions = []BlindAction{
    BlindAction{Value: 127, Name: "PAIR"},
    BlindAction{Value: 252, Name: "DOWN"},
    BlindAction{Value: 253, Name: "STOP"},
    BlindAction{Value: 254, Name: "UP"},
}

Additionally, the fact there is a split between channel and remote probably isn’t necessary. Instead this could just be an arbitrary 24 bit integer, however it is easier to work with splitting it up as an 8 bit int and a 16 bit int. Based on this, I can deduce that the protocol has room for 2^24 remotes (~16.7 million)! That’s a lot of blinds!

I formally write out the checksum function:

func (r *RemoteCode) GuessChecksum() uint8 {
    return r.Channel + r.Remote.GetHigh() + r.Remote.GetLow() + r.Action.Value + 3
}

Additional Tooling

My remote-gen program was good for the purpose of generating codes using a seed remote (although, incorrect due to wrapping issues), however it now needed some additional functionality.

I needed a way to extract information from the captures and verify that all their checksums align with our rule-set for generating checksums. I wrote an info command:

./remote-gen info 00010001110001001101010111011111101010100 --validate
Channel:    196
Remote:     54673
Action:     STOP
Checksum:          42
Guessed Checksum:  42

Running with --validate exits with an error if the guessed checksum != checksum. Running this across all of our captures proved that our checksum function was correct.

Another piece of functionality the tool needed was the ability to generate arbitrary codes to create our own remotes:

./remote-gen create --channel=196 --remote=54654 --verbose
00010001101111110101010111111111010011001    Action: PAIR
00010001101111110101010110011111101101000    Action: DOWN
00010001101111110101010111011111111101000    Action: STOP
00010001101111110101010110111111100011000    Action: UP

I now can generate any remote I deem necessary using this tool.

Wrapping Up

There you have it, that’s how I reverse engineered an unknown protocol. I plan to follow up this post with some additional home-automation oriented blog posts in the future.

From here I’m going to need to build my transmitter to transmit my new, generated codes and build an interface into homekit for this via my homebridge program.

You can view all the work related to this project in the nickw444/homekit/blindkit repo.

As mentioned above, if you are based in Australia, you can purchase these blinds and associated accessories in bulk or individually via www.raexaustralia.com (Full disclosure – my father runs the site)

Sprinkle

Here’s a quick demo of something I quickly jammed together over the weekend for my Dad. More info to come, along with additional pictures, circuitry, and some proper screenshots

Basically it’s an iOS app to control solenoid valves via a Raspberry Pi over a JSONRPC interface.

Quadcopter Update

It’s been a while since my last quadcopter post – mainly because i’ve been in the UK and didn’t take it with me.

The day that I left for the UK, the new frame that I ordered for the quad arrived.  Every time I upgrade to a new frame, the quad seems to shrink in size.

This new frame has quite a small wingspan, resulting in a downsize in propellors to a smaller 5″ diameter. It does however have a much lower weight footprint. Weighing in at only 58g, it’s 20g lighter than the old frame and has much more room for cable management and a nice place underneath (which I’ve modified) to hold the battery safely during flight.

Overall, I’m pretty happy with the new frame, however do hope it will be wide enough to still be able to freely manoeuvre the quad.

A Brilliant Explanation of PID

Quadcopter Electronics Assembly Complete

So far i’ve almost completed all the electronics components of the quadcopter. I am now waiting for my 5v regulator to arrive. I will use this to drive the Raspberry Pi from the 7.2 Lipoly battery. Once this arrives the quad will be able to become fully wireless (currently 5v power inlet is requiring a cable).

I wrote a basic program to control the PWM speeds from the RPi. This is letting me test the power that the quad has. At the moment it can easily lift off at about 40/100 (however I’m not sure if this is linear).

This video shows me bringing the controller speed up to 30/100:

The next challenge is going to be mounting everything in such a way that it’s accessible and still lightweight. At the moment electrical tape is a great mounting tool.

DIY Quad Frame

I just ordered a cheap $30 56g lightweight frame off ebay, however, I need something in the mean time to get set up with. Instead of using the ugly and heavy (~250g) wooden frame, I have built this frame from some spare materials I found in the garage.

Next up, I will need to make all the electronic connections and determine where to house the RPi and battery on the quad. I am going to have to now be careful about positioning of the electronics as the entire frame is conductive :S

Here are a couple of picture of the new frame’s construction

Quadcopter Arrived! (Most of it)

Today a majority of the electronic components of my quadcopter arrived. I received an email from the online shop saying that my order had been dispatched today and they included a tracking number. Upon checking the tracking, it turned out it had actually been posted on the 13th and it was “Delivered”. Being curious, I went downstairs, and there it was, a package on my door step. Excellent.

So it was a given I was going to have the rest of the afternoon off study – now that I had a new fun thing to play with.

As I am not building this from a kit, I had ordered all my parts individually and hoping for the best. For someone with such little hobby RC knowledge, google has definitely been my friend.

My package contained:

  • 4x 10A ESC Combo Kits with a 1700Kv motor (not Kilovolts, RPM in K per volt)
  • 1x Lipoly Balance Charger (For charging my Lipoly battery)
  • 1x 2100mAh Lipoly Battery (2S, 30C)
  • 1x Wooden Frame (Only cost $5, thought it would be worthwhile)
  • 3x Different packs of Propellors
  • 1x Power Distribution (Which I stupidly bought the one with the wrong plug)
IMG_0003

The contents of my package, laid out

So, Let’s get started. What on earth do all those numbers mean? I didn’t know – until I started looking them up. So, let me explain the motors to begin with.

Components

Motors

In quadcopters, you really do need to use brushless motors. This allows you to get maximum efficiency. Unfortunately, to run these, you need a ESC (Electronic Speed Controller). This turns a PWM signal into a voltage which is used to drive the motors. Conveniently, I found a kit which included an ESC and a motor of which were of a decent specification. I will cover connecting the ESC to a Raspberry Pi in a later section

Battery & Power

In newer RC, everyone is making the switch to Lipoly (Lithium Polymer) batteries. These are similar to those which are found in your phone – Lithium Ion. Unfortunately, they require a great deal of care when handling. We have seen many mobile phone explosions over the years due to Lithium Ion batteries exploding – This risk is just as, and even more evident in RC using Lithium Polymer batteries. This is due to the risky nature of the batteries. Very high capacity, in a high risk environment. Any puncture or shock to a battery can cause it to explode. See here

The charging process of a Lipoly battery is the same as Li-ion batteries. You will require a special charger. Most modern chargers and batteries have balance plugs, which makes charging safer and simpler. Older technology required you to balance the cells before charging and after charging. Failure to do so could lead to explosion.

Raspberry Pi

I already had a Raspberry Pi, which is great, and I plan to use it to drive the ESC’s, however it needs a source of power. I am contemplating connecting it to the ESC +5v output, however still deciding whether or not this is a good idea.

Propellors

There is so much choice when it comes to propellor choice. I chose 3 different kits each with 3x CCW and 3x CW props. This is required as the opposite rotation of the quad needs to be offset otherwise you will get unwanted yaw. The kits were to the following specs: 1x 7×4.5, 1x 7×6, 1x 6×5.

Other Things To Come

I’m still waiting on the brains of the quad. I still need to receive my I2c Altitude/Barometer/Temperature sensor as well as my I2C Gyro/Magnetometer/Accelerometer. Once they arrive, I will be ready to start writing the software. In the mean time, it’s a matter of assembling the hardware and doing all the connections.

Software

I lied, I actually did start writing some software today. It was only a basic python program to interface with the ESC’s via PWM on the GPIO pins on my Raspberry Pi.

It was a time consuming process learning how to communicate with the ESC’s via PWM. It’s practically undocumented as I suppose this is just a known thing in the Hobby RC world.

Basically to send a speed control to the ESC you need to use Pulse Width Modulation. This is a signal of a square wave. It has two variable parameters; the period and the duty. The period is the frequency and the duty is how long a pulse is to stay high for.

I knew all about how this worked, but had no clue how on earth to communicate within the range the ESC understood. After some light googling, I found out the range around ESC’s PWM signal. A width of 700 – 2000 microseconds is within the working range of a generic ESC.  However the next issue was how to communicate using python using a value of microseconds. I’m lazy, and using math to turn this into a frequency in Hz unfortunate. Instead it turns out Python/RPi.GPIO has a class in PWM for a servo. It lets you set the value of microseconds directly. Awesome. Here’s a snippet of my code:

from RPIO import PWM
import time

max_PWM = 2000
min_PWM = 700

MOTOR_1_PIN = 25

servo = PWM.Servo()
time.sleep(2)

servo.set_servo(MOTOR_1_PIN, max_PWM)
time.sleep(5)
servo.set_servo(MOTOR_1_PIN, min_PWM)

time.sleep(3)
print("Servo Set Up")

while True:
 try:
 print("Enter New PWM Value:")
 ins = int(raw_input())
 servo.set_servo(MOTOR_1_PIN, ins)

 except KeyboardInterrupt:
 break

servo.stop_servo(MOTOR_1_PIN)
PWM.cleanup()

Unfortunately, that didn’t work. After much head scratching I still hadn’t worked out how to calibrate the PWM signal to the ESC.

To do a calibration process, you must turn on the GPIO PWM to the maximum signal, then switch on the ESC, then turn the PWM signal to minimum. After this occurs, you are now ready to start sending it PWM signal.

I modified my program to be more basic and let me give it any value i wished without the broken calibration code

from RPIO import PWM
import time

MOTOR_1_PIN = 25

servo = PWM.Servo()

while True:
 try:
 print("Enter New PWM Value:")
 ins = int(raw_input())
 servo.set_servo(MOTOR_1_PIN, ins)

 except KeyboardInterrupt:
 break

servo.stop_servo(MOTOR_1_PIN)
PWM.cleanup()

To do all this magic with the software I did need to connect everything up, so here are a few images of the current set up

IMG_0005

Breadboarding, connecting GPIO to the ESC via an old IDE cable.

 

IMG_0007

Cheap and nasty wooden frame – soon to be replaced

Anyway, that’s all for now, hopefully much more to come once everything is hooked up and ready to go!

Exams! / New Phone / QuadCopter

Horray, what a brilliant time of year.

Tomorrow I will be sitting my first of many exams in my time at uni. Since finishing High School I haven’t really done any proper study, so sitting down and forcing myself to put pen to paper and practise math is getting a bit frustrating. I’ve found myself procrastinating and somehow doing things that are not study – such as eating…

Anyway, in the midst of procrastination, I have managed to somehow go shopping. (online shopping lol).

Over the weekend I decided I would use my old RaspBerry Pi to create a quadcopter. On Monday I ordered all the parts, such as ESC’s (Electronic Speed Controllers – they convert PWM to a big electric current for the energy sucking motors), Motors, Batteries, and electronic components such as a gyro/accelerometer/magnometer and a barometer/temperature/altitude meter. These will all connect to the RaspBerry Pi via I2C (I squared C).

So far, although I haven’t even recieved anything in the mail, just the process of gathering parts has taught me so much – Mostly in the electronics, like understanding that I won’t be able to get enough power out of using a standard speed control IC chip, and instead will need a ESC to give power to the motors.

As the project continues, I will continue to blog about the progress and all sorts of the things relating to the project. One stage I am particularly looking forward to within the project is developing a PID software to do the balance of the drone.

In other news, I also managed to buy a Galaxy S5. This does mean that I will be ditching my iPhone 5. Bye bye apple. :’)