a minimal flashrom/serprog SPI programmer implementation for esp32 family, inspired by pico-serprog and stm32-vserprog
Use your ESP32 board as a flashrom compatible SPI flash programmer
- Should be compatible with all ESP32 family MCUs
- Uses ESP32 HW SPI peripheral capable of up to 80 MHz clock rate
- Multiple connection options: USB Serial/JTAG, HW UART, TCP (Wi-Fi)1
1 - flashrom serprog TCP capability is limited to non-windows OSes and requires flashrom version 1.4.0 or later
First you need an esp-idf (v6.0 or newer) installation. The most convenient option is probably the official VS Code esp-idf extension, pick latest stable sdk version.
- Set your target chip using
idf.py set-target your_chipwhereyour_chipis e.g.esp32s3 - Run
idf.py menuconfigand setComponent config -> ESP-STDIO -> Channel for console outputto the serial interface you are going to use. this will initialize the corresponding peripheral and bind stdin and stdout. Leave 'secondary output' as none. - At the beginning of
main.ccheck what are the predefined SPI pins for your target MCU / set your own (if configurable). - Below the SPI config check and modify chosen serial interface configuration (e.g. baud rate if using HW UART).
Serial interface to use options are (MCU dependent):
This is the default option for USB-capable chips (ESP32-S3, ESP32-C3, ESP32-C6 etc.)
If USB Serial/JTAG is not supported by your MCU or works unreliably, HW UART can be used
- available on all ESP32 family MCUs
- a board with USB-UART converter (or an external one) is required though
- uses default TX&RX pins and a default baud rate of 921600
in theory tinyUSB can be used instead of built-in USB Serial/JTAG peripheral, but it is supported only by a subset of USB Serial/JTAG-capable chips (e.g. ESP32-S3 but not ESP32-C3/C6) and since USB Serial/JTAG no longer misbehaves, i don't see any benefit in implementing USB-CDC
serprog TCP support is available only in non-windows builds e.g. linux, wsl2 etc.
warning: flashrom serprog TCP support is working reliably only on flashrom version >= 1.4.0
to enable TCP over Wi-Fi, before building run idf.py menuconfig and go to esp32-serprog,
select Wi-Fi enabled and set up your (existing) Wi-Fi credentials
ESP32 will connect to your Wi-Fi network and start listening for flashrom connections at (default setting) TCP port 8888.
IP address to connect to can be obtained either from default console output or by resolving the hostname (default setting) esp32-serprog / looking at your router client table
flashrom usage is the same as with UART, but instead of dev=/dev/ttyACM0 you pass your ip:port e.g. ip=192.168.0.2:8888:
flashrom -p serprog:ip=192.168.1.120:8888,spispeed=20M -c W25Q32BV/W25Q32CV/W25Q32DV --verbose -w ../8266.bin
run idf.py flash
WARNING: ESP32 family IO operates at 3.3V, DO NOT connect 1.8V flash chips directly, use a levelshifter in that case. even the cheapest BSS138-based one will do the job although the SPI speed will be limited to ~200-400k. a proper levelshifter IC like TXS0108E should work fine at all reasonable SPI speeds
i have successfully reflashed my Steam Deck 1.8V 16MByte BIOS chip using the cheapest aliexpress MOSFET levelshifter and 1.8V LDO at spispeed=250K, although it took like 20 minutes
SPI speed selection is up to you, depends on the flash chip, wiring etc. - ESP32 should support up to 80MHz in theory. If no spispeed parameter is provided default value of 8M is used
UART baud rate matters only when using HW UART, at least serial/JTAG will always transmit as fast as USB allows
(linux/wsl2)
identify a programmer&chip (no action)
flashrom -p serprog:dev=/dev/ttyACM0:921600,spispeed=8M
read to file
flashrom -p serprog:dev=/dev/ttyACM0:921600,spispeed=8M -c <chip> -r flash.bin
erase chip
flashrom -p serprog:dev=/dev/ttyACM0:921600,spispeed=8M -c <chip> --erase
write from file
flashrom -p serprog:dev=/dev/ttyACM0:921600,spispeed=8M -c <chip> -w flash.bin
since i have borrowed snippets from the pico-serprog by stacksmashing, the least common denominator is GPL 3.0