# AGENTS.md — Klubhaus Doorbell Multi-target Arduino/ESP32 doorbell alert system using ntfy.sh. ## Project Overview Three board targets share business logic via a common library: | Board | Display | Library | Build Target | |-------|---------|---------|--------------| | ESP32-32E | SPI TFT 320x240 (ILI9341) | TFT_eSPI | `compile-32e` | | ESP32-32E-4" | SPI TFT 320x480 (ST7796) | TFT_eSPI | `compile-32e-4` | | ESP32-S3-Touch-LCD-4.3 | 800x480 RGB parallel | LovyanGFX | `compile-s3-43` | ## Essential Commands All commands run via **mise**: ```bash # Install all dependencies (shared libs + vendored display libs) mise run install-libs # Build ESP32-32E mise run compile-32e mise run upload-32e mise run monitor-32e # Build ESP32-S3-LCD-4.3 mise run compile-s3-43 mise run upload-s3-43 mise run monitor-s3-43 # Format code mise run format # Clean build artifacts mise run clean ``` **Upload port override**: `PORT=/dev/ttyXXX mise run upload-32e` **Prerequisites**: arduino-cli with `esp32:esp32` platform installed, mise. ## Project Structure ``` libraries/KlubhausCore/src/ Shared Arduino library ├── KlubhausCore.h Umbrella include (board sketches use this) ├── Config.h Constants, timing, WiFiCred struct ├── ScreenState.h State enums/structs ├── IDisplayDriver.h Pure virtual display interface ├── DisplayManager.h Thin wrapper delegating to IDisplayDriver ├── NetManager.* WiFi, HTTP, NTP └── DoorbellLogic.* State machine, ntfy polling boards/ ├── esp32-32e/ │ ├── esp32-32e.ino Main sketch │ ├── board_config.h Board-specific config │ ├── secrets.h WiFi creds (gitignored, copy from .example) │ ├── tft_user_setup.h TFT_eSPI config │ └── DisplayDriverTFT.* Concrete IDisplayDriver for TFT └── esp32-s3-lcd-43/ ├── esp32-s3-lcd-43.ino Main sketch ├── board_config.h Board-specific config ├── secrets.h WiFi creds (gitignored, copy from .example) ├── LovyanPins.h Pin definitions └── DisplayDriverGFX.* Concrete IDisplayDriver for LovyanGFX vendor/ Vendored display libs (recreated by install-libs) ├── esp32-32e/TFT_eSPI/ └── esp32-s3-lcd-43/LovyanGFX/ ``` ## Code Patterns ### Header Guards Use `#pragma once` (not `#ifndef` guards). ### Formatting - 4-space indentation, no tabs - WebKit-based style (see `.clang-format`) - Column limit: 100 - Opening brace stays on same line (`BreakBeforeBraces: Attach`) ### Class Design - Pure virtual `IDisplayDriver` interface in shared library - Each board implements a concrete driver (e.g., `DisplayDriverTFT`, `DisplayDriverGFX`) - `DisplayManager` delegates to `IDisplayDriver` — no display-lib coupling in shared code ### Arduino Patterns - `setup()` runs once at boot — call `begin()` on managers - `loop()` runs continuously — call `update()` on managers - Use `millis()` for timing (not `delay()`) - Serial console at 115200 baud for debug commands ## Testing/Debugging **Serial commands** (type into serial monitor at 115200 baud): | Command | Action | |---------|--------| | `alert` | Trigger a test alert | | `silence` | Silence current alert | | `dashboard` | Show dashboard screen | | `off` | Turn off display | | `status` | Print state + memory info | | `reboot` | Restart device | ## Gotchas 1. **secrets.h is gitignored**: Copy from `.example` before building: ```bash cp boards/esp32-32e/secrets.h.example boards/esp32-32e/secrets.h cp boards/esp32-s3-lcd-43/secrets.h.example boards/esp32-s3-lcd-43/secrets.h ``` 2. **Display libs are vendored**: Each board uses a different display library. The build system uses `--libraries` to link only the board's vendored lib — never link both TFT_eSPI and LovyanGFX in the same build. 3. **No unit tests**: This is an embedded Arduino sketch — no test suite exists. Verify changes by building and deploying to hardware. 4. **LSP errors are expected**: The LSP cannot find `Arduino.h` because it requires arduino-cli to resolve. Ignore clangd/IntelliSense errors about missing Arduino types; builds work correctly via arduino-cli. 5. **Build artifacts in board dirs**: Build output goes to `boards/[board]/build/` — cleaned by `mise run clean`. 6. **WiFi credentials are per-board**: Each board directory has its own `secrets.h` because boards may be on different networks. ## Hardware Research Log ### Hosyond ESP32-32E 4" (320x480) - Planned **Source**: https://www.lcdwiki.com/4.0inch_ESP32-32E_Display | Spec | Value | |------|-------| | Display Controller | ST7796S | | Resolution | 320x480 | | Touch | XPT2046 (resistive) | | Library | TFT_eSPI V2.5.43 (same as 32E) | **GPIO Pinout**: | Function | GPIO | |----------|------| | LCD CS | 15 | | LCD DC | 2 | | LCD MOSI | 13 | | LCD SCLK | 14 | | LCD RST | EN | | LCD BL | 27 | | Touch CS | 33 | | Touch IRQ | 36 | **Quirks**: - SPI pins shared between LCD and touch - Touch IRQ on IO36 (input-only) triggers LOW on touch - Backlight on IO27 (HIGH = on) - Common anode RGB LEDs on IO16, IO17, IO22 (LOW = on)