mpftp firmware flash has a layout check: before writing, it compares the partition table the image carries with the one on the board, and refuses a mismatch. It works over a running REPL — it correctly refused an ESP32-P4 flash on 2026-09-21 whose table did not match the board's.
Flashing an ESP32-S3 through its ROM download port (the only route on a board with no UART bridge: mpftp bootloader -d COMx, the chip re-enumerates as 303A:1001, then mpftp firmware flash … -d <that port>), it prints
[mpftp] could not read on-device partition table; skipping layout check (enable Erase if the board misbehaves)
and writes the image. That is exactly when the check is needed, and there is no reason to skip it: in download mode esptool can read the table directly (read-flash 0x8000 0xC00), which needs no REPL at all.
What it cost
On 2026-09-21 a LilyGO T-Embed S3 was flashed through its ROM port with an S3 image built by a route that had dropped our partition customisation: vfs at 0x390000 where every image that board had run has it at 0x3a0000. esptool verified the write. The board then never enumerated: MicroPython's _boot.py found a first block that was neither blank nor mountable (an old core dump lives at 0x390000 under the old layout) and sat in inisetup.fs_corrupted() — an infinite print/sleep loop that runs before the runtime USB device starts. Nothing on the bus, dark screen, no panic, no core dump. It looked exactly like a boot crash and took several trips to the BOOT button to diagnose. Had the first block been blank, MicroPython would instead have silently formatted a new filesystem over the old one.
Ask
- In download mode, read the on-device table with esptool and run the same comparison; refuse a mismatch the same way.
- "enable Erase if the board misbehaves" is the wrong advice for this failure — the board's files were intact 64 KB further on, and an erase would have destroyed them. Say what differs (partition name, offset, size) instead.
- A standalone
mpftp firmware ptable <image> (print an image's table; diff two) would have found this in seconds. The table is 32-byte entries from 0x8000, magic AA 50.
mpftp firmware flashhas a layout check: before writing, it compares the partition table the image carries with the one on the board, and refuses a mismatch. It works over a running REPL — it correctly refused an ESP32-P4 flash on 2026-09-21 whose table did not match the board's.Flashing an ESP32-S3 through its ROM download port (the only route on a board with no UART bridge:
mpftp bootloader -d COMx, the chip re-enumerates as303A:1001, thenmpftp firmware flash … -d <that port>), it printsand writes the image. That is exactly when the check is needed, and there is no reason to skip it: in download mode esptool can read the table directly (
read-flash 0x8000 0xC00), which needs no REPL at all.What it cost
On 2026-09-21 a LilyGO T-Embed S3 was flashed through its ROM port with an S3 image built by a route that had dropped our partition customisation:
vfsat 0x390000 where every image that board had run has it at 0x3a0000. esptool verified the write. The board then never enumerated: MicroPython's_boot.pyfound a first block that was neither blank nor mountable (an old core dump lives at 0x390000 under the old layout) and sat ininisetup.fs_corrupted()— an infinite print/sleep loop that runs before the runtime USB device starts. Nothing on the bus, dark screen, no panic, no core dump. It looked exactly like a boot crash and took several trips to the BOOT button to diagnose. Had the first block been blank, MicroPython would instead have silently formatted a new filesystem over the old one.Ask
mpftp firmware ptable <image>(print an image's table; diff two) would have found this in seconds. The table is 32-byte entries from 0x8000, magicAA 50.