# Implementation Plan – Laser Cutter MQTT Display > Dieses Dokument beschreibt die Entwicklungsreihenfolge des Projekts. > Tasks werden während der Entwicklung mit `[x]` abgehakt. --- ## Phase 1 – Projekt-Setup & Konfiguration - [x] **1.1** `platformio.ini` mit allen benötigten Bibliotheken erweitern - `MD_Parola`, `MD_MAX72XX` - `PubSubClient` - `WiFiManager` (tzapu/WiFiManager) - `ESPAsyncWebServer` + `AsyncTCP` - `ArduinoJson` - `ElegantOTA` - `Preferences` (ESP32 built-in, kein Extra-Eintrag nötig) - [x] **1.2** `include/config.h` erstellen – zentrale Pin-Definitionen und Konstanten - GPIO-Pins (MAX7219 SPI, Laser-Signal) - Anzahl der Module (8), Zonen-Aufteilung - Standard-Werte (Gratiszeit 20s, MQTT Port 1883, etc.) - MQTT-Topic-Konstanten - [x] **1.3** Build prüfen (leeres Projekt kompiliert fehlerfrei) - [x] **1.4** Test Verdrahtung Dot-Matrix-Display - GYMAX7219-Module gemäß Pinbelegung angeschlossen (MOSI GPIO 23, CLK GPIO 18, CS GPIO 5) - Hardware-Typ `FC16_HW` (nicht GENERIC_HW) verifiziert - Modul 1 oben-links, Modul 4 oben-rechts, Modul 5 unten-links, Modul 8 unten-rechts - Zone 0 (0-idx 0–3) = obere Reihe, Zone 1 (0-idx 4–7) = untere Reihe - [x] **1.5** Test Verdrahtung Potentialfreier Schalter (Phase 1: Push Button) - Push Button an GPIO 4 und GND angeschlossen (INPUT_PULLUP, kein externer Widerstand nötig) - Pegel verifiziert: Button offen = HIGH, Button gedrückt = LOW → Polarität: LOW_ACTIVE - Debounce 50 ms funktioniert --- ## Phase 2 – NVS Persistenz (`Settings`) - [ ] **2.1** `include/settings.h` + `src/settings.cpp` erstellen Speichert und lädt alle Konfigurationswerte über ESP32 `Preferences` (NVS): - MQTT Broker IP, Port, User, Passwort - Gratiszeit (0–120 s) - Signal-Polarität (`LOW_ACTIVE` / `HIGH_ACTIVE`) - Akkumulierte Laserzeit (float, Minuten) - [ ] **2.2** Unit-Test (Serial-Output): Werte schreiben, ESP32 neu starten, Werte lesen und verifizieren --- ## Phase 3 – WiFi & WiFiManager - [ ] **3.1** WiFiManager einbinden - AP-Name: `LaserCutter-Setup` - Captive-Portal-Seite für WLAN-Credentials - Timeout: 120 s, danach Neustart - [ ] **3.2** Verbindungsstatus-Callback implementieren (für spätere Fehleranzeige) - [ ] **3.3** Test: Erstverbindung mit neuem AP, gespeichertes WLAN nach Neustart automatisch verbinden --- ## Phase 4 – Dot-Matrix-Display (`DisplayManager`) - [ ] **4.1** `include/display_manager.h` + `src/display_manager.cpp` erstellen Wrapper um `MD_Parola` mit zwei Zonen: - Zone 0 (Module 0–3, obere Reihe): Laserzeit in Minuten - Zone 1 (Module 4–7, untere Reihe): Countdown / Statusmeldungen - [ ] **4.2** Methoden implementieren: - `showLaserTime(float minutes)` – obere Zeile - `showCountdown(int seconds)` – untere Zeile (während Gratiszeit) - `showIdle()` – `---` in unterer Zeile wenn kein Countdown - `showError(const char* msg)` – Fehlermeldung in unterer Zeile, Modul 1 - `update()` – in `loop()` aufrufen (MD_Parola benötigt regelmäßigen Aufruf) - [ ] **4.3** Test: Statische Zahlen und Scrolltext auf beiden Zonen anzeigen --- ## Phase 5 – Laser-Signaldetektion & Zeit-Tracking (`LaserTracker`) - [ ] **5.1** `include/laser_tracker.h` + `src/laser_tracker.cpp` erstellen - [ ] **5.2** GPIO-Eingang konfigurieren: - `INPUT_PULLUP` auf GPIO 4 - Software-Debounce (min. 50 ms Entprellzeit) - Polarität über `Settings` auslesbar - [ ] **5.3** Session-Logik implementieren: - `onLaserActive()` → Session-Start, Timer starten - `onLaserInactive()` → Session-Ende, akkumulierte Zeit aktualisieren, NVS speichern - [ ] **5.4** Gratiszeit-Logik: - Countdown läuft ab Session-Start - Zeit wird erst nach Ablauf der Gratiszeit zur Gesamtzeit addiert - `getCountdownRemaining()` → Rückgabe verbleibende Sekunden (0 wenn abgelaufen) - [ ] **5.5** Öffentliche Getter: - `getTotalMinutes()` → float - `getSessionSeconds()` → int (aktuelle Session ohne Gratiszeit) - `isActive()` → bool - `getLastSessionSeconds()` → int (für MQTT Publish) - [ ] **5.6** Test: Laser-Signal simulieren (GPIO auf GND ziehen), Zeitmessung verifizieren --- ## Phase 6 – MQTT Client (`MqttClient`) - [ ] **6.1** `include/mqtt_client.h` + `src/mqtt_client.cpp` erstellen Wrapper um `PubSubClient` - [ ] **6.2** Verbindungsaufbau mit Credentials aus `Settings` - [ ] **6.3** Publish-Methode `publishSession(sessionSec, totalMin, gratisSec)`: - Topic: `lasercutter/session` - JSON mit `session_s`, `total_min`, `gratiszeit_s`, `ts` - Wird von `LaserTracker` beim Session-Ende aufgerufen - [ ] **6.4** Publish-Methode `publishStatus(totalMin, ipStr, uptimeSec)`: - Topic: `lasercutter/status` - Heartbeat alle 60 Sekunden - QoS 0 - [ ] **6.5** Subscribe auf `lasercutter/reset`: - Payload `"1"` oder `{"action":"reset"}` → `LaserTracker::resetTotal()` - QoS 1 - [ ] **6.6** Reconnect-Logik (Non-blocking, max. alle 10 Sekunden versuchen) - [ ] **6.7** Test: Session-Daten mit MQTT Explorer empfangen, Reset auslösen und verifizieren --- ## Phase 7 – Webinterface (`WebServer`) - [ ] **7.1** `include/web_server.h` + `src/web_server.cpp` erstellen Basierend auf `ESPAsyncWebServer` - [ ] **7.2** Route `GET /` – Statusseite (HTML): - Aktuelle Laserzeit (Minuten) - Letzter Session-Wert (Sekunden) - WLAN-Status, MQTT-Status - IP-Adresse - [ ] **7.3** Route `GET /config` – Konfigurationsformular (HTML): - MQTT Broker IP, Port, User, Passwort - Gratiszeit (Slider 0–120 s) - Signal-Polarität (Radio-Button) - [ ] **7.4** Route `POST /config` – Konfiguration speichern (via `Settings`, NVS) - [ ] **7.5** Route `POST /reset` – Laserzeit zurücksetzen (Button auf Statusseite) - [ ] **7.6** ElegantOTA unter `/update` einbinden - [ ] **7.7** Test: Alle Seiten im Browser aufrufen, Konfiguration ändern und nach Neustart prüfen --- ## Phase 8 – Integration & Hauptprogramm (`main.cpp`) - [ ] **8.1** `main.cpp` aufräumen und alle Module initialisieren: - Reihenfolge: `Settings` → `DisplayManager` → `WiFiManager` → `MqttClient` → `WebServer` → `LaserTracker` - [ ] **8.2** `loop()` implementieren: - `displayManager.update()` - `laserTracker.update()` (GPIO-Polling + Session-Logik) - `mqttClient.loop()` (Reconnect + Heartbeat) - `displayManager.showLaserTime(laserTracker.getTotalMinutes())` - `displayManager.showCountdown(...)` oder `showIdle()` oder `showError(...)` - [ ] **8.3** Callback von `LaserTracker` → `MqttClient.publishSession(...)` verdrahten - [ ] **8.4** WLAN/MQTT-Fehlerzustand auf Display spiegeln - [ ] **8.5** Watchdog Timer aktivieren (ESP32 WDT, 30 s) --- ## Phase 9 – OTA & Stabilisierung - [ ] **9.1** ElegantOTA in `main.cpp` initialisieren und in `loop()` einbinden - [ ] **9.2** Serielles Logging vereinheitlichen (Log-Level: INFO / DEBUG per `#define`) - [ ] **9.3** Langzeit-Test: 24h Betrieb, Speicherleck-Check via Serial Monitor (`ESP.getFreeHeap()`) - [ ] **9.4** Edge-Cases testen: - Laser aktiv, WLAN wird getrennt → Display läuft weiter - MQTT-Broker nicht erreichbar → Reconnect - Reset während aktiver Session - Neustart nach akkumulierter Zeit → Zeit korrekt aus NVS geladen --- ## Phase 10 – Dokumentation & Abschluss - [ ] **10.1** README.md finalisieren (Screenshots, Schaltplan-Skizze ggf. ergänzen) - [ ] **10.2** `platformio.ini` mit finalen Library-Versionen dokumentieren - [ ] **10.3** MQTT-Topics in README.md validieren (mit realem Broker testen) - [ ] **10.4** Optionaler Schaltplan (Fritzing / ASCII) für Laser-Signal-Anschluss (Optokoppler) --- ## Abhängigkeitsdiagramm (Reihenfolge) ``` Phase 1 (Setup) └── Phase 2 (NVS/Settings) ├── Phase 3 (WiFi) ├── Phase 4 (Display) └── Phase 5 (LaserTracker) └── Phase 6 (MQTT) └── Phase 7 (Webinterface) └── Phase 8 (Integration) └── Phase 9 (OTA/Stabilisierung) └── Phase 10 (Doku) ``` --- ## Verwendete MQTT-Topics (Überblick) | Topic | Richtung | QoS | Auslöser | |------------------------|-----------|-----|-------------------------------| | `lasercutter/session` | Publish | 1 | Session-Ende (Laser inaktiv) | | `lasercutter/status` | Publish | 0 | Heartbeat alle 60 s | | `lasercutter/reset` | Subscribe | 1 | Externer Reset-Befehl | --- *Erstellt: 22. Februar 2026*