Home PageMilight new protocol

Milight new protocol

Milight new protocol is used in the new Milight  lightbulbs and controllers. The protocol is different from both the RGBW and CW WW the so called CCT  bulbs. Thanks to Chris Mullins the Milight new protocol has been decoded including the new encryption method. I have used this to be able to program the nRF24L01 with the correct syncword etc and to decode the protocol.

milight new bulbmilight-rgbwwwcw-remotemilight remote

protocol over air nRF24L01

The new lightbulb is 8W and contains RGB and both warm white and cold white leds compared to only one type of white in the old bulbs. It also reacts to more commands from the new remote than the old ones.

Milight bulb new Milight bulb new on

The most righthand picture shows that it is possible to switch on all the LEDs at the same time which is not possible with the 6W RGBW lightbulb.

To control this milight bulb you need all 5 channels to control the RGB but also the CW and WW signals. It was this that i had in mind when designing the controller PCB for the 6W 4 channel milight bulb. This PCB is published on the RF page but this time the few additional components on the backside of the print are needed to control all 5 channels.  Optionally these (exept one 10k resistor) can be left out if you want to use the controller PCB for the 6W RGBW milight bulb. You can see that there are 6 connections for the milight bulb print above. This conform the requirement of the RGB CW and WW milight bulb printed circuit board.
Below you can find the new code for descrambling the new protocol using arduino code. Now that this is possible i want to get cracking with making my own milight RGB CCT bulb but based on the smaller 6W variety.

[spoiler effect=”blind” show=”RGB CCT Sniffer” hide=”hide me”]
#include <SPI.h>
#include <nRF24L01.h>
#include <RF24.h> //http://tmrh20.github.io/RF24/
#include "PL1167_nRF24.h"
#include "MiLightRadio.h"

// define connection pins for nRF24L01 shield on www.arduino-projects4u.com
#define CE_PIN 9   //ESP8266 2
#define CSN_PIN 10 //ESP8266 15

#define V2_OFFSET(byte, key, jumpStart) ( 
  V2_OFFSETS[byte-1][key%4] 
    + 
  ((jumpStart > 0 && key >= jumpStart && key <= jumpStart+0x80) ? 0x80 : 0) 
)

#define V2_OFFSET_JUMP_START 0x54

uint8_t const V2_OFFSETS[][4] = {
  { 0x45, 0x1F, 0x14, 0x5C },
  { 0x2B, 0xC9, 0xE3, 0x11 },
  { 0xEE, 0xDE, 0x0B, 0xAA },
  { 0xAF, 0x03, 0x1D, 0xF3 },
  { 0x1A, 0xE2, 0xF0, 0xD1 },
  { 0x04, 0xD8, 0x71, 0x42 },
  { 0xAF, 0x04, 0xDD, 0x07 },
  { 0xE1, 0x93, 0xB8, 0xE4 }
};
uint8_t xorKey(uint8_t key) {
  // Generate most significant nibble
  const uint8_t shift = (key & 0x0F) < 0x04 ? 0 : 1;
  const uint8_t x = (((key & 0xF0) >> 4) + shift + 6) % 8;
  const uint8_t msn = (((4 + x) ^ 1) & 0x0F) << 4;
  // Generate least significant nibble
  const uint8_t lsn = ((((key & 0x0F) + 4)^2) & 0x0F);
  return ( msn | lsn );
}
uint8_t decodeByte(uint8_t byte, uint8_t s1, uint8_t xorKey, uint8_t s2) {
  uint8_t value = byte - s2;
  value = value ^ xorKey;
  value = value - s1;
  return value;
}
uint8_t encodeByte(uint8_t byte, uint8_t s1, uint8_t xorKey, uint8_t s2) {
  uint8_t value = byte + s1;
  value = value ^ xorKey;
  value = value + s2;
  return value;
}
void decodeV2Packet(uint8_t *packet) {
  uint8_t key = xorKey(packet[0]);
  for (size_t i = 1; i <= 8; i++) {
    packet[i] = decodeByte(packet[i], 0, key, V2_OFFSET(i, packet[0], V2_OFFSET_JUMP_START));
  }
}
RF24 radio(CE_PIN, CSN_PIN);
PL1167_nRF24 prf(radio);
MiLightRadio mlr(prf);

void setup()
{
  Serial.begin(115200);
  Serial.println();
  delay(1000);
  Serial.println("# OpenMiLight Receiver/Transmitter starting");
  mlr.begin();
}


static int dupesPrinted = 0;
//static bool receiving = true; //set receiving true or false
//static bool escaped = false;
//static uint8_t outgoingPacket[7];
//static uint8_t outgoingPacketPos = 0;
//static uint8_t nibble;
//uint8_t crc;
static enum {
  IDLE,
  HAVE_NIBBLE,
  COMPLETE,
} state;

void loop()
{
    if (mlr.available()) {
      uint8_t packet[9];
      size_t packet_length = sizeof(packet);
      Serial.println();
      Serial.print("<-- ");
      if (packet_length<0x10) Serial.print("0");
      Serial.print(packet_length,HEX);
      Serial.print(" ");     
      mlr.read(packet, packet_length);
      for (int i = 0; i < packet_length; i++) {
        if (packet[i]<0x10) Serial.print("0");
        Serial.print(packet[i],HEX);
        Serial.print(" ");
      }
      Serial.print("Decoded package = ");
      uint8_t key = xorKey(packet[0]);
      uint8_t sum = key;
      Serial.print(key,HEX);Serial.print(" ");
      for (size_t i = 1; i <= 7; i++) {
        packet[i] = decodeByte(packet[i], 0, key, V2_OFFSET(i, packet[0], V2_OFFSET_JUMP_START));
        sum += packet[i];
        if (packet[i]<0x10) {Serial.print("0");}
        Serial.print(packet[i],HEX);Serial.print(" ");
      }
    }
    int dupesReceived = mlr.dupesReceived();
    for (; dupesPrinted < dupesReceived; dupesPrinted++) {
    Serial.print(".");
    } 
}




[/spoiler]

The buttons on the remote have the following codes:
Button             Command         Arguement
ALL ON           01                      00
ALL OFF         01                      05
ZONE 1 ON    01                      01
ZONE 2 ON   01                       02
ZONE 3 ON   01                      03
ZONE 4 ON   01                      04
ZONE 1 OFF 01                      06
ZONE 2 OFF 01                     07
ZONE 3 OFF 01                     08
ZONE 4 OFF 01                     09
S+                    01                     0A
S-                     01                     0B
White               03                    B0-FF – 00-AF
Colourwheel   02                     00-FF
Brightness      04                      80-FF
Saturation     04                       00-7F
Mode              05                      0,1,2,3,4,5,6,7,8

Finally the output from the program showing a RGB CCT remote talking to the arduino. First it shows the scrambled code then the decoded package.
You can see the key lead byte 0x20 followed by the ID1 and ID2 bytes. Then the command argument and sequence bytes and finally the group byte. I have not shown the checksum byte.

RGB_CCT_Decode

It is also possible to decode several differenmt protocols at the same time.
This requires a slightly modified version of the Henryk Plötz version. It allows the automatic switching between keywords, channels and length of the messages so that you can read any milight remote and get the codes. This version includes the new RGB + CCT remotes alswell as the older CCT, RGBW and RGB remotes.

Below you can see the ouput of the program showing several different remotes with different protocols being received on the fly at the same time without changing any code.

milight multi protocol