Signed-off-by: Peter Siegmund <developer@mars3142.org>
5.2 KiB
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Project Overview
ESP32 firmware for a model railway system control unit, targeting ESP32-C6 microcontrollers. Built with ESP-IDF 5.5. Includes a Svelte 5 web UI (website/) and a REST/WebSocket/MQTT API.
ESP-IDF Environment Setup
ESP-IDF is not on PATH by default. The installation lives at a non-standard path; export.sh must be sourced before idf.py is available.
# One-time per shell session — activates idf.py, xtensa/riscv toolchains, etc.
. /Users/mars3142/.espressif/v5.5.3/esp-idf/export.sh
If export.sh cannot find the Python environment it will print an error like
doesn't exist! Please run the install script. In that case invoke idf.py
directly via the venv Python, which bypasses the PATH check:
IDF_PYTHON_ENV_PATH=/Users/mars3142/.espressif/tools/python/v5.5.3/venv \
IDF_TOOLS_PATH=/Users/mars3142/.espressif/tools \
/Users/mars3142/.espressif/tools/python/v5.5.3/venv/bin/python \
/Users/mars3142/.espressif/v5.5.3/esp-idf/tools/idf.py <command>
Key paths:
- IDF root:
/Users/mars3142/.espressif/v5.5.3/esp-idf/ - Python venv:
/Users/mars3142/.espressif/tools/python/v5.5.3/venv/ - IDF tools:
/Users/mars3142/.espressif/tools/
Build Commands
# After sourcing export.sh:
# Firmware (ESP32-C6, default target)
idf.py -DIDF_TARGET=esp32c6 build
# Flash everything (overwrites SPIFFS — paired Thread devices list is lost)
idf.py -p <PORT> flash
# Flash only the app binary (preserves SPIFFS and NVS — use this during development)
idf.py -p <PORT> app-flash
# Flash only the SPIFFS partition (updates menu.json / web assets without touching NVS)
idf.py -p <PORT> storage-flash
idf.py -p <PORT> flash monitor
# Release build (ESP32-C6 only)
make release
# Equivalent: idf.py -B build-release -DSDKCONFIG_DEFAULTS="sdkconfig.defaults;sdkconfig.release" -DIDF_TARGET=esp32c6 fullclean build size
# Web UI
cd website && npm install && npm run build
cd website && npm run test
Architecture
main/— Firmware entry (app_main()inmain.cpp, core app task inapp_task.cpp)components/— ESP-IDF components with isolated business logic:- bifrost (
api-server) — HTTP REST + WebSocket server, mDNS, static file serving - connectivity-manager — WiFi, BLE, captive portal / DNS hijacking
- led-manager — WS2812 strip control, effects, status LED
- message-manager — Observer/broadcast bus for cross-component events
- persistence-manager — NVS abstraction with namespace-scoped typed read/write
- mercedes — Dynamic menu data model (C++ singleton, JSON-driven)
- hermes — u8g2 display rendering (menu, splash, screensaver)
- heimdall — Button/action manager with callback registration
- simulator — Day/night light cycle simulation from CSV schedules
- iris — Thread network device manager; split into
iris.c(public API + state),iris_storage.c,iris_coap.c,iris_discovery.c,iris_master.c,iris_inventory.c; shared internal header atinclude/iris/iris_internal.h
- bifrost (
storage/— Runtime SPIFFS content:menu.json,schema_*.csv,www/(web assets)website/— Svelte 5 + Vite + Tailwind web UI
Multi-target Support
Two targets are supported with distinct pin assignments and sdkconfig defaults:
sdkconfig.defaults— base (shared)sdkconfig.defaults.esp32s3— S3 overrides (obsolete)sdkconfig.defaults.esp32c6— C6 overrides (includes WiFi enable/antenna GPIO)
Do not assume one target's pins or settings apply to the other.
Task Structure
FreeRTOS tasks in priority order:
app_main()— hardware init, startsapp_taskapp_task— main logic on core 1, prioritytskIDLE_PRIORITY + 5, 8KB stackdisplay_update_task— async I2C display writes
Conventions
C/C++ Style
- Braces on new line in C files; follow existing formatting per file.
- Each source file has a local
static const char *TAG = "..."forESP_LOGI/W/E. - HTTP handlers must use
set_cors_headers,send_json_response, andsend_error_responsefor consistency. - Cross-component state changes go through
message_manager_post(...), not direct coupling. - Persist settings via
persistence_managerwith a dedicated namespace per feature.
URI Handler Registration Order (api-server)
- Specific
/api/...handlers - Wildcard API handlers
- Captive-portal detection handlers
OPTIONS /api/*- Static fallback
/*(last)
WebSocket handling must be initialized before API handler registration.
Static Asset Serving
Preserve correct HTTP headers: content-type, CORS, and Content-Encoding: gzip when serving .gz variants.
Key Reference Files
README-API.md— full REST API documentationREADME-menu.md—menu.jsonschemaREADME-captive.md— captive portal behaviorcomponents/bifrost/src/api_server.c— server setup and handler registrationcomponents/bifrost/src/api_handlers.c— REST endpoint implementationscomponents/message-manager/src/message_manager.ccomponents/persistence-manager/src/persistence_manager.cpartitions.csv— flash layout (NVS 16KB, APP 2MB, SPIFFS 1.8MB)