feat(docs): update README and AGENTS.md with RTK instructions
This commit is contained in:
@@ -16,7 +16,6 @@ BOARD=esp32-32e-4 mise run install # Install libs (shared + board)
|
|||||||
|
|
||||||
## Files
|
## Files
|
||||||
- `mise.toml` - Tasks (compile, upload, monitor, log-tail, cmd, state, install, clean)
|
- `mise.toml` - Tasks (compile, upload, monitor, log-tail, cmd, state, install, clean)
|
||||||
- `scripts/lockfile.sh` - Lockfile functions (acquire_lock, kill_locked)
|
|
||||||
- `scripts/monitor-agent.py` - Serial monitor with JSON log + command FIFO
|
- `scripts/monitor-agent.py` - Serial monitor with JSON log + command FIFO
|
||||||
- `scripts/install-shared.sh` - Shared Arduino libs (ArduinoJson, NTPClient)
|
- `scripts/install-shared.sh` - Shared Arduino libs (ArduinoJson, NTPClient)
|
||||||
- `boards/{BOARD}/install.sh` - Board-specific vendor libs
|
- `boards/{BOARD}/install.sh` - Board-specific vendor libs
|
||||||
|
|||||||
5
.markdownlint-cli2.jsonc
Normal file
5
.markdownlint-cli2.jsonc
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"MD013": false,
|
||||||
|
"MD060": false,
|
||||||
|
"MD025": false
|
||||||
|
}
|
||||||
5
.markdownlint.json
Normal file
5
.markdownlint.json
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"MD013": false,
|
||||||
|
"MD060": false,
|
||||||
|
"MD025": false
|
||||||
|
}
|
||||||
216
AGENTS.md
216
AGENTS.md
@@ -84,6 +84,7 @@ Each board directory contains `board-config.sh` which defines:
|
|||||||
| `OPTS` | Additional compiler flags (e.g., `-DDEBUG_MODE`) |
|
| `OPTS` | Additional compiler flags (e.g., `-DDEBUG_MODE`) |
|
||||||
|
|
||||||
**Example** (`boards/esp32-32e-4/board-config.sh`):
|
**Example** (`boards/esp32-32e-4/board-config.sh`):
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
FQBN="esp32:esp32:esp32:FlashSize=4M,PartitionScheme=default"
|
FQBN="esp32:esp32:esp32:FlashSize=4M,PartitionScheme=default"
|
||||||
PORT="/dev/ttyUSB0"
|
PORT="/dev/ttyUSB0"
|
||||||
@@ -97,7 +98,7 @@ OPTS="-DDEBUG_MODE -DBOARD_HAS_PSRAM"
|
|||||||
|
|
||||||
## Project Structure
|
## Project Structure
|
||||||
|
|
||||||
```
|
```text
|
||||||
libraries/KlubhausCore/src/ Shared Arduino library
|
libraries/KlubhausCore/src/ Shared Arduino library
|
||||||
├── KlubhausCore.h Umbrella include (board sketches use this)
|
├── KlubhausCore.h Umbrella include (board sketches use this)
|
||||||
├── Config.h Constants, timing, WiFiCred struct
|
├── Config.h Constants, timing, WiFiCred struct
|
||||||
@@ -111,19 +112,19 @@ boards/
|
|||||||
├── esp32-32e/
|
├── esp32-32e/
|
||||||
│ ├── esp32-32e.ino Main sketch
|
│ ├── esp32-32e.ino Main sketch
|
||||||
│ ├── board_config.h Board-specific config
|
│ ├── board_config.h Board-specific config
|
||||||
│ ├── secrets.h WiFi creds (gitignored, copy from .example)
|
│ ├── secrets.h.example WiFi creds template (copy to secrets.h)
|
||||||
│ ├── tft_user_setup.h TFT_eSPI config
|
│ ├── tft_user_setup.h TFT_eSPI config
|
||||||
│ └── DisplayDriverTFT.* Concrete IDisplayDriver for TFT
|
│ └── DisplayDriverTFT.* Concrete IDisplayDriver for TFT
|
||||||
├── esp32-32e-4/
|
├── esp32-32e-4/
|
||||||
│ ├── esp32-32e-4.ino Main sketch
|
│ ├── esp32-32e-4.ino Main sketch
|
||||||
│ ├── board_config.h Board-specific config
|
│ ├── board_config.h Board-specific config
|
||||||
│ ├── secrets.h WiFi creds (gitignored, copy from .example)
|
│ ├── secrets.h.example WiFi creds template (copy to secrets.h)
|
||||||
│ ├── tft_user_setup.h TFT_eSPI config
|
│ ├── tft_user_setup.h TFT_eSPI config
|
||||||
│ └── DisplayDriverTFT.* Concrete IDisplayDriver for TFT
|
│ └── DisplayDriverTFT.* Concrete IDisplayDriver for TFT
|
||||||
└── esp32-s3-lcd-43/
|
└── esp32-s3-lcd-43/
|
||||||
├── esp32-s3-lcd-43.ino Main sketch
|
├── esp32-s3-lcd-43.ino Main sketch
|
||||||
├── board_config.h Board-specific config
|
├── board_config.h Board-specific config
|
||||||
├── secrets.h WiFi creds (gitignored, copy from .example)
|
├── secrets.h.example WiFi creds template (copy to secrets.h)
|
||||||
├── LovyanPins.h Pin definitions
|
├── LovyanPins.h Pin definitions
|
||||||
└── DisplayDriverGFX.* Concrete IDisplayDriver for LovyanGFX
|
└── DisplayDriverGFX.* Concrete IDisplayDriver for LovyanGFX
|
||||||
|
|
||||||
@@ -136,20 +137,52 @@ vendor/ Vendored display libs (recreated by install-libs)
|
|||||||
## Code Patterns
|
## Code Patterns
|
||||||
|
|
||||||
### Header Guards
|
### Header Guards
|
||||||
|
|
||||||
Use `#pragma once` (not `#ifndef` guards).
|
Use `#pragma once` (not `#ifndef` guards).
|
||||||
|
|
||||||
### Formatting
|
### Formatting
|
||||||
|
|
||||||
- 4-space indentation, no tabs
|
- 4-space indentation, no tabs
|
||||||
- WebKit-based style (see `.clang-format`)
|
- WebKit-based style (see `.clang-format`)
|
||||||
- Column limit: 100
|
- Column limit: 100
|
||||||
- Opening brace stays on same line (`BreakBeforeBraces: Attach`)
|
- Opening brace stays on same line (`BreakBeforeBraces: Attach`)
|
||||||
|
|
||||||
|
### Naming Conventions
|
||||||
|
|
||||||
|
- Classes: `PascalCase` (e.g., `DisplayManager`, `IDisplayDriver`)
|
||||||
|
- Constants/enums: `SCREAMING_SNAKE` (e.g., `POLL_INTERVAL_MS`, `ScreenState::DASHBOARD`)
|
||||||
|
- Variables/functions: `camelCase` (e.g., `currentState`, `updateDisplay`)
|
||||||
|
- Member variables: prefix with `_` (e.g., `_screenWidth`, `_isConnected`)
|
||||||
|
|
||||||
|
### Types
|
||||||
|
|
||||||
|
- Arduino types: `int`, `uint8_t`, `uint16_t`, `size_t`
|
||||||
|
- Use fixed-width types for protocol/serialization (`uint8_t`, not `byte`)
|
||||||
|
- Avoid `bool` for pin states - use `uint8_t` or `int`
|
||||||
|
- Use `size_t` for sizes and array indices
|
||||||
|
|
||||||
|
### Imports Organization
|
||||||
|
|
||||||
|
- Arduino core headers first (`Arduino.h`)
|
||||||
|
- Standard C/C++ library (`<cstdint>`, `<String>`, `<vector>`)
|
||||||
|
- Third-party libraries (e.g., `TFT_eSPI.h`, `ArduinoJson.h`)
|
||||||
|
- Local project headers (e.g., `"Config.h"`, `"ScreenState.h"`)
|
||||||
|
|
||||||
|
### Error Handling
|
||||||
|
|
||||||
|
- Serial logging pattern: `Serial.println("[ERROR] message")`
|
||||||
|
- Use `Serial.printf()` for formatted debug output
|
||||||
|
- Return error codes, not exceptions (exceptions not available in Arduino)
|
||||||
|
- Log state transitions with `[STATE] → STATE` tags
|
||||||
|
|
||||||
### Class Design
|
### Class Design
|
||||||
|
|
||||||
- Pure virtual `IDisplayDriver` interface in shared library
|
- Pure virtual `IDisplayDriver` interface in shared library
|
||||||
- Each board implements a concrete driver (e.g., `DisplayDriverTFT`, `DisplayDriverGFX`)
|
- Each board implements a concrete driver (e.g., `DisplayDriverTFT`, `DisplayDriverGFX`)
|
||||||
- `DisplayManager` delegates to `IDisplayDriver` — no display-lib coupling in shared code
|
- `DisplayManager` delegates to `IDisplayDriver` — no display-lib coupling in shared code
|
||||||
|
|
||||||
### Arduino Patterns
|
### Arduino Patterns
|
||||||
|
|
||||||
- `setup()` runs once at boot — call `begin()` on managers
|
- `setup()` runs once at boot — call `begin()` on managers
|
||||||
- `loop()` runs continuously — call `update()` on managers
|
- `loop()` runs continuously — call `update()` on managers
|
||||||
- Use `millis()` for timing (not `delay()`)
|
- Use `millis()` for timing (not `delay()`)
|
||||||
@@ -157,6 +190,13 @@ Use `#pragma once` (not `#ifndef` guards).
|
|||||||
|
|
||||||
## Testing/Debugging
|
## Testing/Debugging
|
||||||
|
|
||||||
|
**No unit tests exist** - This is an embedded Arduino sketch. Verify changes by building and deploying to hardware:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
BOARD=esp32-s3-lcd-43 mise run compile # compile for default board
|
||||||
|
BOARD=esp32-32e-4 mise run compile # compile for ESP32-32E-4"
|
||||||
|
```
|
||||||
|
|
||||||
**Serial commands** (type into serial monitor at 115200 baud):
|
**Serial commands** (type into serial monitor at 115200 baud):
|
||||||
|
|
||||||
| Command | Action |
|
| Command | Action |
|
||||||
@@ -169,6 +209,7 @@ Use `#pragma once` (not `#ifndef` guards).
|
|||||||
| `reboot` | Restart device |
|
| `reboot` | Restart device |
|
||||||
|
|
||||||
**Touch Test Commands** (ESP32-S3-LCD-4.3 only):
|
**Touch Test Commands** (ESP32-S3-LCD-4.3 only):
|
||||||
|
|
||||||
| Command | Action |
|
| Command | Action |
|
||||||
|---------|--------|
|
|---------|--------|
|
||||||
| `TEST:touch X Y press` | Inject synthetic press at raw panel coords (X,Y) |
|
| `TEST:touch X Y press` | Inject synthetic press at raw panel coords (X,Y) |
|
||||||
@@ -176,6 +217,7 @@ Use `#pragma once` (not `#ifndef` guards).
|
|||||||
| `TEST:touch clear` | Clear test mode (required between touch sequences) |
|
| `TEST:touch clear` | Clear test mode (required between touch sequences) |
|
||||||
|
|
||||||
Note: The S3 touch panel is rotated 180° relative to the display. Use raw panel coordinates:
|
Note: The S3 touch panel is rotated 180° relative to the display. Use raw panel coordinates:
|
||||||
|
|
||||||
- Display (100,140) → Raw (700, 340)
|
- Display (100,140) → Raw (700, 340)
|
||||||
- Display (700,140) → Raw (100, 340)
|
- Display (700,140) → Raw (100, 340)
|
||||||
|
|
||||||
@@ -201,6 +243,7 @@ The monitor daemon automatically starts after upload and is killed before upload
|
|||||||
## Gotchas
|
## Gotchas
|
||||||
|
|
||||||
1. **secrets.h is gitignored**: Copy from `.example` before building:
|
1. **secrets.h is gitignored**: Copy from `.example` before building:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cp boards/esp32-32e/secrets.h.example boards/esp32-32e/secrets.h
|
cp boards/esp32-32e/secrets.h.example boards/esp32-32e/secrets.h
|
||||||
cp boards/esp32-32e-4/secrets.h.example boards/esp32-32e-4/secrets.h
|
cp boards/esp32-32e-4/secrets.h.example boards/esp32-32e-4/secrets.h
|
||||||
@@ -217,9 +260,9 @@ The monitor daemon automatically starts after upload and is killed before upload
|
|||||||
|
|
||||||
6. **WiFi credentials are per-board**: Each board directory has its own `secrets.h` because boards may be on different networks.
|
6. **WiFi credentials are per-board**: Each board directory has its own `secrets.h` because boards may be on different networks.
|
||||||
|
|
||||||
7. **Use BOARD environment variable**: All build commands require the `BOARD` environment variable (e.g., `BOARD=esp32-32e mise run compile`). The default BOARD is `esp32-32e-4` (set in mise.toml).
|
7. **Use BOARD environment variable**: All build commands require the `BOARD` environment variable (e.g., `BOARD=esp32-32e mise run compile`). The default BOARD is `esp32-s3-lcd-43` (set in mise.toml).
|
||||||
|
|
||||||
8. **Lockfile system**: The build system uses lockfiles (`/tmp/doorbell-$BOARD.lock`) to prevent concurrent upload/monitor operations. Use `mise run kill` to clean up stuck processes.
|
8. **Serial port contention**: The `upload`, `monitor`, and `monitor-raw` tasks automatically depend on `kill` which uses `fuser` to terminate any process using the serial port before starting.
|
||||||
|
|
||||||
9. **Monitor state parsing**: The monitor-agent parses serial output for state updates:
|
9. **Monitor state parsing**: The monitor-agent parses serial output for state updates:
|
||||||
- `[STATE] → DASHBOARD` → updates state file to `"screen":"DASHBOARD"`
|
- `[STATE] → DASHBOARD` → updates state file to `"screen":"DASHBOARD"`
|
||||||
@@ -230,7 +273,7 @@ The monitor daemon automatically starts after upload and is killed before upload
|
|||||||
|
|
||||||
10. **Serial baud rate**: All serial communication uses 115200 baud.
|
10. **Serial baud rate**: All serial communication uses 115200 baud.
|
||||||
|
|
||||||
11. **Lockfile mechanism**: The build system uses `/tmp/doorbell-$BOARD.lock` to prevent concurrent operations. The lockfile script (`scripts/lockfile.sh`) provides `acquire_lock()` and `release_lock()` functions. Use `FORCE=1` to override locks.
|
11. **Serial port contention**: The `kill` task uses `fuser` to release the serial port. Both `upload` and `monitor` tasks depend on `kill` to ensure the port is free.
|
||||||
|
|
||||||
## Config Constants
|
## Config Constants
|
||||||
|
|
||||||
@@ -245,6 +288,14 @@ Key timing and configuration values in `Config.h`:
|
|||||||
| `INACTIVITY_TIMEOUT_MS` | 30000 | Turn off display after 30s of inactivity |
|
| `INACTIVITY_TIMEOUT_MS` | 30000 | Turn off display after 30s of inactivity |
|
||||||
| `HOLD_TO_SILENCE_MS` | 3000 | Hold duration to silence alert |
|
| `HOLD_TO_SILENCE_MS` | 3000 | Hold duration to silence alert |
|
||||||
| `LOOP_YIELD_MS` | 10 | Yield to prevent Task Watchdog (configurable) |
|
| `LOOP_YIELD_MS` | 10 | Yield to prevent Task Watchdog (configurable) |
|
||||||
|
| `BOOT_GRACE_MS` | 5000 | Grace period at boot before polling starts |
|
||||||
|
| `SILENCE_DISPLAY_MS` | 10000 | How long to show silence confirmation |
|
||||||
|
| `WIFI_CONNECT_TIMEOUT_MS` | 15000 | WiFi connection timeout |
|
||||||
|
| `HTTP_TIMEOUT_MS` | 10000 | HTTP request timeout |
|
||||||
|
| `HINT_ANIMATION_MS` | 2000 | Hint animation duration |
|
||||||
|
| `HINT_MIN_BRIGHTNESS` | 30 | Minimum brightness for hints |
|
||||||
|
| `HINT_MAX_BRIGHTNESS` | 60 | Maximum brightness for hints |
|
||||||
|
| `TOUCH_DEBOUNCE_MS` | 100 | Touch debounce delay |
|
||||||
|
|
||||||
## Screen States
|
## Screen States
|
||||||
|
|
||||||
@@ -288,6 +339,7 @@ Track changes that were reverted to avoid flapping:
|
|||||||
## Documentation Lookup Rule
|
## Documentation Lookup Rule
|
||||||
|
|
||||||
When uncertain about CLI tool flags or argument syntax:
|
When uncertain about CLI tool flags or argument syntax:
|
||||||
|
|
||||||
1. Run the tool with `-h` or `--help` first
|
1. Run the tool with `-h` or `--help` first
|
||||||
2. If unclear, search for official documentation matching the tool version
|
2. If unclear, search for official documentation matching the tool version
|
||||||
3. Prefer checking the tool's own help/docs over guessing
|
3. Prefer checking the tool's own help/docs over guessing
|
||||||
@@ -312,11 +364,13 @@ The project uses clangd for C++ and arduino-language-server for Arduino. The `.c
|
|||||||
```
|
```
|
||||||
|
|
||||||
**Generate compile_commands.json** for accurate IDE diagnostics:
|
**Generate compile_commands.json** for accurate IDE diagnostics:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
BOARD=esp32-32e-4 mise run gen-compile-commands
|
BOARD=esp32-32e-4 mise run gen-compile-commands
|
||||||
```
|
```
|
||||||
|
|
||||||
**Generate static .crush.json** for a specific board:
|
**Generate static .crush.json** for a specific board:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
BOARD=esp32-32e-4 mise run gen-crush-config
|
BOARD=esp32-32e-4 mise run gen-crush-config
|
||||||
```
|
```
|
||||||
@@ -325,7 +379,7 @@ BOARD=esp32-32e-4 mise run gen-crush-config
|
|||||||
|
|
||||||
### Hosyond ESP32-32E 4" (320x480) - Planned
|
### Hosyond ESP32-32E 4" (320x480) - Planned
|
||||||
|
|
||||||
**Source**: https://www.lcdwiki.com/4.0inch_ESP32-32E_Display
|
**Source**: <https://www.lcdwiki.com/4.0inch_ESP32-32E_Display>
|
||||||
|
|
||||||
| Spec | Value |
|
| Spec | Value |
|
||||||
|------|-------|
|
|------|-------|
|
||||||
@@ -348,7 +402,153 @@ BOARD=esp32-32e-4 mise run gen-crush-config
|
|||||||
| Touch IRQ | 36 |
|
| Touch IRQ | 36 |
|
||||||
|
|
||||||
**Quirks**:
|
**Quirks**:
|
||||||
|
|
||||||
- SPI pins shared between LCD and touch
|
- SPI pins shared between LCD and touch
|
||||||
- Touch IRQ on IO36 (input-only) triggers LOW on touch
|
- Touch IRQ on IO36 (input-only) triggers LOW on touch
|
||||||
- Backlight on IO27 (HIGH = on)
|
- Backlight on IO27 (HIGH = on)
|
||||||
- Common anode RGB LEDs on IO16, IO17, IO22 (LOW = on)
|
- Common anode RGB LEDs on IO16, IO17, IO22 (LOW = on)
|
||||||
|
<!-- rtk-instructions v2 -->
|
||||||
|
# RTK (Rust Token Killer) - Token-Optimized Commands
|
||||||
|
|
||||||
|
## Golden Rule
|
||||||
|
|
||||||
|
**Always prefix commands with `rtk`**. If RTK has a dedicated filter, it uses it. If not, it passes through unchanged. This means RTK is always safe to use.
|
||||||
|
|
||||||
|
**Important**: Even in command chains with `&&`, use `rtk`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# ❌ Wrong
|
||||||
|
git add . && git commit -m "msg" && git push
|
||||||
|
|
||||||
|
# ✅ Correct
|
||||||
|
rtk git add . && rtk git commit -m "msg" && rtk git push
|
||||||
|
```
|
||||||
|
|
||||||
|
## RTK Commands by Workflow
|
||||||
|
|
||||||
|
### Build & Compile (80-90% savings)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
rtk cargo build # Cargo build output
|
||||||
|
rtk cargo check # Cargo check output
|
||||||
|
rtk cargo clippy # Clippy warnings grouped by file (80%)
|
||||||
|
rtk tsc # TypeScript errors grouped by file/code (83%)
|
||||||
|
rtk lint # ESLint/Biome violations grouped (84%)
|
||||||
|
rtk prettier --check # Files needing format only (70%)
|
||||||
|
rtk next build # Next.js build with route metrics (87%)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Test (90-99% savings)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
rtk cargo test # Cargo test failures only (90%)
|
||||||
|
rtk vitest run # Vitest failures only (99.5%)
|
||||||
|
rtk playwright test # Playwright failures only (94%)
|
||||||
|
rtk test <cmd> # Generic test wrapper - failures only
|
||||||
|
```
|
||||||
|
|
||||||
|
### Git (59-80% savings)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
rtk git status # Compact status
|
||||||
|
rtk git log # Compact log (works with all git flags)
|
||||||
|
rtk git diff # Compact diff (80%)
|
||||||
|
rtk git show # Compact show (80%)
|
||||||
|
rtk git add # Ultra-compact confirmations (59%)
|
||||||
|
rtk git commit # Ultra-compact confirmations (59%)
|
||||||
|
rtk git push # Ultra-compact confirmations
|
||||||
|
rtk git pull # Ultra-compact confirmations
|
||||||
|
rtk git branch # Compact branch list
|
||||||
|
rtk git fetch # Compact fetch
|
||||||
|
rtk git stash # Compact stash
|
||||||
|
rtk git worktree # Compact worktree
|
||||||
|
```
|
||||||
|
|
||||||
|
Note: Git passthrough works for ALL subcommands, even those not explicitly listed.
|
||||||
|
|
||||||
|
### GitHub (26-87% savings)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
rtk gh pr view <num> # Compact PR view (87%)
|
||||||
|
rtk gh pr checks # Compact PR checks (79%)
|
||||||
|
rtk gh run list # Compact workflow runs (82%)
|
||||||
|
rtk gh issue list # Compact issue list (80%)
|
||||||
|
rtk gh api # Compact API responses (26%)
|
||||||
|
```
|
||||||
|
|
||||||
|
### JavaScript/TypeScript Tooling (70-90% savings)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
rtk pnpm list # Compact dependency tree (70%)
|
||||||
|
rtk pnpm outdated # Compact outdated packages (80%)
|
||||||
|
rtk pnpm install # Compact install output (90%)
|
||||||
|
rtk npm run <script> # Compact npm script output
|
||||||
|
rtk npx <cmd> # Compact npx command output
|
||||||
|
rtk prisma # Prisma without ASCII art (88%)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Files & Search (60-75% savings)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
rtk ls <path> # Tree format, compact (65%)
|
||||||
|
rtk read <file> # Code reading with filtering (60%)
|
||||||
|
rtk grep <pattern> # Search grouped by file (75%)
|
||||||
|
rtk find <pattern> # Find grouped by directory (70%)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Analysis & Debug (70-90% savings)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
rtk err <cmd> # Filter errors only from any command
|
||||||
|
rtk log <file> # Deduplicated logs with counts
|
||||||
|
rtk json <file> # JSON structure without values
|
||||||
|
rtk deps # Dependency overview
|
||||||
|
rtk env # Environment variables compact
|
||||||
|
rtk summary <cmd> # Smart summary of command output
|
||||||
|
rtk diff # Ultra-compact diffs
|
||||||
|
```
|
||||||
|
|
||||||
|
### Infrastructure (85% savings)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
rtk docker ps # Compact container list
|
||||||
|
rtk docker images # Compact image list
|
||||||
|
rtk docker logs <c> # Deduplicated logs
|
||||||
|
rtk kubectl get # Compact resource list
|
||||||
|
rtk kubectl logs # Deduplicated pod logs
|
||||||
|
```
|
||||||
|
|
||||||
|
### Network (65-70% savings)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
rtk curl <url> # Compact HTTP responses (70%)
|
||||||
|
rtk wget <url> # Compact download output (65%)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Meta Commands
|
||||||
|
|
||||||
|
```bash
|
||||||
|
rtk gain # View token savings statistics
|
||||||
|
rtk gain --history # View command history with savings
|
||||||
|
rtk discover # Analyze Claude Code sessions for missed RTK usage
|
||||||
|
rtk proxy <cmd> # Run command without filtering (for debugging)
|
||||||
|
rtk init --global # Add RTK to ~/.claude/CLAUDE.md
|
||||||
|
```
|
||||||
|
|
||||||
|
rtk init # Add RTK instructions to CLAUDE.md
|
||||||
|
|
||||||
|
## Token Savings Overview
|
||||||
|
|
||||||
|
| Category | Commands | Typical Savings |
|
||||||
|
|----------|----------|-----------------|
|
||||||
|
| Tests | vitest, playwright, cargo test | 90-99% |
|
||||||
|
| Build | next, tsc, lint, prettier | 70-87% |
|
||||||
|
| Git | status, log, diff, add, commit | 59-80% |
|
||||||
|
| GitHub | gh pr, gh run, gh issue | 26-87% |
|
||||||
|
| Package Managers | pnpm, npm, npx | 70-90% |
|
||||||
|
| Files | ls, read, grep, find | 60-75% |
|
||||||
|
| Infrastructure | docker, kubectl | 85% |
|
||||||
|
| Network | curl, wget | 65-70% |
|
||||||
|
|
||||||
|
Overall average: **60-90% token reduction** on common development operations.
|
||||||
|
<!-- /rtk-instructions -->
|
||||||
|
|||||||
32
README.md
32
README.md
@@ -4,10 +4,11 @@ Multi-target doorbell alert system powered by [ntfy.sh](https://ntfy.sh).
|
|||||||
|
|
||||||
## Targets
|
## Targets
|
||||||
|
|
||||||
| Board | Display | Library | Build Task |
|
| Board | Display | Library | Build Command |
|
||||||
|---|---|---|---|
|
|---|---|---|---|
|
||||||
| ESP32-32E | SPI TFT (ILI9341 etc.) | TFT_eSPI | `mise run compile-32e` |
|
| ESP32-32E | SPI TFT (ILI9341 etc.) | TFT_eSPI | `BOARD=esp32-32e mise run compile` |
|
||||||
| ESP32-S3-Touch-LCD-4.3 | 800x480 RGB parallel | Arduino_GFX | `mise run compile-s3-43` |
|
| ESP32-32E-4" | SPI TFT 320x480 (ST7796) | TFT_eSPI | `BOARD=esp32-32e-4 mise run compile` |
|
||||||
|
| ESP32-S3-Touch-LCD-4.3 | 800x480 RGB parallel | LovyanGFX | `BOARD=esp32-s3-lcd-43 mise run compile` |
|
||||||
|
|
||||||
## Quick Start
|
## Quick Start
|
||||||
|
|
||||||
@@ -22,11 +23,14 @@ Multi-target doorbell alert system powered by [ntfy.sh](https://ntfy.sh).
|
|||||||
|
|
||||||
3. Build and upload:
|
3. Build and upload:
|
||||||
|
|
||||||
mise run compile-s3-43
|
BOARD=esp32-s3-lcd-43 mise run compile
|
||||||
mise run upload-s3-43
|
BOARD=esp32-s3-lcd-43 mise run upload
|
||||||
|
|
||||||
mise run compile-32e
|
BOARD=esp32-32e-4 mise run compile
|
||||||
mise run upload-32e
|
BOARD=esp32-32e-4 mise run upload
|
||||||
|
|
||||||
|
BOARD=esp32-32e mise run compile
|
||||||
|
BOARD=esp32-32e mise run upload
|
||||||
|
|
||||||
## Project Structure
|
## Project Structure
|
||||||
|
|
||||||
@@ -45,17 +49,25 @@ Multi-target doorbell alert system powered by [ntfy.sh](https://ntfy.sh).
|
|||||||
│ ├── esp32-32e/ ESP32-32E sketch
|
│ ├── esp32-32e/ ESP32-32E sketch
|
||||||
│ │ ├── esp32-32e.ino
|
│ │ ├── esp32-32e.ino
|
||||||
│ │ ├── board_config.h
|
│ │ ├── board_config.h
|
||||||
│ │ ├── secrets.h (gitignored)
|
│ │ ├── secrets.h (gitignored, copy from .example)
|
||||||
|
│ │ ├── tft_user_setup.h
|
||||||
|
│ │ └── DisplayDriverTFT.*
|
||||||
|
│ ├── esp32-32e-4/ ESP32-32E-4" sketch
|
||||||
|
│ │ ├── esp32-32e-4.ino
|
||||||
|
│ │ ├── board_config.h
|
||||||
|
│ │ ├── secrets.h (gitignored, copy from .example)
|
||||||
│ │ ├── tft_user_setup.h
|
│ │ ├── tft_user_setup.h
|
||||||
│ │ └── DisplayDriverTFT.*
|
│ │ └── DisplayDriverTFT.*
|
||||||
│ └── esp32-s3-lcd-43/ ESP32-S3-LCD-4.3 sketch
|
│ └── esp32-s3-lcd-43/ ESP32-S3-LCD-4.3 sketch
|
||||||
│ ├── esp32-s3-lcd-43.ino
|
│ ├── esp32-s3-lcd-43.ino
|
||||||
│ ├── board_config.h
|
│ ├── board_config.h
|
||||||
│ ├── secrets.h (gitignored)
|
│ ├── secrets.h (gitignored, copy from .example)
|
||||||
|
│ ├── LovyanPins.h
|
||||||
│ └── DisplayDriverGFX.*
|
│ └── DisplayDriverGFX.*
|
||||||
├── vendor/ Per-board vendored display libs
|
├── vendor/ Per-board vendored display libs
|
||||||
│ ├── esp32-32e/TFT_eSPI/
|
│ ├── esp32-32e/TFT_eSPI/
|
||||||
│ └── esp32-s3-lcd-43/GFX_Library_for_Arduino/
|
│ ├── esp32-32e-4/TFT_eSPI/
|
||||||
|
│ └── esp32-s3-lcd-43/LovyanGFX/
|
||||||
└── mise.toml Build harness
|
└── mise.toml Build harness
|
||||||
|
|
||||||
## Serial Commands
|
## Serial Commands
|
||||||
|
|||||||
@@ -204,8 +204,6 @@ TouchEvent DisplayDriverTFT::readTouch() {
|
|||||||
return evt;
|
return evt;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t DisplayDriverTFT::getRawTouchZ() { return _tft.getTouchRawZ(); }
|
|
||||||
|
|
||||||
void DisplayDriverTFT::transformTouch(int* x, int* y) {
|
void DisplayDriverTFT::transformTouch(int* x, int* y) {
|
||||||
// Resistive touch panel is rotated 90° vs display - swap coordinates
|
// Resistive touch panel is rotated 90° vs display - swap coordinates
|
||||||
int temp = *x;
|
int temp = *x;
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ public:
|
|||||||
void setBacklight(bool on) override;
|
void setBacklight(bool on) override;
|
||||||
void render(const ScreenState& state) override;
|
void render(const ScreenState& state) override;
|
||||||
TouchEvent readTouch() override;
|
TouchEvent readTouch() override;
|
||||||
uint16_t getRawTouchZ();
|
|
||||||
HoldState updateHold(unsigned long holdMs) override;
|
HoldState updateHold(unsigned long holdMs) override;
|
||||||
void updateHint(int x, int y, bool active) override;
|
void updateHint(int x, int y, bool active) override;
|
||||||
int width() override { return _tft.width(); }
|
int width() override { return _tft.width(); }
|
||||||
|
|||||||
@@ -146,11 +146,33 @@ void DisplayDriverTFT::drawDashboard(const ScreenState& st) {
|
|||||||
TouchEvent DisplayDriverTFT::readTouch() {
|
TouchEvent DisplayDriverTFT::readTouch() {
|
||||||
TouchEvent evt;
|
TouchEvent evt;
|
||||||
uint16_t tx, ty;
|
uint16_t tx, ty;
|
||||||
if(_tft.getTouch(&tx, &ty)) {
|
uint8_t touched = _tft.getTouch(&tx, &ty, 100);
|
||||||
|
|
||||||
|
// Detect transitions (press/release)
|
||||||
|
if(touched && !_touchWasPressed) {
|
||||||
|
// Press transition: finger just touched down
|
||||||
evt.pressed = true;
|
evt.pressed = true;
|
||||||
|
_touchDownX = tx;
|
||||||
|
_touchDownY = ty;
|
||||||
|
evt.downX = _touchDownX;
|
||||||
|
evt.downY = _touchDownY;
|
||||||
|
} else if(!touched && _touchWasPressed) {
|
||||||
|
// Release transition: finger just lifted
|
||||||
|
evt.released = true;
|
||||||
|
evt.downX = _touchDownX;
|
||||||
|
evt.downY = _touchDownY;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Current position if still touched
|
||||||
|
if(touched) {
|
||||||
evt.x = tx;
|
evt.x = tx;
|
||||||
evt.y = ty;
|
evt.y = ty;
|
||||||
|
evt.downX = _touchDownX;
|
||||||
|
evt.downY = _touchDownY;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Track previous state for next call
|
||||||
|
_touchWasPressed = touched;
|
||||||
return evt;
|
return evt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -29,4 +29,9 @@ private:
|
|||||||
ScreenID _lastScreen = ScreenID::BOOT;
|
ScreenID _lastScreen = ScreenID::BOOT;
|
||||||
BootStage _lastBootStage = BootStage::SPLASH;
|
BootStage _lastBootStage = BootStage::SPLASH;
|
||||||
bool _needsRedraw = true;
|
bool _needsRedraw = true;
|
||||||
|
|
||||||
|
// Touch tracking for press/release detection
|
||||||
|
bool _touchWasPressed = false;
|
||||||
|
int _touchDownX = -1;
|
||||||
|
int _touchDownY = -1;
|
||||||
};
|
};
|
||||||
|
|||||||
36
mise.toml
36
mise.toml
@@ -27,14 +27,16 @@ arduino-cli compile --fqbn "$FQBN" --libraries ./libraries $LIBS --build-propert
|
|||||||
|
|
||||||
[tasks.upload]
|
[tasks.upload]
|
||||||
description = "Upload (uses BOARD env var)"
|
description = "Upload (uses BOARD env var)"
|
||||||
depends = ["compile"]
|
depends = ["compile", "kill"]
|
||||||
run = "source ./boards/$BOARD/board-config.sh && arduino-cli upload --fqbn $FQBN --port $PORT ./boards/$BOARD"
|
run = """
|
||||||
|
source ./boards/$BOARD/board-config.sh
|
||||||
[tasks.monitor-raw]
|
arduino-cli upload --fqbn $FQBN --port $PORT ./boards/$BOARD
|
||||||
|
"""
|
||||||
|
|
||||||
|
[tasks.monitor-raw]
|
||||||
|
depends = ["kill"]
|
||||||
run = """
|
run = """
|
||||||
source ./boards/$BOARD/board-config.sh
|
source ./boards/$BOARD/board-config.sh
|
||||||
source ./scripts/lockfile.sh
|
|
||||||
acquire_lock || exit 1
|
|
||||||
|
|
||||||
PORT="${PORT:-$PORT}"
|
PORT="${PORT:-$PORT}"
|
||||||
TARGET="$(readlink -f "$PORT" 2>/dev/null || echo "$PORT")"
|
TARGET="$(readlink -f "$PORT" 2>/dev/null || echo "$PORT")"
|
||||||
@@ -43,10 +45,9 @@ arduino-cli monitor -p "$TARGET" --config baudrate=115200
|
|||||||
|
|
||||||
[tasks.monitor-tio]
|
[tasks.monitor-tio]
|
||||||
description = "Show tio command to run in separate terminal"
|
description = "Show tio command to run in separate terminal"
|
||||||
|
depends = ["kill"]
|
||||||
run = """
|
run = """
|
||||||
source ./boards/$BOARD/board-config.sh
|
source ./boards/$BOARD/board-config.sh
|
||||||
source ./scripts/lockfile.sh
|
|
||||||
acquire_lock || exit 1
|
|
||||||
|
|
||||||
PORT="${PORT:-$PORT}"
|
PORT="${PORT:-$PORT}"
|
||||||
TARGET="$(readlink -f "$PORT" 2>/dev/null || echo "$PORT")"
|
TARGET="$(readlink -f "$PORT" 2>/dev/null || echo "$PORT")"
|
||||||
@@ -54,20 +55,25 @@ tio --map INLCRNL "$TARGET" -e
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
[tasks.kill]
|
[tasks.kill]
|
||||||
description = "Kill running monitor/upload for BOARD"
|
description = "Kill any process using the serial port for BOARD"
|
||||||
run = '''
|
run = '''
|
||||||
set +e
|
set +e
|
||||||
# Kill any processes using the serial port
|
|
||||||
|
# Load board config
|
||||||
source ./boards/$BOARD/board-config.sh
|
source ./boards/$BOARD/board-config.sh
|
||||||
PORT="${PORT:-$PORT}"
|
PORT="${PORT:-$PORT}"
|
||||||
|
|
||||||
|
# Kill any processes using the serial port
|
||||||
|
echo "Killing processes on $PORT..."
|
||||||
fuser -k "$PORT" 2>/dev/null || true
|
fuser -k "$PORT" 2>/dev/null || true
|
||||||
|
|
||||||
# Kill monitor-agent processes for this board
|
# Kill monitor-agent Python processes for this board
|
||||||
for pid in $(pgrep -f "monitor-agent.py" 2>/dev/null || true); do
|
for pid in $(pgrep -f "monitor-agent.py.*$BOARD" 2>/dev/null || true); do
|
||||||
|
echo "Killing monitor-agent.py (PID: $pid)..."
|
||||||
kill "$pid" 2>/dev/null || true
|
kill "$pid" 2>/dev/null || true
|
||||||
done
|
done
|
||||||
|
|
||||||
# Also clean up lockfile
|
# Clean up any stale lockfiles (legacy)
|
||||||
rm -f "/tmp/doorbell-${BOARD}.lock" 2>/dev/null || true
|
rm -f "/tmp/doorbell-${BOARD}.lock" 2>/dev/null || true
|
||||||
|
|
||||||
sleep 1
|
sleep 1
|
||||||
@@ -77,6 +83,7 @@ exit 0
|
|||||||
|
|
||||||
[tasks.monitor]
|
[tasks.monitor]
|
||||||
description = "Monitor agent with JSON log + command pipe (Python-based)"
|
description = "Monitor agent with JSON log + command pipe (Python-based)"
|
||||||
|
depends = ["kill"]
|
||||||
run = """
|
run = """
|
||||||
python3 ./scripts/monitor-agent.py "$BOARD"
|
python3 ./scripts/monitor-agent.py "$BOARD"
|
||||||
"""
|
"""
|
||||||
@@ -203,5 +210,8 @@ EOF
|
|||||||
echo "[OK] Generated .crush.json with FQBN: $FQBN"
|
echo "[OK] Generated .crush.json with FQBN: $FQBN"
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
[tasks.snap]
|
||||||
|
run = "git add .; lumen draft | git commit -F - "
|
||||||
|
|
||||||
[env]
|
[env]
|
||||||
BOARD = "esp32-s3-lcd-43"
|
BOARD = "esp32-s3-lcd-43"
|
||||||
|
|||||||
@@ -1,65 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
# Lockfile helper for doorbell build harness
|
|
||||||
# Source this from mise tasks
|
|
||||||
|
|
||||||
LOCKFILE="/tmp/doorbell-${BOARD:-esp32-32e-4}.lock"
|
|
||||||
|
|
||||||
acquire_lock() {
|
|
||||||
if [ -f "$LOCKFILE" ]; then
|
|
||||||
OLD_PID=$(cat "$LOCKFILE" 2>/dev/null)
|
|
||||||
if [ -n "$OLD_PID" ] && kill -0 "$OLD_PID" 2>/dev/null; then
|
|
||||||
if [ "${FORCE:-}" = "1" ]; then
|
|
||||||
echo "Force: Killing existing process (PID: $OLD_PID)..."
|
|
||||||
kill "$OLD_PID" 2>/dev/null
|
|
||||||
sleep 1
|
|
||||||
kill -9 "$OLD_PID" 2>/dev/null
|
|
||||||
echo "Killed PID $OLD_PID"
|
|
||||||
else
|
|
||||||
echo "Error: Another instance is running (PID: $OLD_PID)"
|
|
||||||
echo "Kill with: mise run kill BOARD=$BOARD"
|
|
||||||
echo "Or force with: FORCE=1 mise run $TASK_NAME BOARD=$BOARD"
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
echo "Stale lockfile found, removing..."
|
|
||||||
rm -f "$LOCKFILE"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
trap 'rm -f "$LOCKFILE"' EXIT
|
|
||||||
echo "$$" > "$LOCKFILE"
|
|
||||||
echo "Acquired lock: $LOCKFILE (PID: $$)"
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
release_lock() {
|
|
||||||
rm -f "$LOCKFILE"
|
|
||||||
}
|
|
||||||
|
|
||||||
kill_locked() {
|
|
||||||
if [ ! -f "$LOCKFILE" ]; then
|
|
||||||
echo "No lockfile found: $LOCKFILE (nothing to kill)"
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
OLD_PID=$(cat "$LOCKFILE" 2>/dev/null)
|
|
||||||
if [ -z "$OLD_PID" ]; then
|
|
||||||
echo "Lockfile is empty, removing..."
|
|
||||||
rm -f "$LOCKFILE"
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
if kill -0 "$OLD_PID" 2>/dev/null; then
|
|
||||||
echo "Killing PID $OLD_PID..."
|
|
||||||
kill "$OLD_PID" 2>/dev/null
|
|
||||||
sleep 1
|
|
||||||
if kill -0 "$OLD_PID" 2>/dev/null; then
|
|
||||||
echo "Force killing PID $OLD_PID..."
|
|
||||||
kill -9 "$OLD_PID" 2>/dev/null
|
|
||||||
fi
|
|
||||||
echo "Killed PID $OLD_PID"
|
|
||||||
else
|
|
||||||
echo "Process $OLD_PID not running (stale lockfile)"
|
|
||||||
fi
|
|
||||||
rm -f "$LOCKFILE"
|
|
||||||
echo "Lockfile removed"
|
|
||||||
}
|
|
||||||
@@ -7,7 +7,12 @@
|
|||||||
# - /tmp/doorbell-$BOARD-state.json - Current device state
|
# - /tmp/doorbell-$BOARD-state.json - Current device state
|
||||||
# - /tmp/doorbell-$BOARD-cmd.fifo - Command pipe (echo 'cmd' > fifo)
|
# - /tmp/doorbell-$BOARD-cmd.fifo - Command pipe (echo 'cmd' > fifo)
|
||||||
#
|
#
|
||||||
|
# Note: This script should be run after 'mise run kill' to ensure
|
||||||
|
# any existing processes using the serial port are terminated.
|
||||||
|
#
|
||||||
# Usage:
|
# Usage:
|
||||||
|
# mise run kill BOARD=$BOARD
|
||||||
|
# ./scripts/monitor-agent.sh $BOARD
|
||||||
# tail -f /tmp/doorbell-$BOARD.jsonl # Watch logs
|
# tail -f /tmp/doorbell-$BOARD.jsonl # Watch logs
|
||||||
# echo 'dashboard' > /tmp/doorbell-$BOARD-cmd.fifo # Send command
|
# echo 'dashboard' > /tmp/doorbell-$BOARD-cmd.fifo # Send command
|
||||||
|
|
||||||
@@ -15,9 +20,6 @@ set -euo pipefail
|
|||||||
|
|
||||||
BOARD="${1:-esp32-32e-4}"
|
BOARD="${1:-esp32-32e-4}"
|
||||||
source "./boards/$BOARD/board-config.sh"
|
source "./boards/$BOARD/board-config.sh"
|
||||||
source ./scripts/lockfile.sh
|
|
||||||
|
|
||||||
acquire_lock || exit 1
|
|
||||||
|
|
||||||
SERIAL="$PORT"
|
SERIAL="$PORT"
|
||||||
LOGFILE="/tmp/doorbell-$BOARD.jsonl"
|
LOGFILE="/tmp/doorbell-$BOARD.jsonl"
|
||||||
|
|||||||
Reference in New Issue
Block a user