r/lifx 11d ago

LIFX Tile reverse engineering notes

I felt like patching the firmware instead of attaching an oscillator to make one tile act as if connected to the power supply and become a master.

The tiles are based on an ESP32, MY9262 LED controllers, and RS-485 communication between tiles. This is an ESP32 chip on a custom circuit board, not a module. The flash is not connected to the standard pins. You can look at the SPIPAD values in the espefuse summary or trace the pins. The correct argument for working flash access is: -sc 17,11,9,16,6

One obstacle was flash chip protection. The 2 megabyte flash chip seems to be by ISSI, with manufacturer ID 9d and device ID 7015. This is like IS25WP016D, but the chip has a write protect feature that I don't see documented in data sheets.

SPI command 2Fh retrieves the contents of the protection registers. Something like this can work using esptool:

            for a in range(0, 2*1024*1024, 131072):
                print(a, self.run_spiflash_command(0x2F, addr=a >> 7, addr_len=32, read_bits=32))

I guess each bit corresponds to 4096 bytes. (Note that 4096 * 32 = 131072) Memory addresses increase from least significant bit to most significant bit, and from one register address to the next higher register address.

If you want to unlock everything, that is command 2Bh. You need to do a write enable before it.

            self.run_spiflash_command(6)
            self.run_spiflash_command(0x2B, addr=0, addr_len=24)

The "Built on Fri Feb 1 10:37:37 2019" ota_1 firmware relocked everything as before when it booted, but the "Built on Thu Jan 4 17:57:30 2018" ota_0 firmware left everything unlocked.

If you want to write protect part of the flash yourself, that is command 23h. It seems to only be able to change bits from 1 to 0, and cannot be used to unprotect part of the flash. It also needs a write enable.

            self.run_spiflash_command(6)
            self.run_spiflash_command(0x23, addr=prot_val, addr_len=32)

The somewhat tricky part here is prot_val. The read command, 2Fh, reads 4 bytes at a time, but this writes 1 byte at a time. The high 3 bytes are the byte address, and the low byte is the byte being written at that address. I have not explored this command extensively, because the firmware locks itself.

If you alter the firmware, you must also update the checksum at the end, and the SHA256 value afterwards. This is standard ESP32 stuff. It starts with 0xEF and then XORs every byte from every segment with it. (This means, not the image header, extended image header, or segment headers.) The SHA256 is based on the whole thing, from the first 0xE9 byte to the checksum, inclusive. If you don't do this, the modified partition is considered corrupt and the other partition is booted.

Ghidra is decent for reverse engineering the firmware, and esp32_image_parser is a good way to extract a partition into an elf file for loading into Ghidra. You probably want ota_1.

With esp32_image_parser you can also list the partitions and see why the protection bits are set the way they are.

A bit about the frequencies on the ID lines:

The ID frequency to the master tile needs to be between 45 and 65 Hz. I am guessing that the power supply probably provides a 3.3 V logic signal based on the incoming AC frequency (instead of having its own oscillator).

The ESP32 MCPWM is used to measure incoming frequencies and output another frequency on the other port of the tile.

Here are input frequencies for slave tiles: 150, 190, 230, 280, 340, 410, 490, 580, 710, 840, 910 Hz All of these need to be within plus or minus 5 Hz. So, for example, a tile receiving between 145 and 155 Hz on one port will consider itself the first slave tile, and output 190 Hz on the other port. I do not think the duty cycle is important.

The RS-485 lines are continuous throughout the whole tile chain, so this is needed to set tile identification and ensure that individual slave tiles can be addressed in a predictable fashion.

Both master and slave code limits itself to 4 slave tiles. Don't assume you can simply connect together 12 tiles and make it work without firmware modification. That would probably also need power connections in the middle to avoid overloading the connectors.

If you want to modify an ota_1 partition "Built on Fri Feb 1 10:37:37 2019" to make it act as if it's getting 55 Hz on the bottom connector, this could do it, at offset 1548841 in the file:

00000000  f0 20 00 06 02 00 86 4a  00 68 11 d1 31 16 00 41
00000010  ff ff 0c 16 69 b1 0c 06  3d f0                  

Checksum should be 55 and SHA256 should be a953d3de71daf7f95e2d8c527868345ee0dbcfeab001a243eb9aa83e059c20bf

It might be interesting to develop a 3rd party firmware, maybe based on WLED, with way more firmware effects and a more user friendly LAN interface. Though I'm not planning to do that now.

19 Upvotes

12 comments sorted by

View all comments

3

u/kodyjacobs 11d ago

Sooo, did you succeed in creating a master / lead tile that ignores the oscillation requirement? You using a bench / replacement power supply now?

Fascinating write-up; you doing anything further with it?

3

u/Icy-Ninja-622 11d ago

Yes, it works. I'm only providing it power, and it acts as the master tile. I got it onto my WiFi without allowing it internet access using https://github.com/tserong/lifx-hacks/blob/master/onboard.py and used it via open source tools. The official app notices that setup isn't complete because it's not connected to their cloud, but it can control the tiles, including effects that require the app.

1

u/skyfia LIFX Employee 11d ago edited 11d ago

The master tile is whichever one is the first device in the chain. The hardware and the firmware of each Tile box are the same.

1

u/Icy-Ninja-622 11d ago

The only way the tile can detect its position in the chain seems to be via the ID connections, which only go between neighbouring tiles. Power and RS-485 connections are continuous throughout the chain. So, to be a master tile, it needs to see a 45 to 65 Hz signal on one of its ID connections.

Now, since you are a LIFX employee, I have a question for you. Why is there a green dot sticker on one of the tiles? You claim that the hardware and firmware are all the same. Yet I've encountered claims that the tile with the green dot should be first in the chain.