13 KiB
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
-
1.1
platformio.inimit allen benötigten Bibliotheken erweiternMD_Parola,MD_MAX72XXPubSubClientWiFiManager(tzapu/WiFiManager)ESPAsyncWebServer+AsyncTCPArduinoJsonElegantOTAPreferences(ESP32 built-in, kein Extra-Eintrag nötig)
-
1.2
include/config.herstellen – 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
-
1.3 Build prüfen (leeres Projekt kompiliert fehlerfrei)
-
1.4 Test Verdrahtung Dot-Matrix-Display
- GYMAX7219-Module gemäß Pinbelegung angeschlossen (MOSI GPIO 23, CLK GPIO 18, CS GPIO 5)
- Hardware-Typ
GENERIC_HW(verifiziert; physische 90° Verdrehung per Software CCW kompensiert) - Externes 5 V-Netzteil erforderlich (~0,5 A / ~2,5 W gemessen; USB-Port reicht nicht)
- SPI-Taktrate auf 1 MHz reduziert für stabile Ansteuerung aller 8 Module
- 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
-
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.cpperstellen
Speichert und lädt alle Konfigurationswerte über ESP32Preferences(NVS):- MQTT Broker IP, Port, User, Passwort
- Gratiszeit (0–120 s)
- Signal-Polarität (
LOW_ACTIVE/HIGH_ACTIVE) - Akkumulierte Laserzeit (float, Minuten)
- Globale Instanz
settings,settings.begin()inmain.cpp
-
2.2 Unit-Test (Serial-Output): Werte schreiben, ESP32 neu starten, Werte lesen und verifizieren
test_sketches/test_nvs.cpperstellt,test-nvsEnvironment inplatformio.ini- Round-Trip-Test + Persistenz-Check über Neustart
Phase 3 – WiFi & WiFiManager
-
3.1 WiFiManager einbinden
include/wifi_connector.h+src/wifi_connector.cpperstellt- AP-Name:
LaserCutter-Setup(ausconfig.h) - Captive-Portal-Seite für WLAN-Credentials
- Timeout: 120 s, danach
ESP.restart()
-
3.2 Verbindungsstatus-Callback implementieren (für spätere Fehleranzeige)
WifiStatusCallback-Typ +onStatusChange()Methode- Status-Enum:
DISCONNECTED,AP_ACTIVE,CONNECTING,CONNECTED - Globale Instanz
wifiConnector,wifiConnector.begin()+.loop()inmain.cpp
-
3.3 Test: Erstverbindung mit neuem AP, gespeichertes WLAN nach Neustart automatisch verbinden
test_sketches/test_wifi.cpperstellt,test-wifiEnvironment inplatformio.ini- BOOT-Taste (GPIO 0, 3 s) löscht Credentials und erzwingt Portal
- Erstverbindung via Captive Portal ✅, Auto-Reconnect nach Neustart ✅
Phase 4 – Dot-Matrix-Display (DisplayManager)
-
4.1
include/display_manager.h+src/display_manager.cpperstellen
Implementierung mit rohemMD_MAX72XX(nicht MD_Parola) für vollständige Rotationskontrolle:- Zone 0 (Module 0–3, obere Reihe): Laserzeit in Minuten
- Zone 1 (Module 4–7, untere Reihe): Countdown / Statusmeldungen
rotateCCW()kompensiert physische 90°-CW-Verdrehung der Module- 17 Zeichen-Bitmaps definiert (0–9, Sonderzeichen, Buchstaben)
-
4.2 Methoden implementiert:
showLaserTime(float minutes)– obere Zeile (4 Formate je Wertebereich)showCountdown(int seconds)– untere Zeile, rechtsbündig (während Gratiszeit)showIdle()–--in unterer Zeile wenn kein Countdown aktivshowStatus(const char* msg)– max. 4-Zeichen-Statusmeldung (untere Zeile)setBrightness(),allLedsOn(),allLedsOff(),clear(),printToSerial()update()– inloop()aufrufen (no-op, Interface für künftige Animationen)main.cppintegriert:display.begin(),display.showIdle(),display.update()
-
4.3 Test:
test_sketches/test_display_manager.cpp, Environmenttest-display-mgr- Alle LEDs EIN/AUS ✅
showLaserTime()alle 12 Grenzwerte (0.0 – 9999.0 min) ✅showCountdown()5→0 ✅showIdle()→--✅showStatus()mit „Err ", „AP ", „WiFi", „ oF" ✅- Realistischer Loop: Laserzeit steigt, Countdown 20→0, dann Idle ✅
Phase 5 – Laser-Signaldetektion & Zeit-Tracking (LaserTracker)
-
5.1
include/laser_tracker.h+src/laser_tracker.cpperstellt- Globale Instanz
laserTracker, inmain.cppintegriert
- Globale Instanz
-
5.2 GPIO-Eingang konfiguriert:
INPUT_PULLUPauf GPIO 4- Software-Debounce 50 ms (
LASER_DEBOUNCE_MSausconfig.h) - Polarität aus
settings.get().signalPolarity(LOW_ACTIVE / HIGH_ACTIVE)
-
5.3 Session-Logik implementiert:
onSessionStart()intern →_sessionActive = true, Timer setzenonSessionEnd()intern → Netto-Sekunden berechnen,_totalMinutesBaseaddieren, NVS viasettings.saveTotalMinutes()
-
5.4 Gratiszeit-Logik:
- Countdown läuft ab Session-Start
getSessionSeconds()gibt erst nach Ablauf der Gratiszeit > 0 zurückgetCountdownRemaining()→ verbleibende Sekunden (0 wenn abgelaufen / inaktiv)getTotalMinutes()= NVS-Basis + laufende Session-Netto-Minuten (Live-Anzeige)
-
5.5 Öffentliche Getter:
getTotalMinutes()→ float (Live)getSessionSeconds()→ int (Netto ohne Gratiszeit)getCountdownRemaining()→ int (verbleibende Gratiszeit)isActive()→ boolgetLastSessionSeconds()→ int (letzte abgeschlossene Session, für MQTT)resetTotal()→ Gesamtzeit auf 0 + NVS-Speicherung
-
5.6 Test:
test_sketches/test_laser_tracker.cpp, Environmenttest-laser-tracker- Button GPIO 4 simuliert Laser-Signal
- BOOT-Taste (GPIO 0, 3 s) löscht Gesamtzeit
- Boot-Output: NVS-Werte geladen, Display zeigt Basiswert + Idle ✅
- Manuelle Verifikation: Button drücken → Countdown, Netto-Zeit, Session-Ende → NVS
-
5.7
showSessionRing(int seconds)– Kreisanzeige auf Modulen 5–7, 12-Uhr-Start, Uhrzeigersinn (Phase-5-Erweiterung)- 60 LEDs auf 3 Modulen (je 8×8, aber nur die äußere Reihe vollständig umlaufend)
- Jede Sekunde NET_COUNTING: eine weitere LED zugeschaltet
- Nach 60 Sekunden: Kreis voll, Minutenanzeige (Module 1–3) inkrementiert
- Laser-AUS: Anzeige zurück auf
-- - Implementierung in
display_manager.h/cpp - Aufruf in
main.cppwährendNET_COUNTINGstattshowIdle()
Phase 6 – MQTT Client (MqttClient)
-
6.1
include/mqtt_client.h+src/mqtt_client.cpperstellen
Wrapper umPubSubClient -
6.2 Verbindungsaufbau mit Credentials aus
Settings(TLS automatisch bei Port 8883) -
6.3 Publish-Methode
publishSession(sessionSec, summeSession, gratisSec):- Topic:
lasercutter/session - JSON-Felder (aktualisiert):
session_minuten(int, ceiling: 62 s = 2 min)session_seconds(int, Rohwert Netto-Sekunden)gratiszeit_s(int)ip(string)
- Kein
total_min, keinsession_start_time(NTP später) - Wird von LaserTracker-Logik in main aufgerufen
- Topic:
-
6.4 Publish-Methode
publishHeartbeat(totalMin, ipStr, uptimeSec):- Topic:
lasercutter/status(retained) - Heartbeat alle 30 Sekunden
- LWT
{"online":false}bei Verbindungsverlust - JSON-Felder (aktualisiert):
online(bool)session_sum(string, Summe Session in Minuten als string mit 2 Dezimalen)machine_running_time_min(string, Maschinenlaufzeit aus NVS)ip(string)uptime_s(int)
- Topic:
-
6.5 Subscribe auf
lasercutter/reset:- Payload
"1",{"reset":true}oder{"reset":1}→LaserTracker::resetTotal() - QoS 1
- Payload
-
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
⚠️ Offen: MQTT-Error Modul 5 bei Verbindungsverlust, Reconnect nach WiFi-Ausfall
Phase 7 – Webinterface (WebServer)
-
7.1
include/web_server.h+src/web_server.cpperstellen
Basierend aufESPAsyncWebServer -
7.2 Route
GET /– Statusseite (HTML):- Summe Session (Minuten, RAM), Maschinenlaufzeit gesamt (NVS)
- WLAN-Status (IP), MQTT-Status (verbunden/getrennt)
- Broker-Info, Gratiszeit
- Auto-Reload alle 10 s
-
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 (viaSettings, NVS)
Redirect + Hinweis „Neustart für MQTT-Änderungen" -
7.5 Route
POST /reset– Session zurücksetzen (laserTracker.resetSession())
Gesamtzeit (NVS) bleibt erhalten; Wartungsreset nur per BOOT-Taste oder MQTT -
7.6 ElegantOTA unter
/updateeinbinden
FlagELEGANTOTA_USE_ASYNC_WEBSERVER=1inplatformio.ini -
7.7 Test: Alle Seiten im Browser aufrufen, Konfiguration ändern und nach Neustart prüfen
Phase 8 – Integration & Hauptprogramm (main.cpp)
-
8.1
main.cppaufräumen und alle Module initialisieren:- Reihenfolge:
Settings→DisplayManager→WiFiManager→MqttClient→WebServer→LaserTracker
- Reihenfolge:
-
8.2
loop()implementieren:displayManager.update()laserTracker.update()(GPIO-Polling + Session-Logik)mqttClient.loop()(Reconnect + Heartbeat)displayManager.showLaserTime(laserTracker.getTotalMinutes())displayManager.showCountdown(...)odershowIdle()odershowError(...)
-
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 via
web_server.cppeingebunden (kein separaterloop()-Aufruf nötig)ELEGANTOTA_USE_ASYNC_WEBSERVER=1→ vollständig async, keinElegantOTA.loop()inmain.cpp- Bugfix: Include-Konflikt
ESPAsyncWebServer↔WiFiManager(HTTP_GET/POST-Enum-Clash) gelöst via PIMPL-Pattern:web_server.hnur Forward-DeclarationAsyncWebServer*, vollständiger#include <ESPAsyncWebServer.h>ausschließlich inweb_server.cpp
-
9.2 Serielles Logging vereinheitlichen:
LOG_D-Makro inconfig.hergänzt- Aktiv wenn
CORE_DEBUG_LEVEL >= 3, sonst no-op
- Aktiv wenn
-
9.3 Speicherleck-Monitoring:
ESP.getFreeHeap()alle 30 s viaLOG_Dinloop() -
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.inimit 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 30 s |
lasercutter/reset |
Subscribe | 1 | Externer Reset-Befehl |
MQTT JSON-Formate
lasercutter/session
{
"session_minuten": 2,
"session_seconds": 125,
"gratiszeit_s": 20,
"ip": "192.168.1.100"
}
lasercutter/status (retained)
{
"online": true,
"session_sum": "2.08",
"machine_running_time_min": "1234.75",
"ip": "192.168.1.100",
"uptime_s": 3600
}
Erstellt: 22. Februar 2026