Dashboard físico de mesa que mostra, em tempo real, o uso do Claude Code.
O Craudiômetro exibe a sessão de 5 horas e a janela semanal do Claude Code num ESP32 com display colorido, navegado por um encoder rotativo. O mascote do projeto é o Craudinho, releitura pixel-art do mascote do Claude Code que anima conforme a intensidade de uso — e dorme quando o daemon está desligado.
Inspirado conceitualmente no Clawdmeter. Nenhum código ou asset daquele projeto foi copiado (ele não possui licença open-source): protocolo, firmware, daemon e arte foram criados do zero para este repositório.
| Dashboard | Craudinho | Configurações | Menu |
|---|---|---|---|
![]() |
![]() |
![]() |
![]() |
Capturas reais do dispositivo, tiradas pela serial com tools/screenshot.py
(ver Screenshots do dispositivo).
%%{init: {'theme':'base','themeVariables':{'primaryColor':'#E3F2FD','primaryTextColor':'#0D47A1','primaryBorderColor':'#1976D2','lineColor':'#546E7A'}}}%%
flowchart LR
accTitle: Arquitetura do Craudiômetro
accDescr: Daemon Python no macOS lê o Keychain e a API da Anthropic e envia o uso por BLE ao ESP32, que renderiza no display e recebe input do encoder.
classDef host fill:#FFF3E0,stroke:#EF6C00
classDef device fill:#E3F2FD,stroke:#1976D2
subgraph MAC["macOS"]
KC["Keychain<br/>token OAuth do Claude Code"]:::host
API["api.anthropic.com<br/>/api/oauth/usage"]:::host
DAEMON["daemon Python<br/>bleak + httpx"]:::host
end
subgraph ESP["ESP32 DevKit"]
BLE["GATT server<br/>NimBLE"]:::device
UI["LVGL 9<br/>ScreenManager"]:::device
DISP["GMT020-02<br/>ST7789V 240x320"]:::device
ENC["KY-040<br/>encoder"]:::device
end
KC --> DAEMON
API --> DAEMON
DAEMON -->|"JSON a cada 60 s"| BLE
BLE --> UI
ENC --> UI
UI --> DISP
| Componente | Modelo |
|---|---|
| Microcontrolador | ESP32 DevKit (WROOM-32) |
| Display | GMT020-02-7P ver 1.3 (IPS 2.0", ST7789V, 240x320, SPI) |
| Encoder | KY-040 (rotativo com botão) |
| Display | ESP32 | Função |
|---|---|---|
| VCC | 3V3 | Alimentação |
| GND | GND | Terra |
| SCL | GPIO 18 | SPI clock |
| SDA | GPIO 23 | SPI data (MOSI) |
| RST | GPIO 4 | Reset |
| DC | GPIO 2 | Data/Command |
| CS | GPIO 5 | Chip select |
Se as cores aparecerem em negativo, troque CRAUDIO_DISPLAY_INVERT para 0
em firmware/platformio.ini (há variações entre lotes do painel).
| Encoder | ESP32 | Função |
|---|---|---|
| CLK | GPIO 32 | Quadratura A |
| DT | GPIO 33 | Quadratura B |
| SW | GPIO 25 | Botão |
| + | 3V3 | Pull-ups do módulo |
| GND | GND | Terra |
| Ação | Efeito |
|---|---|
| Girar | Move o foco entre widgets/itens |
| Clique curto | Seleciona / confirma |
| Clique longo (0,8 s) | Abre o menu global; dentro do menu, volta |
Telas da v1: dashboard (sessão 5h + semana com countdowns), craudio (mascote
animado), configurações (timeout de tela, tema, reset de pareamento BLE) e o
menu global — gerado automaticamente do registro de telas em
firmware/src/main.cpp.
Requisitos: PlatformIO (brew install platformio).
cd firmware
pio run # compila
pio run -t upload # flasha via USB
pio device monitor # log serial (115200)Stack: Arduino + LVGL 9 + LovyanGFX (ST7789 com DMA) + NimBLE + ArduinoJson.
O daemon lê o token OAuth do Claude Code no Keychain (service
Claude Code-credentials), consulta GET /api/oauth/usage (sem custo de
tokens) e grava o resultado no dispositivo via BLE a cada 60 segundos.
cd daemon
./install.sh # bundle + venv em ~/.craudiometro + LaunchAgent (inicia no login)
./uninstall.sh # remove tudoO instalador precisa de um Python com binário real (o stub do Xcode não cria
venv com --copies). Se o python3 padrão falhar, indique outro:
PYTHON=/opt/homebrew/opt/python@3.13/bin/python3.13 ./install.sh-
Logs:
~/Library/Logs/craudiometro.log -
O daemon roda dentro de um bundle
Craudiometro.appmínimo: sem oNSBluetoothAlwaysUsageDescriptiondo Info.plist, o macOS aborta processos do launchd que tocam Bluetooth sem nem exibir o prompt de permissão. -
Na primeira execução, aprove o prompt de Bluetooth do "Craudiômetro". Se negar por engano, resete a decisão e reinicie o daemon:
tccutil reset BluetoothAlways com.craudiometro.daemon launchctl kickstart -k gui/$(id -u)/com.craudiometro.daemon -
Se o token do Keychain expirar, o daemon sinaliza falha ao dispositivo e se recupera sozinho na próxima vez que o Claude Code renovar o token.
Service 6a485858-14a2-4918-9841-d3c2d2474fbc, characteristic de escrita
23aefc82-8cba-470b-ba8f-d1c54f9f0656, nome de advertising craudiometro.
Payload JSON:
| Campo | Significado |
|---|---|
s |
Utilização da sessão 5h (0-100) |
sr |
Minutos até o reset da sessão |
w |
Utilização da semana (0-100) |
wr |
Minutos até o reset semanal |
st |
Status (allowed quando tudo normal) |
ok |
Coleta na API bem-sucedida |
pl |
Nome do plano da conta (ex.: Max) |
tm |
Minutos desde a meia-noite no fuso do Mac (base do relógio) |
Releitura pixel-art do mascote do Claude Code (Clawd, da Anthropic), desenhada do zero por script — nenhum arquivo de asset de terceiros é usado. O mascote original pertence à Anthropic; esta releitura destina-se a uso pessoal no dispositivo.
python3 tools/gen_craudio_sprites.py # desenha os PNGs em assets/craudio/
python3 tools/png2lvgl.py # converte para arrays C do LVGL (I4)Estados: dormindo (sem daemon), tranquilo (< 30%), trabalhando (30-70%) e frenético (> 70%), usando a maior utilização entre sessão e semana. O mesmo desenho, em 32x32, é o ícone do header da tela inicial.
O firmware aceita comandos pela Serial: shot transmite a tela renderizada
(protocolo SNAP, RGB565) e screen <nome> troca de tela. O script remonta os
PNGs — pixels reais do display, com dados reais:
python3 -m venv .venv && .venv/bin/pip install pyserial pillow # uma vez
.venv/bin/python tools/screenshot.py --all --out-dir docs/screenshotsAbrir a porta serial reseta o ESP32 (o driver do CH340 aciona o DTR); o script espera o boot e o primeiro payload BLE antes de capturar, e mescla ciclos de refresh até cobrir a tela inteira.
- Crie uma classe que herda de
Screenemfirmware/src/screens/. - Implemente
name(),create()e, se precisar de dados,onStateChanged(). - Registre a instância com
manager.registerScreen(...)emmain.cpp.
O menu global lista a tela automaticamente.
Contribuições são bem-vindas: issues, correções, telas novas e melhorias na arte. Para mudanças grandes, abra uma issue antes do PR para alinhar a ideia.
| Área | Onde | Stack |
|---|---|---|
| Firmware (UI, BLE, telas) | firmware/ |
Arduino + LVGL 9 + LovyanGFX + NimBLE |
| Daemon de coleta (macOS) | daemon/ |
Python 3 (bleak, httpx, rumps) |
| Arte do mascote | tools/gen_craudio_sprites.py |
Python + Pillow |
| Captura de screenshots | tools/screenshot.py |
pyserial + Pillow |
-
Faça fork e crie um branch a partir de
main. -
Firmware:
cd firmware && pio runprecisa compilar limpo. Mudanças de UI ou BLE devem ser testadas no dispositivo (pio run -t upload). -
Daemon: rode em foreground para depurar:
cd daemon python3 -m venv .venv && .venv/bin/pip install -r requirements.txt .venv/bin/python craudiometro_daemon.py
-
Mudou uma tela existente? Regenere as capturas do README com
tools/screenshot.py(ver Screenshots do dispositivo). -
Tela nova? Siga Como adicionar uma tela nova.
- Commits: Conventional Commits em pt-BR (
feat:,fix:,docs:, ...), com verbo no infinitivo e sem emojis. - Código e identificadores em inglês; documentação em pt-BR.
- Diagramas em Mermaid; sem emojis em código, UI ou documentação.
- Não inclua assets de terceiros: toda a arte é gerada por script (ver Craudinho (mascote)).
Ao contribuir, você concorda que sua contribuição seja licenciada sob a licença MIT do projeto.
Distribuído sob a licença MIT — veja LICENSE.




