237 lines
16 KiB
Markdown
237 lines
16 KiB
Markdown
# Feature Requests & Bug Fixes
|
||
|
||
> Dieses Dokument verfolgt laufende Bug Fixes und kleinere Feature Requests nach Abschluss der Hauptentwicklungsphasen (Implementation-Plan.md Phase 1–9).
|
||
> 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
|
||
|
||
- [x] **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: `1524a2a`
|
||
|
||
|
||
### Version 1.6.0
|
||
|
||
- [x] **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
|
||
|
||
- [x] **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
|
||
|
||
- [x] **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 M2–M4 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
|
||
|
||
- [x] **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
|
||
|
||
- [x] **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](https://www.home-assistant.io/integrations/mqtt/#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
|
||
|
||
- [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*
|