Packet Surgery

A project log for Reverse-Engineering the milight on-air protocol

Mi-Light/Limitless LED are a type of RGB(W) LED lamps with standard light bulb sockets and a proprietary 2.4GHz radio control protocol

Henryk PlötzHenryk Plötz 05/26/2015 at 13:430 Comments

Based on what I knew so far I could a) describe the Mi-Light frame format (if not the contents) and b) try to map this to the nRF51 or nRF24 frame format. Way back in 2011 the neighbourly Travis Goodspeed described how to achieve promiscuous sniffing with the nRF24L01+. Even though I didn't use this technique here (having done both SDR and logic analyzer sniffing instead), you should read his post, since it contains some basic knowledge that also applies to the PL1167 format.

Below is the framing I deduced for both nRF51422 (in little endian mode) and nRF24L01+ (which seems to operate in big endian), illustrated using an example from my received data:

The graphic shows the bits I sniffed from the air (preamble bits that my decoder didn't output but which must have been there are shown in grey at the beginning; conversely: bits that weren't sent but just represent the idle radio are shown in grey at the end), and their value in units of 4 bits (little endian, hex). Below that is the PL1167 framing as it would have been at the sender, deduced from the PL1167 data sheet/user manual and logic analyzer traces. Note that there is a very inconvenient 4-bit "trailer" after the sync field.

I then, using the nRF51 Series Reference Manual section 17.1.2, mapped this to nRF51 RADIO peripheral frames, when using the radio in little endian mode, data whitening disabled, CRC disabled, lengths of S0, LENGTH and S1 set to zero. Both this and the nRF24L01+ layout are tested, that is, when configuring the radio with the addresses described I get a payload as displayed.

It's slightly unfortunate: Because the PL1167 preamble is so long, bleeding into the nRF sync field, and also due to that strange 4-bit trailer, the nRF "payload" contains part of the PL1167 sync field, the trailer and length field, and is shifted by a nibble. I have to demangle that in my firmware after receiving it from the radio (and "re-mangle" before sending). I investigated using the nRF51 advanced fields: S0 (0 or 1 byte), LENGTH (0 through 15 bits), and S1 (0 through 15 bits), but didn't get immediate success. If I could subsume the extra data into these fields and align the PL1167 and nRF51 payloads, I could use the nRF51 CRC mechanism and generally clean up my usage of the radio. Maybe I'll re-try the advanced approach later, the simple one is working for now.

Finally I've been using an nRF24L01+ module on an Arduino with the RF24 library. Getting this to work took several attempts: the nRF24L01+ seems to receive data in big endian byte and bit order, so I had to use a different address, and need to mirror each byte of the payload before interpreting.

For a proper framing implementation there's still the question of the CRC. The PL1167 documentation doesn't describe it, only saying that it's 16 bits. Luckily, I have a little tool, that I built for similar problems years ago. It allows me to brute force CRC parameters, even allowing for uncertainties regarding which data is included in the CRC. Running on the payload, optionally prepended with the length, the result is: CRC polynomial 0x8408, initial value 0, final XOR 0, calculated over the length byte and the payload, output LSByte first.

Implementing a full receiver with CRC check and dupe detection gave me a good look at the packet contents for the first time.