Docs / Contact / devices/flash-firmware

Flashing MicroPython

A brand-new ESP32 usually ships with the vendor's AT firmware, not MicroPython. The Device tab flashes it for you, in the browser, with no esptool install, no Python environment, and no driver hunt.

ESP32 family only

Flashing speaks the ESP boot-ROM protocol, so it works on ESP32, ESP32-S2, ESP32-S3, and ESP32-C3 only. Other boards use their own vendor mechanism: Raspberry Pi Pico W takes a UF2 drag-and-drop, and OpenMV boards are flashed with the OpenMV IDE. Everything after flashing (provisioning, files, terminal, camera, vision) works on all of them.

Choosing firmware

From micropython.org. The card fetches the live build list for your board, newest first, and preselects the build that matches your board's memory: the SPIRAM variant if the board actually has PSRAM, otherwise the generic build. The list also includes other build variants (UNICORE, D2WD, and so on); if you pick one that doesn't match the board's real PSRAM, the card warns you. Tick Show preview / nightly builds to see unreleased builds too. (The list is fetched through Contact's server because micropython.org serves no CORS-friendly index; the firmware itself still downloads to your browser and is written from there.)

If that lookup fails for any reason, the card falls back to a pinned known-good build for the board and tells you it did.

Tendrl ROMFS firmware. A low-RAM MicroPython build that includes a ROMFS partition, matched to your board (chip × PSRAM × flash size). Flash this to enable the ROMFS install, where the SDK runs from flash instead of the heap. You normally don't need to pick this manually: choosing Tendrl firmware during provisioning flashes it for you when the board needs it.

Images are prebuilt per board combination, and only these exist today:

Chip PSRAM Flash Status
ESP32 (Xtensa) no 4 MB ✅ built
ESP32 (Xtensa, WROVER) yes 4 MB ✅ built
ESP32-C3 (RISC-V) n/a (chip has none) 4 MB ✅ built
ESP32-S2 with or without 4 MB ✅ built
ESP32-S2 with or without 16 MB ✅ built
ESP32-S3 with or without (quad) 4 MB ✅ built
ESP32-S3 with or without (quad) 16 MB ✅ built
ESP32-C6 / H2 / C2 / C5, or 8 MB flash not built yet

The S2 and S3 images are built with CONFIG_SPIRAM_IGNORE_NOTFOUND, so one image initialises PSRAM when it's there and boots normally when it isn't. The 16 MB builds use the whole chip (a 2.5 MB app slot and a 13.5 MB filesystem) rather than leaving 12 MB unused.

An unrecognised chip is never given a fallback image. If provisioning can't identify the variant it reports that and stops, because guessing would mean flashing (for example) Xtensa firmware onto a RISC-V part.

The original ESP32 ships two images rather than one, because its PSRAM build compiles in CONFIG_SPIRAM_CACHE_WORKAROUND, a silicon erratum workaround that costs performance and code size, and which a board without PSRAM shouldn't pay for. The right one is selected automatically from what the board reports.

ESP32-S3 with octal PSRAM

The S3 image is a quad-PSRAM build. A board with octal PSRAM (common on N16R8 modules) will flash and boot, but its PSRAM won't initialise; the board silently comes up with internal RAM only. Provisioning can't detect this: the device reports PSRAM as present or absent, not which mode. If you're on an octal-PSRAM S3 and need the extra memory, build the SPIRAM_OCT variant and flash it with the Local .bin file option.

Anything outside that matrix reports exactly what is missing (no ROMFS firmware built yet for chip=… psram=… flash=…MB) and leaves the board untouched. The check runs before the erase, so an unmatched board is never wiped. Those boards use the normal filesystem install, which ships the same precompiled bytecode.

This option works on a blank board: the chip and flash size are read during the boot-ROM handshake, so there is nothing to connect to first.

Local .bin file. Pick Local .bin file to flash a custom build, a version newer than the index knows about, or something you compiled yourself.

Flash offset

The offset is filled in from the chip family and rarely needs changing:

If you're flashing a local .bin that isn't a combined MicroPython image, set the offset your build expects.

What happens

The flash runs as six stages, each reported live with a log underneath:

1

Look up firmware

resolve the build (skipped for a local file)

2

Download firmware

fetch the image into the browser

3

Connect to boot ROM

the port is taken over by the ESP loader

4

Erase flash

a full chip erase, so nothing from the old firmware survives

5

Write firmware

with a live progress bar

6

Reset device

the board reboots into MicroPython

The erase is total

Flashing erases the entire chip. Any files on the board, including a config.json from a previous provisioning, are gone. Re-provision after flashing.

If it can't connect

Most ESP boards auto-reset into the bootloader when the loader asserts DTR/RTS. Some don't, particularly bare modules and boards with unusual USB-serial chips. Put the board into bootloader mode by hand:

Then flash again.

The other common failure is a busy port. The flasher needs exclusive access, so the tab closes its own REPL connection first, but an mpremote, Thonny, OpenMV IDE, or screen session on the same board will block it. Close those and retry.

After flashing

When the flash finishes, the card asks you to unplug the board and plug it back in. A fresh power-up boots the new firmware cleanly, while a reset over USB often lands the chip back in the bootloader, so the physical replug is the reliable path (and it's required on native-USB boards like the S2/S3, whose USB re-enumerates on reset). Then click Connect and go to Provisioning to write its config and install the SDK. The board comes back up running stock MicroPython with an empty filesystem.