5.9 KiB
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Projektüberblick
AquaMaster MQTT ist die ESP32-Firmware für die Master-Einheit eines "Aquacross / NinjaCross"-Sport-Timers (zwei Bahnen, Start/Stopp-Taster). Der Master stellt einen WiFi-AP und/oder STA bereit, hostet einen MQTT-Broker, einen Async-Webserver mit WebSocket-Live-Updates und kommuniziert mit batteriebetriebenen Funktastern. Die README.md im Repo-Root ist veraltet/falsch (Gitea-MCP-Text) — als Quelle für Projektkontext stattdessen API.md, TODO.md, Bedienungsanleitung_NinjaCross_Timer.html und den Code selbst nutzen.
Build & Flash (PlatformIO)
Default-Environment ist esp32thing_CI (siehe platformio.ini). Weitere Envs: wemos_d1_mini32, esp32thing, esp32thing_OTA (OTA an 192.168.1.96), um_feathers3, um_feathers3_debug.
pio run # Default-Env bauen
pio run -e esp32thing # Spezifisches Env
pio run -e esp32thing -t upload # Flashen
pio run -e esp32thing -t buildfs # SPIFFS-Image aus data/ bauen
pio run -e esp32thing -t uploadfs # SPIFFS flashen (data/ → ESP)
pio device monitor -b 115200 # Serieller Monitor
pio run -e esp32thing_OTA -t upload # OTA-Upload (Ziel-IP in platformio.ini)
Tests gibt es nicht — test/ enthält nur ein leeres README.
CI
.github/workflows/build.yml baut bei jedem Push firmware.bin und spiffs.bin mit pio run -e esp32thing_CI und erzeugt automatisch ein GitHub-Release mit Tag esp32thing-<datum>-<sha7>. Wenn der Build lokal funktioniert, aber CI nicht, ist esp32thing_CI (board=esp32dev, platform=espressif32) die maßgebliche Konfiguration.
Architektur (das Wesentliche)
Header-only-Pattern (wichtig!)
Es gibt nur eine .cpp-Datei: src/master.cpp. Alle anderen Module unter src/*.h enthalten sowohl Deklarationen als auch Implementierungen und definieren teilweise globale Objekte (z. B. AsyncWebServer server(80) in webserverrouter.h, Preferences preferences in licenceing.h, PicoMQTT::Server mqtt in communication.h). Konsequenzen:
- Jeder dieser Header darf nur in
master.cppinkludiert werden, sonst gibt es Multiple-Definition-Linkerfehler. - Header inkludieren sich gegenseitig (
master.h↔webserverrouter.h↔communication.h). Beim Hinzufügen neuer Header die bestehende Include-Reihenfolge inmaster.cppbeibehalten. - Globale Timer-/Button-State-Variablen (
timerData1,timerData2,buttonConfigs,localTimes,learningMode,gamemode, …) leben insrc/master.hund werden überall direkt referenziert.
Wer eine neue Datei anlegt: entweder als weiteren Header dem Pattern folgen und in master.cpp einklinken, oder bewusst eine echte .cpp mit extern-Deklarationen erstellen.
Laufzeit-Module
master.cpp—setup()/loop(). Reihenfolge insetup()ist relevant (SPIFFS → API-Setups →load*()aus Preferences → WiFi → OTA → Routes → WebSocket → MQTT → RFID).loop()priorisiert MQTT vor WebSocket vor RFID.communication.h— PicoMQTT-Broker. Tasten publishen aufaquacross/button/<MAC>;readButtonJSON()parst, ordnet die MAC einer der vier Rollen (start1/stop1/start2/stop2) zu und triggert die Timerlogik. Hält pro MACTimestampDatafür Drift-Berechnung.webserverrouter.h—ESPAsyncWebServerauf Port 80 + WebSocket/ws. Liefert statische Seiten aus SPIFFS (/,/settings,/leaderboard,/rfid) und alle/api/...-Endpunkte. Vollständige Routenliste inAPI.md.wificlass.h— AP-Modus auf192.168.10.1(eindeutiger SSID-Suffix), STA-Fallback wenn gespeicherte Credentials vorhanden. BindetPrettyOTA(lokale Bibliothek unterlib/PrettyOTA/) und mDNS ein.preferencemanager.h— Persistierung in NVS (Preferences). Namespaces u. a.buttons,leaderboard, plus WiFi-/Location-/Settings-Slots. Beim Ändern persistierter Strukturen (z. B.ButtonConfigs) auf Größenkompatibilität achten —loadButtonConfig()lädt nur, wenngetBytesLength == sizeof(buttonConfigs).licenceing.h— HMAC-SHA256 (mbedtls) gegensecretüber die STA-MAC; bestimmt Tier/Online-Funktionen. Lizenz wird zusammen mit jeder Backend-Anfrage alsAuthorization: Bearer …gesendet.databasebackend.h— HTTPS-Client gegenhttps://ninja.reptilfpv.de(Locations, Leaderboard-Upload, Health). Funktioniert nur bei verbundenem STA + gültiger Lizenz.rfid.h— Adafruit PN532 (I²C/SPI). Liest UIDs nur, wennisRFIDReadingActive(); UID landet inTimerData*::RFIDUIDund wird mit Namen auslocalUsers/Backend verknüpft.gamemodes.h— Modus0=individual,1=wettkampf; steuert, wann Timer als „bereit/armiert/laufend" gilt und wie Bestzeiten abgelegt werden (lokaleslocalTimes-Vektor + optional Backend).timesync.h/debug.h/statusled.h/battery.h/buttonassigh.h/helper.h— Hilfsmodule (NTP/Zeitzone, Debug-API, Status-LED, Akku, Lerne-Mode für Tasten-Zuordnung).
Web-Frontend
data/ enthält index.html, settings.html, leaderboard.html, rfid.html plus zugehörige CSS und ein pictures/-Verzeichnis. Diese Dateien werden via pio run -t uploadfs ins SPIFFS geschrieben und vom Webserver direkt ausgeliefert. Frontend-Änderungen erfordern ein erneutes uploadfs — ein normaler Firmware-Upload aktualisiert sie nicht.
data/firmware.bin wird unter /firmware.bin ausgeliefert (Buttons können sich darüber selbst aktualisieren).
API
Vollständige HTTP-/WebSocket-API in API.md (autoritativ; apientpoints ist eine ältere Kurzversion). Alle POST-Routen erwarten Form-Parameter, kein JSON-Body. Antworten sind JSON, außer bei statischen Dateien.
Sprache
Code-Kommentare und einige Variablennamen sind deutsch (bahn, wettkampf, „Anlernmodus"). Beim Erweitern bei der vorhandenen Sprache bleiben statt halb zu übersetzen.