Monitor and replay

Serial output with decoded defmt logs, named boot reasons and symbolized panics. Record it, play it back.

espx monitor shows a board's serial output. espx run and espx flash --monitor end in the same monitor. It works inside a project and outside one; inside, the project's release binary lets espx decode logs and resolve panics.

Two monitors

espx has two ways to watch a board, and picks one for you.

espflash's monitorespx's own monitor
WhenIn a terminal, with none of the flags belowWith --until, --expect, --reject, --timeout, --json, --record or --input, when stdin is not a terminal, or when another espx process holds the board
What runsespflash monitor with --elf and --log-format defmt as the project calls forespx reads the port itself
KeysCtrl+R resets, Ctrl+C exitsCtrl+C exits
Outputespflash'sTyped events: lines, boots, panics, exceptions, resets, plugs and unplugs

The interactive path keeps espflash's key handling, which espx's monitor does not have. Everything else on this page is about espx's own monitor. Arguments after -- are for espflash monitor, so combining them with the flags above stops with monitor_extra_args.

espx monitor                         # espflash monitor in your terminal
espx monitor --timeout 10s           # espx reads the port for 10 seconds
espx monitor | grep -i error         # piped, so espx reads the port
espx monitor --dir ~/code/blinky     # decode against that project's binary

Speed

The monitor reads at 115200 baud. MONITOR_BAUD changes the default and --baud overrides both. A board on native USB ignores the setting.

What espx's monitor does

Resets the board

Opening the port resets the board through the serial lines, so the log starts at boot. The first event is reset.

Names each boot

Every ROM banner line such as rst:0x15 (USB_UART_HPSYS),boot:0x6c (SPI_FAST_FLASH_BOOT) becomes a boot event with a reason and the ROM's code:

reasonPrinted as
power_onpower-on
softwaresoftware reset
watchdogwatchdog reset
brownoutbrownout reset
panicreset after a panic (a software reset right after a panic or exception)
deep_sleepwake from deep sleep
externalexternal reset
usbreset over USB
unknownunknown reset

The codes mean different things on the original ESP32 and on the later chips; espx reads the banner to tell which. The reset espx does when it opens a native-USB port shows as usb. In the terminal a watchdog, panic or brownout boot prints as a warning:

warn: board booted: watchdog reset (rst:0x7 (TG0_WDT_HPSYS),boot:0x6c (SPI_FAST_FLASH_BOOT))

Decodes defmt

When the project has defmt in [dependencies] and a release binary in target/, espx cuts esp-println's defmt frames out of the byte stream and decodes them against the binary. Each decoded line carries its level, module and location:

[INFO ] Hello world! (src/bin/main.rs:39)

Without the binary, the bytes pass through unchanged and one warning event says so. A defmt board monitored without --dir pointing at its project shows the boot log, then raw frame bytes. Build first and pass --dir.

Resolves panics

A panic printed by esp-backtrace becomes a panic event with the message and one frame per address, resolved against the release binary to a function, file and line. Without the binary the frames carry only addresses.

esp-generate's template has no esp-backtrace unless you ask for it, and its default panic handler prints no backtrace. Create the project with espx new <name> -o esp-backtrace to get symbolized panics.

Reports CPU exceptions

A CPU exception (a load from an invalid address, an illegal instruction) is an exception event instead, with its kind, the faulting address when the kind has one, and the faulting instruction as the first frame.

Follows the board

When the board drops off USB (deep sleep, a replug, a flash from another process), the monitor reports detached, waits, then reports reconnected and keeps going. It follows the board by its USB serial number, so a new port path does not lose it.

An ESP32-C6 on native USB loses its USB port in deep sleep and starts printing before the host has enumerated it again, so the first half second or so after a wake is lost. In a tight deep-sleep loop the monitor sees only resets. A board behind a USB serial bridge keeps its port through sleep.

Talk to the board

espx monitor --input

--input sends each line you type on stdin to the board, followed by a newline. The board's output keeps printing as before.

Record and replay

espx monitor --record crash.jsonl --until 'PANIC' --timeout 5m
espx replay crash.jsonl
espx replay crash.jsonl --fast --json

--record PATH writes every event, plus the raw bytes as bytes events in hex, to a newline-delimited JSON file. espx replay PATH plays it back with its original timing, or as fast as it can with --fast. Replay sends the events through the same code path as a live board, so --until, --expect, --reject, --timeout, --json and the exit codes behave as they do on monitor.

Replay does not decode again, so it needs no ELF: a defmt recording plays back decoded on any machine. That makes a recording a bug report, a test fixture and a demo in one file. The MCP server plays the same files through monitor_start with replay, and the desktop app records and plays them from its Recordings page.

Sharing a board

A serial port can be open in only one process at a time. espx shares boards between its own processes through a board hub on a Unix socket, ~/.espx/hub.sock, readable and writable by your user only.

  • A running espx monitor, espx run or espx flash --monitor serves the hub while it runs, when nothing else does. So do espx mcp and the desktop app.
  • Every other espx command that needs a board goes through the process that serves it, and says so:
=> espx monitor holds the boards; going through it

What that allows:

  • espx flash in a second terminal flashes the board a monitor is watching, and the monitor shows the new boot.
  • A second espx monitor reads the same stream as the first.
  • espx erase, espx info and espx devices --probe borrow the board for espflash and give it back.
  • An agent and a terminal, or two agents, share one board.

While a board is in use, espx devices --probe and agents see who holds it, for example espx flash or espx mcp (claude-code). When the serving process quits, the next command opens the board itself again.