MQTT-Display-LaserCutter/Feature-Requests.md

16 KiB
Raw Blame History

Feature Requests & Bug Fixes

Dieses Dokument verfolgt laufende Bug Fixes und kleinere Feature Requests nach Abschluss der Hauptentwicklungsphasen (Implementation-Plan.md Phase 19).
Größere Änderungen (neue Module, neue Architektur) werden weiterhin im Implementation-Plan.md dokumentiert. Wird ein offener Punkt erledigt, wird dieser von ## Offen nach ## Erledigt verschoben, mit Details zum Fix/Feature und Commit-Hash und mit der Version in der Überschrift ### Version X.Y.Z, dokumentiert.


Format

- [ ] **FR-NNN** Kurzbeschreibung  
  - Details, Kontext, betroffene Dateien  
  - Commit: `<hash>` (wird nach Erledigung eingetragen)

Status: [ ] = offen · [x] = erledigt


Offen


Erledigt

Version 1.6.1

  • FR-017 Bug: Maschinenlaufzeit ist in HA nicht verfügbar (MQTT Discovery Sensor fehlt)
    • Symptom: Home Assistant zeigte die Maschinenlaufzeit (Gesamtbetriebszeit) nicht als eigenständigen Sensor an.
    • Ursache: In publishDiscovery() fehlte ein dedizierter numerischer Discovery-Sensor für total_minutes. Der bestehende Sensor #4 "Gesamtzeit" formatiert den Wert als hh:mm-String (kein numerischer HA-Sensor, keine Langzeitstatistik möglich).
    • Fix: Neuer Discovery-Sensor #6 "Maschinenlaufzeit" hinzugefügt: value_template: {{ value_json.total_minutes }}, unit_of_measurement: min, state_class: total_increasing → HA kann Statistiken und Diagramme erstellen. Anzahl publizierter Entities von 8 auf 9 erhöht.
    • Betroffene Dateien: src/mqtt_client.cpp
    • Commit: f6dfdeb

Version 1.6.0

  • FR-015 Feature: Sofortiger MQTT-Status-Publish nach jeder Statusänderung

    • Motivation: HA zeigte veraltete Werte bis zum nächsten Heartbeat. Änderungen durch Webinterface, MQTT-CMD oder lokale Buttons sind nun sofort in HA sichtbar.
    • Umsetzung: publishHeartbeat() von private nach public verschoben; wird nach Display-Toggle, Session-Reset und Reboot-CMD sofort aufgerufen (zusätzlich zum 10s-Timer). Heartbeat-Intervall von 60s auf 10s reduziert. HA MQTT Discovery Switch: state_on/state_off ergänzt damit Display-Zustand korrekt angezeigt wird.
    • Betroffene Dateien: include/mqtt_client.h, src/web_server.cpp, src/mqtt_client.cpp, include/config.h
    • Commit: fc5e169
    • Version: 1.6.0
  • FR-016 Feature: Webinterface Live-Aktualisierung ohne Neuladen

    • Motivation: Nach einer Aktion im Webinterface blieb die Statusseite veraltet bis zum manuellen Reload.
    • Umsetzung: Neuer WebSocket-Endpunkt /status-ws (AsyncWebSocket). sendStatusWs() wird am Ende von publishHeartbeat() aufgerufen deckt damit alle Auslöser ab (10s-Timer, Web-Aktionen, Laser-Statuswechsel). Statusseite und Config-Seite nutzen /status-ws für Live-DOM-Updates ohne Reload. Reboot-Button auf Config-Seite wird automatisch deaktiviert wenn Laser aktiv. Alle Action-Buttons (Reboot, WLAN-Reset, Maschinenlaufzeit-Reset) auf fetch() umgestellt.
    • Betroffene Dateien: include/web_server.h, src/web_server.cpp, src/mqtt_client.cpp
    • Commit: fc5e169
    • Version: 1.6.0

Version 1.5.2

  • FR-014 Bug: Display Störung bei Relais Umschaltung
    • Symptom: Nach Relais-Umschaltung (Laser an/aus) friert das Display ein oder zeigt Störungen (z.B. Modul M2M4 leer, keine Sekundenanzeige)
    • Ursache: EMV-Störung durch Relais → SPI-Leitungen fangen Spike auf → MAX7219-interne Register (Decode-Mode, Scan-Limit) werden korrumpiert
    • Fix: Neue Methode display.reinit() in DisplayManager — setzt nur Kontroll-Register neu (kein clear()), danach wird der zuletzt angezeigte Zustand via redraw() sofort neu gezeichnet. State-Tracking in allen show*()-Methoden. LaserTracker::onSessionStart() und onSessionEnd() rufen display.reinit() auf.
    • Betroffene Dateien: include/display_manager.h, src/display_manager.cpp, src/laser_tracker.cpp
    • Commit: 6c8be70
    • Version: 1.5.2

Version 1.5.1

  • FR-013 Bug: binary_sensor Laser aktiv zeigt "In Betrieb" / "Außer Betrieb" statt "An" / "Aus"
    • Symptom: HA Tile-Card zeigt unter dem Sensor-Status "Außer Betrieb" statt "Aus"
    • Ursache: device_class: running in der MQTT Discovery-Config erzeugt HA-spezifische Texte ("In Betrieb" / "Außer Betrieb")
    • Fix: device_class aus der publishDiscovery()-binary_sensor-Config in mqtt_client.cpp entfernt → HA zeigt generisch "An" / "Aus"
    • Betroffene Dateien: src/mqtt_client.cpp
    • Commit: db1fd0b
    • Version: 1.5.1

Version 1.5.0

  • FR-012 Feature: Home Assistant MQTT Discovery (automatische Device-Erkennung ohne configuration.yaml)
    • Ziel: Der ESP32 soll in Home Assistant als vollständiges Device mit allen Entities automatisch erkannt werden, ohne manuelle Einträge in configuration.yaml
    • Mechanismus: MQTT Discovery Gerät publiziert beim Connect einmalig JSON-Config-Nachrichten an homeassistant/<typ>/lasercutter-display/<entity>/config
    • Implementiert:
      • MQTT_TOPIC_AVAILABILITY + MQTT_DISCOVERY_PREFIX in config.h
      • LWT auf lasercutter/availability (offline), nach Connect online (retained)
      • publishDiscovery(): 8 retained Entities nach jedem (Re-)Connect
      • Heartbeat: neue Felder laser_active, session_minutes_sum, session_seconds, total_minutes für HA value_template
      • PubSubClient Buffer: 512 → 1024 Bytes
    • Entities in HA (automatisch gruppiert):
      • Steuerelemente: Display (switch)
      • Sensoren: Laser aktiv, Laserzeit Aktuell, Laserzeit Summe
      • Konfiguration: Neustart, Session zurücksetzen (buttons)
      • Diagnose: Firmware
    • Betroffene Dateien: src/mqtt_client.cpp, include/mqtt_client.h, include/config.h, platformio.ini
    • Commit: ae3e40f
    • Version: 1.5.0

Version 1.4.1

  • FR-011 Bug: PANIC/EXCEPTION durch Heap-Korruption bei MQTT-Verbindungsabbruch (TLS)
    • Symptom: ESP32 crasht mit CORRUPT HEAP: Bad tail + assert failed: multi_heap_free wenn der MQTT-Broker nicht erreichbar ist und eine bestehende TLS-Session unerwartet abbricht
    • Fehlermeldung: (-76) UNKNOWN ERROR CODE (004C) = MBEDTLS_ERR_NET_CONN_RESET
    • Crash-Kette:
      1. Broker bricht TLS-Verbindung ab (Connection reset by peer)
      2. PubSubClient::connected()WiFiClientSecure::connected()available()
      3. available() erkennt EOF → ruft intern stop()stop_ssl_socket() auf
      4. mbedtls_ssl_free() wird auf einem bereits inkonsistenten SSL-Kontext aufgerufen
      5. Heap-Corruption → multi_heap_free assert → PANIC
    • Backtrace-Frames: multi_heap_freeheap_caps_freeesp_mbedtls_mem_freembedtls_freembedtls_ssl_freestop_ssl_socketWiFiClientSecure::stop()WiFiClientSecure::available()WiFiClientSecure::connected()PubSubClient::connected()MqttClient::_taskLoop()
    • Root Cause: WiFiClientSecure-Objekt wird nach einem TLS-Verbindungsabbruch nicht neu erstellt — der interne mbedTLS-State ist korrupt, beim nächsten connected()-Aufruf crasht mbedtls_ssl_free()
    • Fix: _rebuildClient() — zerstört und erstellt WiFiClientSecure, WiFiClient und PubSubClient vor jedem Reconnect-Versuch neu auf Core 0. mbedTLS startet damit immer mit sauberem Heap-Kontext. Broker/Port werden in _broker/_port gecacht.
    • Betroffene Dateien: src/mqtt_client.cpp, include/mqtt_client.h
    • Commit: 1ef0464
    • Version: 1.4.1

Version 1.4.0

  • FR-010 Feature: Webinterface-Redesign + MQTT-Steuerung
    • / Laser Cutter Status (öffentlich, kein Auth)
      • Seitentitel Laser Cutter Status, bereinigte Tabelle: Laserzeit Summe, Laserzeit Aktuell (m:ss), Laserstatus (an/aus), Gratiszeit, Firmware
      • Button Summe Laserzeit zurücksetzen (kein Auth, bewusst öffentlich)
      • Button 💡 Display ausschalten/einschalten Toggle via fetch() ohne Seitenwechsel, Route POST /display-toggle antwortet 204
      • Button 🔒 Laser Cutter Setup & Status/config
      • Auto-Refresh alle 10 s
    • /config Laser Cutter Setup & Status (Auth erforderlich)
      • H2: Laser Cutter Status Maschinenlaufzeit h:mm:ss, roter Reset-Button (/reset-total)
      • H2: MQTT Broker, H2: Webzugang, WLAN-Abschnitt
      • H2: Gerät Reboot-Button 🔄 ESP32 neu starten (grau, bei aktivem Laser disabled)
      • H2: Tools OTA Update, Log Console
      • Button ← Laser Cutter Status zurück auf /
    • MQTT-Steuerung: lasercutter/reset ersetzt durch lasercutter/cmd
      • {"reset_session_nvs":true} Session-Summe + NVS-Maschinenlaufzeit auf 0
      • {"reset_session_ram":true} nur RAM-Session-Summe auf 0, NVS bleibt
      • {"display":true/false} Display ein-/ausschalten
      • {"reboot":true} ESP32 neu starten
    • Heartbeat lasercutter/status: Feld "display_on": true/false hinzugefügt
    • DisplayManager: setEnabled() / isEnabled() via MAX7219 SHUTDOWN-Modus
    • Betroffene Dateien: src/web_server.cpp, src/mqtt_client.cpp, include/config.h, include/display_manager.h, src/display_manager.cpp
    • Commit: c61a67f
    • Version: 1.4.0

Version 1.2.0

  • FR-009 Bug: session_start_time bei nachgelieferten Sessions (Queue) falsch

    • Bei Offline-Puffer (Queue) wird session_start_time erst beim Publish aus laserTracker.getSessionStartTime() gelesen → zeigt immer die aktuelle Session-Startzeit, nicht die der gepufferten Session
    • Beispiel: Session A (22:00) läuft offline, Session B (22:10) beendet, dann Reconnect → beide Sessions wurden mit Startzeit von Session B publiziert
    • Fix: SessionPayload-Struct um time_t startTime erweitert; Startzeit schon in publishSession() via laserTracker.getSessionStartTime() in den Payload geschrieben
    • Betroffene Dateien: include/mqtt_client.h (SessionPayload), src/mqtt_client.cpp (publishSession, _doPublishSession)
    • Commit: aae34fe
  • FR-007 Feature: Laufende Session-ID in MQTT Session-Payload

    • Jede Session erhält eine aufsteigende Ganzzahl (session_id), die im MQTT-Payload MQTT_TOPIC_SESSION mitgesendet wird
    • Empfänger (z. B. Home Assistant, Node-RED) kann fehlende Sessions sofort erkennen: Lücke zwischen session_id 47 und 49 → Session 48 ging verloren
    • Zähler wird im RAM gehalten (kein NVS nötig), startet bei 0 nach jedem Reboot — Lücke beim Reboot ist akzeptabel
    • Zähler wird auf 0 zurückgesetzt bei {"reset":true} und {"reset_session":true} via MQTT
    • Betroffene Dateien: include/mqtt_client.h (SessionPayload, _sessionCounter, resetSessionCounter()), src/mqtt_client.cpp
    • Commit: c40668f

Version 1.1.1

  • FR-006 Bug: MQTT Session-Publish nicht zuverlässig bei Verbindungsausfall
    • Bug A Race Condition: _pendingSession = false vor _client->publish() → bei Abbruch gehen Session-Daten still verloren
    • Bug B Nur 1 Slot: volatile Einzelslot überschreibt ältere Session bei mehreren Offline-Sessions
    • Fix: QueueHandle_t mit 128 Slots; xQueuePeek + Dequeue erst nach erfolgreichem Publish → kein Datenverlust
    • Betroffene Dateien: src/mqtt_client.cpp, include/mqtt_client.h
    • Commit: 9650891

Version 1.1.0

  • FR-008 Bug: Reset-Befehle setzen Maschinenlaufzeit (NVS) ungewollt zurück
    • Bug A MQTT {"reset":true} ruft resetTotal() auf → settings.saveTotalMinutes(0.0f) → Maschinenlaufzeit (Gesamtbetriebszeit) wird unwiderruflich aus dem NVS gelöscht
      • Gewünscht: {"reset":true} soll nur die Session-Summe (Tages-/Wochenzähler) zurücksetzen, nicht die Gesamtbetriebszeit
      • resetTotal() sollte gar nicht per MQTT erreichbar sein (oder einen separaten, expliziten Befehl erfordern, z.B. {"reset_total":true})
    • Bug B Web-Button "Summe Laserzeit zurücksetzen" ruft resetSessionSum() auf → NVS wird nicht angefasst, aber _sessionNetSec = 0getTotalMinutes() sinkt sofort in der Anzeige (weil _sessionNetSec-Anteil verschwindet), obwohl der NVS-Wert korrekt bleibt. Täuscht einen Gesamt-Reset vor.
      • Fix: _totalMinutesBase beim resetSessionSum() um den bereits akkumulierten _sessionNetSec-Anteil erhöhen, bevor _sessionNetSec auf 0 gesetzt wird → Kontinuität der Gesamtzeit sicherstellen
    • Betroffene Dateien: src/laser_tracker.cpp (resetSessionSum, resetTotal), src/mqtt_client.cpp (MQTT-Reset-Handler)
    • Commit: c636add

Version 1.?.?

  • FR-005 Bug: WDT-Crash + Display-/Browser-Freeze durch blockierenden TLS-Handshake
    • MQTT reconnect() mit TLS (Port 8883) blockiert loop() auf Core 1 bis zu 15 s
    • Folge: Task-Watchdog (30 s) feuert wenn zwei Reconnect-Versuche in einem 30s-Fenster → Neustart mit WATCHDOG (Task)
    • Zusätzlich: Display-Updates, WebServer-Responses und WiFi-Stack auf Core 1 eingefroren während TLS-Handshake
    • Fix: MqttClient komplett auf FreeRTOS-Task (Core 0) ausgelagert
      • begin() startet nur Task (xTaskCreatePinnedToCore, Stack 16 KB), kein Netzwerk-Zugriff auf Core 1
      • WiFiClientSecure/mbedtls wird ausschließlich auf Core 0 initialisiert und verwendet (Cross-Core-Heap-Korruption vermieden)
      • mqttClient.loop() in main.cpp ist No-Op alle MQTT-Arbeit im Task
      • publishSession() von Core 1 safe: setzt nur volatile-Flags, Task auf Core 0 führt den Publish aus
    • Version: 1.1.0 → 1.1.1
    • Commit: a7c6edb (Task auf Core 0), b91b3ca (Heap-Fix: Objekte per new auf Core 0)

Version 1.?.?

  • FR-002 Web Console serieller Monitor über Browser (WebSocket)
    • Route /log liefert HTML-Seite mit automatisch scrollendem Terminal (dunkles Theme)
    • WebSocket-Endpunkt /log-ws via AsyncWebSocket im bestehenden ESPAsyncWebServer
    • LOG_I/LOG_E/LOG_D in config.h formatieren in lokalen Puffer und rufen webLogForward() auf
    • webLogForward() sendet per ws.textAll() läuft nativ in AsyncTCP, kein Konflikt
    • "Log Console"-Button auf Statusseite / automatischer WebSocket-Reconnect nach Trennung
    • Commit: 4dd4ce0

Version 1.?.?

  • FR-004 Bug: session_sum im MQTT-Heartbeat falsch (Binärzahl statt Dezimal)
    • serialized(String(getAllSessionsSumMinutes(), 2))String(int, basis) interpretiert 2. Argument als Basis, nicht Dezimalstellen
    • Beispiel: 18 Min → "10010" (18 in Binär), 20 Min → "10100"
    • Fix: doc["session_sum"] = laserTracker.getAllSessionsSumMinutes() direkt als JSON-Integer, kein serialized() nötig
    • machine_running_time_min war korrekt: String(float, 2) = 2 Dezimalstellen anderer Overload
    • Version: 1.0.1 → 1.0.2
    • Commit: 83537e3

Version 1.?.?

  • FR-003 Bug: NTP Zeitzone falsch (UTC statt CET/CEST)
    • configTime(0, 0, ...) lieferte UTC → Anzeige 1h zu früh (CET) / 2h zu früh (CEST)
    • wifi_connector.cpp: configTzTime("CET-1CEST,M3.5.0,M10.5.0/3", "pool.ntp.org", "time.nist.gov") automatische Sommerzeit
    • mqtt_client.cpp: gmtime()localtime(), Z-Suffix entfernt Payload zeigt Lokalzeit
    • Version: 1.0.0 → 1.0.1
    • Commit: 3a31082

Version 1.?.?

  • FR-001 Firmware-Version auf Webseite und im MQTT-Status-Payload
    • Definition: FIRMWARE_VERSION als build_flags in platformio.ini [env]-Basisblock (Single Source of Truth)
    • Fallback #define FIRMWARE_VERSION "0.0.0" in config.h (Arduino IDE ohne build_flags)
    • Format: 1.0.0 (Feb 26 2026) Version + C++-Makro __DATE__ (Compile-Zeitpunkt)
    • Web: Firmware-Zeile in der Statustabelle auf / + Footer auf /config
    • MQTT: "firmware_version": "1.0.0 (Feb 26 2026)" im lasercutter/status-Payload
    • Commit: 46a8c59

Erstellt: 26. Februar 2026 Zuletzt aktualisiert: 28. Februar 2026