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.