Categories
Home Automation

Custom firmware on a Magic Home LED controller

I’m just getting into more serious home automation and playing around with Home Assistant, and while I don’t have very many smart devices yet, I do have a cheap WiFi LED controller by Magic Home. When I first got it, I used the companion app, which was terrible, and I hated having to have separate apps for everything. As I dove into configuring Home Assistant (HA), I decided to see if there was a way to magic that controller work with HA—and it turns out, there is!

By flashing ESPHome firmware onto the controller, it becomes immediately discoverable by HA and no longer requires a separate app to manage. Now, it’s still a WiFi controller, and I don’t like the idea of saturating my WiFi network with smart devices, but I already have it so I decided to make it work.

Prep the controller for flashing

The first thing I needed to do was prep the controller to be flashed. I popped open the plastic enclosure, and identified the chip as an ESP8285, which is a version of the classic ESP8266 with 1MB of flash memory built in. This was good news and meant I should be able to flash my own firmware on it.

Magic Home WiFi LED controller opened up, showing the ESP8285 chip.
Magic Home WiFi LED controller opened up

On the back of the controller were several solder pads. After scouring tutorials online, I determined the ones that I needed for this project were labeled TXD, RXD, IO0, and GND. I went ahead and soldered solid-core wire leads to each of these pads.

Leads soldered onto the Magic Home LED controller ESP8285 chip
Leads soldered onto the Magic Home LED controller

Use an Arduino as a serial converter

Next, I needed something that could talk to the controller. You can use a USB-to-UART or USB-to-TTL cable to do this, but I didn’t have one on hand. What I do have is an Arduino Uno that doesn’t get much love, and it turns out to be a pretty simple task to turn an Arduino into a serial converter.

The Arduino has TX (transmit) and RX (receive) pins. Normally, any communication would need to go through the Arduino’s ATmega328P chip, which would involve writing a sketch and uploading it to the Arduino. However, there’s another option: you can jumper the Arduino’s RESET pin to GND (ground) and bypass the chip completely, giving you direct access to the Arduino’s USB-to-TTL converter and thus TX and RX pins over USB.

The Arduino wiring is pretty simple here. I used a breadboard to lay things out really cleanly.

  1. Jumper RESET to GND.
  2. Run a wire from GND to the negative rail on the breadboard.
  3. Run a wire from TX to a terminal strip on the breadboard.
  4. Run a wire from RX to another terminal strip on the breadboard.
  5. Run a wire from the negative rail to a 3rd terminal strip on the breadboard.
  6. Run a second wire from the negative rail to a 4th terminal strip on the breadboard.
Arduino connected to the Magic Home LED controller to flash firmware
Arduino connected to the Magic Home LED controller

This may be a bit excessive with the breadboard, but it made it really easy to connect the leads from the controller, especially as I played around with it to get it working correctly.

A note on TX and RX serial connections

Normally, when connecting serial devices together, the TX pin of one device is connected to the RX pin of the other. This is so when the first device transmits (TX), the other device receives (RX) that transmission.

However, for this project, since I’m accessing the Arduino’s built-in USB-to-TTL converter directly instead of going through the ATmega328P chip with a sketch, I needed to connect the TX pin on the Arduino to the TXD pin on the controller instead of the RXD pin, and likewise the RX pin on the Arduino to the RXD pin.

This is because the Arduino pins are labelled from the perspective of the ATmega328P chip: a serial message would be received on the Arduino’s RX pin, which in turn is mapped to the USB-to-TTL converter’s TX pin, as in normal serial communication. Same for transmitting: the Arduino’s TX pin is mapped to the converter’s RX pin. Since we’re bypassing the ATmega328P chip completely, the Arduino’s RX pin is effectively the converter’s TX pin.

Normal operation with ATmega328P chip

USB-to-TTL converterATmega328P chipSerial device
RXDTXRX
TXDRXTX

Bypass operation

USB-to-TTL converterArduino boardLED controller
RXD“TX”TXD
TXD“RX”RXD

Connect LED controller to Arduino

With the clean breadboard layout, it’s now a simple task to connect the LED controller to the Arduino via the breadboard. Effectively, the pins map like so:

Arduino boardLED controller
TXTXD
RXRXD
GNDGND
GNDIO0

The LED controller’s IO0 pin needs to be grounded for the controller to boot in “flash mode” to allow us to flash the firmware.

A note on voltages

The Arduino operates on 5V, while the LED controller operates on 3.3V. I read several tutorials that strongly recommended stepping down the voltage sent to the TXD pin on the controller from 5V to ~3.3V, but I was unable to communicate with the controller when using a voltage divider circuit. The only way I was able to make it work was using a direct connection with the full 5V. If you do this, continue at your own risk! It could damage the controller.

Build the custom firmware

To create the firmware, I used ESPHome through Home Assistant. Once ESPHome was installed as a Supervisor Add-On in HA, I went to Supervisor → Dashboard → ESPHome → Open web UI and clicked the + button to add a new “node”.

I gave it a name and WiFi information so it could connect to my home network, and selected ESP8266 as the ESP device.

Once the node is created, I clicked Edit to edit the YAML. It should already have the basics, and I only needed to add the following snippet at the end to be able to control the LED strip. Note that my controller and strip are only for single-color LEDs; you can set this up for RGB LEDs too with a little more configuration. ESPHome has lots of documentation.

# Define the light component
light:
  - platform: monochromatic
    name: "Monitor Backlight"
    output: output_component1

# Define the output to control the LEDs
output:
  - platform: esp8266_pwm
    id: output_component1
    pin: GPIO12

The real key piece in that snippet is the last line: pin: GPIO12. This tells the firmware to use IO pin 12 to control the LED strip, which I managed to find documented here. There are other Magic Home models on that site as well. For RGB LEDs, for example, you would need to control 3 pins.

Once I edited the YAML, I clicked Save and then Install. Since I’m manually flashing the firmware over USB, I selected Manual download, which compiles and downloads a .bin (binary) file with custom firmware generated from the YAML.

Flash the firmware to the controller

All the pieces are coming together now! The last step is to actually flash the new custom firmware onto the LED controller. Start with everything disconnected from power. Plug the USB cable into the Arduino and computer. Then, plug the LED controller into its power source. With the IO0 pin grounded, it should boot up in flash mode and be ready to receive the firmware.

I used esptool.py to flash the firmware. It’s developed by Espressif, the same company that makes the ESP8266 chip. I’m on a Mac, so I used these steps to install and run it using Terminal.

Install esptool using pip

pip is a package manager for Python.

$ sudo python -m ensurepip --upgrade
$ sudo pip install --upgrade esptool

Find the USB serial port

If you have multiple USB devices connected to the computer, this might be trickier. It’s easiest if you unplug everything except the Arduino.

$ ls /dev/tty.usb*

Flash the firmware

Using the correct USB serial port and the .bin file I downloaded, I flashed the firmware to the LED controller.

$ esptool.py --port /dev/tty.usbmodem14101 write_flash -fm dout 0x00000 monitor-backlight.bin

A few notes on this command:

  1. The documentation states that the flash should be erased before writing in order for it to be successful. However, I didn’t find that to be the case. I was able to write only without erasing, and it ran successfully.
  2. The --port /dev/tty.usbmodem14101 options specifies which serial port to use. The value should match what you found by running ls /dev/tty.usb* in the previous step.
  3. The -fm dout option tells it to use the “dual output” flash mode. There are other modes described here which I did not try, but this mode worked for me.
  4. The 0x00000 option tells it to write the firmware starting at the very first memory address.
  5. The monitor-backlight.bin is the name of the firmware file that was compiled and downloaded by ESPHome. I ran this command from the same folder the file was in, but you may need to provide a path to the file here rather than just the filename.

If everything works successfully, you should see output similar to this:

esptool.py v3.1
Serial port /dev/tty.usbmodem14101
Connecting...
Failed to get PID of a device on /dev/tty.usbmodem14101, using standard reset sequence.
.
Detecting chip type... ESP8266
Chip is ESP8285N08
Features: WiFi, Embedded Flash
Crystal is 26MHz
MAC: d8:f1:5b:a2:9c:9a
Uploading stub...
Running stub...
Stub running...
Configuring flash size...
Flash will be erased from 0x00000000 to 0x00065fff...
Compressed 417792 bytes to 290846...
Wrote 417792 bytes (290846 compressed) at 0x00000000 in 25.3 seconds (effective 132.2 kbit/s)...
Hash of data verified.

Leaving...
Hard resetting via RTS pin...

Test the controller

After a successful flash, disconnect the LED controller from power, disconnect the Arduino, and remove the LED controller leads from the breadboard. Connect the controller to power again and to the LED strip.

If the firmware was configured properly, you should be able to go to Supervisor → Dashboard → ESPHome → Open web UI and see the device come online in a few seconds automatically

Magic Home LED controller connected to ESPHome in Home Assistant
Magic Home LED controller connected to ESPHome in Home Assistant

That’s it! The LED controller is now available to be added as an integration in Home Assistant, and then a card can be added to the dashboard to control it. I used a button card to turn it on and off, and I added automations to turn it on and off and set it to different brightnesses.

Happy hacking!