Bare-Metal Ada on the ESP32-S3 A step-by-step guide to running Ada on the ESP32-S3 with no ESP-IDF, no FreeRTOS, and no Python.

Step 50 of 56

Esp_Loader: your board as the programmer

The ESP32 serial ROM protocol spoken as the host, so a jig or a product can flash another ESP32 — and none of them can run Python.

The device-side twin of esptool

This is the same protocol the SDK's own espflash host tool speaks from a PC (step 7), implemented so that the board itself is the programmer. A production jig, a field programmer, or a product that reflashes its own daughterboard all need exactly this, and none of them can run Python.

Every exchange is a SLIP frame (0xC0 delimited, 0xDB escaped) carrying a command and payload; the target answers with the same opcode and a status pair.

Only the ROM loader is spoken — no downloadable stub — so there is no compression and no stub-only command. That costs transfer time and nothing else: raise the rate with Set_Baud and a megabyte moves in a few seconds.

Streaming, so a megabyte costs a kilobyte

Begin_Image declares the length, Write takes whatever chunks the source produces, and full 1 KB blocks go out as they fill. Flashing a megabyte therefore costs a kilobyte of RAM — and a truncated source is an error rather than a corrupt target, because the declared length is checked against what arrived.

Knowing what you are talking to

The ROM protocol is not uniform across the family, and guessing wrong corrupts flash rather than failing cleanly. The original ESP32 ends every reply with four status bytes instead of two; the ESP32 and ESP8266 take a shorter FLASH_BEGIN payload; the ESP8266 has no SPI_ATTACH at all, plus a bug in how it sizes an erase.

So Connect identifies the target first — via GET_SECURITY_INFO where the chip supports it (S3 and later), and the magic register otherwise. A chip newer than the table still connects, as Unknown, driven with the modern defaults.

Pass-through: the auto-reset circuit in software

Esp_Loader.Auto_Reset is the circuit every ESP dev board has, implemented in software. When a board sits between a PC and a target as a USB-serial bridge, esptool on the PC expects to reach the target's ROM loader by wiggling DTR and RTS — this makes that work.

It reproduces the real circuit's cross-coupling, so a terminal emulator asserting both lines on open does not reset the target, and it emulates the capacitor so esptool's ClassicReset (which moves the lines one at a time) works regardless of what the target board has on its EN pin. Those two details are the difference between "usually works" and "works".

Serial_Link is the ready-made transport over a UART and two GPIOs, and MD5 is here for SPI_FLASH_MD5: the target hashes what its flash actually holds and you compare, so "programmed OK" means something.

Host-verified only, against a simulated ROM that validates every frame and impersonates each chip family in turn (with the per-chip handling deliberately broken three ways to prove the checks bite). The real ROM's timing still wants a target board on a real UART.