From 6d51234f21b2c62f5f10a118b5bc0bf8c63e62ae Mon Sep 17 00:00:00 2001 From: David Gwilliam Date: Thu, 19 Feb 2026 23:10:55 -0800 Subject: [PATCH] docs: Add justfile for build automation --- .gitignore | 1 + boards/esp32-32e-4/DisplayDriverTFT.cpp | 8 +- boards/esp32-32e/DisplayDriverTFT.cpp | 2 +- justfile | 177 ++++++++++++++++++++++++ 4 files changed, 183 insertions(+), 5 deletions(-) create mode 100644 justfile diff --git a/.gitignore b/.gitignore index 9a2a52e..d75e0e9 100644 --- a/.gitignore +++ b/.gitignore @@ -16,4 +16,5 @@ vendor/esp32-s3-lcd-43/GFX_Library_for_Arduino/ *~ .DS_Store compile_commands.json +.board-last .cache/ diff --git a/boards/esp32-32e-4/DisplayDriverTFT.cpp b/boards/esp32-32e-4/DisplayDriverTFT.cpp index 92c04a9..9ad6b50 100644 --- a/boards/esp32-32e-4/DisplayDriverTFT.cpp +++ b/boards/esp32-32e-4/DisplayDriverTFT.cpp @@ -209,15 +209,15 @@ void DisplayDriverTFT::drawAlert(const ScreenState& st) { void DisplayDriverTFT::drawDashboard(const ScreenState& st) { _tft.fillScreen(TFT_BLACK); - // Header + // Header - using standard bitmap font for reliable positioning _tft.fillRect(0, 0, DISPLAY_WIDTH, 30, 0x1A1A); // Dark gray header - setBodyFont(); + _tft.setTextSize(1); _tft.setTextColor(TFT_WHITE); - _tft.setCursor(5, 10); + _tft.setCursor(5, 20); // y=28 is baseline, text sits above this _tft.print("KLUBHAUS"); // WiFi indicator - _tft.setCursor(DISPLAY_WIDTH - 60, 10); + _tft.setCursor(DISPLAY_WIDTH - 60, 20); _tft.print(st.wifiSsid.length() > 0 ? "WiFi:ON" : "WiFi:OFF"); // Get tile layouts from library helper diff --git a/boards/esp32-32e/DisplayDriverTFT.cpp b/boards/esp32-32e/DisplayDriverTFT.cpp index 6c68333..2fc0f02 100644 --- a/boards/esp32-32e/DisplayDriverTFT.cpp +++ b/boards/esp32-32e/DisplayDriverTFT.cpp @@ -200,7 +200,7 @@ void DisplayDriverTFT::drawDashboard(const ScreenState& st) { _tft.setTextColor(TFT_WHITE, TFT_BLACK); _tft.setTextSize(1); - _tft.setCursor(5, 5); + _tft.setCursor(5, 10); _tft.printf("KLUBHAUS — %s", deviceStateStr(st.deviceState)); int y = 30; diff --git a/justfile b/justfile new file mode 100644 index 0000000..821babf --- /dev/null +++ b/justfile @@ -0,0 +1,177 @@ +# Klubhaus Doorbell - justfile +# Run with: just +# Set BOARD: just BOARD=esp32-32e-4 compile + +# Default recipe - show help +default: + @just --list + +# Set default BOARD +BOARD := "esp32-32e-4" + +# Helper to source board config +# just passes args as positional, so we source in each recipe + +# Compile firmware +compile: + #!/usr/bin/env bash + set -e + source ./boards/{{BOARD}}/board-config.sh + + # Only regenerate compile_commands.json if needed (board changed or first run) + NEED_GEN=false + if [ ! -f compile_commands.json ]; then + NEED_GEN=true + elif [ ! -f .board-last ] || [ "$(cat .board-last)" != "{{BOARD}}" ]; then + NEED_GEN=true + fi + + if [ "$NEED_GEN" = "true" ]; then + rm -rf /tmp/arduino-build + arduino-cli compile --only-compilation-database --fqbn "$FQBN" --libraries ./libraries $LIBS --build-property "compiler.cpp.extra_flags=$OPTS" --build-path /tmp/arduino-build ./boards/{{BOARD}} + cp /tmp/arduino-build/compile_commands.json . + echo "{{BOARD}}" > .board-last + echo "[OK] Generated compile_commands.json for {{BOARD}}" + else + echo "[SKIP] compile_commands.json already up to date for {{BOARD}}" + fi + + arduino-cli compile --fqbn "$FQBN" --libraries ./libraries $LIBS --build-property "compiler.cpp.extra_flags=$OPTS" --warnings default ./boards/{{BOARD}} + +# Upload firmware +upload: kill + #!/usr/bin/env bash + set -e + source ./boards/{{BOARD}}/board-config.sh + arduino-cli upload --fqbn "$FQBN" --port "$PORT" ./boards/{{BOARD}} + +# Kill processes using serial port +kill: + #!/usr/bin/env bash + set +e + source ./boards/{{BOARD}}/board-config.sh + PORT="${PORT:-$PORT}" + echo "Killing processes on $PORT..." + fuser -k "$PORT" 2>/dev/null || true + 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 + done + rm -f "/tmp/doorbell-{{BOARD}}.lock" 2>/dev/null || true + sleep 1 + echo "[OK] Killed processes for {{BOARD}}" + +# Monitor raw serial +monitor-raw: kill + #!/usr/bin/env bash + source ./boards/{{BOARD}}/board-config.sh + PORT="${PORT:-$PORT}" + TARGET="$(readlink -f "$PORT" 2>/dev/null || echo "$PORT")" + arduino-cli monitor -p "$TARGET" --config baudrate=115200 + +# Show tio command +monitor-tio: kill + #!/usr/bin/env bash + source ./boards/{{BOARD}}/board-config.sh + PORT="${PORT:-$PORT}" + TARGET="$(readlink -f "$PORT" 2>/dev/null || echo "$PORT")" + echo "Run: tio --map INLCRNL $TARGET -e" + +# Monitor with JSON logging +monitor: kill + #!/usr/bin/env bash + python3 ./scripts/monitor-agent.py "{{BOARD}}" & + +# Tail colored logs +watch: + #!/usr/bin/env bash + tail -f "/tmp/doorbell-{{BOARD}}.jsonl" | while read -r line; do + ts=$(echo "$line" | python3 -c "import sys,json; print(json.load(sys.stdin)['ts'])" 2>/dev/null) + txt=$(echo "$line" | python3 -c "import sys,json; print(json.load(sys.stdin)['line'])" 2>/dev/null) + if [[ "$txt" == *"[STATE]"* ]]; then + echo -e "\033[1;35m[$ts]\033[0m $txt" + elif [[ "$txt" == *"[ADMIN]"* ]]; then + echo -e "\033[1;36m[$ts]\033[0m $txt" + elif [[ "$txt" == *"[TOUCH]"* ]]; then + echo -e "\033[1;33m[$ts]\033[0m $txt" + elif [[ "$txt" == *"ALERT"* ]]; then + echo -e "\033[1;31m[$ts]\033[0m $txt" + else + echo "[$ts] $txt" + fi + done + +# Send command to device +cmd command: + #!/usr/bin/env bash + echo -n "{{command}}" > "/tmp/doorbell-{{BOARD}}-cmd.fifo" + echo "[SENT] {{command}}" + +# Show device state +state: + @cat "/tmp/doorbell-{{BOARD}}-state.json" + +# Detect connected board +detect: + #!/usr/bin/env bash + bash ./scripts/detect-device.sh + +# Install shared libraries +install-libs-shared: + #!/usr/bin/env bash + ./scripts/install-shared.sh + +# Install all libraries +install: install-libs-shared + #!/usr/bin/env bash + ./boards/{{BOARD}}/install.sh + +# Clean build artifacts +clean: + #!/usr/bin/env bash + rm -rf vendor/ + rm -rf boards/esp32-32e/build + rm -rf boards/esp32-32e-4/build + rm -rf boards/esp32-s3-lcd-43/build + echo "[OK] Build artifacts cleaned" + +# Clean Arduino cache +arduino-clean: + #!/usr/bin/env bash + echo "Checking ~/.arduino15..." + du -sh ~/.arduino15/staging 2>/dev/null || echo "No staging folder" + du -sh ~/.arduino15/packages 2>/dev/null || echo "No packages folder" + read -p "Delete staging + packages folders? [y/N] " -n 1 -r + echo + if [[ $REPLY =~ ^[Yy]$ ]]; then + rm -rf ~/.arduino15/staging + rm -rf ~/.arduino15/packages + echo "[OK] Arduino staging + packages cleared" + else + echo "Aborted" + fi + +# Format code +format: + #!/usr/bin/env bash + clang-format -i --style=file \ + boards/esp32-32e/*.cpp \ + boards/esp32-32e/*.h \ + boards/esp32-32e/*.ino \ + boards/esp32-32e-4/*.cpp \ + boards/esp32-32e-4/*.h \ + boards/esp32-32e-4/*.ino \ + boards/esp32-s3-lcd-43/*.cpp \ + boards/esp32-s3-lcd-43/*.h \ + boards/esp32-s3-lcd-43/*.ino \ + libraries/KlubhausCore/src/*.cpp \ + libraries/KlubhausCore/src/*.h \ + libraries/KlubhausCore/*.properties + +# Generate crush config +gen-crush-config: + #!/usr/bin/env bash + set -e + source ./boards/{{BOARD}}/board-config.sh + printf '{\n "lsp": {\n "arduino": {\n "command": "arduino-language-server",\n "args": ["-fqbn", "%s"]\n },\n "cpp": {\n "command": "clangd"\n }\n }\n}\n' "$FQBN" > .crush.json + echo "[OK] Generated .crush.json with FQBN: $FQBN"