MQTT-Display-LaserCutter/README.md

31 KiB
Raw Blame History

Laser Cutter Dot Matrix Display und MQTT Client

Dieses Projekt implementiert einen ESP32-basierten MQTT-Client mit Dot-Matrix-Display für einen Laser Cutter. Es misst und visualisiert die aktive Laserzeit, sendet Session-Daten an einen MQTT-Broker und bietet ein Browser-Webinterface zur Konfiguration und Steuerung.


Inhaltsverzeichnis

  1. Hardware
  2. Pinbelegung
  3. Display
  4. Laser-Signaldetektion
  5. Zeit-Tracking & Gratiszeit
  6. MQTT Client
  7. Webinterface
  8. Konfiguration & Persistenz
  9. WiFi-Setup (WiFiManager)
  10. OTA Firmware-Update
  11. Remote-Debugging (Web Console)
  12. Fehlerverhalten
  13. Bibliotheken
  14. Build & Flash
  15. Tests
  16. Beitragen / Commits

Skizze und Screenshots

Front-Ansicht des Laser Cutter Displays

Das Bild zeigt die physische Anordnung der 8 GYMAX7219-Module im 4×2-Format mit LED-Positionen und Beschriftung der Anzeigebereiche.

Laser Cutter Display

LaserCutter Display

🔒 Laser Cutter Setup and Status

Konfiguration

🔒 OTA Firmware-Update Seite

OTA Firmware-Update

🔒 Log Console

Web Log Console

Hardware

Komponente Modell / Beschreibung
Mikrocontroller AZ-Delivery ESP32 DevKit V4 EPS32-WROOM (Dual Core)
Dot-Matrix-Display 8× GYMAX7219 Module (kompatibel zu MAX7219), 8×8 LEDs je Modul, Typ: GENERIC_HW
Display-Anordnung 4 Module nebeneinander × 2 Reihen = 32×16 LEDs
Display-Kaskadierung Alle 8 Module in einer einzigen SPI-Kette
Stromversorgung Display Externes 5 V-Netzteil erforderlich (gemessen: ~0,5 A / ~2,5 W bei 8 Modulen, Vollast bis ~2 A möglich) ESP32 USB-Port reicht nicht aus
Laser-Aktivsignal Potentialfreier Ausgang des Laser Cutters (Optokoppler empfohlen)
Optional Shelly PM Mini G3 als externer Leistungszähler (separater MQTT-Service)

Optkoppler Schaltung


Pinbelegung

Signal ESP32 GPIO Beschreibung
MAX7219 MOSI GPIO 23 SPI Data (VSPI) → DIN Modul 1
MAX7219 CLK GPIO 18 SPI Clock (VSPI)
MAX7219 CS GPIO 5 SPI Chip Select (alle Module)
Laser-Signal GPIO 4 Potentialfreier Eingang (INPUT_PULLUP, konfigurierbar)

Die Polarität des Laser-Signals (LOW_ACTIVE oder HIGH_ACTIVE) ist über das Webinterface konfigurierbar.

SPI-Ketten-Schaltbild

 ESP32                     OBERE REIHE                            UNTERE REIHE

 GPIO 23 (MOSI) ─── DIN  ┌──────┐ DOUT─DIN ┌──────┐ DOUT─DIN ┌──────┐ DOUT─DIN ┌──────┐
                         │ M 1  │          │ M 2  │          │ M 3  │          │ M 4  │
                         │oben  │          │      │          │      │          │oben  │
                         │links │          │      │          │      │          │rechts│
 GPIO 18 (CLK)  ─────────┤ CLK  ├──────────┤ CLK  ├──────────┤ CLK  ├──────────┤ CLK  ├──
 GPIO  5 (CS)   ─────────┤ CS   ├──────────┤ CS   ├──────────┤ CS   ├──────────┤ CS   ├──
                         └──────┘          └──────┘          └──────┘          └──┬───┘
                      ┌───────────────────────────────────────────────────────────┘ DOUT
                      │  ┌──────┐ DOUT─DIN ┌──────┐ DOUT─DIN ┌──────┐ DOUT─DIN ┌──────┐
                      │  │ M 5  │          │ M 6  │          │ M 7  │          │ M 8  │
                      └─ │unten │          │      │          │      │          │unten │
                         │rechts│          │      │          │      │          │links │
 GPIO 18 (CLK)  ─────────┤ CLK  ├──────────┤ CLK  ├──────────┤ CLK  ├──────────┤ CLK  │
 GPIO  5 (CS)   ─────────┤ CS   ├──────────┤ CS   ├──────────┤ CS   ├──────────┤ CS   │
                         └──────┘          └──────┘          └──────┘          └──────┘

Hinweis: Die untere Reihe läuft im Schaltbild von ebenfalls von links nach rechts, weil DOUT von Modul 4 zu DIN von Modul 5 geführt wird.


Display

Das Display besteht aus 8 GYMAX7219-Modulen in einer 4×2-Anordnung (32×16 LEDs gesamt).
Es ist in zwei Zonen aufgeteilt, die unabhängig voneinander beschrieben werden.

Physisches Layout

  DIN ← ESP32 GPIO 23
  ↓
 ┌─────────┬─────────┬─────────┬─────────┐
 │ Modul 1 │ Modul 2 │ Modul 3 │ Modul 4 │  ← Zone 0 (oben)   0-idx: 03
 │ oben    │         │         │ oben    │    idx 0    -> Anzeige: WiFi-Fehler (E = Fehler, leer = OK)
 │ links   │         │         │ rechts  │    idx 1 -3 -> Anzeige: Summe Laserzeit (Minuten) -> Summe aller Sessions bis zum Reset
 ├─────────┼─────────┼─────────┼─────────┤
 │ Modul 5 │ Modul 6 │ Modul 7 │ Modul 8 │  ← Zone 1 (unten)  0-idx: 47
 │ unten   │         │         │ unten   │    idx 4    -> Anzeige: MQTT-Fehler (E = kein Broker, leer = OK)
 │ links   │         │         │ rechts  │    idx 5 -7 -> Anzeige: Countdown / Status 
 └─────────┴─────────┴─────────┴─────────┘
  SPI-Kette: ESP32 → M1 → M2 → M3 → M4 → M5 → M6 → M7 → M8

Hardware-Typ: GENERIC_HW (verifiziert durch Hardware-Test; physische Ausrichtung erfordert 90° CCW Software-Rotation)

Stromversorgung: Die 8 Module müssen über ein externes 5 V-Netzteil versorgt werden. GND des Netzteils mit ESP32-GND verbinden. Gemessene Leistungsaufnahme im Betrieb: ca. 0,5 A / 2,5 W; bei allen LEDs EIN (Testmuster) bis ca. 2 A möglich.

Modul-Belegung

Modul(e) 0-Index Physisch Anzeige-Inhalt
0 0 Oben links WiFi-Fehler (E = kein WLAN, leer = OK)
13 13 Oben Mitterechts Session-Minuten ganzzahlig (0 bei Neustart, RAM-only)
4 4 Unten links MQTT-Fehler (E = kein Broker, leer = OK)
57 57 Unten Mitterechts Countdown (Gratiszeit in Sek.) oder -- (Idle/Netto)
  • Summe Session (Module 13) inkrementiert mit jeder angefangenen Minute pro Session (Ceiling). Beispiel: 10 s Netto = +1 Min, 65 s Netto = +2 Min (1 bei Sekunde 1, 2 ab Sekunde 61).
  • Die Netto-Zeit beginnt erst nach Ablauf der Gratiszeit (Laser muss länger als gratisSeconds aktiv bleiben).
  • Die Anzeige aktualisiert sich im loop() ohne Blocking-Delays.
  • Bibliotheken: MD_MAX72XX (direkte Puffer-Steuerung, keine MD_Parola)

Laser-Signaldetektion

Der ESP32 überwacht einen digitalen GPIO-Eingang mit internem Pull-Up-Widerstand (INPUT_PULLUP).
Der potentialfreie Ausgang des Laser Cutters (z.B. Schließer-Kontakt über Optokoppler) wird an diesen Pin angeschlossen.

  • Polarität konfigurierbar: Im Webinterface einstellbar, ob LOW oder HIGH den aktiven Zustand bedeutet.
  • Debounce: Software-Entprellung, um Fehlmessungen bei Schaltflanken zu vermeiden. [TODO] Parameter für Debounce-Zeit (z.B. 50 ms) konfigurierbar machen.
  • Eine Session beginnt, wenn der Laser aktiv wird, und endet, wenn er inaktiv wird.
  • Eine Session besteht aus Abglaufener Gratizeit + Laser-AN-Zeit (Netto-Zeit). Eine "Session" ohne Laser Zeit ist keine Session und wird auch nicht als MQTT Session veröffentlicht. Unabhängig von einer vollständigen Session wird die Laser-AN-Zeit immer in der NVS-Gesamtzeit akkumuliert (für Wartungsintervalle).

Optokoppler Schaltung

Der Laser Cutter stellt einen potentialfreien Relaisausgang (Schließer) bereit. Die galvanische Trennung erfolgt über einen Optokoppler direkt am ESP32. Das Verbindungskabel ist 10 m lang (verdrillte, geschirmte Ader Twisted Pair), der Schirm wird nur laserseitig aufgelegt (kein Erdschleifen-Problem).

  Laser Cutter                                 ESP32-Seite
  ─────────────                                ────────────────────────────────────────

  Relais                                       5V (extern)          3,3V (ESP32)
  (Schließer)                                   │                         │
                                              [200Ω]                    [1kΩ]
                                                │                         │
                                                │  ┌──────────────┐       │
                                                └─►┤ A         C  ├───────┴──── GPIO 4 (ESP32)
  ┌─────────┐                                      │  Optokoppler │            
  │  Pin 1  ├──── ~~~ 10m Twisted Pair ~~~ ─────►──┤ K         E  ├──────────── GND (ESP32)
  │  Pin 2  ├──── ~~~ 10m Twisted Pair ~~~ GND ─   └──────────────┘             
  └─────────┘    (Schirm: nur laserseitig)   (ESP32 = gemeinsame Masse)
Signal Beschreibung
Relais Pin 1 → Kathode (K) über Twisted Pair zum Optokoppler
Relais Pin 2 → GND (ESP32) gemeinsame Masse, über Twisted Pair
Anode (A) → 200 Ω → 5 V extern (Vorwiderstand für Optokoppler-LED)
Kathode (K) → Relais Pin 1 (schließt bei Laser aktiv)
Emitter (E) → GND (ESP32)
Collector (C) → GPIO 4 (ESP32) + 1 kΩ Pull-Up nach 3,3 V

Logikpegel: Laser aktiv → Relais schließt → LED leitet → Transistor sättigt → GPIO 4 = LOW
→ Polarität im Webinterface auf „LOW = aktiv" einstellen.

Hinweise zur Verkabelung:

  • Schirm nur am Laser Cutter auflegen (Schutzerde / Gehäuse), nicht am ESP32 verhindert Erdschleifen über das 10-m-Kabel.
  • Twisted Pair reduziert kapazitive und induktive Einkopplung (typisch in EMV-belasteter Maschinen-Umgebung).
  • Der 200-Ω-Vorwiderstand begrenzt den LED-Strom auf ~20 mA bei 5 V (V_F ≈ 1,2 V → (5 V 1,2 V) / 200 Ω ≈ 19 mA).

Zeit-Tracking & Gratiszeit

Drei getrennte Zeitkreise

Kreis Speicher Reset Verwendung
Session-Minuten RAM bei Neustart / resetSessionSum() MQTT Session in Minuten, Web-UI
Summe Session RAM bei Neustart / resetSessionSum() Display Module 13, Web-UI, nicht in MQTT
Gesamtzeit NVS (total_min) nur per resetTotal() MQTT, Web-UI, Wartungsstatistik

Session-Zustandsmaschine

Jedes Laser-AN-Ereignis durchläuft drei Zustände:

INACTIVE ──(Laser an)──► GRATIS ──(Gratiszeit abgelaufen)──► NET_COUNTING
    ▲                       │                                      │
    └───────────────────────┴──────────(Laser aus)─────────────────┘
Zustand Display unten Netto-Zeit NVS
INACTIVE --
GRATIS Countdown (z.B. 19, 18, ...) läuft nicht
NET_COUNTING Sekundenzähler siehe Session-Sekunden läuft
→ Laser aus (aus NET_COUNTING) -- wird addiert gespeichert
→ Laser aus (aus GRATIS) -- 0 addiert Dauer verwendeter Gratiszeit wird gespeichert

Gratiszeit

  • Konfigurierbar: 0120 Sekunden (Standard: 20 s)
  • Startet neu bei jedem Laser-AN-Ereignis
  • Geht der Laser während der Gratiszeit wieder aus: keine Session-Zeit, aber NVS zählt die Einschaltdauer
  • Einstellbar über das Webinterface

NVS-Gesamtzeit

Zählt jede Sekunde Laser-AN, inklusive Gratiszeit. Eignet sich für Wartungsintervalle (tatsächliche Einschaltdauer des Lasers).

Session-Minuten (Display)

Zählt nur die Netto-Zeit (nach Ablauf der Gratiszeit). Jede angefangene Minute pro Session wird aufgerundet gezählt (Ceiling). Beispiele:

  • Session mit 10 s Netto → +1 Minute
  • Session mit 65 s Netto → live +1 bei Sekunde 160, dann +2 ab Sekunde 61
  • Session mit 120 s Netto → live +1 bei Sek. 160, +2 ab Sek. 61120

Wird bei Neustart und resetSessionSum() auf 0 zurückgesetzt.

Session-Sekunden

Dies wird in Form eines umlaufenden Kreis um die Module 57 angezeigt wie es auf einer Analogen Uhr überlich ist. 3 Module haben umlaufenden 60 Leds. Jede Sekunden wird eine weitere LED dazu geschaltet. Nach 60 Sekunden ist der Kreis voll und die Minutenanzeige (Module 13) wird inkrementiert. Bei Laser-Aus wird diese Anzeige wieder auf "--" zurückgesetzt.


MQTT Client

Der ESP32 verbindet sich mit einem MQTT-Broker (konfigurierbar über Webinterface).

Topics

Richtung Topic Format Beschreibung
Publish lasercutter/session JSON Wird beim Ende eines Laser-Bursts oder nach Session-Reset gesendet
Publish lasercutter/status JSON (retained) Heartbeat alle 10 Sekunden; sofortiger Publish nach jeder Statusänderung
Publish lasercutter/availability online / offline Verbindungsstatus (retained, LWT)
Subscribe lasercutter/cmd JSON Steuerkommandos: Reset, Display, Reboot
Publish homeassistant/…/config JSON (retained) HA MQTT Discovery nach jedem Connect

JSON-Format lasercutter/session

{
  "session_minutes": 2,
  "session_seconds": 95,
  "session_start_time": "2026-02-23T13:34:56",
  "freetime_s": 20,
  "ip": "192.168.2.62"
}

session_start_time ist ein Lokalzeit-Zeitstempel (ISO 8601, CET/CEST), synchronisiert via NTP (pool.ntp.org) unmittelbar nach WLAN-Connect. Zeitzone wird automatisch zwischen CET (UTC+1) und CEST (UTC+2) umgeschaltet.
Wert "unknown" wenn die NTP-Synchronisation beim Session-Start noch nicht abgeschlossen war.

Bei einem Session-Reset (Web oder MQTT {"reset_session_ram":true}) wird ebenfalls ein lasercutter/session-Publish ausgelöst mit den bis dahin akkumulierten Werten identisches Format wie beim normalen Session-Ende.

JSON-Format lasercutter/status

{
  "online": true,
  "laser_active": false,
  "session_sum": "42.50",
  "session_minutes_sum": 42,
  "session_seconds": 30,
  "machine_running_time_min": "1234.75",
  "total_minutes": 1234,
  "display_on": true,
  "ip": "192.168.1.100",
  "uptime_s": 3600,
  "firmware_version": "1.5.0 (Mar  4 2026)",
  "reset_reason": "POWERON"
}

laser_active true wenn der Laser gerade ein aktives Burst meldet.
session_minutes_sum / session_seconds sind Integer-Felder für HA-value_template.
total_minutes entspricht machine_running_time_min als Integer-Minuten.
reset_reason mögliche Werte: POWERON, SOFTWARE, PANIC, TASK_WDT, INT_WDT, WDT, BROWNOUT, EXT_PIN, DEEPSLEEP, SDIO, UNKNOWN. Der Wert wird einmalig beim Start gespeichert und bleibt für die gesamte Laufzeit konstant.

JSON-Format lasercutter/cmd

Alle Steuerkommandos werden als JSON an lasercutter/cmd gesendet:

{ "reset_session_nvs": true }  // Session-Summe + NVS-Maschinenlaufzeit auf 0
{ "reset_session_ram": true }  // nur RAM-Session-Summe auf 0, NVS bleibt
{ "display": true }            // Display einschalten
{ "display": false }           // Display ausschalten
{ "reboot": true }             // ESP32 neu starten

Mehrere Kommandos können in einem JSON kombiniert werden, z. B. {"reset_session_ram":true, "display":false}.

LWT (Last Will and Testament) & Availability

Der Client setzt beim Connect ein LWT auf lasercutter/availability mit Payload offline.
Nach erfolgreichem Connect publiziert er sofort online (retained) auf dasselbe Topic.
Bei Verbindungsabbruch sendet der Broker automatisch offline.

lasercutter/availability  →  "online"   (nach Connect, retained)
lasercutter/availability  →  "offline"  (LWT bei Verbindungsabbruch)

Verhalten

  • Non-Blocking Reconnect alle 10 s, falls MQTT-Broker nicht erreichbar.
  • Bei aktivem Laser läuft die Zeitmessung unabhängig vom MQTT-Status weiter.
  • Client-ID: lasercutter-display-XXXXXX (mit MAC-Suffix, eindeutig auf Public Broker).
  • TLS (Port 8883): wird automatisch aktiviert wenn Port 8883 konfiguriert ist (WiFiClientSecure, setInsecure).

Home Assistant MQTT Discovery

Ab v1.5.0 meldet sich der ESP32 automatisch als vollständiges HA-Device an, ohne manuelle Einträge in configuration.yaml.

Funktionsweise

Nach jedem (Re-)Connect publiziert der Client 9 retained JSON-Nachrichten an
homeassistant/<typ>/lasercutter-display/<entity>/config.
Home Assistant erkennt das Device daraufhin automatisch und zeigt alle Entities gruppiert an.

Entities

HA-Typ Name im HA Quelle (lasercutter/status)
binary_sensor Laser aktiv laser_active
sensor Laserzeit Summe session_minutes_sum (min)
sensor Laserzeit aktuell (s) session_seconds (s)
sensor Gesamtlaufzeit total_minutes → Jinja2 hh:mm
sensor Maschinenlaufzeit total_minutes (min, state_class: total_increasing)
sensor Firmware firmware_version (Diagnose)
switch Display display_onON/OFF
button Session zurücksetzen lasercutter/cmd {"reset_session_ram":true}
button Neustart lasercutter/cmd {"reboot":true}

Voraussetzungen

  • MQTT-Integration in HA aktiviert und mit demselben Broker verbunden.
  • Discovery-Prefix bleibt auf Standard homeassistant (konfigurierbar in config.h).

Webinterface

Das Webinterface ist über die IP-Adresse des ESP32 im Browser erreichbar.

Seite URL Auth Funktion
Laser Cutter Status / Laserzeit, Laserstatus, Display-Toggle, Session-Reset; Live-Updates via WebSocket
Laser Cutter Setup /config MQTT, Web-Auth, WLAN, Maschinenlaufzeit-Reset, Gratiszeit, Polarität, Reboot
Session-Reset /reset Setzt die Session-Summe (RAM) auf 0 NVS bleibt erhalten
Maschinenlaufzeit-Reset /reset-total Setzt Maschinenlaufzeit im NVS auf 0
Display-Toggle /display-toggle Display ein-/ausschalten (POST, antwortet 204)
Reboot /reboot ESP32 sofort neu starten
OTA Update /update Firmware-Update über Browser (ElegantOTA)
Log Console /log Live-Log über WebSocket (/log-ws)
Status WebSocket /status-ws Live-Status-Push für Statusseite und Config-Seite

/ ist öffentlich (kein Passwortschutz) der Display-Toggle und Session-Reset sind bewusst ohne Auth erreichbar. Alle sicherheitsrelevanten Seiten (Setup, Reboot, Maschinenlaufzeit-Reset) erfordern HTTP Basic Auth, sofern ein Web-Passwort gesetzt ist.


Konfiguration & Persistenz

Alle Einstellungen werden im NVS (Non-Volatile Storage) des ESP32 gespeichert und sind über das Webinterface änderbar:

Einstellung Standard Beschreibung
MQTT Broker broker.hivemq.com Hostname oder IP des MQTT-Brokers
MQTT Port 1883 Port des MQTT-Brokers (8883 = TLS auto)
MQTT User (leer) MQTT Benutzername (optional)
MQTT Passwort (leer) MQTT Passwort (optional)
Gratiszeit 20 Sekunden, 0120
Signal-Polarität LOW_ACTIVE LOW_ACTIVE oder HIGH_ACTIVE
Akkumulierte Zeit 0.0 Gespeicherte Laserzeit in Minuten (NVS)
Web-Benutzername admin HTTP-Basic-Auth Benutzername
Web-Passwort (leer) Leer = kein Passwortschutz; setzen über /config

WiFi-Setup (WiFiManager)

Beim ersten Start (oder wenn keine gespeicherten WLAN-Daten vorhanden) öffnet das Gerät einen Access Point mit Captive Portal:

  • SSID: LaserCutter-Setup
  • Im Browser öffnet sich automatisch die Konfigurationsseite.
  • WLAN-Credentials werden nach erfolgreicher Verbindung im NVS gespeichert.

OTA Firmware-Update

Firmware-Updates können kabellos über die Weboberfläche unter /update eingespielt werden (ElegantOTA).


Remote-Debugging (Web Console)

Die Route /log zeigt alle Log-Ausgaben live im Browser.

  • Live-Streaming über WebSocket (/log-ws) kein Polling
  • Auto-Scroll, manuelles Leeren, farbkodierte Log-Level (Info/Debug/Error)
  • Aufruf: http://<ip>/log (Auth erforderlich wenn Web-Passwort gesetzt)

Fehlerverhalten

Zustand Modul 0 (oben links) Modul 4 (unten links) Verhalten
WLAN getrennt W (blinkt) Zeiterfassung läuft weiter, Reconnect
MQTT nicht erreichbar M (blinkt) Zeiterfassung läuft weiter, Reconnect
WLAN + MQTT OK leer leer Normaler Betrieb

Bibliotheken

Bibliothek Zweck
MD_MAX72XX Treiber für MAX7219/GYMAX7219, direkte Puffer-Steuerung
MD_Parola (Dependency von MD_MAX72XX, nicht direkt genutzt)
PubSubClient MQTT Client
WiFiManager WiFi Captive Portal
ESPAsyncWebServer Asynchroner Webserver
AsyncTCP TCP-Basis für ESPAsyncWebServer
ArduinoJson JSON Serialisierung/Deserialisierung
Preferences NVS-Zugriff (built-in ESP32 Arduino)
ElegantOTA OTA-Update über Webinterface

Build & Flash

Via USB (Standard)

# Haupt-Firmware bauen und flashen
pio run -e az-delivery-devkit-v4 --target upload

# Serieller Monitor
pio device monitor -e az-delivery-devkit-v4

Ziel-Board: az-delivery-devkit-v4 (ESP32), Upload-Port: COM3


Via WiFi (OTA)

Sobald das Gerät einmal per USB geflasht wurde und im WLAN erreichbar ist, kann jeder weitere Flash-Vorgang kabellos erfolgen.

Voraussetzung: ESP32 läuft und ist im WLAN verbunden.

1. IP-Adresse in platformio.ini eintragen (einmalig):

[env:az-delivery-devkit-v4-ota]
upload_port = 192.168.2.62   ; <-- hier die IP des ESP32 eintragen

Die aktuelle IP ist im Router (DHCP-Tabelle) oder über mDNS (lasercutter-display.local) im Browser erreichbar.

2. Flashen via PlatformIO:

pio run -e az-delivery-devkit-v4-ota --target upload

3. Flashen via Arduino IDE:

Im Menü Tools → Port erscheint nach ein paar Sekunden automatisch ein Netzwerk-Port:

lasercutter-display at 192.168.x.x

Diesen auswählen und normal über Sketch → Hochladen flashen.

Hinweis: Falls ein Web-Passwort gesetzt ist, verwendet ArduinoOTA dasselbe Passwort. Es muss in platformio.ini per upload_flags übergeben werden:

[env:az-delivery-devkit-v4-ota]
upload_flags = --auth=DEIN_WEBPASSWORT

Arduino IDE fragt das Passwort beim Upload automatisch ab.

Wichtig (Erstinbetriebnahme): Der erste OTA-Flash setzt voraus, dass die aktuelle Firmware bereits ArduinoOTA enthält. Nach einem Neuflash via USB (az-delivery-devkit-v4) ist OTA dauerhaft verfügbar.


Projektstruktur

MQTT-Display-LaserCutter/
├── src/
│   ├── main.cpp              # Hauptprogramm
│   ├── display_manager.cpp   # Display-Implementierung (MD_MAX72XX)
│   ├── laser_tracker.cpp     # Signal-Detektion, Burst-Logik, Zeiterfassung
│   ├── settings.cpp          # NVS-Persistenz (Preferences)
│   ├── wifi_connector.cpp    # WiFiManager-Wrapper
│   ├── mqtt_client.cpp       # MQTT-Wrapper (PubSubClient, TLS, Phase 6)
│   └── web_server.cpp        # Webinterface (ESPAsyncWebServer, Phase 7)
├── include/
│   ├── config.h              # Pin-Definitionen, Konstanten, LOG-Makros
│   ├── display_manager.h     # Display-API (showLaserTime, showCountdown, ...)
│   ├── laser_tracker.h       # SessionState-Maschine, getAllSessionsSumMinutes(), ...
│   ├── settings.h            # Settings-Struct, SettingsManager
│   ├── wifi_connector.h      # WiFi-Verbindungsmanagement
│   ├── mqtt_client.h         # (Phase 6) MQTT-Wrapper (PubSubClient)
│   └── web_server.h          # (Phase 7) Webinterface (ESPAsyncWebServer)
├── test_sketches/
│   ├── test_display.cpp          # 1.4 - GYMAX7219 Moduldignose
│   ├── test_button.cpp           # 1.5 - Potentialfreier Schalter
│   ├── test_nvs.cpp              # 2.2 - NVS Persistenz
│   ├── test_wifi.cpp             # 3.3 - WiFiManager
│   ├── test_display_manager.cpp  # 4.3 - DisplayManager
│   ├── test_laser_tracker.cpp    # 5.6 - LaserTracker
│   ├── test_mqtt_client.cpp      # 6.5 - MqttClient (TLS, Session, Heartbeat)
│   ├── test_web_server.cpp       # 7.7 - WebServer (Routen, Config, OTA)
│   └── mqtt_test_secrets.h       # (gitignoriert) persönliche Broker-Credentials
├── platformio.ini
└── README.md

Tests

Die tests sind in test_sketches/ als eigenständige Sketches organisiert, die jeweils spezifische Funktionalitäten der Hardware oder Software testen. Auch die Beschreibung der tests befindet sich in der README.md in dem Verzeichneis test_sketches/ um die Übersicht zu behalten.


Implementierungsstand

Phase Beschreibung Status
1 Hardware-Grundtest (Display, Button) abgeschlossen
2 NVS-Persistenz (Settings) abgeschlossen
3 WiFiManager abgeschlossen
4 DisplayManager abgeschlossen
5 LaserTracker (Burst-Logik, Zeiterfassung) abgeschlossen
6 MqttClient (PubSubClient, TLS, Heartbeat, Session, Reset) abgeschlossen
7 WebServer (ESPAsyncWebServer, Config-UI, Auth, OTA) abgeschlossen
8 Integrationstest (Display + MQTT + Web) abgeschlossen
9 OTA & Stabilisierung (ArduinoOTA, non-blocking WiFi, Auth) abgeschlossen
10 Produktiv-Deployment, Dokumentation final abgeschlossen

Beitragen / Commits

Dieses Projekt verwendet Conventional Commits für alle Git-Commit-Nachrichten.

Format

<type>(<scope>): <kurze Beschreibung>

[optionaler Body]

[optionaler Footer]

Wichtigste Typen

Typ Verwendung
feat Neues Feature
fix Bugfix
docs Nur Dokumentation
refactor Code-Umstrukturierung ohne Feature/Fix
test Test-Sketches / Test-Code
chore Build-System, Abhängigkeiten, Konfiguration

Beispiele

feat(display): add GENERIC_HW rotation compensation
fix(nvs): prevent crash on empty broker string
docs(readme): add power supply requirements
chore(platformio): reduce SPI clock to 1 MHz for stability

Breaking Changes werden mit ! nach dem Typ markiert oder im Footer mit BREAKING CHANGE: beschrieben.