6f9d2912d3
- Generate BUILD_TOKEN from 'git describe --always --dirty' - Include via -include flag in justfile compile recipe - Print BUILD_TOKEN in boot banners (DoorbellLogic + uno-r4-wifi) - Remove all [tasks] from mise.toml; keep only [tools] + [env] - Update AGENTS.md to use just commands as single source of truth - Add 'just full' recipe (compile -> upload -> monitor -> watch) - Switch BOARD var to env_var_or_default for mise set compatibility - DRY clean/clean-temp with wildcard patterns
5.8 KiB
5.8 KiB
AGENTS.md — Klubhaus Doorbell
Multi-target Arduino/ESP32 doorbell alert system using ntfy.sh. Default BOARD: esp32-s3-lcd-43.
Build Commands
# All tasks use just (see justfile). Default BOARD: esp32-s3-lcd-43
# Set target board
just BOARD=esp32-32e-4 compile # ESP32-32E 4" (320x480 ST7796)
just BOARD=esp32-32e compile # ESP32-32E 3.5" (320x240 ILI9341)
just BOARD=esp32-s3-lcd-43 compile # ESP32-S3-Touch-LCD-4.3 (800x480 RGB)
just BOARD=uno-r4-wifi compile # Arduino Uno R4 WiFi (12x8 LED matrix)
# Core commands
just compile # compile for current BOARD
just upload # upload (auto-kills monitor first)
just monitor # start JSON monitor daemon (background)
just kill # kill monitor/release serial port
just full # compile + upload + monitor + log-tail (full feedback loop)
just watch # tail colored logs
# Formatting & cleanup
just format # format code with clang-format
just clean # remove build artifacts
just clean-temp # remove monitor logs/FIFOs/state files
# Debugging
just cmd dashboard # send command to device
just state # show device state
just monitor-raw # raw serial monitor (115200 baud)
just monitor-tio # show tio command for terminal UI
# Install dependencies
just install-libs-shared # shared libs (ArduinoJson, NTPClient)
just install # shared + board-specific libs
# LSP / IDE
just gen-compile-commands # generate compile_commands.json
just gen-crush-config # generate .crush.json for BOARD
Serial debug commands (115200 baud): alert, silence, dashboard, off, status, reboot
No unit tests exist — verify changes by compiling and deploying to hardware.
Code Style
Formatting (.clang-format)
- BasedOnStyle: WebKit
- 4-space indentation, no tabs
- Column limit: 100
- Opening brace on same line (
BreakBeforeBraces: Attach) - Run
just formatto format code
Header Guards
Use #pragma once (not #ifndef guards).
Naming Conventions
| Type | Convention | Example |
|---|---|---|
| Classes | PascalCase | DisplayManager, IDisplayDriver |
| Constants/enums | SCREAMING_SNAKE | POLL_INTERVAL_MS, ScreenState::DASHBOARD |
| Variables/functions | camelCase | currentState, updateDisplay |
| Member variables | _ prefix |
_screenWidth, _isConnected |
Types
- Use fixed-width types for protocol/serialization (
uint8_t, notbyte) - Use
size_tfor sizes and array indices - Avoid
boolfor pin states — useuint8_torint
Imports Organization (in order)
- Arduino core (
Arduino.h) - Standard C/C++ (
<cstdint>,<String>,<vector>) - Third-party libs (
TFT_eSPI.h,ArduinoJson.h) - Local project (
"Config.h","ScreenState.h")
Error Handling
- Serial logging:
Serial.println("[ERROR] message") - Use
Serial.printf()for formatted debug - Return error codes, not exceptions
- Log state:
[STATE] → DASHBOARD
Architecture Patterns
Display Driver Interface
- Pure virtual
IDisplayDriverin sharedKlubhausCore - Each board implements concrete driver (
DisplayDriverTFT,DisplayDriverGFX) DisplayManagerdelegates toIDisplayDriver— no display-lib coupling in shared code
Arduino Patterns
setup()— callbegin()on managersloop()— callupdate()on managers- Use
millis()for timing (notdelay()) - Serial baud: 115200
Style System
- Style constants in board's
board_config.h:STYLE_SPACING_X,STYLE_COLOR_BG, etc. - Font abstraction via
IDisplayDriver:setTitleFont(),setBodyFont(), etc. - Layout helpers in
KlubhausCore/src/Style.h
Key Files
libraries/KlubhausCore/src/
├── KlubhausCore.h # Umbrella include
├── Config.h # Timing, WiFiCred struct
├── ScreenState.h # State enums/structs
├── IDisplayDriver.h # Pure virtual interface
├── DisplayManager.h # Delegates to IDisplayDriver
├── NetManager.* # WiFi, HTTP, NTP
└── DoorbellLogic.* # State machine, ntfy polling
boards/{BOARD}/
├── {BOARD}.ino # Main sketch
├── board_config.h # Board-specific config (ESP32 boards)
├── secrets.h # WiFi credentials (optional, use env vars)
├── tft_user_setup.h # TFT_eSPI config (TFT boards)
└── DisplayDriver*.{h,cpp} # Concrete IDisplayDriver (ESP32 boards)
boards/uno-r4-wifi/ # Uno R4 WiFi (LED matrix display)
├── uno-r4-wifi.ino # Main sketch (standalone)
├── test_messages.sh # Test script to send ntfy messages
└── README.md # Quick start guide
Gotchas
- secrets.h: Boards with
-DLOCAL_SECRETSuse localsecrets.h; others useKlubhausCore/src/secrets.h - Vendored libs: Each board links only its display lib — never TFT_eSPI + LovyanGFX together
- LSP errors: Run
just gen-compile-commandsthen restart LSP; build works regardless - Serial port:
upload/monitorauto-depend onkillto release port - State tags: Use
[STATE] → DASHBOARD,[ADMIN],[TOUCH],[ALERT]for monitor parsing
Config Constants (Config.h)
| Constant | Default | Description |
|---|---|---|
FW_VERSION |
"5.1" | Firmware version |
POLL_INTERVAL_MS |
15000 | ntfy.sh poll interval |
ALERT_TIMEOUT_MS |
120000 | Auto-clear alert |
INACTIVITY_TIMEOUT_MS |
30000 | Display off timeout |
HOLD_TO_SILENCE_MS |
3000 | Hold to silence |
WIFI_CONNECT_TIMEOUT_MS |
15000 | WiFi timeout |
HTTP_TIMEOUT_MS |
10000 | HTTP request timeout |
Screen States
- BOOT — Initializing
- DASHBOARD — Normal operation
- ALERT — Doorbell ring detected
- OFF — Display backlight off (polling continues)