Browse by type

A small ESP32 dashboard I made for my desk to keep an eye on Claude Code usage.
It runs on a Waveshare ESP32-S3-Touch-AMOLED-2.16 as well as a few other alternative boards and pairs over Bluetooth, the splash screen plays pixel-art Clawd animations that get busier when your usage rate climbs. The two side buttons send Space and Shift+Tab over BLE HID for Claude Code's voice mode and mode-toggle shortcuts.
The device boots into the splash. Tap the screen anywhere to switch to the Usage view; tap again to flip back to the splash.
| Splash | Usage |
|---|---|
![]() |
![]() |
| Splash; touch-toggle anytime | Session and weekly utilization |
While the splash is up, the middle (PWR) button cycles animations. Hold the power button for 3 seconds, then release, to put the device into pairing mode — this clears the saved Bluetooth bond and re-advertises. The firmware also auto-rotates animations every 20 s within the current usage-rate group, so a long stretch on the splash isn't just one Clawd on loop.
Boards supported out of the box:
Please check if a pull request exists for your alternative hardware port before opening a new one, providing QA feedback and testing on the same hardware is more valuable than duplicate pull requests.
Porting to another board: the firmware is a thin HAL with per-board folders under firmware/src/boards/. Drop in a new folder and a new PlatformIO env — main.cpp, ui.cpp, and splash.cpp never need to change. See docs/porting/adding-a-board.md for the walk-through and docs/porting/hal-contract.md for the interfaces a port must implement.
curl, bluetoothctl, busctl, dbus-monitor (BlueZ Bluetooth stack), python3, setsid, stdbuf (util-linux / coreutils), systemctl (systemd user services)python3 (the installer sets up a venv with bleak and httpx)python3 3.11+ (the installer sets up a venv with bleak, httpx, and pystray)The macOS host pieces — Python daemon, LaunchAgent, and flash helper — were ported by Chris Davidson (@lorddavidson). Thanks Chris!
./flash-mac.sh waveshare_amoled_216 # ESP32-S3 2.16" (auto-detects /dev/cu.usbmodem*)
./flash-mac.sh waveshare_amoled_216_c6 # ESP32-C6 2.16" variant
./flash-mac.sh waveshare_amoled_18 /dev/cu.usbmodem1101 # ESP32-S3 1.8" (or pass an explicit USB serial port)
The board env name is required. Run ./flash-mac.sh with no args to see the available envs (scraped from firmware/platformio.ini).
After flashing, open System Settings → Bluetooth and click Connect next to "Clawdmeter". The daemon only ever connects to the peripheral this Mac is paired/connected to — it never scans for a nearby device — so once it's connected here the daemon picks it up on its next poll (~60 s).
The daemon reads your Claude OAuth token from the macOS Keychain (service Claude Code-credentials), polls usage every 60 s, and pushes it to the display over BLE.
./install-mac.sh
The installer creates a Python venv in daemon/.venv/, installs bleak and httpx, renders a LaunchAgent into ~/Library/LaunchAgents/com.user.claude-usage-daemon.plist, and loads it. The first run is launched interactively so macOS prompts for Bluetooth permission.
Useful commands:
launchctl list | grep claude-usage # check it's running
tail -F ~/Library/Logs/claude-usage-daemon.out.log # live logs
launchctl unload ~/Library/LaunchAgents/com.user.claude-usage-daemon.plist # stop
launchctl load -w ~/Library/LaunchAgents/com.user.claude-usage-daemon.plist # start
./flash.sh waveshare_amoled_216 # ESP32-S3 2.16" (defaults to /dev/ttyACM0)
./flash.sh waveshare_amoled_216_c6 # ESP32-C6 2.16" variant
./flash.sh waveshare_amoled_18 /dev/ttyACM1 # ESP32-S3 1.8" (or pass an explicit USB serial port)
The board env name is required. Run ./flash.sh with no args to see the available envs (scraped from firmware/platformio.ini).
After flashing, the device advertises as "Clawdmeter". Pair it once:
# Scan for the device
bluetoothctl scan le
# When "Clawdmeter" appears, pair and trust it
bluetoothctl pair F4:12:FA:C0:8F:E5 # use your device's MAC
bluetoothctl trust F4:12:FA:C0:8F:E5
To re-pair later, hold the power button for 3 seconds then release — the device clears its saved bond and re-advertises.
The daemon polls your Claude usage every 60 seconds and sends it to the display over BLE.
./install.sh
systemctl --user start claude-usage-daemon
Check status: systemctl --user status claude-usage-daemon
View logs: journalctl --user -u claude-usage-daemon -f
To change the poll interval, set poll_interval = <seconds> in ~/.config/claude-usage-monitor/config (see daemon/config.example). The daemon picks it up without a restart. Polling slower than ~80s is fine: between polls the daemon replays the last payload every heartbeat_interval seconds (default 60) with the reset countdowns aged, so the firmware's 90s freshness window never lapses and no reflash is needed.
Runs natively on Windows — no WSL required. A system-tray app polls your usage and pushes it over BLE, and starts automatically at login.
claude login completed. The token is read from %USERPROFILE%\.claude\.credentials.json (falling back to %LOCALAPPDATA%\Claude\ then %APPDATA%\Claude\).%USERPROFILE%\Clawdmeter), not a \\wsl$ share — the installer refuses a WSL path.pio run -d firmware -e waveshare_amoled_216 -t upload --upload-port COM5 # use your device's COM port
Run pio run -d firmware with no env to see the available board envs.
The device is a bonded BLE HID keyboard, so pair it once: Settings → Bluetooth & devices → Add device → Bluetooth, then select "Clawdmeter". Pairing is required — it enables the physical buttons and keeps a persistent connection (the device keeps showing your last-synced usage even after the daemon quits). To undo, use Remove device (this disables the buttons).
From the repo root in PowerShell:
powershell -ExecutionPolicy Bypass -File install-windows.ps1
This creates a venv, installs bleak/httpx/pystray/Pillow from the in-repo requirements (no internet downloads), registers a per-user login-autostart entry (HKCU\…\Run, no admin needed), and launches the tray app headlessly (no console window).
python -m venv .venv
.venv\Scripts\Activate.ps1 # if blocked: Set-ExecutionPolicy -Scope CurrentUser RemoteSigned, then retry
pip install -r daemon\requirements-windows.txt
python daemon\claude_usage_daemon_windows.py # runs in the foreground; Ctrl+C to stop
The icon's corner bubble shows state — green Connected, amber Scanning, red Error — and hovering shows the status (Connected · last update HH:MM). A notification fires once when it enters Error (e.g. an expired token). Right-click for the menu:
Get-Content $env:LOCALAPPDATA\Clawdmeter\daemon.log -Tail 30 # view logs
reg delete "HKCU\Software\Microsoft\Windows\CurrentVersion\Run" /v Clawdmeter /f # remove autostart
| Symptom | Fix |
|---|---|
Device not found |
Power on the device; make sure it's in range and paired. |
token expired toast / API HTTP 401 |
Re-run claude login, then restart the daemon. |
Connection failed |
Toggle Windows Bluetooth off/on in Settings. |
Warning: running under Linux/WSL |
Run from a native PowerShell window, not a WSL shell. |

Claude Code-credentials) on macOS, or from ~/.claude/.credentials.json on Linux (%USERPROFILE%\.claude\.credentials.json on Windows).api.anthropic.com/v1/messages — one token of Haiku, basically free.anthropic-ratelimit-unified-5h-utilization and friends).The board has three side buttons. Left and right send HID keys; the middle (PWR) button cycles splash animations and, held for 3 seconds, triggers pairing mode.
| Button | GPIO | Function |
|---|---|---|
| Left | GPIO 0 | Hold to send Space (Claude Code voice-mode push-to-talk) |
| Middle (PWR) | AXP2101 PKEY | On splash: cycle animations. Hold 3s + release: pairing mode |
| Right | GPIO 18 | Press to send Shift+Tab (Claude Code mode toggle) |
Space and Shift+Tab go out as standard BLE HID keyboard reports, so they trigger in whatever window has focus on the paired host — not just Claude Code.
The device advertises a custom GATT service alongside the standard HID keyboard service:
| UUID | |
|---|---|
| Data Service | 4c41555a-4465-7669-6365-000000000001 |
| RX Characteristic (write) | 4c41555a-4465-7669-6365-000000000002 |
| TX Characteristic (notify) | 4c41555a-4465-7669-6365-000000000003 |
| HID Service | 00001812-0000-1000-8000-00805f9b34fb |
JSON payload format (written to RX):
{ "s": 45, "sr": 120, "w": 28, "wr": 7200, "st": "allowed", "ok": true }
Fields: s = session %, sr = session reset (minutes), w = weekly %, wr = weekly reset (minutes), st = status, ok = success flag.

pio run -d firmware -e
sim, then cd firmware && .pio/build/sim/program). See
SIM-USAGE.md for controls, scenarios, and headless
screenshots.research/clawd-official/;
node tools/convert_official_clawd.js regenerates
firmware/src/splash_animations.h. See tools/README.md.tools/png_to_lvgl.js. See tools/README.md.docs/fonts.md.docs/porting/adding-a-board.md
and docs/porting/hal-contract.md.$ claude mcp add Clawdmeter \
-- python -m otcore.mcp_server <graph>