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 monitor | espx's own monitor | |
|---|---|---|
| When | In a terminal, with none of the flags below | With --until, --expect, --reject, --timeout, --json, --record or --input, when stdin is not a terminal, or when another espx process holds the board |
| What runs | espflash monitor with --elf and --log-format defmt as the project calls for | espx reads the port itself |
| Keys | Ctrl+R resets, Ctrl+C exits | Ctrl+C exits |
| Output | espflash's | Typed 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.
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:
reason | Printed as |
|---|---|
power_on | power-on |
software | software reset |
watchdog | watchdog reset |
brownout | brownout reset |
panic | reset after a panic (a software reset right after a panic or exception) |
deep_sleep | wake from deep sleep |
external | external reset |
usb | reset over USB |
unknown | unknown 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:
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:
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.
Talk to the board
--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
--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 runorespx flash --monitorserves the hub while it runs, when nothing else does. So doespx mcpand the desktop app. - Every other espx command that needs a board goes through the process that serves it, and says so:
What that allows:
espx flashin a second terminal flashes the board a monitor is watching, and the monitor shows the new boot.- A second
espx monitorreads the same stream as the first. espx erase,espx infoandespx devices --probeborrow 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.
Related
- Scripts and CI:
--until,--expect,--rejectand--timeout. - JSON events: every event the monitor emits.
espx monitorin the command reference