MQTT-Display-LaserCutter/Feature-Requests.md

166 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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
*(keine offenen Punkte)*
---
## Erledigt
### Version 1.4.1
- [x] **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_free``heap_caps_free``esp_mbedtls_mem_free``mbedtls_free``mbedtls_ssl_free``stop_ssl_socket``WiFiClientSecure::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
- [x] **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
- [x] **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`
- [x] **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
- [x] **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
- [x] **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 = 0``getTotalMinutes()` 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.?.?
- [x] **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.?.?
- [x] **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.?.?
- [x] **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.?.?
- [x] **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.?.?
- [x] **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*