Library / SDK
espressif/esp-serial-flasher avatar
espressif/esp-serial-flasher

esp-serial-flasher: programming Espressif chips from a microcontroller

Library for flashing Espressif SoCs from other MCUs.

564 stars155 forksCApache-2.0

At a glance

What is it?
ESP Serial Flasher is a portable C library for programming and talking to Espressif SoCs from a host microcontroller rather than from a PC. It presents one API over UART, USB CDC ACM, SPI and SDIO, and a stub loader on the target unlocks the fast paths: higher baud rates, flash sizes above 2 MB, deflate compressed writes and fast flash reads.
Who is it for?
esp-serial-flasher suits a team building a production fixture, a field programmer or an OTA bootstrapper that needs to write an ESP image from an STM32, a Zephyr board or a Raspberry Pi Pico without a PC in the loop, and it suits anyone who needs the secure download and eFuse paths that a host-side script cannot offer.
Can I use it commercially?
Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 1 day ago.
What is it written in?
Mainly C, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The host and the target, and why a C library instead of a script

ESP Serial Flasher is a portable C library for programming and interacting with Espressif SoCs from other host microcontrollers. The vocabulary in the README is worth using precisely, because the library draws a line that other tools blur.

The host is the flashing device running this library, and the target is the ESP-series SoC being programmed. The host controls the target, and everything in the library is written from that direction.

That framing answers the question of who needs this. The README compares the library to esptool and says why it exists: it serves a similar purpose, but is designed for embedded hosts without a PC or a Python runtime, or on less powerful single board computers. So the target user is someone building a device that has to flash an ESP chip as part of its job. A production test fixture that programs the radio module on an assembly line, a field service unit that updates a sensor node, a gateway that brings up a downstream board over UART.

The operation set is listed in four groups. Connection and identification covers connecting to targets, autodetecting the chip family, reading the MAC address and retrieving security info. Flash operations cover write, read, erase, detecting the flash size and verifying data integrity with MD5. RAM download and execution loads binaries into RAM and runs them. Registers and control covers reading and writing registers, changing the transmission rate and resetting the target.

Autodetection of the chip family is the feature that makes a multi-product fixture practical. A fixture that has to be told which chip it is talking to is a fixture with a configuration file; one that identifies the target is a fixture that works on the whole product line.

The licence is Apache-2.0, the primary language is C, and the package is published on the Espressif Component Registry as well as on GitHub.

Four transports, and they are not interchangeable

The supported communication interfaces are UART, USB CDC ACM, SPI and SDIO, and the README is careful about how far each one goes.

UART is the baseline and works with every target in the table. USB CDC ACM is the USB virtual serial port path, supported on the newer chips: the ESP32-S3, C3, H2, H21, C6, C5, P4, C61, S31 and H4, and not on the ESP8266, the original ESP32, the S2 or the C2.

SPI is narrower still. The README states plainly that SPI is for RAM download only, and the target table agrees: SPI is marked supported for the ESP32-S3, C2, C3 and H2, and under development for the H21, P4 and H4. There is no flash write over SPI at all, which is the constraint that matters for anyone planning a fixture around it.

SDIO is the experimental one. The README calls it experimental and says it currently supports only the ESP32-C5 and ESP32-C6 as a target. It uses the esp-flasher-stub project and supports the full stub command protocol over SDIO, and it connects through the stub automatically.

So the honest summary of the transport situation is that UART and USB CDC ACM are the general purpose paths, SPI is a loader path for the chips that support it, and SDIO is a new path with a narrow target list.

The target table also carries a fact that surprises people: the original ESP32 is marked with a construction symbol under SDIO, meaning under development, while the ESP32-C6 and C5 have it supported. Support for a new transport is arriving chip by chip.

The stub loader is what unlocks the fast operations

The feature matrix by interface is the most useful table in the README, and its legend is where the design becomes clear.

Connecting to the ROM bootloader works over every interface. Connecting with stub is supported over UART, USB CDC ACM and SDIO, but not over SPI. Secure download mode works over UART and USB CDC ACM. Flash write works over UART, USB CDC ACM and SDIO. Compressed flash write with deflate and fast flash read are marked with a symbol meaning they require connecting with stub. Slow flash read works everywhere except SDIO. Flash erase by chip and by region, MD5 verify, RAM download, getting security info, changing the baud or clock rate, and eFuse all work over UART and USB CDC ACM.

The recommendation in the README is to connect with stub rather than through the plain ROM bootloader when the flash size on the host is not a limiting constraint. The stub enables faster flashing speeds with higher baud rates, flash sizes larger than 2 MB, compressed writes using deflate, and fast flash read. All supported chips now have a bundled stub, and the README points at a Flash Size Footprint section for the flash overhead that the bundled stubs introduce.

That overhead is the trade-off. A stub is a small program that runs on the target and handles the flash protocol in RAM, which is why it can compress data and talk faster, and it has to be present in the host's own flash so it can be loaded. If the host has little flash to spare, the plain ROM bootloader path is the one that fits, with slower writes and no compression.

The stub connection is a single call:

bash
esp_loader_connect_with_stub()

The helper functions in examples/common/ are there to make the setup easier, though the README notes they are not part of the library API.

eFuse is supported, with a per-chip matrix you have to read

The eFuse capability deserves its own mention, because it is the part of the flashing workflow that is hardest to get right and the part where a wrong choice is permanent.

The README states that eFuse read and burn are supported on all target devices above except the ESP8266, and points at the eFuse Guide, docs/efuse.md, for the per-chip capability matrix and the staged-write model.

Two things are worth noting. First, the ESP8266 is excluded, so a fixture that has to handle the oldest chip in the family cannot do it through this library. Second, the feature matrix places eFuse under UART and USB CDC ACM as supported, under SDIO as supported, and under SPI as not supported, which is consistent with SPI being a RAM download path.

The staged-write model referred to in the guide is the part to look up before using the feature. Burning an eFuse bit is a one-way operation on the silicon, and a library that offers it needs to make the sequence of writes explicit rather than implicit. The README does not describe the model itself, only where to find it, which is the right division for a document of this size.

Getting security info is the softer operation in the same family, and it is listed as supported over UART, USB CDC ACM and SDIO, so a host can read the security state of a target before deciding what to do with it.

Host platforms, and what a Linux host adds

The supported host platforms are the ones running this library and performing the flashing: STM32 microcontrollers, ESP32 series microcontrollers, Zephyr OS compatible devices, the Raspberry Pi Pico (RP2040) and Raspberry Pi Pico 2 (RP2350), and Linux via UART or a USB serial port, with optional GPIO control.

That list covers the three kinds of host that exist in practice. A bare microcontroller host, which is the case the library was written for. A microcontroller with an RTOS, which is the Zephyr entry, and where the port has to fit an existing driver and threading model. And a Linux system, which is the general case, where the port is a serial device and the interesting option is the GPIO control.

The GPIO control on Linux deserves attention because it is the one host-side feature that has no equivalent in a serial port library. Controlling reset and boot mode lines directly from the host is what lets a fixture put a chip into the right mode and hold it there, rather than relying on the host software to time the reset correctly around the connection. For a fixture that flashes thousands of boards, that is the difference between a reliable operation and one that needs retries.

The port implementations live under port/ and the README is explicit about their status. They are reference implementations that depend on the SDK of the target platform, they are tested against the specific SDK versions listed in the Platform Setup Guide, and they do not carry their own semantic version guarantee. Ports are maintained on a best-effort basis, with breaking changes minimised but sometimes necessary to stay up to date with upstream platform SDKs, and when such an update is needed it is released as a minor or patch version of the library.

That is a real constraint for anyone building on a port, and it is a different constraint from the one the public API carries.

Getting started: CMake, submodules and the example programs

The prerequisites are short. You need CMake 3.22 or later as the build system, and Git for cloning the repository with submodules, so the clone has to bring the submodules along rather than fetching only the top level project.

The per-host instructions, including the SDK versions each port is tested against, live in the Platform Setup Guide at docs/platform-setup.md.

The top level of the repository shows the shape of the project. CMakeLists.txt and the cmake/ directory hold the build definition, Kconfig carries the configuration options for the platforms that use that system, idf_component.yml is the manifest for the ESP-IDF component build, and port/ holds the host implementations. The public headers are in include/, the implementation in src/, private headers in private_include/, and there are zephyr/ and tools/ directories for the Zephyr integration and the build tooling.

The examples directory is the best documentation, and there are sixteen of them, each a self-contained program. The names map onto the feature matrix: esp32_example, esp32_stub_example for the stub path, esp32_usb_cdc_acm_example, esp32_sdio_example and esp32_sdio_load_ram_example for the SDIO work, esp32_spi_load_ram_example for the RAM-only SPI path, esp32_deflate_example for compressed writes, esp32_read_flash_example, esp32_load_ram_example, esp32_fast_reflash_example, esp32_get_target_info_example for identification, and esp32_runtime_interface_switching_example for changing transport at runtime.

There are host examples too: linux_example, pi_pico_example, stm32_example and zephyr_example. The helpers they share live in examples/common/, which the README says are utility functions used by the examples and are not part of the library API, though they can be used as a reference.

Versioning, the v2 break, and when this is the wrong tool

The versioning policy has two halves, and the difference between them is the thing to understand.

The public API defined in the include/ folder, which is esp_loader.h, esp_loader_io.h, esp_loader_error.h and esp_loader_efuse.h, follows semantic versioning and maintains backward compatibility within the same major version, with no breaking changes introduced in minor or patch releases. The ports do not, as described above.

Version 2 was a breaking release, and the README marks that with an IMPORTANT callout: v2 introduces breaking changes to the public API and the port layer, with a Migration Guide at docs/migration-v1-to-v2.md holding the complete list of changes and step-by-step upgrade instructions. So an existing v1 integration is a migration project, not an upgrade, and the guide is the place to start.

The release history shows three recent points: v1.10.0 on 2025-09-26, v1.11.0 on 2025-12-05 and v2.0.0 on 2026-06-04, with the last push to the repository on 2026-10-22.

There are clear cases where this library is the wrong tool. On a workstation, with Python available and a USB cable, the Espressif Python tooling is the quicker route and this library adds a build step to no benefit. For a design that needs to write flash over SPI, the answer is that it cannot be done through this library at all. For the ESP32-C5 or C6 with SDIO, you are on the experimental path. And for the ESP8266, eFuse operations are out of reach.

The alternative approach is the host-side script, and the difference is not capability so much as where it runs. A script needs an operating system, an interpreter and a serial port, and it can use features of the vendor tooling that a bare-metal host cannot have. This library inverts the constraint: it needs only a C compiler and the host SDK, so it can live inside the product you are building rather than beside it.

Editorial conclusion

esp-serial-flasher suits a team building a production fixture, a field programmer or an OTA bootstrapper that needs to write an ESP image from an STM32, a Zephyr board or a Raspberry Pi Pico without a PC in the loop, and it suits anyone who needs the secure download and eFuse paths that a host-side script cannot offer. It does not suit a desktop workflow, where the Python tooling already does the job faster, and it does not suit an SPI-only design that needs flash writes, because SPI is limited to RAM download. Check three things before you commit: which of your host and target pairs are marked supported rather than under development in the interface tables, whether the bundled stub fits in the flash your host has room for, and whether the port for your platform SDK has moved under you, since the ports in port/ carry no semantic versioning guarantee of their own. The last push to the repository was on 2026-10-22 and the current major version is 2.0.0.

Frequently asked questions

What is ESP Serial Flasher for?

It is a portable C library for programming and interacting with Espressif SoCs from other host microcontrollers, with a unified API over UART, USB CDC ACM, SPI and SDIO. It is aimed at embedded hosts without a PC or Python runtime, or on less powerful single board computers, and covers connection and identification, flash operations, RAM download and register control.

Which host platforms can run esp-serial-flasher?

STM32 microcontrollers, ESP32 series microcontrollers, Zephyr OS compatible devices, the Raspberry Pi Pico (RP2040) and Pico 2 (RP2350), and Linux via UART or a USB serial port with optional GPIO control. The port implementations under port/ are reference implementations that depend on the target platform SDK.

What does connecting with the stub give me?

The README recommends esp_loader_connect_with_stub() over a plain ROM bootloader connection when host flash size is not a limiting constraint, because the stub unlocks faster flashing speeds with higher baud rates, flash sizes larger than 2 MB, compressed writes using deflate, and fast flash read. All supported chips have a bundled stub.

Can I write flash over SPI?

No. SPI is limited to RAM download, and the feature matrix marks flash write, erase, MD5 verify and eFuse as not supported over SPI. SPI is listed as a supported target interface for the ESP32-S3, C2, C3 and H2, and under development for the H21, P4 and H4.

How do I upgrade a project that uses version 1?

The README marks this as an IMPORTANT point: v2 introduces breaking changes to the public API and the port layer, and the Migration Guide at docs/migration-v1-to-v2.md lists the changes with step-by-step upgrade instructions.

Official sources

  1. espressif/esp-serial-flasher on GitHub
  2. Issues
  3. License: Apache-2.0
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/espressif-esp-serial-flasher.svg)](https://hysenlabs.com/projects/espressif-esp-serial-flasher)

Was this useful?