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.
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.
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.
- Jumper
RESET
toGND
. - Run a wire from
GND
to the negative rail on the breadboard. - Run a wire from
TX
to a terminal strip on the breadboard. - Run a wire from
RX
to another terminal strip on the breadboard. - Run a wire from the negative rail to a 3rd terminal strip on the breadboard.
- Run a second wire from the negative rail to a 4th terminal strip on the breadboard.
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 converter | ATmega328P chip | Serial device |
RXD | TX | RX |
TXD | RX | TX |
Bypass operation
USB-to-TTL converter | Arduino board | LED 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 board | LED controller |
TX | TXD |
RX | RXD |
GND | GND |
GND | IO0 |
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:
- 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.
- The
--port /dev/tty.usbmodem14101
options specifies which serial port to use. The value should match what you found by runningls /dev/tty.usb*
in the previous step. - 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. - The
0x00000
option tells it to write the firmware starting at the very first memory address. - 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
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!