A small desk gadget built on an ESP32-C3 with a small color TFT screen that shows your Claude subscription usage in real time. It reads straight from the Anthropic API response headers, the same numbers behind the usage page on claude.ai.
The claude.ai usage page is fine, but you have to go look at it. I wanted a thing on my desk that just shows me at a glance how much headroom I have left. So now there is a little screen that updates every minute and I never have to think about it.
- ESP32-C3 Mini (also sold as ESP32-C3 SuperMini)
- 1.8" ST7735S TFT, 128x160 pixels, SPI (e.g. the LCDWiki MSP1803 module). 4-wire SPI, write-only — no MISO/touch needed. The firmware rotates it into landscape (160x128) on boot.
- Six jumper wires
- USB-C cable
- A Claude subscription (Pro, Max, Team, or Enterprise)
The module's 8 pins, left to right as silkscreened on most of these boards: VCC GND CS RESET A0 SDA SCK LED. A0 is the DC (data/command) line, SDA is MOSI (the module has no MISO — it never talks back to the ESP32).
| TFT pin | ESP32-C3 pin | Wire color |
|---|---|---|
| GND | GND | cinza |
| VCC | 3.3V (not 5V — the ESP32's GPIOs are not 5V tolerant, and this module's logic follows its VCC) | vermelho |
| CS | GPIO 10 | azul |
| RESET | GPIO 4 | marrom |
| A0 (DC) | GPIO 3 | roxo |
| SDA (MOSI) | GPIO 7 | verde |
| SCK | GPIO 6 | amarelo |
| LED | 3.3V | laranja |
Colors above are arbitrary — they just match the specific spool of jumper wires used for this build (wiring diagram), not any electrical standard.
LED is the backlight, active-high — wiring it straight to 3.3V just leaves it always on, which is fine for a 24/7 desk gadget. These GPIOs were picked to avoid the ESP32-C3's boot-strapping pins (2, 8, 9) and the BOOT button (GPIO 9), so nothing here interferes with flashing or booting.
The device authenticates using a Claude Code OAuth token. To get one:
- Install Claude Code if you do not have it yet:
npm install -g @anthropic-ai/claude-code - Run
claude setup-tokenin a terminal - Log in through the browser when it opens
- Copy the token it prints to your terminal
The token starts with sk-ant-oat01- and stays valid for a year. Copy it the moment you see it because it is not saved anywhere you can retrieve it from later.
Requires a Claude Pro, Max, Team, or Enterprise subscription.
Two options. Pick whichever suits you.
- Flash the firmware (see the Flashing section below)
- On first boot the device creates a WiFi network called
ClaudeMonitor. Connect to it from your phone or laptop. - Open
192.168.4.1in your browser - Enter your home WiFi name and password, then paste your token
- Hit save. The device reboots, connects to your network, and starts showing usage.
The config page stays available at the device IP address shown in the footer of the screen, so you can always go back and change things.
Open src/main.cpp and fill in the values near the top:
#define WIFI_SSID "MyHomeNetwork"
#define WIFI_PASSWORD "supersecret"
#define OAUTH_TOKEN "sk-ant-oat01-..."
#define OAUTH_CLIENT_ID "9d1c250a-..."The WiFi pair is what matters for skipping the portal. If both SSID and password are set, the device connects straight to your network on boot. The token can be left blank and added later through the browser. If the screen says "Add your token" after connecting, that is what it is waiting for.
To get the OAUTH_CLIENT_ID, run this in a terminal on any machine that has Claude Code installed:
node -e "
const fs = require('fs');
const b = fs.readFileSync(require('which').sync('claude'));
const m = b.toString('latin1').match(/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/g);
const id = m && m.find(x => x.startsWith('9d1c'));
console.log(id || 'not found');
"This is the Claude Code application ID, not yours. Every installation has the same value. It is not a secret, but Anthropic can change it between versions so extracting it from your own binary keeps things in sync. The build will refuse to compile if you leave it blank.
Leave the WiFi fields blank and it falls back to the portal, so both options work from the same build.
Warning
If the token is hardcoded it is stored as plain text in the firmware binary. Fine for your own device, but do not share the compiled binary with anyone else.
This is a PlatformIO project. Open it in VS Code with the PlatformIO extension, or run these commands from a terminal:
platformio.exe run --target erase # wipe the flash clean
platformio.exe run --target uploadfs # upload the config web page
platformio.exe run --target upload # upload the firmware
platformio.exe device monitor # watch serial output (optional)
The erase step is worth doing the first time, or any time the device behaves unexpectedly. The device stores its settings in flash, and a leftover file from a previous flash can quietly override your compile-time credentials. If it keeps booting into setup mode when it should not, erase and reflash.
Run uploadfs at least once so the config page lands on the device. After that, upload is enough unless you change the page.
If the upload fails with a chip mismatch, check your board is set to esp32-c3-devkitm-1 in PlatformIO.
The screen is landscape (160x128) and cycles automatically through a set of pages. You can also flip manually with the BOOT button on the board.
192.168.1.42 30s .ıl
Current session
────────────────────────────
16%
────────────────────────────
[███████████░░░░░░░░░░░░░░░]
Resets in 1h 21m
The top row is always the same regardless of page: device IP on the left, poll countdown / last-poll status / WiFi signal on the right. Below that, a bar page shows the metric name, a large percentage (the thing you actually glance at from across the room), the progress bar, and the reset countdown.
The percentage and bar are color-coded by how much headroom is left:
- green — under 50%
- yellow — 50–79%
- red — 80% and up
Two bar pages are always on:
- Current session (the 5 hour rolling window)
- Weekly, all models (7 day window)
If your plan does not return a usage percentage for a window, the bar shows the reset time instead so you still get something useful. A clock page (with the current time and date) is also in the rotation.
While the device is polling the API, it shows a small pixel-art nod to the Claude Code mascot blinking its eyes — the one moment it's actually "thinking".
Clauled also watches the recent usage trend in RAM. Once it has at least eight samples spanning ten minutes, it estimates when each available usage window will reach 100%. If a window is predicted to fill within 30 minutes and before its API-provided reset time, a pulsing Risk alert card is added to the page carousel and the configuration page explains the projected deadline.
The history is intentionally cleared on reboot to avoid frequent flash writes.
The live status endpoint now includes risk and forecasts objects for local
tools that want to consume the same prediction.
Once the device is on your network, open its IP address in any browser. The page is mobile-friendly and works from your phone.
At the top of the page you see live usage for both windows. Current session on the left, weekly all models on the right. The percentage and bar update every time you hit Refresh or Poll now. The reset time shows how long until that window resets.
Enter your home network name and password here. The SSID field pre-fills with the currently saved network. Leave the password blank to keep the existing one. Saving a new WiFi network reboots the device immediately.
Paste your sk-ant-oat01- token in the access token field. Once saved, the field shows "access token saved" in green so you can confirm it landed. Leave it blank when saving other settings to keep the existing token. The refresh token is optional and only needed if you want the device to renew the access token automatically when it expires.
Poll interval controls how often the device calls the API. Every 60 seconds is the default and costs one minimal API call per minute. Page cycle time controls how quickly the screen flips between pages. Set it to Manual if you prefer to use the BOOT button yourself.
Weekly Sonnet only is for Max plan users who have a separate Sonnet bucket. Device uptime shows how long the device has been running since its last boot. Both are off by default.
Poll now fires an immediate API call and refreshes the usage display. Refresh reloads the status from the device without polling the API again. Save settings writes your changes to flash. Factory reset wipes everything and returns the device to first-boot setup mode.
Screen stays blank. Double-check the wiring table above, especially CS/RESET/A0 (DC) — those three are the ones most often swapped since the module's silkscreen order doesn't match a logical left-to-right pin function order. Confirm VCC is 3.3V, not 5V.
Colors look inverted, shifted, or offset by a few pixels from the edge. Common with these clone ST7735 boards — the panel needs a slightly different init sequence ("tab color") than the one this firmware assumes. In src/main.cpp, find tft.initR(INITR_BLACKTAB) in setup() and try INITR_GREENTAB or INITR_REDTAB instead, re-flash, and see which one renders correctly.
Landscape orientation is mirrored or rotated the wrong way. tft.setRotation(1) in setup() picked a direction that worked for this build, but clone boards aren't all consistent about which way is "clockwise". Try tft.setRotation(3) instead and re-flash.
Keeps going back to setup mode. Both SSID and password need to be present. Check the serial monitor on boot for [cfg] configured=yes. If it says no, the WiFi details are not sticking. Run the erase step first and try again.
Screen says "Add your token". WiFi is working but no token is set. Open the device IP in a browser and paste your sk-ant-oat01- token into the config page.
Bars show dashes instead of numbers. The token is not working. Open the serial monitor and look for [poll] 5h=.. 7d=... If you see 401 errors, the token is wrong or expired and needs replacing.
Reset countdown shows dashes. The device syncs time over NTP when it connects to WiFi. If your network blocks NTP the countdown will not work, but the bars still show whatever data the API returns.
Save button spins forever when entering WiFi credentials. When you save new WiFi details from the setup portal, the device reboots and joins your home network. The ClaudeMonitor network disappears with it, so the browser never gets a response back. The page handles this and shows "Saved, reconnecting". Reconnect your device to your home WiFi and open the IP shown on the screen.
This reads Anthropic's own usage headers from normal API responses. No scraping, no reverse engineering, no workarounds. Just numbers Anthropic already sends back on every request, shown somewhere more convenient.
MIT.





