Milight WiFi Gateway Emulator on an ESP8266

Milight bulbs* are cheap smart bulbs that are controllable with an undocumented 2.4 GHz protocol. In order to control them, you either need a remote* (~$13), which allows you to control them directly, or a WiFi gateway* (~$30), which allows you to control them with a mobile app or a UDP protocol.

A few days ago, I posted my Arduino code to emulate a Milight WiFi gateway on an ESP8266 (link). This allows you to use an NRF24L01+ 2.4 GHz tranceiver module* and an ESP8266* to emulate a WiFi gateway, which provides the following benefits:

  1. Virtually unlimited groups. The OTS gateways are limited to four groups.
  2. Exposes a nice REST API as opposed to the clunky UDP protocol.
  3. Secure the gateway with a username/password (note that the 2.4 GHz protocol used by the bulbs is inherently insecure, so this only does so much good).

I wanted to follow up with a blog post that details how to use this. I’m going to cover:

  1. How to setup the hardware.
  2. How to install and configure the firmware.
  3. How to use the web UI and REST API to pair/unpair and control bulbs.

Shopping List

This should run you approximately ~$10, depending on where you shop, and how long you’re willing to wait for shipping. Items from Chinese sellers on ebay usually come at significant discounts, but it often takes 3-4 weeks to receive items you order.

  1. An ESP8266 module that supports SPI. I highly recommend a NodeMCU v2*.
  2. An NRF24L01+ module. You can get a pack of 10* on Amazon for $11. You can also get one that supports an external antenna if range is a concern (link*).
  3. Dupont female-to-female jumper cables (at least 7). You’ll need these to connect the ESP8266 and the NRF24L01+.
  4. Micro USB cable.

If you get a bare ESP8266 module, you’ll need to figure out how to power it (you’ll likely need a voltage regulator), and you’ll probably have to be mildly handy with soldering.

Setting up the Hardware

The only thing to do here is to connect the ESP8266 to the NRF24L01+ using the jumper cables. I found this guide pretty handy, but I’ve included some primitive instructions and photos below.

NodeMCU Pinout

NRF24L01+ Pinout


NodeMCU Pin NRF24L01+ Pin
3V (NOT Vin) VCC

Installing drivers

There are a couple of different versions of NodeMCUs (I’m not convinced they’re all actually from the same manufacturer). Depending on which one you got, you’ll need to install the corresponding USB driver in order to flash its firmware.

The two versions I’m aware of are the v2 and the v3. The v2 is smaller and has a CP2102 USB to UART module. You can identify it as the small square chip near the micro USB port:

NodeMCU v2 with CP2102 circled

Install drivers for the v2 here.

The v3 is larger and has a CH34* UART module, which thin and rectangular:

NodeMCU v3 with CH34* circled

The CH34* drivers seem more community-supported. This blog post goes over different options.

I’ve been able to use both the v2 and v3 with OS X Yosemite.

Installing Firmware

If you’re comfortable with PlatformIO, you can check out the source from Github. You should be able to build and upload the project from the PlatformIO editor.

Update – Mar 26, 2017: I highly recommend using PlatformIO to install the firmware. The below instructions are finicky and unless you get the arguments exactly right, the filesystem on your ESP will not work correctly. Using PlatformIO is a more robust way to get a fresh ESP set up. Further instructions are in the README.

Update – Feb 26, 2017: if you’ve used your ESP for other things before, it’s probably a good idea to clear the flash with --port /dev/ttyUSB0 erase_flash . Thanks to Richard for pointing this out in the comments.

If not, you can download a pre-compiled firmware binary here. If you’re on Windows, the NodeMCU flasher tool is probably the easiest way to get it installed.

On OS X (maybe Linux?), following the NodeMCU guide, you should:

  1. Connect the NodeMCU to your computer using a micro USB cable.
  2. Install esptool
  3. Flash the firmware:

    Note that  /dev/cu.SLAB_USBtoUART should be substituted for  /dev/cu.wchusbserial1410 if you’re using a v3 NodeMCU. Be sure to specify the real path to the firmware file.
  4. Restart the device. To be safe, just unplug it from USB and plug it back in.

Setup firmware

Note that you’ll have to do all of these things before you can use the UI, even if you used the pre-compiled firmware:

  1. Connect the device to your WiFi. Once it’s booted, you should be able to see an unsecured WiFi network named “ESPXXXXXX”, where XXXXXX is a random identifier. Connect to this network and follow the configuration wizard that should come up.
  2. Find the IP address of the device. There are a bunch of ways to do this. I usually just look in my router’s client list. It should be listening on port 80, so you could use nmap  or something.

You should now be able to navigate to http://<ip_of_isp>.

Using the Web UI

The UI is useful for a couple of things.

If you have Milight bulbs already, you probably have them paired with an existing device. Rather than unpairing them and re-pairing with the ESP8266 gateway, you can just have the ESP8266 gateway spoof the ID of your existing gateway or remote. Just click on the “Start Sniffing” button near the bottom and push buttons in the app or on the remote. You should see packets start to appear:

The “Device ID” field shows the unique identifier assigned to that device. To have the ESP8266 gateway spoof it, scroll up to the top and enter it:

The controls should not work as expected. You can click on the “Save” button below if you want to save the identifier in the dropdown for next time.

The UI is also useful for pairing/unpairing bulbs. Just enter the gateway ID, click on the group corresponding to the bulb you wish to pair/unpair, screw in the bulb, and quickly (within ~3-5s) press the appropriate button. The bulb should flash on and off if it was successful.

Using the REST API

The UI is great for poking around and setting things up, but if you want to tie this into a home automation setup, you’ll probably want a programmatic interface. The API is fully documented in the Github readme, but here’s a quick example:

This will turn bulbs paired with device 0xCD86, group 2 on and set the color to red (hue = 0).

UPDATE – Feb 12, 2016

I realized this project would be a lot more immediately useful to people if it just supported the existing Milight UDP protocol. This would allow people to use the existing integrations others have built for OpenHab, Home Assistant, SmartThings, etc.

The Web UI has a section to manage gateway servers. Each server will need a device ID and a port.

* Amazon affiliate link.

322 thoughts on “Milight WiFi Gateway Emulator on an ESP8266

    • Just added support for that today. I updated the end of the post with some (sort of terse) instructions. Basically you set up a UDP gateway for one or more device IDs, and configure OpenHAB as you normally would.

      Let me know if you have questions.

    • This is awesome, thanks so much Chris! Really looking forward to creating this.

      Now i just need to pick up some NRF24L01 modules, thought I had some but unfortunately only have a stack of RFM69HW modules.

    • Hi, thx for adding the UDP gateway for openhab.
      Tried it with openhab for two days now but it seems that often commands don’t reach the bulbs. openhab log says teh command has been sent but the LED won’t switch on/off.

      Interestingly when just using the milight hub webinterface everything seems to work quite well. so it’s not a RF problem I guess.

    • My guess is that this is related to this issue. It seems like the ESP had a really high UDP packet drop rate (20-30%), especially when several commands are sent at once.

      I made a few changes over the weekend, biggest of which was to decrease the number of repeated 2.4 GHz packets, which should give the OS more time to respond to UDP packets. This made a pretty huge difference in my tests.

      If you haven’t already, try the most recent version of the firmware and let me know what you find.

      EDIT – probably the reason that the web UI seems more responsive is that it’s using the HTTP API, which is obviously over TCP, so dropped packets are far less common.

    • v5 currently. I structured the code in a way that should make it easy to support either protocol (on a per-port basis) in the future.

      I don’t have a device that runs the v6 protocol, so it’s a little hard to test. I ordered one that I think probably does. Maybe you know which ones do.

    • Okay, cool. I ordered what I was hoping is a v6 bridge from Ebay yesterday, and I think your link makes me more sure it is a v6 hub.

      I noticed that CCT (adjustable white temperature) bulbs don’t work with this. There’s actually something further down the stack that’s different because the 2.4 GHz packets aren’t showing up. Probably some stupid constant that’s different. On my list of things to investigate/fix. 🙂

    • Hey Chris, thanks for this – so cool! I was about to buy a v6 hub for the color temperature support (I have 2 of the v4 hubs). You’re saying for now, it does not support it? I would personally love this feature :).

  1. Hi

    Got as far as Firmware setup step 3.

    POST index.html to /web:
    curl -vvv -X POST -F ‘image=@web/index.html’ http://<ip_of_esp>/web

    I have set up on my network, can see the device via the router, and ping it.


    Where does above post go !!

    Thanks Laurie

    • Hi Laurie,

      Basically this step is uploading the web UI to the ESP8266, as the firmware doesn’t ship with it.

      You should replace “<ip_of_esp>” with the IP of your ESP8266. So if its IP is 192.168.155, you should run the command:

      curl -vvv -X POST -F 'image=@web/index.html'

      from the directory you have esp8266_milight_hub checked out to.

      Does that help?

  2. Hi Chris

    Thanks for the reply. This look really good so want to get this working and understand it better.

    I don’t understand this, from the directory you have esp8266_milight_hub checked out to.

    Can you give me some step by step instructions as to exactly where I should be when I enter this.

    Regards Laurie

    • Hey Laurie,

      Did you check out the code from github? If so, you’d run it in the directory you have the codebase checked out from.

      Basically you want to upload this file:

      to your ESP. If you don’t have the code checked out, you can just run this command from wherever:

      INDEX_FILE=$(mktemp -t index.html.XXXXXX) wget -O $INDEX_FILE && curl -vvv -X POST -F "image=@$INDEX_FILE" && rm $INDEX_FILE

      (replacing with the IP of your ESP, of course).

    • Arg…

      I have tried with platformIO to load this.

      Completely lost. Been trying things for hours now.

      Any videos that you know of that could visually show the process as I am going round in circles

    • No video, sorry.

      Oh, I thought you’d already flashed the firmware? It sounded like your ESP was connected to your network.

      Actually think it’s easier to load the firmware with esptool (this is what I cover in the blog post). Are you not using a NodeMCU? If you are using a NodeMCU or a Wemos D1, you can just use the pre-compiled firmware here:

      EDIT – I think there are some videos on Youtube showing how to flash ESPs if that’s helpful. I just meant that I haven’t made one. 🙂

    • Hi Chris

      Sorry it maybe my explanation. Yes I have flashed the NodeMCU, logged it onto the network. I can see it via the router. I has been assigned I can’t get to the web interface. I think I should be going to This brings up Not found: /web. It is the second part of the procedure you talk about that I do not understand.
      How to upload the web/index.html file to the nodmcu. If using the command you specify what program should I use and what is the procedure. I sure it is quite simple, and I am just missing the point, but need a step by step guide.

      Sorry to drive you crackers with this. Thanks for your help.


    • Not driving me crackers (haha, I like that phrase) at all. Happy to help. 🙂

      Are you using OS X or Linux? You’d just paste that command I gave in a previous comment into a terminal.

      It’s on my TODO list for this project to make this step unnecessary, but I’ve not gotten around to it yet.

    • Hi Chris

      Tried the branch. Loaded on but

      seemed unresponsive.

      going to

      just produced message Not found: /web

      also tried, this produced


      Also tried Cygwin, but not sure how to connect to board. Is this similar to putty. I did try putty but connectionwas refused when looking at 105 ip

    • Cygwin is basically a Linux emulator that runs on top of Windows. Assuming everything you need is installed (might need to install curl?), you could probably just paste the command into it.

      I noticed that this new feature seems to work better if you clear the ESP before installing the firmware. Guessing it has something to do with the size of the code changing. Can you try that?

    • Hi Chris tried clearing down, but no change. Can get to the settings so know I have the right ip and the nodemcu is connected.


    • Tried this in terminal of IOplatform, but got this

      Ip has changed from previous to 103

      PS C:\Users\Laurie\Downloads\esp8266_milight_hub-master> INDEX_FILE=$(mkte
      mp -t index.html.XXXXXX) wget
      266_milight_hub/master/web/index.html -O $INDEX_FILE && curl -vvv -X POST
      -F “image=@$INDEX_FILE” && rm $INDEX_FILE
      At line:1 char:145
      + … h/esp8266_milight_hub/master/web/index.html -O $INDEX_FILE && curl
      -v …
      + ~~
      The token ‘&&’ is not a valid statement separator in this version.
      At line:1 char:215
      + … v -X POST -F “image=@$INDEX_FILE” && rm

      $IND …
      + ~~
      The token ‘&&’ is not a valid statement separator in this version.
      + CategoryInfo : ParserError: (:) [], ParentContainsErrorRe
      + FullyQualifiedErrorId : InvalidEndOfLine

    • Yeah, I wouldn’t expect that to work unfortunately. I’m assuming it’s opening a windows cmd prompt, which is different than bash (the most common linux/os x shell). You’d need to use a linux emulator or something.

      Anyway – can you try what I mentioned in my previous comment (link)?

    • Hello chris.
      after working this infos step by step ..

      i hang in the last steps ..
      browser need a password for connect to the /web folder of esp8266

      befor curl via Xpost — says:

      < HTTP/1.1 401 Unauthorized

      any passord and user for /web needed?

    • Hi schluri,

      Did you happen to configure an admin username/password? Can you also double-check that you’re hitting the right IP? What happens if you access <ip_of_esp>/settings via a browser?

  3. doublchecked with wget to.

    wget  (ip of the esp)

    –2017-02-25 19:12:55–
    Verbindungsaufbau zu… verbunden.
    HTTP-Anforderung gesendet, warte auf Antwort… 401 Unauthorized

    Authentifizierung mit Benutzername/Passwort fehlgeschlagen.


    firmware was the compiled binary from ur hint

    thanks for fast reply


  4. with the same  .. 401 . .he want User and Pass


    Bin file from :


    • Not currently. I just bound an IP to the ESP in my router. Could probably expose a setting if you think it’d be useful.

  5. Hi Chris,

    Fantastic project and really clear instructions – thanks!

    I’m struggling to get this to sniff the traffic correctly (and have not tried to send commands yet). This may be a v5 / v6 issue? Occasionally when sniffing it will show the following (the majority of the bytes are the same in every message), but a lot of the time it doesn’t pick up anything:

    I’ve never had it spit out the nicely formatted 10 byte messages shown in your writeup.

    A photo of the WiFi bridge I’m using is here: dropbox link

    FYI, I’m currently controlling the lights through Home Assistant’s Limitless LED component with the WiFi bridge in the photo – it works well, but I’m looking forward to using your ESP device instead of the bridge.

    Also, in case it helps anyone: To get this to work initially I had to fully erase my ESP using the following command under Linux:

    Presumably I had something already on from an earlier bit of tinkering that was causing an issue.

    • UPDATE:
      I have paired to a bulb successfully using a manually chosen ID and clicking pair (within 3 secs of powering the bulb). Everything works well!

      I guess this shows us that hardware wise my setup is good, but the WiFi bridge I have is incompatible (which doesn’t really matter since I won’t be using it)

      I’m using a RGB+CT bulb (AMAZON)

      The CT part doesn’t work (I see from the comment on 17th that you already knew this). I’d be happy to help dig into this with you.

    • Hi Richard,

      I made some changes to the firmware to accommodate the newer 2.4 GHz protocol used by the RGB+CCT bulbs after I wrote the blog post. Because the protocol has different packet sizes/structures, I removed the nice formatting, but I’ve been meaning to add it back. No support for the new protocol yet, but I’m going to take a crack at it when I get a bulb.

      Sniffing isn’t super robust because it’s hopping between three different channel sets, and I think it’s likely that it misses a packet while it’s jumping around. In the future it probably makes sense to sniff on a channel set taken as input. You just kind of have to spam commands until you see a packet.

      Sounds like you figured it out, but the 2nd and 3rd bytes are the device ID.

      Thanks for raising that you needed to clear your flash. I’ll add something about that in the post.

      Would love to collaborate on reversing the new 2.4 GHz protocol. I’ve only looked briefly, but it seems like it’s going to be significantly trickier than the old one. It looks like they added some obnoxious scrambling/encryption.

  6. Some code for esp8266 if somebody interested by iBox2 programming :

    #include <ESP8266WiFi.h>
    #include <WiFiUdp.h>

    #define zone 1

    // WiFi connection variables
    const char* ssid = “yourssid”;
    const char* password = “yourpassword”;
    IPAddress ip(192,168,1,30);
    IPAddress gateway(192,168,1,1);
    IPAddress subnet(255,255,255,0);
    boolean wifiConnected,udpConnected = false;

    // UDP variables
    unsigned int milightUdpPort = 5987;
    unsigned int localUdpPort = 5987;
    IPAddress milightIP(192, 168, 1, 10);

    //Mi-Light variables
    uint8_t V6_Preamble[] = { 0x80, 0x00, 0x00, 0x00, 0x11 };
    uint8_t V6_GetSessionID[] = { 0x20, 0x00, 0x00, 0x00, 0x16, 0x02, 0x62, 0x3A, 0xD5, 0xED, 0xA3, 0x01, 0xAE, 0x08, 0x2D, 0x46, 0x61, 0x41, 0xA7, 0xF6, 0xDC, 0xAF, 0xD3, 0xE6, 0x00, 0x00, 0x1E };
    uint8_t V6_RGBWW_On[10] = { 0x31, 0x00, 0x00, 0x08, 0x04, 0x01, 0x00, 0x00, 0x00, zone };
    uint8_t V6_RGBWW_Off[10] = { 0x31, 0x00, 0x00, 0x08, 0x04, 0x02, 0x00, 0x00, 0x00, zone };
    uint8_t V6_RGBWW_SetColor[10] = { 0x31, 0x00, 0x00, 0x08, 0x01, 0xBA, 0xBA, 0xBA, 0xBA, zone };
    uint8_t V6_RGBWW_SetBrightnessLevel[10] = { 0x31, 0x00, 0x00, 0x08, 0x03, 0xBE, 0x00, 0x00, 0x00, zone };
    uint8_t V6_RGBWW_White_On[10] = { 0x31, 0x00, 0x00, 0x08, 0x05, 0x64, 0x00, 0x00, 0x00, zone };
    uint8_t V6_RGBWW_Night_On[10] = { 0x31, 0x00, 0x00, 0x08, 0x04, 0x05, 0x00, 0x00, 0x00, zone };
    uint8_t V6_RGBWW_Disco_Mode1[10] = { 0x31, 0x00, 0x00, 0x08, 0x06, 0x01, 0x00, 0x00, 0x00, zone };
    uint8_t V6_RGBWW_Disco_Mode2[10] = { 0x31, 0x00, 0x00, 0x08, 0x06, 0x02, 0x00, 0x00, 0x00, zone };
    uint8_t V6_RGBWW_Mode_Speed_Up[10] = { 0x31, 0x00, 0x00, 0x08, 0x04, 0x03, 0x00, 0x00, 0x00, zone };
    uint8_t V6_RGBWW_Mode_Speed_Down[10] = { 0x31, 0x00, 0x00, 0x08, 0x04, 0x04, 0x00, 0x00, 0x00, zone };

    //Program variables
    uint8_t RBuffer[UDP_TX_PACKET_MAX_SIZE];
    uint8_t command[10] = { 0x31, 0x00, 0x00, 0x08, 0x04, 0x01, 0x00, 0x00, 0x00, zone };
    uint8_t bridgeID_1;
    uint8_t bridgeID_2;
    int packetSize = 0;
    uint8_t CommandCntr = 1;
    long int now, timecommand, timecheckID, timecheckcmd, timeout = 0;
    boolean startcommand, sendIDreq, ackID, sendcmd, checkcmd, ackcmd = false;
    uint8_t color = 0;
    boolean toggle = false;

    WiFiUDP udp;

    //Connect to WiFi function
    boolean connectWifi()
    boolean state = true;
    int i = 0;
    WiFi.config(ip, gateway, subnet);
    WiFi.begin(ssid, password);
    Serial.println(“Connecting to WiFi”);

    // Wait for connection
    while (WiFi.status() != WL_CONNECTED)
    if (i > 10)
    state = false;
    if (state)
    Serial.print(“Connected to “);
    Serial.print(“IP address: “);
    Serial.println(“Connection failed.”);
    return state;

    //Connect to UDP function
    boolean connectUDP()
    boolean state = false;

    Serial.println(“Connecting to UDP”);

    if(udp.begin(localUdpPort) == 1)
    Serial.println(“Connection successful”);
    state = true;
    Serial.println(“Connection failed”);
    return state;

    //Program Setup
    void setup()
    //Initialise Serial connection

    // Initialize WiFi connection
    wifiConnected = connectWifi();

    //If WiFi OK Initialize UDP
    udpConnected = connectUDP();

    //Initialize Mi-Light comunication

    //Main Loop
    void loop()
    //Check if WiFi connection successful
    if (wifiConnected)
    //Check if UDP connection successful
    if (udpConnected)
    //User code
    now = millis();
    if (((now – timecommand) > 500) && !startcommand)
    if (toggle)
    Serial.println(“Send On”);
    memcpy( command, V6_RGBWW_On, 10);
    Serial.println(“Send Off”);
    memcpy( command, V6_RGBWW_SetColor, 10);
    toggle = !toggle;*/
    memcpy( command, V6_RGBWW_SetColor, 10);
    if (color < 255)
    color += 2;
    color = 0;

    command[5] = color;
    command[6] = color;
    command[7] = color;
    command[8] = color;

    startcommand = true;
    //Serial.print(“startcommand = “);
    timecommand = now;
    timeout = now;

    //Mi-Light ID message check
    now = millis();
    if (((now – timecheckID) > 100) && sendIDreq && !ackID)
    timecheckID = now;

    //Command answer check
    now = millis();
    if (((now – timecheckcmd) > 100) && checkcmd && !ackcmd)
    timecheckcmd = now;

    //Timeout check
    now = millis();
    if (((now – timeout) > 1500) && startcommand)

    //Mi-Light ID request
    if (startcommand && !sendIDreq)

    //ID extraction
    if (ackID)

    //Mi-Light command send
    if (sendcmd && !checkcmd)

    //Command reset
    if (ackcmd)

    //Command reset function
    void initcommand()
    startcommand = false;
    sendIDreq = false;
    ackID = false;
    sendcmd = false;
    checkcmd = false;
    ackcmd = false;
    timeout = now;

    //Mi-Light ID message check function
    void checkID()
    packetSize = udp.parsePacket();
    if ((packetSize != 0) && (packetSize == 22))
    ackID = true;
    //Serial.print(“ackID = “);
    Serial.print(“Response : “);
    for (int i =0; i < packetSize; i++)
    byte value = RBuffer[i];
    Serial.print(” “);

    //Command answer check function
    void checkCmd()
    packetSize = udp.parsePacket();
    if ((packetSize != 0) && (packetSize == 8))
    ackcmd = true;
    //Serial.print(“ackcmd = “);
    Serial.print(“Result : “);
    for (int i =0; i < packetSize; i++)
    byte value = RBuffer[i];
    Serial.print(” “);

    //Mi-Light ID request function
    void requestID()
    udp.beginPacket(milightIP, milightUdpPort);
    sendIDreq = true;
    //Serial.print(“sendIDreq = “);

    //ID extraction function
    void GetBridgeID()
    bridgeID_1 = 0;
    bridgeID_2 = 0;

    bridgeID_1 = RBuffer[0x13];
    bridgeID_2 = RBuffer[0x14];
    sendcmd = true;
    //Serial.print(“sendcmd = “);

    //Mi-Light command send function
    void startCommand(const uint8_t *pCmd)
    uint8_t OutBuffer[100];
    //uint8_t RBuffer[100];
    int wPointer = 0;
    memcpy(OutBuffer + wPointer, V6_Preamble, sizeof(V6_Preamble)); wPointer += sizeof(V6_Preamble);
    OutBuffer[wPointer++] = bridgeID_1;
    OutBuffer[wPointer++] = bridgeID_2;
    OutBuffer[wPointer++] = 0x00;
    OutBuffer[wPointer++] = (uint8_t)(CommandCntr++);
    OutBuffer[wPointer++] = 0x00;

    int wPointerCmd = wPointer;
    memcpy(OutBuffer + wPointer, pCmd, 10); wPointer += 10;

    OutBuffer[wPointer++] = 0x00;

    //Calculate checksum
    uint8_t crc = 0;
    for (int ii = 0; ii < wPointer- wPointerCmd; ii++)
    crc = (crc + OutBuffer[wPointerCmd+ii]) & 0xFF;
    OutBuffer[wPointer++] = crc;

    Serial.print(“Command : “);
    for (int i =0; i < wPointer; i++)
    byte value = OutBuffer[i];
    Serial.print(” “);

    udp.beginPacket(milightIP, milightUdpPort);
    checkcmd = true;

    • Nice! Thanks for sharing. Definitely want to add support for the v6 protocol, mostly because it includes receipt confirmation packets.

  7. After quite a bit of trial and error, I was finally able to get the firmware load (both via binary and building from source via platformio), configure and load the UI but I haven’t been able to get it to sniff any codes.

    I have checked my wiring several times, tried w/ different dupont cables, switched out rf boards, even switched the nodemcu.  The nodemcu lights up blue if I disconnect the rf board while sniffing and the serial monitor scrolls the following until I reconnect the rf board then NOTHING even when I try to control the already gateway synced lights via remote.

    Any help would be greatly appreciated.

    • I’ve noticed that the sniffing can be a little flakey too. As far as I can tell, it seems like the nrf24l01 gets into a bad state. I’ve had some luck disconnecting and re-connecting the nrf’s vin line a few times. Sometimes restarting the nodemcu helps. Once it starts working, it seems to work pretty reliably. I’ve been trying to reverse engineer the RGB+CCT protocol and I’ve successfully scraped nearly 1 million packets with this.

      Just to make sure, your UI looks like this right?

      Which model of bulbs are you trying to control? I think there are 4-5 models out, and they all seem to have slightly different RF settings (channels, syncwords, etc.).

    • Yes, my interface looks exactly like that at the bottom. Changing the bulb type does not do anything except when selecting RGBW+CCT – I get an error in serial monitor about an unknown type.

      My bulbs were purchased from Amazon in 2015 and I have white and RGBW. I have the same gateway controller mentioned in the comments above which now does not show up in my iPhone apps so I can’t control them from there any longer; however, I can still call from node.js through the gateway so I am completely confused now.

      I have tried pulling the vin and rebooting the nodemcu – I’ve yet to get it to sniff anything.

    • Cool. I’ll fix the error in the serial monitor soon. RGB+CCT isn’t useful yet anyway ’cause I’m still working on reversing the protocol.

      I’m thinking some of those bulbs probably use radio settings the firmware doesn’t support yet.

      As far as I’ve been able to tell, every screen in the Milight app uses a different radio config (set of 3 channels and different syncwords).

      This is what my Milight app looks like (hoping yours looks similar):

      The 4th screen is CCT, the 6th/last is RGBW.

      You may have already been doing this, but can you try listening on RGBW and spamming some buttons on the 6th screen of the app?

    • My app looks pretty much the same as that. I was able to get my controller back online. I put it into sniff mode and I mashed on every button in the RGBW controller w/in the app. I could see the bulb changing colors, brightness, to white, on/off, disco, etc. but nothing showed up in the serial monitor or on the web page.

    • That’s pretty weird.

      Is there a way for you to test if the nrf is able to send any data? You could try unlinking a bulb with your controller and linking with the mcu. If it works it should be easy to unpair it. If this works we could at least rule out hardware issues.

      I’m noticing that it behaves horribly when switching between radio configs. It never works to switch between the configs without resetting the nrf. What seems to consistently work for me is:

      1. Start listening on the config I want
      2. Disconnect vin on the nrf for a few seconds
      3. Reconnect vin

      I’ll then start receiving packets. Sometimes I need to repeat this process — all while continuing to listen on the config I want — a few times.

      I created this issue to remind myself to investigate:

    • I unsynced from the gateway controller but I have not been able to pair it to the nodemcu. 

      I did set up a pair of nodemcu and can have them communicate via RF (Getting Started) Arduino example from the this library so I know the hardware works.

      Image of communication between the pair:

      One difference is that in the example above I used D2 (gpio2) for CE instead of D0 (gpio16) which I tried to replicate (unsuccessfully) in the settings of the MiLight Hub…

    • Do you see any traffic on a nodemcu listening for general RF traffic when the milight firmware is supposed to be sending data?

      I assume you’re trying to link one of the RGBW bulbs? Some of the ones you linked earlier will almost certainly not work without adding some different RF configs.

      Can you paste in what’s being returned by the ‘/settings’ endpoint? Should be a JSON blob.

    • I now have it running – I can sniff, pair, unpair and control the lights.

      I rebuilt the firmware and flashed to a third nodemcu after erasing the flash and then changed the CE pin to 2.

      Thanks for your patience and assistance!

  8. Hello, I’m using a Wemos D1 mini, but I haven’t gotten control of my milight yet. I spent some hours learning how to use platformioto get the program flashed correctly. I have the web ui running but I haven’t been able to sniff any traffic. I purchased just the milight and no remote with the hopes that I would not need the remote.

    I could not figure out how to invoke from a Windows command prompt. To erase flash, followed these instructions. Instead of the ESP flash tool, I used nodemcu flasher, and flashed a 1MB file full of zeroes to four offsets (000000,100000,200000,300000).

    When trying to use the curl command in Windows, there is a substitute program that is called instead, but it does not do what’s needed. To get around that, I used this command:

    remove-item alias:curl

    then downloaded cURL from this site and placed only the executable in my windows directory.

    The next time I tried the curl command, it worked to install the web ui.

    Is there a way I can get some control over my bulb or sniff some traffic without a remote? I’ve tried using pin 2 (D4) and pin 16 (D0) for CE, no luck yet.

    • Hey there,

      Sorry for the slow response — been out of town this week.

      Thanks for sharing instructions for Windows! That’s really helpful.

      I’ve noticed that listening is really flaky. It’s definitely a bug of some sort (github issue).

      I’ve had decent luck disconnecting and re-connecting Vin on the nrf24l01 a few times while it’s listening. Sometimes restarting the ESP helps too.

      Sniffing is probably the only sane way to find the ID of your remote. If you just want to control the bulbs with the ESP, you could unpair them with the remote and pair them with the ESP using an arbitrary ID.

  9. Pingback: Reverse engineering the new Milight/LimitlessLED 2.4 GHz Protocol | Chris Mullins

  10. I’m trying to set this up on a Wemos D1 mini (same as this reddit post) but can’t seem to get the latest version to work.

    I first set it up about a week ago (Mar 21) using esptool on macOS and successfully paired/controlled a RGBW LED Strip controller (FUT038) using the web UI and Gateway. Since I couldn’t sniff my RGB+CCT remote (FUT092) or control my RGB+CCT lights I waited for a version that could.

    So, yesterday I tried again, this time using Platformio (erasing the flash before I started), I couldn’t get the commands to work “platformio run -u..." just gave me the Error: no such option: -u but I got it working using the menu option PlatformIO > Run other target…

    Now I can successfully sniff my RGB+CCT remote but the ID shifts around a bit sometimes (if I press the same button 15 times I get 12*210D, 2*2109, 1*2101) not sure if this is a problem.

    I can’t pair or control any lights though, not even the RGBW controller that worked before, it doesn’t matter if I use the remote ID or an arbitrary ID

    When I try to send any command I get things like the one posted below in the serial monitor and the Wemos seems to reboot. I’ve tried connecting to a better usb power supply thinking there might be a power issue but I still still get the same problem.

    • So, yesterday I tried again, this time using Platformio (erasing the flash before I started), I couldn’t get the commands to work “platformio run -u…” just gave me the Error: no such option: -u but I got it working using the menu option PlatformIO > Run other target…

      Sorry about that. It should be -e, not -u. I updated the README. Thanks very much for raising.

      Now I can successfully sniff my RGB+CCT remote but the ID shifts around a bit sometimes (if I press the same button 15 times I get 12*210D, 2*2109, 1*2101) not sure if this is a problem.

      This is something another person noticed today (see the last few comments in this GitHub issue). Because one has to do more guesswork as to what the true decoded value of the ID bytes are, there are multiple possible values for the parameters in the encoding/decoding algorithm. I think this is happening because I chose the wrong ones.

      It would be super helpful if you could get me a bunch of example packet captures from your device. The ideal case is having an example for all 256 possible values of the first byte, but anything will help.

      I can’t pair or control any lights though, not even the RGBW controller that worked before, it doesn’t matter if I use the remote ID or an arbitrary ID

      I’m guessing this is for a similar reason that your ID is flipping.

      I think the way to fix this is to flash your ESP with an older version of the firmware (this revsion should do), use it to unpair your device using the same ID you paired it with, and then re-flash with the new hardware.

      Here’s some background:

      There were several possible parameter values to decode the ID bytes consistently for the few devices I had on hand. I chose a few arbitrarily. I later noticed that my Milight Wifi gateway was capable of emulating older remotes that didn’t have scrambled protocols, and so I had thought that the unscrambled ID would be the same as the ID in the scrambled protocol. I used this assumption to choose what I thought were the “correct” values for the scrambling parameters.

      Unfortunately, it seems like that assumption is not true. Last night I noticed a different unscrambled protocol had a different device ID. And now two people are reporting that the ID isn’t being decoded consistently, which pretty much certainly means the wrong values were chosen again.

      When I try to send any command I get things like the one posted below in the serial monitor and the Wemos seems to reboot. I’ve tried connecting to a better usb power supply thinking there might be a power issue but I still still get

      That looks like a watchdog reset, which is kinda funky (means some code is running without yielding to the OS for too long). I think I’ve only seen that in two circumstances:

      1. When I had the NRF hooked up incorrectly.
      2. After I fried my NRF by connecting it to 5v.

    • It would be super helpful if you could get me a bunch of example packet captures from your device. The ideal case is having an example for all 256 possible values of the first byte, but anything will help.

      Sure, should I just press random buttons or the same one?

      That looks like a watchdog reset, which is kinda funky (means some code is running without yielding to the OS for too long). I think I’ve only seen that in two circumstances:

      1. When I had the NRF hooked up incorrectly.
      2. After I fried my NRF by connecting it to 5v.

      Hopefully I’ve just hooked it up incorrectly, thought I double-checked everything but I’ll tear it down and start from scratch. Can’t see how I could have fried it, wouldn’t that affect the receiving/sniffing as well? Sadly I don’t have any spare NRF but I may order some.


    • Sure, should I just press random buttons or the same one?

      Any button or combination of buttons is fine.

      I think I was able to find the right parameters. Narrowed it down to a single option with the data from my devices and packet captures from the person in the GitHub issue I linked. But it’d be nice to validate that with more data.

      Hopefully I’ve just hooked it up incorrectly, thought I double-checked everything but I’ll tear it down and start from scratch. Can’t see how I could have fried it, wouldn’t that affect the receiving/sniffing as well? Sadly I don’t have any spare NRF but I may order some.

      If you’re still getting it, you could try enabling debug logging. Compiling with -D DEBUG_PRINTF should do it.

    • Any button or combination of buttons is fine.

      Here’s ~6000 captures, just tell med if you want more:

      If you’re still getting it, you could try enabling debug logging. Compiling with -D DEBUG_PRINTF should do it.

      So I picked everything apart, and started again, this time I’m getting a different kind of error. I get it every time when changing hue in RGBW or RGB mode (but not RGB+CCT), nothing else have triggered it so far

      I might try compiling with -D DEBUG_PRINTF later.

    • Cool. Verified the ID bytes in these packet captures are staying fixed with the new parameters. Thanks for providing the packet captures!

      Weird. This sounds like a bad reference error judging from this. Which ESP are you using? I’ll verify that these are still working with my ESPs later today.

    • Weird. This sounds like a bad reference error judging from this. Which ESP are you using? I’ll verify that these are still working with my ESPs later today.

      I’m using a Wemos D1 Mini V2.1.0. I have more of them so I could try another board or compiling with -D DEBUG_PRINTF if you think that would give any more information.

      I’ve also ordered a couple of new NRF, but It might be a while before they arrive.

    • I did try with a wemos pro and noticed that things didn’t seem to run as smoothly as they did on the nodemcus or raw ESP-12/ESP-07s. I kind of just figured mine was defective or something, though.

      I’ll try with a wemos mini later today.

    • Just tried with a Wemos Mini.

      It appears to be working with my RGBW and RGB+CCT bulbs. I did find a bug in the dev branch where color wasn’t working with RGBW. This same bug almost certainly would cause reference errors under some circumstances, so it makes sense that you were seeing that.

      Can you try flashing with the latest revision in the dev branch and see if that fixes your issue?

    • So, i flashed the lastest version and all the errors are gone.

      It also seems to have solved all other problems. I’m now able to consistently sniff the ID of my remote, pair, and control lights. I’ve also connected it to Home Assistant and so far everything works great.

      Awesome work.

  11. Hi Chris,


    Have you considered supporting the philips hue API?

    This would allow instant integration into may other systems (harmony, alexa, etc)

    Also another idea I had was to use a sonoff switch as the host – then you get a PSU and an enclosure for a few more $. I dont think that the necessary ESP pins are easy to connect to though 🙁

    • Hey James,

      I’ve thought about it. I’m still not sure how valuable it’d be given that stuff like HomeAssistant already support the MiLight API. Do you think there would be a lot of people that use this that also wouldn’t already be running something like HA?

      Cool idea using a sonos!

  12. I bought a FUT015 RGB/CCT bulb without a remote hoping I could get it to pair using this solution, but so far I’m not having any luck.

    I have a bunch of questions:

    Is it supposed to be possible to pair my bulb without first pairing it with a remote, or am I trying to do something which is not implemented yet?
    Are the newish RGB/CCT bulbs supported, or are there still issues?
    I don’t see anything in the sniff logs when turning on the bulb. Are the bulbs receive-only, or two way? If two way: are they supposed to be silent when turned on?
    Could the bulb already be paired, and does it matter? I have tried doing the pairing process a bunch of times with different device IDs, and maybe it worked and I just have the wrong device ID.

    Any other advice?

    • Is it supposed to be possible to pair my bulb without first pairing it with a remote, or am I trying to do something which is not implemented yet?

      Yes, this is supported. I’ve tested extensively with bulbs that I have at home. I’ll double-check that things are working with the latest version.

      Did you select the “RGB+CCT” bulb type in the UI?

      Are you sure the NRF is sending data? The easiest way to verify is to hook up another ESP+NRF and listen for traffic while the other one is sending.

      Are the newish RGB/CCT bulbs supported, or are there still issues?

      Yes, they’re supported. I use them all over my house 🙂

      I don’t see anything in the sniff logs when turning on the bulb. Are the bulbs receive-only, or two way? If two way: are they supposed to be silent when turned on?

      Bulbs never send packets, so it’s not surprising that you don’t see anything. They are indeed supposed to be silent when you turn them on.

      Could the bulb already be paired, and does it matter? I have tried doing the pairing process a bunch of times with different device IDs, and maybe it worked and I just have the wrong device ID.

      In my experience, it depends a bit on the bulb type. It seems that with the newer bulbs, they’re happy to pair with multiple remotes. They pair with up to 4 (?), and forget the oldest if you try to pair with more. Older bulbs will ignore commands coming from a different device until you unpair them.

  13. I’m so excited that I’ve found this project! Really amazing work. It’s way past my bedtime but I won’t be able to sleep without flashing a Node first!

    One thing I’m wondering is that Andune mentioned that they have managed to discover the NodeMCU as a MiLight bridge within Home Assistant. Does Home Assistant’s LimitlessLED plugin allow (almost!) unlimited zones? I really hope this is the case as I almost bought two more bridges today!

    Do commands start to stack if issued too frequently? I ask as I really want to sync my lights to music by grabbing samples through a 3.5mm line in and a MSGEQ7 graphic equalizer.

    One more thing, does anyone know the maximum amount of remotes a zone controller can be paired with? I think I read that it is four. Does pairing the controller to a bridge count as one of the four?

    • Glad you’re excited about it! 😀

      One thing I’m wondering is that Andune mentioned that they have managed to discover the NodeMCU as a MiLight bridge within Home Assistant. Does Home Assistant’s LimitlessLED plugin allow (almost!) unlimited zones? I really hope this is the case as I almost bought two more bridges today!

      This project definitely enables unlimited zones within HomeAssistant. Basically you point each bridge in your HA config at a different UDP port on the ESP, with each port being bound to a different Milight device ID.

      Do commands start to stack if issued too frequently? I ask as I really want to sync my lights to music by grabbing samples through a 3.5mm line in and a MSGEQ7 graphic equalizer.

      It’s reasonably responsive, but I’ve not done any load testing. You can fiddle with the number of repeat packets to tradeoff IP responsiveness for RF responsiveness.

      There’s also a new feature in the 1.2.0 dev branch to add support for LT8900 transceivers (NRF24L01 alternative), which are in theory more performant because they’re basically the same model of transceiver used by Milight devices and don’t need software to emulate parts of the communication stack. Tests showed that they’re roughly 4x faster.

      One more thing, does anyone know the maximum amount of remotes a zone controller can be paired with? I think I read that it is four. Does pairing the controller to a bridge count as one of the four?

      Think it depends on the device. Some devices are limited to a single remote/gateway. Others support up to 4, I think. Pairing a controller with a bridge should count, yes.

  14. I see further up in the comments, that you’ve got it working with an esp-12. I assume this means without NodeMCU.

    How are you hooking it up? I’m especially concerned with GPIO15 and 16, as they are used for other things as well.

    • Yeah, it should work with ESP-12. I tested it with NodeMCU, Wemos D1 Mini, ESP-07, and ESP-12.

      Hooked it up with the same pins shown in the writeup. GPIO 15 is SPI CS and can’t be changed. That function won’t work with any other pin. The pin used for CE (GPIO 16 by default) is configurable via the UI or the settings REST endpoint.

  15. Hello,

    I have built a version with NodeMCU and one with Wemos D1 Mini.
    I have tried about 10 different NRF boards.
    I can reach the website, but Sniff Traffic does not show any results.

    Now I do not know any more. Is there a debugging facility to find the error?

    Greetings Peter


    • Hi Peter,

      You can try building the firmware with the DEBUG_PRINTF flag set. This will cause it to print out some hopefully helpful error info to the serial console.

      I assume you’re pressing buttons on a remote or some other device while sniffing traffic?

      I’ve had luck kicking the NRF a few times by disconnecting and reconnecting Vin. This shouldn’t be necessary, though.. in my experience sniffing works reasonably reliably without any manual effort. Would be curious to see your serial output with DEBUG_PRINTF turned on.


  16. I do not get sniff traffic. Is not so bad.
    But I can pair the old lamps and control.

    The point “Gateway Servers” I did not understand.
    Chris, can you explain what to do with it?



    • There’s some light documentation on this in the README. Each entry spins up a UDP server on the specified port. It’s an easy way to add compatibility with home automation systems that already have integrations with Milight products, like Home Assistant, and OpenHAB.

  17. Hello Chris,

    I have the following lines in the platformio.ini registered:

    A terminal program at 9600 baud shows me only the current IP address. I can not see more.

    The 3.3V for the NRF I have placed on a switch and switched off several times.

  18. Oh, there comes something:

    Sending packet: B0001000010300
    Elapsed: 171
    Sending packet: B0001000010401
    Elapsed: 171
    Radio is available
    Packet received: A4AE00D00080080C088A5900
    Packet transformed: 07B0001000010311A509
    Successfully parsed packet of length 8

    Received packet (len = 8)!
    NRF24MiLightRadio – received packet!
    NRF24MiLightRadio – Checking packet length (expecting 8, is 8)
    Packet id: 45073
    Radio is available
    Packet received: A4AE00D0008008020486C6E0
    Packet transformed: 07B00010000104123676
    Successfully parsed packet of length 8
    Received packet (len = 8)!
    NRF24MiLightRadio – received packet!
    NRF24MiLightRadio – Checking packet length (expecting 8, is 8)
    Packet id: 45074
    Radio is available
    Packet received: A4AE00D00080080C0C8ED540
    Packet transformed: 07B0001000010313B72A
    Successfully parsed packet of length 8
    Received packet (len = 8)!
    NRF24MiLightRadio – received packet!
    NRF24MiLightRadio – Checking packet length (expecting 8, is 8)
    Packet id: 45075
    Radio is available
    Packet received: A4AE00D00080080202800C80
    Packet transformed: 07B00010000104140013
    Successfully parsed packet of length 8
    Received packet (len = 8)!
    NRF24MiLightRadio – received packet!
    NRF24MiLightRadio – Checking packet length (expecting 8, is 8)
    Packet id: 45076

  19. Now the sniffer also shows something:
    Is already funny, I have the same determined already 50 times:
    Sent with one Nodemcu and another tries to sniffen. Now it’s working.


  20. But it is only indicated when I send with the second Nodemcu.
    When I switch my lamps over a real Milight bridge nothing is displayed. I have here several V3 and two V6 bridges here also the appropriate remote controls.

  21. With a V6 remote control the terminal shows Errors:

    Packet received: 18A905F0D99835BB5F6593CBD68F
    Packet transformed: 09FAB099C1DAAD6F9A3CBD16
    Failed CRC: expected 5789, got 48406
    Radio is available
    Packet received: 18A945F0D99835BB5F6593CB968F
    Packet transformed: 29FAB099C1DAAD6F9A3C9D16
    Failed CRC: expected 47656, got 40214
    Radio is available
    Packet received: 19A901CC5F946746982D426C05EF
    Sync 3h fail (0: 98)

    Radio is available
    Packet received: 18A931CC5F946746982D426C05EF
    Packet transformed: C938A39F622E96412B64037A
    Failed CRC: expected 35214, got 890
    Radio is available
    Packet received: 186901CC5F946742982D4AAC55EF
    Packet transformed: 0938A39F622E94412B55A37A
    Failed CRC: expected 25471, got 41850
    Radio is available
    Packet received: 18A90643EA4B36F4EBEE4BCE379F
    Packet transformed: 09267C25CDF6727D273DC79E
    Failed CRC: expected 14408, got 51102
    Radio is available
    Packet received: 28A90443EA4B2A6FCBAE4BCE279F
    Sync 3h fail (0: 14)
    Radio is available
    Packet received: 9AA92EAA053F3B7554673A99A4AF
    Sync 3h fail (0: 59)
    Radio is available
    Packet received: 18AA4A55151F9B36D663208BC4B4
    Packet transformed: 25A58A8A9FCDB6664C103DD2
    Failed CRC: expected 47310, got 15826
    Radio is available
    Packet received: 19AD04657C1B55D3342AE427AA8D
    Sync 3h fail (0: 98)
    Radio is available
    Packet received: 28A94068EE652639CC743C96610D
    Sync 3h fail (0: 14)

    • Keep in mind that it only sniffs for packets for one RF config at a time. So it won’t hear commands sent by an RGB remote when sniffing for RGB+CCT packets.

      I use the term “RF config” to mean 2.4 GHz channel, expected packet length, and the PL1167 syncwords.

      It looks like everything you’re seeing is consistent with listening on an RF config that doesn’t match the remote you’re using.

      What model are you using? (good list found here: When using the wifi box, what does the remote you’re using within the app look like?

  22. The last log is from an iBox2, a FU T092 remote and a FU T015 lamp.
    In the Android app, the remote control looks just like the FU T092.

    I just noticed something else
    Setting Sniffing: RGB + CTT

    All commands I send with the Android app are shown in the terminal error-free (Successfully parsed packet of length 10)

    The commands sent by the remote control show
    Packet transformed: 155655F51DB60DB7F8441908
    Failed CRC: expected 5192, got 6408

    The remote control always switches the lamp reliably.

    • Would you be able to post a picture of the remote you’re using?
      It sounds like it’s using a different RF config.

      Are you able to control the lights with the ESP/NRF?



    I can switch the lamp with the Android app, with the remote control and also with your Milight WiFi Gateway Emulator. Meanwhile, everything seems to be perfect.

    I have also set up a gateway server with V6 protocol.
    It appears in the Android app and also in openHAB2. So I paired an old lamp. This also works.

    Uh, my house automation is getting more complicated. 🙂

    What I do not like is the ESP chip, because it is over Wifi in the network
    I use a lot of ESPs (NodemcuV2) for my self-built light controls.

    They work without problems for many weeks. Then an ESP loses the contact to the network. I have to press the reset button, then it goes again a few weeks.
    I’d rather have a solution with a network cable.

    Or via the MySensors network, with a gateway connected via a network cable.


    • Hmm… capturing packets from that remote should definitely work. I have three of them, and have successfully captured packets from all of them.

      I’ve had one running for many weeks with no problems. It’s even survived me resetting my router a few times without any effort.

      There is an option in the settings to schedule a reboot every N minutes. Maybe that’d suit your needs. It’s probably also possible to automatically reboot when the connection is lost, but that’d require some changes.

  24. Awesome project! I’m very happy with this!

    The Milight V6 versions have a “nightmode”. Is it possible to integrate this in the project? For UDP  in /lib/Udp/V6RgbCctCommandHandler.cpp on line 46:

    For HTTP in /lib/WebServer/MiLightHttpServer.cpp, function “void MiLightHttpServer::handleUpdateGroup”, add below “if (request.containsKey(“command”)) {“:

    Thanks in advance!

  25. I get a new remote control and two lamps tomorrow. I then tell you whether the new remote control is properly decoded.

    I managed to kill both ESPs last night. After I had entered a gateway server, I very often updated the website. Then the Wemos and the Nodemcu crashed.

    With my ESPs a router reset is also no problem, they logon when the router is online again.

    The problem is when they just crash.  Unfortunately, the ESPs do not always start after a soft reset. Otherwise they could be booted every 24 hours.

    I’ll be back tomorrow.

    • Are you saying you crashed it by refresh-spamming the index page? I can’t even get it to crash running 5 requests in parallel:

      $ seq 1 100 | xargs -P 5 -n 1 -I % bash -c 'curl -s http://esp/ > /dev/null && echo %'

      Anyway, this doesn’t really seem like normal behavior, so I’m not actually worried if the thing does crash when you refresh spam it. 😛

    • Sure, go for it. Just add some parameters to autoConnect:

      //first parameter is name of access point, second is the password
      wifiManager.autoConnect("AP-NAME", "AP-PASSWORD");

  26. Wow, that’s easy (if you know) 🙂

    I have a little trouble with the Wemos Board.
    It breaks the connection again and again.
    Now I have a Nodemcu V3 and one with V2 in use.

    I’m still waiting for the two new lamps and the remote control.

    • Very cool! Nice work. Glad to see you got it working with HubAction.

      I’d eventually like to document getting this to work with popular HA platforms. When I do, I’ll make sure to link to this. 🙂

  27. So I have now moved on.

    Both remote remotes can be decoded perfectly. I was probably at the first attempts too far from the gateway. (Ground floor – basement)

    I do not use the Wemos any more, it has crashed too often.

    Currently the gateway runs on a Nodemcu V3 and on a V2. Everything works.

    I just do not know how to control it. My house automation is openHAB 2. Unfortunately, the milight binding is very unreliable.

    For my own ESPs I use mqtt. So I control and switch everything.

    Chris, can you perhaps install an mqtt access?
    This makes the gateway extremely versatile.

  28. Hello Chris,
    have already seen that you are thereby to integrate mqtt.

    You write:
    To configure your ESP to integrate with MQTT, fill out the following settings:

    But where can I find the settings?

    Greetings Peter

    • You need to update the web UI. Do this by either flashing a new SPIFFS image (this will erase your settings), or by POSTing the index.html to the /web endpoint.

  29. Chris,

    You’re a great programmer!!
    But your work will appreciate only a few people. Most will not know how to use the program.
    A few practical (with more precise syntax) examples would also help beginners.

    • Hey Peter,

      Are you referring specifically to the MQTT integration/documentation? I agree there are more moving pieces than there should be. It’s a bit of a work in progress.

      Obviously I’m happy to take suggestions if you have them. 🙂


  30. I have problems with english. I work with the Google translator.

    I probably will not get a connenct with my mqtt server.
    I have in the platformio.ini:
    Build_flags =! Python

    In the webgui:
    Milight_gw / + / + / +
    The terminal shows:

    * WM: AutoConnect
    * WM: Connecting as wifi client …
    * WM: Using last saved values, should be faster
    * WM: Connection result:
    * WM: 3
    * WM: IP Address:
    * WM:
    MqttClient – Connecting to:
    Parsed: 1883
    MqttClient – connecting

    • Hey Peter,

      Try making the topic pattern:


      On the publisher side, you would publish a message to topics that look like this:


      I’ll try to make the documentation clearer. Thanks for the feedback!

  31. I think I’m not connected to the mqtt server.

    The terminal shows only:
    MqttClient – Connecting to:
    Parsed: 1883
    MqttClient – connecting

    I have assumed that the Terminal shows  the incoming mqtt commands.
    But more is not to see.

    In Gui I have now set the following:

    Milight_gw /: device_id /: device_type /: group_id

    I’ll send with:
    Mosquitto_pub -h -t milight_gw / 0x0001 / rgb_cct / 1 / set_white / off / 40/30 -m “1” on Linux

    • Maybe this is just Google Translate being goofy, but in your topic:

      Milight_gw /: device_id /: device_type /: group_id

      Are the spaces really present? They shouldn’t be. Make it


      There are also spaces in the topic you’re publishing with mosquitto_pub.

      I’m not familiar with mosquitto_pub, but it should probably be:

      mosquitto_pub -h -t Milight_gw/0x0001/rgb_cct/1 -m '{"status":"on","command":"set_white"}'

      It should indeed print out messages it receives if you’ve compiled the firmware with MQTT_DEBUG.

  32. Hi Chris,

    The Spaces probably come from the Google translator.
    I have compiled the source with -D MQTT_DEBUG via platformio.
    So no connect to the mqtt server.
    Do you have any idea?

    PS. I have several mqtt servers here for my home automation.
    No one works.

    • Hey Peter,

      We’ll get it working! 🙂

      Is there a way for you to check if your MQTT server(s) are seeing connections from the ESP? Maybe logs or something? My Mosquitto install shows logs when clients connect.

      Can you try flashing the latest version (1.2.0-dev7)? I added some additional debug prints when MQTT_DEBUG is enabled.

      My serial session looks like this:

      *WM: AutoConnect
      *WM: Connecting as wifi client...
      *WM: Using last saved values, should be faster
      *WM: Connection result:
      *WM: 3
      *WM: IP Address:
      MqttClient - Connecting to:
      MqttClient - connecting
      MqttClient - subscribing to topic: lights/+/+/+
      MqttClient - Successfully connected to MQTT server
      MqttClient - Got message on topic: lights/0x118D/rgb_cct/1
      MqttClient - device 118D, group 1

  33. Milight_gw /: device_id /: device_type /: group_id
    As you had written seems to be wrong.

    When I use milight_gw/device_id/device_type/group_id  I get:

    MqttClient – Successfully connected to MQTT server
    MqttClient – Connecting to:
    Parsed: 1883
    MqttClient – connecting
    MqttClient – subscribing to topic: milight_gw / device_id / device_type / group_id
    MqttClient – Successfully connected to MQTT server
    But I believe this is the wrong way.
    I mean right would be:

    Subscribe to:

    Mosquitto_pub -h -t Milight_gw -m ‘{“command”: “set_white”, “status”: “on”, “level”: 100 “}’


    • The colons (“:”) are important, don’t leave them out. Try


      This will case the ESP to subscribe to the topic:


      This line in the serial logs:

      MqttClient – subscribing to topic: milight_gw / device_id / device_type / group_id

      should read:

      MqttClient - subscribing to topic: milight_gw/+/+/+

  34. When i use topic:


    i get:

    MqttClient – Connecting to:
    MqttClient – connecting
    MqttClient – subscribing to topic: milight_gw/ / /
    MqttClient – Successfully connected to MQTT server


    The + (plus) is missing.


    • I’m wondering if Google Translate is eating my formatting. Can you try turning it off and copy-pasting what’s in the code block below as your topic pattern?

      The topic should be:


  35. Do you really mean:

    connect to:



    i get:


    MqttClient – Connecting to:
    MqttClient – connecting
    ERROR: Failed to connect to MQTT server



    • No, haha. Use your own MQTT server. But the topic pattern should be what you set it to.

  36. milight_gw/:device_id/:device_type/:gateway_id

    MqttClient – connecting
    MqttClient – subscribing to topic: milight_gw/+/+/:gateway_id
    MqttClient – Successfully connected to MQTT server


    mosquitto_pub -h -t Milight_gw/rgbw/2/0x0010 -m ‘{“status”:”off”}’

    and nothing

    • Sorry man, typo.

      Should be :group_id, not :gateway_id.


  37. The word “lights” is the Magic:

    mosquitto_pub -h -t lights/0x0010/rgbw/2 -m ‘{“status”:”off”}’


    MqttClient – Connecting to:
    MqttClient – connecting
    MqttClient – subscribing to topic: lights/+/+/+
    MqttClient – Successfully connected to MQTT server
    MqttClient – Got message on topic: lights/0x0010/rgbw/2
    MqttClient – device 0010, group 2
    Sending packet: B0001000020602
    Elapsed: 33

  38. Everything seems to work.
    Light off – light on. 🙂

    I’m going to sleep now, today was a hard day.
    Thank you for your patience.

  39. Do you know why the Topic milight_gw is not working? Does the underline bother?

    Something else:
    When I enter lights/0x0010/rgbw / 2 in Gui the program makes lights/+/+/+

    Then you can hard code  /+/+/+ and we only need the topic “lights” in Gui.

    • It should work just fine with milight_gw in place of lights. Just tested it and it worked.

    • Yes, you should be able to sniff packets from the iBox. Just select the remote that the iBox is emulating.

  40. When I send to the 4 Lamps with the Android phone I can sniff all 4 groups.
    The iBox I can also control, but see nothing during the sniffing.
    Do you know which group_id the iBox has?

    • It should have the same range of group IDs as a physical remote. Sniffing is a little unreliable right now. Try rebooting the NRF (unplug and re-plug Vin), and rebooting the ESP if that doesn’t work.

  41. First,

    Great work, really impressed with the work.

    I will admit, the installation instructions were somewhat tough, but I eventually got it working.

    I will write a tutorial for “How to if new to NodeMCU” and drop a later, it’s the least I could do.

    I also filed a few issue reports, I, however, didn’t file one, because it’s not something I can repeat easily.

    So I will just ask, is it normal (by normal… I mean you are still working on it) for the website to be …. slow…and sometimes unresponsive?

    • Thank you for opening the issues on GitHub!

      Definitely not normal. You’re using an LT8900, right? I’m thinking this is probably related to the other issues you’re seeing with that radio.

  42. Thanks for your great work! It´s amazing!

    Have you thought again on implementing the HUE API? Would really love to have this as a stand alone solution acting as HUE lights beeing controlled by Alexa.

    • Hi Alex,

      Can you clarify what you mean? It’s certainly possible to sniff and send packets emulating an arbitrary number of physical devices over time.


    • Hi Chris, thank you for the quick reply.
      I currently have 3 of the wifi boxes (because of the 4 zone limit ) .
      I use it with Openhab and also its own controller app.
      Would I need to repair my bulbs to the esp gateway or if I enter the device id / udp ports in gateway servers,it would allow me to keep same controls?
      What device id would the new esp be if I order few new bulbs and just pair there?

      Thank you

  43. Hi Chris,

    The gateway with the NRF024 now works for days without problems.
    Today I got a few LT6910 and tried without success.

    Have you ever tried the LT8910?

  44. Hi Chris,

    The gateway with the NRF024 now works for days without problems.
    Today I got a few LT8910 and tried without success.

    Have you ever tried the LT8910?

    • To be honest I’m not really sure what the difference is. I don’t think it’s been tested if that’s what you mean. 🙂

  45. Yes, that’s what I mean. Because I did not get the LT8900, I ordered the LT8910.

    But it does not work.
    I have wired according to the following scheme:

    D0 GPIO16 PKT
    D5 GPIO14 SCK
    D6 GPIO12 MISO
    D7 GPIO13 MOSI
    D3 GPIO0 RST
    D8 GPIO15 SS

    • Hey Peter,

      Which type of bulb were you trying to control? Just found and fixed an issue with RGBW bulbs and the LT8900.

      It looks like the LT8910 is component-wise the same as the LT8900, and just has a different PCB layout. Same with LT8920. So it should work.

  46. OK.


    The wifimanager always tries to start an access point:

    Connecting as wifi client …
    * WM: Using last saved values, should be faster
    * WM: Connection result:
    * WM: 0
    * WM: SET AP STA
    * WM:
    * WM: Configuring access point …

    Although I have in the main.cpp     wifiManager.autoConnect (“SMC”, “myPassword”);

    Can I not access the data in the platformio.ini?

    I do not want to have an Acces Point.

    • I’d suggest changing the code to use the standard way to connect to wifi. The code you pasted above will still put the ESP in AP mode.

  47. Success, works with an LT8910 !!

    I have two questions:

    How is the syntax when I want to write the index.html into the SPIFFS?
    Can you still integrate send and receive Led ?

    • Nice! Glad it’s working 🙂

      Should be the same command mentioned in the blog post:

      curl -vvv -X POST -F 'image=@data/web/index.html' http:///web

      > Can you still integrate send and receive Led ?

      I’m not too sure what you mean by this. Can you clarify?

  48. Thanks, the Curl Command I had overlooked.

    I mean a Send Led, which lights up at the reception of Milight package and a second which lights up when the gateway sends something.

    So two pins:
    Send data
    Receive data


    I’d suggest changing the code to use the standard way to connect to wifi. The code you pasted >above will still put the ESP in AP mode.


    Thank you !!!

    • Oh, gotcha. If your ESP has a builtin LED, you can try changing the configurable pins to whatever pin the LED is tied to. A couple of mine happen to be tied to the default pin and will light up when sending/receiving.

  49. Pingback: My Home Automation Setup | Blogging to Nowhere

  50. Amazing project!

    Here’s my question:
    A lot of people use remotes along their phones for MiLight. I’m using MiLight with Home Assistant. When I switch the light off on the remote, it obviously doesn’t show up in Home Assistant.

    I can see that during the set up of NodeMCU you can read the codes sent by the remotes.

    Would it be possible to set NodeMCU in “listening” mode, and send these codes in a UDP command to Home Assistant to update the status of the lights?

    • Cool idea. It’s certainly possible in theory, but it’s not really built for that right now. I think that’d require:

      – More robust listening
      – Reverse translations for RF and UDP protocols (it only translates in one direction currently).

  51. Hi

    I’m having a strange problem with setting the rgbw group to white via Home Assistant/mqtt_json. I tried color name, rgb, and even xy. All other colors work, but “white” in all variations returns red. Any ideas?



    – platform: mqtt_json
    name: Living Room
    command_topic: “milight/0x1E42/rgbw/1”
    state_topic: “milight/0x1E42/rgbw/1”
    brightness: true
    rgb: true
    effect: true
    xy: true
    retain: true


    – name: White
    state: on
    brightness: 255
    color_name: white
    state: on

  52. Ok, I’m running Windows 10. Installed the drivers, got a NodeMCU v2 as you suggested, installed Atom and PlatformIO just as suggested. Cloned the Git repository, added project to PlatformIO, built it succesfully, uploaded it to the NodeMCU, everything with success.

    And there’s no WiFi network appearing… The blue indicator on the NodeMCU only blinks once when I connect the power, then goes off and that’s it. Nothing in Serial Monitor.

    Do I have to flash the firmware if it’s a NodeMCU? Or is the problem elsewhere?

    • UPDATE:

      Flashed the firmware using the NodeMCU flasher for Windows. Everything working perfect. Thank you for this amazing project!


    Any ideas on why sniffing is not returning anything? Alternative means to get device IDs?

    I have tested all options, and played with several buttons while sniffing is going on.



    • Are you sniffing for button presses on a physical remote? Do you know which model the remote is? has a pretty complete listing.

      You can try compiling the firmware with the -D DEBUG_PRINTF flag, which will direct the ESP to print out debug information to a serial console.

  54. Hi!

    First thank you for your great work! 🙂

    I am not able to sniff the device id for my FUT035 CCT (not CCT + RGB) controller.

    There is some traffic shown in RGB+CCT sniffing mode, but no device id:

    Packet received (9 bytes):
    Raw packet: 06 9D D3 BD C6 9E AF 88 2E

    Key : 06
    b1 : 21
    ID : 589B
    Command : 01
    Argument : 06
    Sequence : 96
    Group : 03
    Checksum : 5E
    Packet received (9 bytes):
    Raw packet: 58 84 71 F2 CE 30 8F CC 2C

    Similiar info is shown when using the Milight app.

    What is going wrong here?


    • I think this controller uses the same protocol that the RGB+CCT bulbs use. The device ID in your example is 0x589B.

      The labels are meant to specify a particular RF protocol, but suggest bulb features that aren’t necessarily present.

      I think you should be able to use the RGB+CCT protocol here.

    • OK, then the device id is 0x589B. When sniffing RGBW the “id” is labeled as “device id” which was a little bit confusing to me. 😉

      I have assumed before that 0x589B might be the device id. But at my first test there where no reactions from the CCT controller; neither with CCT nor RGB+CCT control mode. Also the ESP freezes from time to time and needed a power cycle.

      I am using a NodeMCU v3 flashed with platformio by the way.

      Out of nowhere the CCT controller is now controllable with the RGB+CCT mode. 🙂

      There were also freezes when adding gateways, which seems to be gone now too. 🙂

    • Should be a consistent label. Sorry for the confusion. 🙂

      Not sure if this applies to your situation, but I noticed quite a bit of confusion with my setup when I was using dupont wires to connect the NRF to the ESP. It improved significantly when I switched to soldered connections.

    • I am having real trouble getting this working with some FUT035’s and a NodeMCU v2 . Partly this is down to my complete lack of knowledge in regards to how the Milight stuff works!

      So i have one FUT035 paired with button 2 on a remote, i can sniff this as follows:

      cct packet received (4 bytes):
      Request type : 5A
      Device ID : 2621
      b1 : 02
      b2 : 0D
      b3 : 1D
      Sequence Num. : D4

      Which turns the light on.

      If i then try and emulate this with Web interface i get this:

      cct packet received (4 bytes):
      Request type : 5A
      Device ID : 2621
      b1 : 02
      b2 : 0D
      b3 : 0E
      Sequence Num. : 0E

      But the light doesnt respond.

      Any ideas?

    • How far away is the bulb? Have you tried increasing the number of repeated packets? The default (10) is a little low for my setup. I have more luck with 50 (and setting http repeat factor to 1).

    • Interestingly I’ve just tried another white strip controller (an older model that doesnt have a FUT number) and that works perfectly!

      Strangely all 3 of the LED controllers (2 x Fut035 and the older variant) work from the same remote which is sending the same data according to the sniffer. However when i simulate the remote only the older model works…

      Also, im having real range issues, my house is pretty small and its a new house so thinish walls, but im getting no where near 20m range. If i can get this working i may have to use a couple of NodeMCU’s around the house instead of one central one. Probably my cheap Chinese NRF’s!

    • Ah, yeah. Sounds like a bad nRF unit. More repeat packets might help a little. Just change the setting in the UI.

      Pretty weird the newer one doesn’t work. No luck if the nRF is right next to the controller?

      It’s possible there’s something about the packet structure I’ve missed. The last two bytes in the CCT protocol both seem to be some kind of sequence, but it’s not clear if they’re supposed to be correlated somehow.

      You could try sending raw packets from the ESP. (spartan) documentation here:

    • Got it. Definitely sounds like your controller is ignoring the packets, which probably means there are some minor differences in assumptions it makes about the protocol.

      Sending raw commands should be pretty straightforward with something like curl. Just mash together all of the bytes from an intercepted packet:

      You might need to change (probably increment) the value of the last byte, as devices typically ignore future packets having the same sequence number as the last one they saw.

      Substitute milight-hub.local for the IP of your ESP if mDNS isn’t working.

      By the way – definitely worth opening an issue on GitHub. Might be easier to iterate there 🙂

    • Hi Chris

      Yeah I’ve tried it right next to the controller, makes no difference.
      Its weird as they are both cct and both react to the cct remote. I’ve done some googling and cant find anyone who has noticed any difference in the protocol for CCT devices.
      I might take both models apart and see how they look on the inside (more for giggles than anything else).
      I will also try firing some raw packets when i get home and let you know how I get on, I may need some tips on that though as I’m not much one for hex 😀

  55. fantastic… on the path to integrating with SMARTTHINGS.

    30 minutes from parts arriving to working with a test RBGW light.

    Now onto phase 2.

    Only bit of feedback, I used a v3 and your pre-compiled image.  It took me 5 minutes of brain power do understand the idiots (me) guide over the advanced guide.  You could be clearer and separate the two paths to install.   But the fact I worked it out, means it isn’t too difficult.  The challenge was, when I went to install PlatformIO, I didn’t have an existing IDE installed to attach to.

    Many thanks


    • Glad you’re finding it useful!

      fireboy1919 wrote some SmartThings integrations in case that ends up being handy:

      Good feedback. You’re right, there are two distinct install paths, and they’re not clearly distinguished in this post. I’ll try to clear that up at some point in the near future.

  56. did smartthings itegration and with alexa in a couple of hours between doing family stuff today.

    I built the second hub and had it running in about 15 minutes.

    All working fantastically (except a known bug with colour wheel which means the colours dont change properly but thats fixable.)

    so not only does smarthings work, but alexa can control the lights.

    What can I say but thank you very much.

    No worries about the clarity, but going to write up in ST community some comments about how easy this was.

    One little question, how easy would it be to write the UDP control strings to do the old white only lights?  It is just I have strip lights on an old white only controller so I need to write this at some point.

    Million thanks


    LimitlessLED White
    35 00 55 – All On
    39 00 55 – All Off
    3C 00 55 – Brightness Up
    34 00 55 – Brightness Down (There are ten steps between min and max)
    3E 00 55 – Warmer
    3F 00 55 – Cooler (There are ten steps between warmest and coolest)
    38 00 55 – Zone 1 On
    3B 00 55 – Zone 1 Off
    3D 00 55 – Zone 2 On
    33 00 55 – Zone 2 Off
    37 00 55 – Zone 3 On
    3A 00 55 – Zone 3 Off
    32 00 55 – Zone 4 On
    36 00 55 – Zone 4 Off
    B5 00 55 – All On Full (Send >=100ms after All On)
    B8 00 55 – Zone 1 Full (Send >=100ms after Zone 1 On)
    BD 00 55 – Zone 2 Full (Send >=100ms after Zone 2 On)
    B7 00 55 – Zone 3 Full (Send >=100ms after Zone 3 On)
    B2 00 55 – Zone 4 Full (Send >=100ms after Zone 4 On)
    B9 00 55 – All Nightlight (Send >=100ms after All Off)
    BB 00 55 – Zone 1 Nightlight (Send >=100ms after Zone 1 Off)
    B3 00 55 – Zone 2 Nightlight (Send >=100ms after Zone 2 Off)
    BA 00 55 – Zone 3 Nightlight (Send >=100ms after Zone 3 Off)
    B6 00 55 – Zone 4 Nightlight (Send >=100ms after Zone 4 Off)



    • Glad it’s working!

      Would also need to implement the RF packets for plain-white bulbs, but it should be straightforward. The code should be structured pretty well for this kind of extension.

      Could you create a GitHub issue?

      Would be helpful to get both the UDP and RF packet structures up, as I don’t have bulbs to test with.

  57. Hi! I have been using your project for some time now and it works perfectly along with Home Assistant emulating hubs.

    But my bulb rgb-cct  lags alot, sometimes doesn’t even respond either though the webportal or though Home Assistant.

    Also when I try to set up a MQTT account I get about 1 minute lag.


    Any tips you can give me? MQTT is not an issue (is just something I noticed) as its working perfectly already with Home Assistant, but my rgb-cct is a major issue as it is my bedroom light.


    best regards

    • Lag meaning you perform an action, and it takes ~1 minute for the bulb to respond? This could basically only be because there’s something blocking the ESP from sending the request.

      Could be a loose connection, but that wouldn’t affect a single bulb type. My setup was significantly more stable when I switched to soldered connections.

      I use RGB+CCT bulbs in three different lamps around my place. Since getting the setup stable (~4 months ago), I’ve not noticed bulbs miss a command even once, and there’s no noticeable lag. Hopefully that means your problem is fixable! 🙂

    • Hi Chris, thanks for the fast reply, yes that kind of lag i mean, sometimes its even longer then a minute or just totally ignored.

      I have ordered a few more nrf24l01 one with external antenna and LT8900.

      Are all or rgb+cct bulbs near the gateway? Mine is in different division then the gateway

    • The distance to the gateway shouldn’t matter with this kind of problem. Distance could matter if commands are dropped, but would not cause the symptoms (lag) that you’re seeing. That can pretty much only happen if something is causing the ESP to spin its wheels.

      Probably the best way to get more information is to compile the firmware with all of the DEBUG flags enabled. Just add the commented out lines in platformio.ini to the build_flags line like so:

      and then re-flash the firmware. Then you should see some debug prints in the serial terminal.

  58. Just swapped the NodeMCU V3 for a HiLetgo D1 Mini NodeMCU because I wanted the package to be smaller.

    Soldered the wires and fitted into a tiny box (60x30x20) with a tail for USB power hanging out.

    Also, (and it is noted in Github issues) it is controlling my old warm white light strip on a 2014 controller which uses the old white remote.

    5 different sets of lights now running under Smartthing and also Alexa.

    Works perfectly.

  59. Wondering if someone has any insight into my issue.

    I am using a nodemcu v3 and trying to control a CCT (dual white) bulb via homeassistant i.e. using the UDP server. I created a gateway using the UI and specified 4000 as the port. I then paired the bulb with the gateway id and can control the bulb using the UI.

    However, I can’t seem to get it working in homeassistant. My homeassistant config looks like this

    Using wireshark, I see that homeassitant is sending out UDP packets every 10 seconds. This looks like the bridge initialization command from the python-limitlessled package. Does this mean that the nodemcu is supposed to respond to the initialization command but isn’t?

    Tonight, I will try the debug flags and reflash the nodemcu to see if I can gather additional info.

    Another question – should the limitlessled Android app be able to discover the NodeMCU as a gateway? In my case, it doesn’t.

    • Could you paste the output you see when you navigate to /settings? I suspect there’s a version mismatch.

      If you’re using HASS, I’d highly recommend using the mqtt_json platform instead of UDP. Since MQTT is TCP, it’s more reliable than UDP. And since communication is over pre-established connections, there’s not much of a latency penalty.

      Unfortunately debug messages don’t show up in the UI. They’re sent to serial. You can enable debug messages by compiling with any of the debug flags:

      If you’re sure you want to use UDP, pasting the output of /settings and getting some debug output would be the next step.

    • Having no luck with mqtt. I keep getting a “Failed to connect to MQTT server” error. I have checked that HASS can actually connect and publish to the mosquitto server (although I had to set protocol to 3.1 in HASS).

      I also tried allowing anonymous in mosquitto but no go.

  60. Hey Chris,

    Thanks for your reply. You are awesome.

    I didn’t know of mqtt_json as an option. I will explore this further. Thanks!! With mqtt_json, I am guessing I setup a platform in HA for every group I want to control and then set the topic to suit the various parameters. I will try this tonight.

    As for settings, I will post that later tonight and see what debug brings up as well.

    • Some debug logs from Serial Monitor. The Packet sent by UI looks quite different to when triggered by HomeAssistant. The device id doesn’t seem to match up? I am trying to use OxAAAA as the device id for the gateway.

      Also output from settings here:


      Also, tried using version 5 both in NodeMCU and HA but that results in unknown packet length errors. Wonder why.

      Yet to setup a MQTT server locally. My current one is in the cloud and I suspect it will be too slow for my use. Will get to it soon.

    • Not sure what version of HASS you’re on, but older versions had problems overriding the version number. The config looks fine. Could you paste the serial debug logs?

      The packets that are resulting from the UDP commands are for RGB+CCT bulbs, not CCT-only bulbs. What does your HASS config look like?

      Odd that it’s unable to connect to MQTT. I’ve been using this for months with no problems! Does your MQTT server have a password? The output of /settings would be helpful too (obviously clear your password if you have one).

    • Curious – do you use a domain name or IP address for the mqtt gateway? Does that make a difference?

      I have tried mqtt with username/password and also without. Neither appear to work.

      Will post my new settings when I get back from work. I may also try reflashing.

      With UDP, I finally managed to get it working by forcing version 5 on both ends. Like you said though, it is slow to react and a bit hit-and-miss. I haven’t managed to sync the bulb’s saturation with what HASS thinks it should be. 🙂 For e.g. setting to warm white in HASS actually changes the bulb to cool white. That could also be because of previous UDP packets.

    • I use a domain name, but it shouldn’t make a difference.

      If your MQTT server runs on a non-default port, make sure to put that in the server (e.g., It defaults to 1883.

      From the debug logs you included, it looks like HASS was sending UDP packets intended for RGB+CCT bulbs. Posting your HASS config would help too. 🙂

      It’s entirely possible the temperatures are swapped. I’ve really only tested with RGB+CCT bulbs. Definitely let me know if it’s backwards.

  61. Hi Chris,

    Could you implement controls for B8 wall remote? It receives and decodes messages however group_id is messed up (on is decoded as off for same id or on for another id, ids are above 8, etc).


    • Done.

      Yes, B8 can connect fine to RBG_CCT bulbs.

      On a second thought, it could be a different protocol as it works fine with GU10 RGB_CCT however not with E27 (RGBW).

      Using device_id sniffed doesn’t work to pair with RGBW (or might be a different device_id when pairing to RGBW than the one for RGB_CCT?)

      Anyway, after pairing the RGB_CCT from the webpage with the code sniffed, it works as intended with B8 physical device.

  62. Hello again

    I’m using the gateway in following config:
    milight hub <=> gateway <=> mqtt <=> mqtt_json light <=> HASS

    mqtt json light component supports “effects, and effect lists; is it currently possible to call milight modes natively as effects? I know about shell commands, question is can I go without them?

    - platform: mqtt_json
    name: Bath
    command_topic: "milight/0x1E42/rgbw/4"
    state_topic: "milight/0x1E42/rgbw/4"
    brightness: true
    rgb: true
    effect: true
    - Mode 1
    - Mode 2
    - three
    - four
    - five
    - six
    - seven
    - eight 2
    - nine


    • Cool – this is how I have mine set up as well, and I’m really happy with it. Very cool to see HASS state get updated if I use the ESPMH UI.

      Would recommend making the command and state topics different. Mine are milight/commands/... and milight/updates/.... Otherwise seems like you might get some kind of crazy infinite loop where an update triggers a command.

      The codepath that handles MQTT messages is exactly the same as the one that receives REST commands (documentation here). You can set a “mode” directly by sending a command of the form {"mode":1}

      If it would be useful to have the mode key aliased to something else, that can certainly be arranged.

      EDIT – all this is to say, I’m not sure if what you’re doing is possible. Sort of depends on how the mqtt_json component handles effects.

    • Here’s how it looks in UI:

      Milight can do 9 effects, right? How are they identified? By digit, or otherwise? Does numbering start with 0, or 1?

      The thing is… I am able to get where I want with curl, but the way is long, and messy =)
      If effects thing worked, the following could be done with only native HA scene component.
      Here’s how I add a dynamic scene to hadashboard (“mode”: 1 doesn’t work with LED strip controllers):
      Dashboard widget calls a HASS script that calls a shell command
      - service: ...
      - service: shell_command.mode

      Shell command calls a linux shell script:
      mode: "bash"

      …which isn’t exactly pretty, and I’ve got a bunch of them J:

    • Putting it all together in one coherent reply – your commenting platform was a bit confusing at first, please kill comments below.
      Here’s how it looks in UI:

      Milight can do 9 effects, right? How are they identified? By digit, or otherwise? Does numbering start with 0, or 1?
      Found it, 1-9. Didn’t help J

      I also found an implementation of effects for HASS, and “raw” RGB LEDs. Can you please have a look?

      The thing is… I am able to get where I want with curl, but the way is long, and messy =)
      If effects thing worked, the following could be done with only native HA scene component.
      Here’s how I add a dynamic scene to hadashboard (“mode”: 1 doesn’t work with LED strip controllers):
      Dashboard widget calls a HASS script that calls a shell command:

      Shell command calls a linux shell script:

      …which ain’t pretty at all, and I’ve got like 8 of those =\

      Would recommend making the command and state topics different. Mine are milight/commands/… and milight/updates/…. Otherwise seems like you might get some kind of crazy infinite loop where an update triggers a command.

      I was just wondering why milight groups switch on their own sometimes… thanks a million. But how to make an updates topic?
      My topic pattern is milight/:device_id/:device_type/:group_id

      On HA side:

    • Yeah WordPress, or at least this theme, isn’t amazing for a detailed discussion. Feel free to open GitHub issue(s) if it’d be easier.

      Having a hard time seeing how to do this cleanly if you can’t set the mode directly. Something is going to have to keep track of state. Right now ESPMH does not track bulb state.

      If you end up needing to use command, note that you can instead use commands, which takes an array of commands (repeats are allowed). Ex:

      The setting is mqtt_update_topic_pattern. If you’re not seeing it in the UI, you probably need to update the UI. Unfortunately it’s not bundled with the firmware. Fastest way to update it is by going to /download_update/web, but this currently causes crashes because there’s not enough RAM to handle SSL. The alternative is to update via curl from the esp8266_milight_hub project dir:

  63. picrelated… again:

    Would recommend making the command and state topics different. Mine are milight/commands/… and milight/updates/…. Otherwise seems like you might get some kind of crazy infinite loop where an update triggers a command.

    I was just wondering why milight groups switch on their own sometimes… thanks a million.
    BTW, apologies for chaotic replies – unfamiliar comment platform+no editing.

  64. Still having trouble with MQTT connect. I have reflashed a no. of times but that hasn’t solved the problem. Here’s my settings



    When I monitor using Wireshark, I see the following

    Can’t see a response from MQTT for the Connect command. I am wondering if the hub is timing out before my broker responds. Of course, maybe because my broker is slow to respond.

    Is there somewhere I can change the socket timeout for the MqttClient? I am not a C dev but I can see that MqttClient uses something called PubSubClient to handle the connection. I couldn’t find the code for it though.

    • Figured it out. Mosquitto support MQTT 3.1 while the PubSubClient defaults to 3.1.1. Modified the PubSubClient.h file and rebuilt it. The hub can now connect to the MQTT Broker!! Yay!!

      I think the colour temperature for cct is definitely inverted. Setting to Warm white in HASS sets the bulb to Cool white and vice versa.

      Here’s the log when I set to warmest white in HASS

      and here’s when I set it to coolest white

    • Huh. I’m using Mosquitto too. I guess maybe it’s worth mentioning in the documentation that 3.1.1 is required. Looks like you found it, but PubSubClient is a library pulled in from PlatformIO.

      Anyway, glad you got it working!

      I’ll open a ticket for inverted white temperatures. Thanks for catching it.

    • My previous replies have gone missing – trying again.

      I was on Ubuntu 14.04 and apparently the mosquito package from the repository only supports 3.1.

      Anyway, I now have a working mqtt_json implementation. Thanks for your patience and the awesome project.

      Do you have a donate page somewhere?

    • Aah, that makes sense. Thanks for the update! Glad everything is working.

      Thank you very much for your thoughtfulness. This is a for-fun project, but if you’re so inclined (no obligation, of course), I support these charities (in no particular order):

      Cure Alzheimer’s Fund
      charity: water

  65. Chris,

    I have a NodeMCUv3 and an NRF24L01. I downloaded the source and flashed with PlatformIO using “NodeMCU 1.0 (ESP-12E Module)” as the board. I get a successful flash. I can connect to it and configure it to connect to my wifi network. It shows up on my device list in my router. I connect to the IP address and the web interface won’t load. It gave me a message saying the web interface was not loaded and if I clicked the link, it would attempt to download and install it? But that didn’t work for me. Back in PlatformIO I click on “Run other target” and select “PIO Upload SPIFFS image” it uploads index.html to the NodeMCUv3 but I can see it uploads every board version in the console output? (NodeMCUv2, d1_mini, esp12, esp07) I do somehow still get a successful upload message in the console. So I reboot the board and I can now access the web interface. I have RGB-CCT bulbs. The sniffer works. I put in my device ID of “02D5” in the form of “0x02D5”. I can send commands to my bulbs, but the weird thing is that the commands only go through to the bulbs after send a command using my remote. (i.e. if I click green in the web interface, nothing happens, but as soon as i click anything on my remote, red for example.. the lights go red for a split second then straight to green as I had originally selected in the web interface.) The board will also queue commands I select in the web interface until I press a button on my remote. (i.e. if I click green, blue, then white in the web interface, nothing happens, but as soon as i click anything on my remote, red for example.. the lights go red for a split second then flash green, then blue, then straight to solid white as I had originally selected in the web interface.) Do you have any idea what could be causing this? Is it because I have a NodeMCUv3 board? Are those supported by your code? As a side note, I have also wiped the board using ESPTool and can replicate this every time. I also tried wiping and then flashing the NodeMCUv2 firmware .bin file using ESPTool, but I get the same results.

    • It sounds like you left listen mode on when you were using other features in the UI. Listen mode blocks anything else from happening. Turn it off and things should work as expected.

    • Chris,

      Thanks for the quick reply. I’ll give that a try when I get home. Am I okay to flash “esp8266_milight_hub_nodemcuv2-1.4.0.bin” on my NodeMCUv3 as a fresh start after wipe? Or do I need to compile a different version for the v3? Also, is “esp8266_milight_hub_nodemcuv2-1.4.0.bin” supposed to include the web interface?

    • Yes, v3 nodemcu should be fine. The only thing that matters is flash size, and they both have 4MB flash.

      No, the firmware image doesn’t include the web UI. That needs to be uploaded separately. You should see a default webpage with a link to download it, though.

    • I do see that link. After clicking on it, it just sits there loading the page though. Does it link to a file I’m supposed to download? If so, how do I compile that to flash it?

      Thanks again for your continued help with this.

    • Just commenting to say that the issue was that I had left the sniffer on. Thanks again Chris.

    • Nice, glad that fixed it. This is definitely a UI deficiency. It shouldn’t let you do other stuff if listening is blocking.

      What’s happening behind the scenes with the download link is it’s downloading the web UI from Github. If it’s loading forever, it’s probably because it ran out of RAM and rebooted. Unfortunately SSL is a complete memory monster, and there’s very little wiggle-room on these devices to spare.

      You can manually upload the web UI. I left a comment here showing how to do it with curl.

      It seems like the Github downloader is at a point where it’s completely unreliable. I should probably pursue a different approach.

      The good news with that is it gives the project more RAM buffer, and I can use websockets for sniffing rather than HTTP long polling, which would solve your other problem 🙂

  66. Great work!

    I’m using the MQTT passive listening with the MiLight remote.  In 1.4.0 pressing Speed (S+, S-) and Mode (M) buttons on the remote don’t publish unique events.  All of them publish something like this:



    Would it be possible to publish an event that identifies that one of these buttons was pressed?  For example:

    {“device_id”:16184,”device_type”:”rgbw”,”group_id”:3,”state”:”ON”, “last_btn”:”S+”}

    {“device_id”:16184,”device_type”:”rgbw”,”group_id”:3,”state”:”ON”, “last_btn”:”M”}


    My use case doesn’t involve controlling lights.  I’m using the remote to control music using MQTT and it would be great to be able to use the M,S+,S- for other purposes.


    Thank you

    • Thanks for opening some Github issues! Much appreciated. I’ll try to take a look sometime this weekend.

      Should be easy to add support for modes. Super cool to hear you’re using the remote for other things. 🙂


    Hi Chris,

    Just two questions 🙂

    A. Did you find significant improvements in range for LT8900 vs. NRF24L01?

    I understand that speed is superior for LT8900 in absence of emulation but I’m unsure about the range. Regular 2.4/5 Ghz wifi can cover the location fine however NRF24L01 can’t (anyway, I think there’s a large difference in power that it’s supplied by microcontrollers boards compared to regular wifi routers).

    With NRF24L01 I had sometimes an issue that for groups having more than one bulb, the commands from the gateway don’t reach all the bulbs (some on/some off) and I needed several on/off cycles to get them in sync. With original MiLight remotes I haven’t seen any issue.

    I tried also with external antenna for NRF24L01 (link below) however it’s even worse in terms of range than the regular one. Instead it is so hot (90-100 degrees C) that I thought I connected the Vin not 3.3V pin.

    With NRF24L01 on a NodeMCU v3 I got no more than 3.5 – 4 m radius, so one unit is not sufficient to reliably cover a middle sized house, even if placed centrally.

    With a mesh network of MiLight gateways – I’ve added a second unit (on a WeMos D1 Mini) – it now works way better and dropped commands have decreased.

    B. Could you add an Arduino Mega (+ Ethernet shield) flavor for MiLight gateway project?

    My setup for turning the lights (only on and off with white light for now; different scenes with brightness for night/day, PIR sensor/switch, TV/HTPC playing triggers/etc will follow in the second phase) is as follows:

    (1) Cheap 433 switches/PIR sensors -> (2) RFtoMQTT gateway (Arduino Mega + Ethernet shield) -> (3) MQTT broker (Rpi gen1; Ethernet; as HBMQTT broker is broken 🙂 on HA AiO since 0.47 or 0.48) – > (4) Home Assistant (Rpi gen2; Ethernet) -> (3) MQTT broker -> (5) MiLight Gateway (NodeMCU and WeMos D1 mini) -> (6) Light Bulb.

    Despite the entire length of the circuit, the light turns off or on in about 1/10th of a second after trigger (pushing the switch or the PIR sensor recording movement) so it is noticeable but not significant.

    I think that using copper (on an Ethernet Arduino shield) will improve even further the latency of the gateway. Also, I found cable units more stable for other DIY projects (or at least it feels more): before switching the RFtoMQTT gateway to Arduino Mega it was housed on a NodeMCU v3 and it was unreliable at best (limited range and call drops). Now it can cover 10-12 m radius without dropping signals.

    • Did you find significant improvements in range for LT8900 vs. NRF24L01?

      I’ve actually had more luck with the NRF. I use the NRF in my personal setup, which has something like a 20m range through walls and stuff.

      Have you tried increasing the number of repeat packets? I think I set mine to 50.

      Could you add an Arduino Mega (+ Ethernet shield) flavor for MiLight gateway project?

      I like wired stuff a lot more too. 🙂

      I could be way off, but my impression is this project is going to be way too RAM hungry to run on an ATmega. I think those things only have 2 KBs of RAM right? I think this project is using something on the order of 10s of KBs.

      There was some discussion of ATmega/ethernet support in this ticket. Someone there linked an ethernet controller for ESP8266. I have no experience with it, but maybe that’s a decent direction?


    • My best guess is that the connections to your NRF24L01 are loose or maybe not hooked up 100% correctly. Easiest way to verify is set up another NodeMCU in listen mode and see if you can pick up the traffic from the other one.

  69. I checked the link, it seems correct.
    I also tried to change firmware, I’m using precompiled versions, but when I send to strips the nodemcu seems to crash



    • WDT reset means the firmware is hanging in a place where it shouldn’t ever hang, and the OS watchdog is resetting the device.

      I’ve only seen this when connections are loose or wrong, or if the NRF I’ve used is defective. I’d suggest:

      1. Recompiling the firmware with the DEBUG_PRINTF flag set. This will give you more information as to where the code is hanging.

      2. Making sure the pins on your NodeMCU are labeled correctly. I’ve heard that they’re sometimes wrong. Easiest way to test is probably with a breadboard and some LEDs.

      3. Making sure the dupont wires you’re using form a solid connection. If they’re loose at all you can pinch the metal contact with some pliers or something to make it tighter.

      4. If at all possible, use a soldered connection.

      5. Try a different NRF24L01/NodeMCU. A few of the NRFs I’ve received have been bad.

  70. I little bit of help please, I just finished building the HUB, I have a led strip connected to the remote channel 2. When I run the sniffer in the GUI I see device ID 128A, I go to the top of the GUI and I ADD 0x128A and pick channel 2 but it’s not doing anything, I see the esp controller flashes blue but that about it. it’s not controlling the strip, everything works with the remote.

    • I’d recommend compiling with the DEBUG_PRINTF flag (edit platformio.ini) and reflash the ESP. Should reveal if the right packets are getting sent.

  71. Hello good people,

    This is my first attempt at a project of this kind, so if I’ve done something obviously stupid, please be gentle!

    I am writing this as a last resort after spending around 30 hours trying to get the hub going. After having purchased a NodeMCUv2 and a NRF24L01+ and following every step down to a T, a number of times, I have been unable to get the unit to show an Access Point after flashing a MiLight Hub firmware.

    I have built and flashed the firmware using PlatformIO, flashed 3 different versions of the binary FW using NodeMCU flasher tool and, all of which complete successfully, however no AP has become visible.

    To confirm it is not a hardware fault, I built and flashed a custom firmware using the NodeMCU cloud build service (, and got it to successfully show an ESP_XXXX Access Point.

    What am I doing wrong? Having gone through all the comments, it seems that I am the only person getting stuck at this point. What have I missed?


    • Hey Bobby,

      Have you used this ESP with anything else where you did successfully connect to wifi? ESP stores wifi credentials in a special location on flash, and the library I use to manage wifi connections will always attempt to reconnect to the stored AP before setting up the captive portal.

      Have you tried looking at Serial logs? WiFiManager prints some stuff out that might be helpful in debugging.


    • Hey Chris,

      Thanks for responding! The ESP has never been connected to anything else as far as I know. I bought it new and this is the first FW i flashed on there.

      I’ll try figure out how to get the serial logs and post again once I have something that can be used.

      Thanks again!

    • Just to close the loop on this for anyone else that may have a similar issue – it turned out to be a bad NodeMCU unit. I ordered another one and all is well now.

  72. Hi,

    If of any interest, here’s an automation to use your gateway modes from mqtt_json light in HA (which instead uses “effect”). Effects (0 to 8) need to be defined for the light component. The topic is the default one (not the update).

    – alias: Effect2Mode
    initial_state: True
    – platform: mqtt
    topic: ‘milight_gw/+/rgb_cct/+’
    condition: template
    value_template: ‘{{ “effect” in trigger.payload }}’
    – service: mqtt.publish
    topic: “milight_gw/{{ trigger.topic.split(‘/’)[-3] }}/rgb_cct/{{ trigger.topic.split(‘/’)[3] }}”
    payload_template: ‘{ “mode”: {{ trigger.payload_json.effect }} }’

  73. Hi Chris,

    If I try to get the web UI by using curl, it results in the following error:

    curl -vvv -X POST -F ‘image=@data/web/index.html’ http://192.168.x.x/data/web
    Note: Unnecessary use of -X or –request, POST is already inferred.
    * Trying 192.168.x.x…
    * TCP_NODELAY set
    * Connected to 192.168.x.x (192.168.x.x) port 80 (#0)
    * couldn’t open file “data/web/index.html'”
    * stopped the pause stream!
    * Closing connection 0
    curl: (26) couldn’t open file “data/web/index.html'”

    I hope you have any ideas to get the web UI worked.



    • That command needs to be run from the esp8266_milight_hub project directory (it’s referencing a file in that dir: data/web/index.html). Alternatively, you can specify the fully qualified path.

      You can also try v1.5.0-dev1, which doesn’t require the separate web UI upload:

      v1.5.0 will be released when I have time to finish another feature or two I have planned for it. The current dev version should be stable.

  74. The web UI is working, and I get results if I use sniffing. I use my remote for sniffing. If I try to control the Milight bulbs (RGBW) by using the sniffed device_id (0xc35a), nothing happens. Any idea?

  75. Having the exact same problem as Rick. Flashed fine, joined the network fine, web UI is working, sniffing works :

    rgbw packet received (7 bytes):
    Request type : B0
    Device ID : 08B4
    b1 : 3D
    b2 : 98
    b3 : 02
    Sequence Num. : A6

    But nothing happens if I try to use any of the commands in the web UI.

    Some questions if I may

    Never had a gateway connected to these light does that matter? (have about 6 of them in a box, hopefully this will make them defunct)
    If its sniffing ok does that mean I have the hardware right? or is it possible to have sniffing wired correctly and transmit wrong?


    Sooooo looking forward to getting this working then on to integrate it with smartthings!


    • I’ve noticed that the first few (2-10) packets sent after the device boots aren’t recognized by my bulbs. Works very reliably after that. Maybe try spamming a few commands?

      The only other things I know to try here are:

      1. Compile the firmware with the DEBUG_PRINTF flag and look at Serial output. Will show what packets are being sent.
      2. Put the firmware on another ESP and put it in listen mode to verify that the packets are being sent.

    • Thanks for the response!

      I guess I will have to figure out how to compile it etc then as it was a precompiled binary I had working.

      Will install just now on another ESP and see whats what.


    • Installing on another ESP first is probably the most helpful next step. If the other ESP can see packets, but bulbs aren’t responding, there’s definitely something very funky going on.

  76. It looks like the ESP is not sending packages. I also have an RFlink gateway with an NRF24L01+ installed on it. I use it in combination with Domoticz. Domoticz also has a sniffer function. It can see packages if I use the Milight remote, but not if I use the ESP.

    If I use Domoticz for sending commands to the bulbs, my ESP can see the packages.

    • Would it be possible for you to try esp8266_milight_hub on the same hardware that works with Domoticz? I’ve had at least one bad NRF24L01 that could receive packets, but sending was super spotty and weak.

    • I disconnected the working NRF24L01 from my RFlink and connected it to the ESP. Unfortunately with the same results, so I can conclude that there is no problem with the NRF24L01 module.

      I can try to flash the software to the RFlink. The RFlink gateway is attached to an Arduino Mega 2560 and I’m not sure if I can make wifi connection with it. The setup is the same as the one on the image.

  77. I have the same issue like Rick und Squiggley.

    Sniffing the ID works, but sending is not possible.

    I also have openmilight on a raspberry pi which also does not recognize any signals sent via the webinterface. The raspberry pi also uses the nRF2401 to interface with the bulbs, which works (badly) and was not a complete solution..

    Used the latest release firmware esp8266_milight_hub_esp12-1.4.1.bin and a custom PCB holding the ESP12(S), an AMS1117(3,3V) and the nRF2401 module. So it may also be my PCB or the cheap nRF2401 modules from china (which I have four of), but after reading the last two comments I don’t think so.

    Already tried adding a 100nF capacitor to one of the nRF2401’s without success.

    Maybe I’ll get some steps back and try it with a NodeMCU again.

    I did that before designing the PCB some weeks ago and it worked, no idea which firmware I used before.


    • After setting up PlatformIO and enabling debug output, it came out was a wiring/routing issue. I connected the “CE” pin of the nRF2401 to GPIO2 at the ESP12 somehow.

      Luckily you implemented the settings menu so I could reassign the pin don’t have to get new boards 😀

      I’ll publish the PCB design soon, maybe an improved version using the default CE pin.

      Works perfectly now. Very nice webinterface. Can’t wait to get started with the API.

  78. Pingback: ESP8266 + nRF2401 MiLight gateway PCB –

  79. Hi,

    When trying to access ESP through web browser i get this “Web app not installed. Click here to attempt to download it from GitHub.”, nothing happens when going on github link. Any ideas? Help would be appreciated.

  80. Using a nodemcu. Latest 1.5.0dev 2 im finding performance flaky..   And now the nodemcu seems to lock up with red led on frequently.

    Control from ha via udp…  Hit and miss.

    Avoided using mqtt for the moment as setting rgbw bulbs to white isnt straight forward.

    Cant get platformio to work for me… Stuck with the .bins for the moment.

    Any suggestions?


    Ive had lots of nodemcu boards several of the transmitter boards.   The ones with long range antenna never worked reckon they are dodgy.




    • It should not be locking up under any circumstance that I’m aware of. I’ve seen it with my hardware when the connection between the ESP and NRF is spotty. I think what happens is the ESP tries to send some SPI packet to the NRF, and spotty connection means the SPI internals block in weird places. Switching to soldered connections, or at least headers/sockets, completely solved the problem.

      Easiest to confirm this by enabling debug flags, which will be difficult if you can’t compile the firmware. But it’s probably worth trying soldered connections regardless.

    • Also, setting RGBW bulbs to white with the HTTP API or MQTT integrations is pretty straightforward. Just a JSON key like {"command":"set_white"}.

      Did you mean it’s difficult within HASS? That should be resolved in the release of 1.5. Going to do the same thing the official milight protocol does – set to white when selected color is close to white.

    • Thanks for the quick reply and advice.

      My current hardware is using headers and wires, I have had soldered in the past but made no difference with home assistant issue.

      I’ve discovered something interesting while using the native web interface all works well fast and solidly, but in home assistant….

      I went with using the limitless led way of adding the lights in Home Assistant – it worked with the original bridge flawlessly. I have both set to v5 and it looks like something in home assistant isnt liking the nodemcu hub. It just randomly responds and then locks up after reset it works again.

      So I think I will try the mqtt_json way next, as I have other mqtt devices. I didnt go this way before as we use the web interface in Home assistant to control the lights a lot…. its early days not got scenes or many sensors setup yet 🙂 and i couldnt figure out the set back to white light stuff or the ‘automation’ above that converts the effects to modes so you can call them from home assistant (from above in this thread). Im just not great yet at json or yaml. I tried copy and paste the stuff above but it threw up errors and i couldnt figure it out.

      Im using 1.5dev2 so when i get mqtt_json working on a test lamp i will try the white…

      I just really need a brain transplant or some example scripts etc i can copy and paste 🙂

      Platform IO btw installs for me then vanishes for some bizarre reason. couldnt be a**d battling with that.

      Many Thanks!

    • Got mqtt_json basically working fine, it is much more stable, usable now. However selecting colour close to white in home assistant doesnt change bulbs to white. All my lamps are RGBW type. Unless you mean the current dev 1.5 doesn’t have the near to white feature yet ?

      I will have a fiddle with a slider to set them to white meanwhile.


  81. Hey Chris, been using this for a while now with the NRF. I am doing UDP servers with a server for each light. I have about 10 milights. Everything works perfect if I am doing one at a time, but if I trigger a scene in Home Assistant (ie: turn everything off) 1-2 lights don’t seem to be getting the message and then are the wrong state. I have to go in and manually toggle each light on/off again before it’s back. This happens like 70% of the time. All the lights are within about 5 meters of the hub and there are no walls in between any of them. Any advice for getting this more reliable?

    • Hi Brad,

      My guess is that the UDP packets are getting dropped.

      I’ve noticed that the ESP isn’t great at handling a flood of UDP traffic. I think fundamentally a buffer is filling up and packets are getting dropped. The firmware doesn’t run ontop of a RTOS, so it only context switches to system functions when it explicitly chooses to do so. Might be possible to insert some yields in more places, but I kind of doubt it.

      The easiest fix, I think, is to switch to an integration that uses TCP. I use the mqtt_json component for my own setup, and it works flawlessly. I also have fewer lights, but I would expect you’d at least not get dropped packets.

      There’s some documentation on the integration here:

      My HASS config looks like this:

      In the off chance that it’s not UDP, you can try increasing the number of repeated packets (there’s a setting in the UI). Increasing this probably makes the chance that UDP packets get dropped higher, though, because more time is being spent outside of system functions.

  82. Firstly, thanks for this! I’ve bought all the goodies and got everything loaded and working.


    When I click Start Sniffing nothing happens. In the console I have an error “sniffing is not defined”. Full message is Uncaught Reference: Sniffing is not defined at HTMLButton.

    This is followed by Sniffing is not defined at WebSocket.Websocket.onmessage ((index:3))

    I have no preceding errors which suggest jquery did not load.


    Anything I have done wrong?

    • It should’ve happened automatically, but Travis CI is being a jerk. Don’t have the time to look at it right now, but will look when I get the chance.

    • Thanks! Downloaded and flashed it.

      I now click “Start Sniffing” and the button changes to “Stop sniffing” but nothing else. Should another window / info shown up?
      No errors in console.

    • Ok, so there must be something wrong with my radio setup as I see nothing.

      Is there some method in the REST to get radio status / availability to ensure it’s working?

      I’m using a D1 Mimi node.

    • No REST endpoints for radio status. Not sure if the NRF exposes anything like that. Could add if so.

      There is an endpoint to sniff packets (request just hangs until a packet is intercepted) if that’s helpful.

      What kind of remote are you expecting to see sniffed packets from?

    • I’m using remote from phone with a mi-light controller. Both work and the bulbs change accordingly but the sniffing shows up nothing.

      I’m not convinced it’s not my radio so still digging at that.

      I’ll try the method you mentioned and see if it returns anything.

    • ok, so tried 3 different radios (gotta love Amazon pack of ten 🙂
      I did an erase_flash on it and then reflashed with the dev 4 version.

      Clicking on Start Sniffing says “Stop sniffing” but nothing else changes.

      I can see the bridge and I can see the bulb changing but nothing seems to be picked up on the gui.


    • “It’s ALIVE!” he screamed.

      It looks like it was just a range thing. The bridge sitting near the Hue bridge, the Lightwaverf bridge and an AP.

      I guess the height of the bulb gave it an advantage over my and my breadboard on the floor near another AP 🙂

      Anyway – it works now and I can control it from the laptop too.

      Awesome software, thanks for the effort!

    • Will do.
      Need to get it all integrated into Domoticz now so may well have some comments from that.

      That’s my weekend sorted 🙂

  83. Quick one…

    Managed to get it all working well with 1.5 dev 4

    I am now wondering about MQTT update messages when sending a command to

    command_topic: “milight/0xAB11/rgbw/0”

    Which is the group for simultaneous control of the 4 light channels on 0xAB11

    In my case I sort of expected 5 mqtt update messages back for 0 1 2 3 and 4 not just on 0. This would ensure in my case that Home Assistant updates the state of the individual bulbs as well as the group of bulbs.


    But only get the update back on


    I tend to use the ‘0 group’ in scenes so the changes look smoother and manually alter individual bulbs through the home assistant web interface.

    Does this make sense ? Perhaps there is some way to make home assistant toggle all 4 bulbs with the 0 topic that i dunno about. But I kinda thought would make sense for the hub to send the 5 updates since its unique to milight setup.

    Also does the software support transition times on the bulbs?

    Cheers everyone for the help so far…


    • Yeah noticed the same thing for my setup and accomplished it by expanding the forward out in HomeAssistant:

    • Glad it’s working for you.

      I’m not familiar with Blynk, so it’s hard for me to say. But there are a lot of integration paths with this project. It supports the original Milight UDP protocol, has a REST API, and an MQTT integration.

    • Hi Chris,

      Thank you for your fast reply. I will look into it with my limited knowledge, but i am sure i will figure it out!


  84. Dear Chris,

    Thanks for the great work. It was really useful.

    I managed to setup the nodemcu V3. When i try to control the lights using the UI, i see that the packets that my remote sends are different than the one from the UI.

    For Ex: When I switch on the third button of my RBGW remote i get the below packets:

    rgbw packet received (4 bytes):
    Request type : B0
    Device ID : C17A
    b1 : 06
    b2 : BB
    b3 : 07
    Sequence Num. : 1F

    But when i do the same with UI, i get the below.

    rgbw packet received (4 bytes):
    Request type : B0
    Device ID : C17A
    b1 : 00
    b2 : 03
    b3 : 07
    Sequence Num. : 06

    Am I missing something here? Your inputs would be very helpful.



    • Can you find the device you’re trying to control here?

      It’s possible this is just a device issue. The NRF might not be transmitting, or might be transmitting a very weak signal.

      There’s a REST endpoint to send raw RF packets. You could try using that to rule that out. Example:

      You might need to fiddle with the last byte as it’s the sequence number, and bulbs ignore commands having the same sequence number as the last command they handled.

      If bulbs aren’t responding to commands sent that way, you probably have a faulty NRF24L01. Also possible the wiring is mixed up or loose.

  85. I have what it seems a Mi-Light WiFi gateway with this firmware:  V1.0.04a-JCY-1.

    Don’t know if that is v4/v5 definitelty not v6. Bulbs & LEDS are paired and working. I tried setup your code on NodeMCUv2 (thanks for clarifying on your website. It seemd Ali seller send me v2 .. ordered v3.) Well in this case I followed the windows instructions to flash the NodeMCUv2 binary directly.

    Web Interface runs. However If I press ‘sniffing’.  Nothing appears. I double checked the cables to NRF and checked them again with your images. Nothing strange. Any chance I ‘sniff’ nothing because I probably have v4 devices and those are not supported?



  86. I was searching for weeks to get remote control for the old (the very old) RGB bulbs and the new ones and finally i found your Gateway. Just perfect, thank you 😀

  87. Pingback: ESP8266 + nRF2401 MiLight Gateway Leiterplatten „Sandwich“ –

  88. Hi,

    I trying to control some Mi-Light RGBW lights (FUT018)

    I have tried using both a “WeMos D1 Mini V2 ESP8266” and a “NodeMcu Lua ESP8266 CH340”

    I can sniff packets from the remote fine…

    This is from the On command

    Running version “1.6.0-dev3-3-gbb28224 (nodemcuv2)” when I click the on button through the web page I get this through the serial port

    Any help gratefully received.


  89. I tried both the pre-compiled and compiling my own and deploying with PlatformIO.

    Same crash regardless of which type of bulb, I’m probably doing something very stupid.




    • How do you have the nRF connected to the ESP? I’d recommend double-checking the wiring is correct and making sure nothing is loose.

      If you’re still getting it, can you flash with v1.5.0 and paste the whole stacktrace like you did before?

    • Hi,

      Sorry been away for a few days. Ok so I double checked the pins the photo below shows how I have them set up…

      I downloaded and installed the 1.5 firmware but serial port debugging looks to be turned off. Just trying to build a 1.5 version but platformio is broken. Just trying to sort it.




    • Hi Chris,

      I managed to narrow down the crash to this line..

      currentRadio->write(packet, currentRadio->config().getPacketLength());

      in MiLightClient.cpp

      Unfortunately this is where my c knowledge fails me.

      Thanks for looking into it for me.

    • No luck with the latest dev build. I am deploying the right version? I have been using the “nodemcuv2” build this is my board…

      My Board

      But I get similar results deploying the “d1_mini” build with this board…


    • nodemcuv2 should be fine. The only real thing that matters with these is the size of the flash chip, and almost everything has 4 MB.

      This is what I get when I decode your stacktrace:

      Can you try 1.4.1?

      Given the trace, I’m thinking there’s a change in 1.5 that is probably related, but I think it would only cause problems if something is screwy with your ESP.

      Have you already tried erasing flash and starting fresh? I usually do that with

    • Hi Mike,

      I couldnt get the 1.4.1 to build in PlatformIO and when I installed the image downloaded from github it didnt seem to work. I’m going to assume that there is something wrong with my hardware and try again.

      Thanks for your help.


    • I will give it a go when I get back from work and let you know.

      And thanks again for this.


  90. Hello there! I wonder how hard it could be to port this to a Raspberry? I am asking because I already have an RPi with NRF24L01 sitting around as my MySensors gateway and would love to – if this would at all be possible – to sometimes switch a few MiLights (RGB-CCT type) (thus sharing the NRF module between the two running programs / services)

    My problem is that all other solutions I found so far, namely OpenMilight dont seem to support RGBCCT.

    On the other hand I immediately had success with the ESP8266 solution presented here!

    Thank you for the effort to develop this gem. If I cant make it work on the RPi, I will stay with the ESP – even though I’d love to spare the extra power consumption/hardware.

  91. Hi and thanks for your work, it’s really amazing !

    I have planned to buy a NodeMCU tomorrow and I want to know some point :

    Can I use the android APP to see the emulated bridge ?
    The emulated bridge use the same protocol than the officila v6 ? So, theoretically, I can include it in my domoticz installation like the official one ? (it’s support the official ibox (like a bulb) bridge actually)

    • Hi Romain,

      This has worked in the past by setting up a UDP server on the default port (5987), but newer versions of the android app don’t seem discover the ESP. Probably because of some minor differences in the expected packet (the protocol is very sparsely documented). If this is an important feature to you, please open an issue on the Github project.

      Yes, the UDP protocol is the same. I believe there are other uses that integrate with Domoticz.

  92. Ok, so I will open a issue on GitHub. And I will try tomorrow to integrate it with Domoticz. I will keep you in touch about this. Thanks again for your answer 😉

  93. Hi Chris,

    Looks like a great project that inspired me to get hold of some Nodemcuv3’s and NRF24’s.

    I was having similar problems to Phil above when attempting to transmit it looks like the system crashed when building with debug mode turned on.  I have two boards and can sniff on the second board – it picks up the remote but not from the transmitting nodemcu. I was using source built using PlatformIO on Windows.

    I just flashed the boards using the your 1.6.0-dev6 bin file.  I can now see packets transmitted when I have the second nodemcu setup to sniff.

    I have some LED Downlights dual white CCT that I am trying to get to work, that I have not been successful as yet.   The lights work with a dual white CCT milight remote and I also have a iBox1 that they work with.



    • Hi Justin,

      Thanks for providing all of that info upfront. That narrows it down quite a bit.

      There’s an open issue about binaries built on Windows crashing (link). I’ve not had the time to look into it yet.

      There’s also an issue about FUT035s (dual-white LED controller) not responding to CCT packets (link). It sounds like it’s because newer devices have stricter expectations about the protocol. This is another one I’ll look at soon, but haven’t had the time for yet. A couple of people have had luck unpairing the device with the remote, and then re-pairing with the ESP using the RGB+CCT protocol.


  94. This is a REALLY great piece of software. Tried an implementation of the protocol on a raspi some years ago. So had a transceiver lurking around here. 🙂
    Orderd a NodeMCU yesterday and just set it up, following your great tutorial. Works like a charm! Will try to spoof my bridges at home later and then configure my Light-Switching-App to use it. 🙂

    If you guys are interessed: You can check out this project here:

    Supports 433 MHz-Gateways, MiLight (up to v6), Actions, Timers, Groups, different User-Profiles and much more. 🙂


  95. Hello. This is my first time messing around with the little ESP8266, and the first time with DIY electronics like this. I have played with arduino and stuff before, but have not in 3 years. Great with soldering, no problem hooking the two boards together. But I am having some issues. Using a NodeMCU v3 and I installed Atom, the platform package and clang onto my Mac running the latest OS. Connected the boards, downloaded the zip and built in in Atom. Uploaded it and went. Can connect through the web UI and connected to my wifi network. However, to send more than one command, I have to reset. It seems the board freezes after the first command. This is something I did. I did not do anything else. Am I supposed to install anything to the other wifi module, or select my board version or install firmware? I only built it and installed it. Thank you.

    • I believe the Node is crashing. I can issue a command from the WEB UI, then watching the serial monitor, it does a bunch of characters then I can see it rebooting.

    • If you’re positive everything’s hooked up correctly (and sounds like you are), it’s likely a hardware problem. I think there are a ton of knockoff NRF24L01s on the market, and they’re really hit or miss. Something like 10-20% of the units I’ve dealt with are entirely or nearly useless.

      I’d try:

      1. Compiling with debug flags (these are in platformio.ini) and checking Serial output.
      2. Making sure the pin labels on your ESP match expectations. Sometimes they’re mislabeled.
      3. Trying a different ESP / NRF
    • Don’t know what I did, but it stopped and now I have a reliable bridge. However, I can not control them through the normal milight app and another milight app I use for iOS, Home Remote. It seems they can not see the bridge. Any ideas? And, what if I want to change the network its connected to? Hitting the reset wifi config in the Web UI did nothing, neither did refreshing the board with new firmware. Granted, I am probably doing something wrong.

Leave a Reply

Your email address will not be published. Required fields are marked *