See what
your agents
are doing.

A physical dashboard for Claude Code and Codex. Lives next to your keyboard. Streams the current session, the running tool, the token spend. Tap to approve or deny.

§ Quickstart — 30 seconds

Clone, build, flash,
and the dashboard
knows what you're doing.

The toolkit is esp-harness. The protocol is one-line JSON. The bridge is a single Python file. No cloud, no telemetry, no daemon you can't read.

~/code
# 1. toolkit (esp-harness — provides build / flash / console) $ git clone github.com/Caldis/esp-harness $ pip install -e esp-harness/tools/esp-harness/ # 2. this repo $ git clone github.com/Caldis/esp32-agent-dashboard $ cd esp32-agent-dashboard # 3. build + flash (Waveshare ESP32-S3-Touch-AMOLED-2.16) $ esp-harness build --project . build OK in 18.2s · 1.6 MB $ esp-harness flash --project . --port COM9 flashed · device alive # 4. wake the bridge — listens for Claude Code hooks $ python tools/claude_buddy_bridge.py serve --serial-port COM9 [bridge] serving on 127.0.0.1:7321 | dry_run=False | serial=COM9 [bridge] device alive · console v1.7.5 · scenes=5 # …now start a Claude Code session. The panel wakes. EVT: permission id=req_abc decision=once
§ Five scenes

One panel,
five states,
zero context switch.

▸ when quiet

idle

Gentle "zZz" pulse. No agent has emitted an event in 60 s.

▸ when running

sessions

One row per agent. Status pip, cwd, last 3-5 transcript entries. Totals header.

▸ when blocked

prompt

Tool name, command preview, BOOT to approve, USER to deny. 60 s timeout.

▸ on demand

tokens

Cumulative + today's spend. 24 h sparkline. Per-agent breakdown.

▸ on demand

status

Battery, heap, uptime, build info. The "is the box healthy" view.

§ Architecture

Hook → bridge
→ wire → panel,
and back again.

▸ Host side

Hooks & the bridge

Claude Code's hook system calls hook_dispatch.py on every PreToolUse / PostToolUse / Stop. Codex's JSONL stream is tee'd through codex_wrapper.py. Both forward to the long-running claude_buddy_bridge.py which aggregates per-agent state and throttles pushes to ≤ 1 / 250 ms. Read the host integration doc →

▸ Wire

One verb per state class

dash snapshot for periodic state, dash prompt when blocked on approval, dash event for transient transcript lines, dash tokens for spend, dash idle to sleep. Replies are OK: / ERR: / EVT: framed by esp-harness's console_protocol. v1 over USB-Serial. Read the protocol spec →

▸ Device side

LVGL on a 466 round AMOLED

Firmware built on the esp-harness scene framework (LVGL 9.x, ESP-IDF v6.0+). Five scenes registered through harness_scene_register. BOOT & USER buttons routed into EVT: permission id=… decision=… events back to the bridge.

§ Where this sits

What other tools
give you, vs what
this adds.

Project Transport Agents Hardware Approval button
claude-desktop-buddy BLE (NUS) Claude Desktop (single) nRF reference / generic BLE — display only
claude-dashboard plugin in-app status line Claude Code none (lives in your terminal) — no hardware
esp32-agent-dashboard (this repo) USB-Serial → BLE → WiFi Claude Code + Codex (multi-agent) Waveshare ESP32-S3-Touch-AMOLED-2.16 ✓ BOOT / USER + 60s timeout
§ Roadmap

Three milestones,
each adding
a transport.

✓ shipped

v0.1 — USB-Serial

Multi-agent dashboard over USB-Serial. Claude Code hooks + Codex wrapper, five scenes, physical approval button, TCP mock for CI.

⊕ in design

v1.0 — BLE NUS

Nordic UART Service for tetherless Claude Desktop pairing. Same wire format, just framed over BLE characteristics. No USB cable on your desk.

○ sketched

v2.0 — WiFi push

Headless dev box on a server, dashboard on your desk. mDNS discovery, TLS to localhost relay, USB fallback when the network's down.

§ Try it

Open the repo,
read the 30-second quickstart.