espx
ESP32 firmware tools.
Set up the toolchain, start an esp-hal project, then build, flash and watch the board on your desk.
$ espx runESP32-C6 on /dev/cu.usbmodem101Set up
espflash, cargo-espflash and esp-generate, pinned and SHA-256 checked.
espx setupCreate
An esp-hal project from esp-generate's template, with an AGENTS.md.
espx new blinkyNBuild
Release build. Errors at file and line, the image against the app partition.
espx buildBFlash
Progress per flash region. If the board needs BOOT held, espx says so.
espx flashUMonitor
Boot reason named, defmt decoded, panics resolved to file and line.
espx runR
Serial monitor
espx reads the port itself. It decodes defmt against your release binary, resolves panics to a file and line, and names the reason for every boot.
defmt and panic decoding
defmt frames become log lines with their source location. Panic addresses become a function, a file and a line.
target/riscv32imac-unknown-none-elf/release/blinky$ espx monitor[INFO ] Hello world! (src/bin/main.rs:40)panic: panicked at src/bin/main.rs:24:9 0x42010190 blinky::explode at src/bin/main.rs:24 0x420116c0 main at src/bin/main.rs:43Boot reasons
espx reads the
rst:code in the ROM banner and names the reset.rst:0x1power-onrst:0x5wake from deep sleeprst:0x7watchdog resetrst:0xfbrownout resetrst:0x15reset over USB
Download mode
If the bootloader won't answer, even at 115200 baud, espx says which buttons to press.
Hold BOOT, tap RESET, then let go of BOOT.Shared board access
espx flashin a second terminal flashes the board the app is watching, through~/.espx/hub.sock.Recording and replay
espx replayplays one back with its original timing, already decoded, with no ELF needed.$ espx replay crash.jsonl0.001=> board booted: reset over USB0.001INFO - Hello world!1.528panic: panicked at src/bin/main.rs:24:91.619warn: board booted: reset after a panic1.752INFO - Hello world!
Scripting and CI
Exit codes CI can read: 0 on a match, 1 on a rejected line, 124 on a timeout.
Each works on run, monitor and replay.
# Exit 0 on the first line that matches.$ espx run --until 'Hello world!' --timeout 2m=> building and flashing blinky for the ESP32-C6 on /dev/cu.usbmodem101 (cargo espflash flash --release)=> monitoring /dev/cu.usbmodem101 at 115200 baud until `Hello world!` for at most 2m=> flashed 65536 bytes in 2.1s=> board booted: reset over USB (rst:0x15 (USB_UART_HPSYS),boot:0x6c (SPI_FAST_FLASH_BOOT))INFO - Hello world!=> expectation 1 met: `Hello world!`Coding agents
espx mcp lets a coding agent build, flash and debug firmware on the board on your desk.
Connect
~/blinky$ espx mcp install --client claude-codeclaude mcp add espx -- /path/to/espx mcpCodex works the same way. For Cursor, VS Code and Claude Desktop, espx writes the config file.
Allow
espx.toml[agent]boards = ["58:8C:81:53:AD:2C"]erase = falseflash = trueA flash, erase or reset outside this table fails with
policy_denied. Your own commands are never checked.Log
~/.espx/agent.log{"ts":1762862645123,"client":"claude-code", "tool":"flash", "board":"58:8C:81:53:AD:2C", "outcome":"ok", "ms":8400} Every tool call an agent makes, with its board, outcome and duration.
espx logprints them.
Toolchain
Every download is verified against a SHA-256. Nothing goes in ~/.cargo/bin, and espx uninstall removes it.
- 1SHA-256 checked must match to go in
- 2espflash 4.5.0
- 3cargo-espflash 4.5.0
- 4esp-generate 1.3.0
- 5~/.espx/bin
Supported chips
The ESP32, S2 and S3 need Espressif's Rust fork. The other six build on stable Rust.
- Architecture
- RISC-V
- Rust target
riscv32imac-unknown-none-elf- Toolchain
stable, Rust stable, with the target added by rustup- Status
- Stable
$ espx setup # adds the RISC-V targets$ espx new blinky --chip esp32c6$ cd blinky$ espx runEvery chip esp-generate can scaffold. The ESP32-P4 joins when esp-generate supports it.
Questions
Anything else is in the docs.
The ones esp-generate can scaffold: ESP32, ESP32-S2 and ESP32-S3 on Xtensa, and ESP32-C2, C3, C5, C6, C61 and H2 on RISC-V. espx chips marks the C5 and C61 as preview. The ESP32-P4 joins when esp-generate supports it.
No. An espx project is bare-metal Rust on esp-hal (no_std), generated from esp-generate's template, and nothing espx installs is part of ESP-IDF.
A Mac with Apple silicon, or Linux on arm64 or x86_64, and rustup with a stable toolchain. espx installs everything else.
espx setup puts pinned builds of espflash, cargo-espflash and esp-generate in ~/.espx/bin, each checked against a checksum before it lands on disk, and adds rust-src and the RISC-V targets through rustup. None of it goes in ~/.cargo/bin. ESPX_HOME moves ~/.espx, and espx uninstall --all takes it back out.
Those Xtensa chips need Espressif's fork of the Rust compiler, about 1 GB, which rustup can't install. espx fetches espup for it only when you ask for one of them, as in espx setup --chip esp32s3. It sources ~/.espx/export-esp.sh before every cargo call, so espx build needs no shell setup.
curl -fsSL https://www.espx.dev/install.sh | sh installs the espx command, and sh -s -- --desktop installs the app with the command inside it. The download page has the disk image and every file with its SHA-256 and minisign signature. The app updates itself, and espx update updates the command. You can also build both from source with cargo.
Yes, if the project allows it. espx mcp install --client claude-code connects espx's MCP server to Claude Code, and Cursor, Codex, VS Code and Claude Desktop work the same way. The [agent] table in espx.toml lists the boards an agent may flash, erase or reset, and ~/.espx/agent.log records every call.
Yes, with the board plugged into the runner. espx run --until ready --timeout 2m exits 0 on a match, 1 when a --reject pattern matches and 124 when time runs out, and --json prints one event per line. espx replay runs the same checks on a recording, without a board.