Wiki / Software

esptool.py

Espressif's official flashing tool. Talks to the ESP32/ESP8266 serial bootloader over UART to identify the chip and read, write, or erase its flash, no desoldering needed.

esptool.py is Espressif's official flashing tool for the ESP32 and ESP8266 families. It talks to the chip's serial bootloader over UART to identify it, read and write flash, and erase it.

What it is

Every ESP32 and ESP8266 has an immutable first-stage ROM bootloader that can be dropped into UART download mode. In that mode it speaks a simple serial protocol, and esptool.py is the reference client for it. Over three wires (TX, RX, GND) plus the boot-strapping lines it can read the chip's identity, dump or program the entire external flash, erase it, and read the eFuse-derived MAC. It is pure Python, cross-platform, and the same tool the ESP-IDF build system calls under the hood when you idf.py flash.

Why it matters

If you can reach an ESP32's UART, esptool.py lets you pull the entire flash image without desoldering anything. That dump contains the application, the second-stage bootloader, the partition table, and the NVS partition where Wi-Fi SSIDs, PSKs and cloud tokens frequently sit in plaintext. On a board that never enabled Flash Encryption, and most do not, a UART header and esptool.py are the whole cost of a full firmware read. It is the fastest, least invasive way onto an ESP target.

How to use it

pip install esptool
esptool.py -p /dev/ttyUSB0 flash_id                          # chip type and flash size
esptool.py -p /dev/ttyUSB0 read_flash 0x0 0x400000 dump.bin  # dump 4 MB of flash
esptool.py -p /dev/ttyUSB0 write_flash 0x10000 app.bin       # reflash an image at an offset
esptool.py -p /dev/ttyUSB0 erase_flash                       # wipe everything (destructive)
esptool.py -p /dev/ttyUSB0 -b 921600 read_flash 0x0 0x400000 dump.bin  # faster baud

flash_id first: it confirms the link works and prints the flash size, which tells you the right length for read_flash (a 4 MB part is 0x400000, an 8 MB part 0x800000). Wiring is a usb-uart adapter's TX to the board's RX, RX to TX, and a common GND, at 3.3V logic.

Getting into download mode

The chip enters UART download mode when GPIO0/BOOT is held low as it comes out of reset. Most dev boards automate this with the EN/BOOT button pair or a DTR/RTS auto-reset circuit, so esptool.py toggles it for you. On a bare module you do it by hand: hold BOOT/GPIO0 low, pulse EN/reset, release BOOT. If esptool.py prints Failed to connect ... Wrong boot mode detected, the chip booted its app instead of the bootloader, so repeat the sequence.

Pitfalls

  • Flash Encryption defeats the read. If the target burned Flash Encryption, read_flash returns ciphertext, not the plaintext firmware. Check with espefuse.py summary before assuming a dump is useful.
  • erase_flash and write_flash are destructive. They overwrite the target. Always read_flash a full backup first so you can restore the device.
  • Auto-reset is not universal. Boards without the DTR/RTS circuit need the manual BOOT/EN sequence; forgetting it is the usual cause of connect failures.
  • Baud rate. The default is safe but slow for a 4 MB dump; -b 921600 speeds it up, but drop back down if the read errors out on a long cable.

What it pairs with

esptool.py is the extraction step for the whole ESP32 workflow. Pair it with a usb-uart adapter for the physical link, espefuse.py to first check whether the offline read is even possible, and once you have dump.bin, binwalk or an NVS parser to carve out the secrets. See the esp32 entry for the chip, boot chain and partition layout it is reading.

Further reading