diff --git a/Feature-Requests.md b/Feature-Requests.md
index 529e85c..108f1e7 100644
--- a/Feature-Requests.md
+++ b/Feature-Requests.md
@@ -19,6 +19,23 @@ Status: `[ ]` = offen · `[x]` = erledigt
## Offen
+- [ ] **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 (`_espClient`) wird nach einem TLS-Verbindungsabbruch nicht neu erstellt — der interne mbedTLS-State ist korrupt, beim nächsten `connected()`-Aufruf crasht `mbedtls_ssl_free()`
+ - **Fix-Strategie**: Nach jedem fehlgeschlagenen Verbindungsversuch (`rc == -2` oder `!client->connected()`) das `WiFiClientSecure`-Objekt zerstören und neu auf Core 0 erstellen (`delete _espClient; _espClient = new WiFiClientSecure(); _espClient->setInsecure(); _client->setClient(*_espClient)`). Damit startet mbedTLS immer mit einem sauberen Kontext.
+ - **Reproduktion**: Broker stoppen während aktiver TLS-Session → innerhalb weniger Minuten PANIC
+ - Betroffene Dateien: `src/mqtt_client.cpp`, `include/mqtt_client.h`
+ - Commit: `TODO`
+ - Version: 1.4.1
+
---
## Erledigt
@@ -46,7 +63,7 @@ Status: `[ ]` = offen · `[x]` = erledigt
- **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: `TODO`
+ - Commit: `c61a67f`
- Version: 1.4.0
### Version 1.2.0
diff --git a/doc/Front.svg b/doc/Front.svg
index 35167c7..3e86cfe 100644
--- a/doc/Front.svg
+++ b/doc/Front.svg
@@ -24,8 +24,8 @@
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="mm"
inkscape:zoom="0.71891835"
- inkscape:cx="584.90648"
- inkscape:cy="639.15464"
+ inkscape:cx="634.98171"
+ inkscape:cy="835.28262"
inkscape:window-width="1920"
inkscape:window-height="1009"
inkscape:window-x="1912"
@@ -149,56 +149,62 @@
xml:space="preserve"
style="font-weight:600;font-size:7.40833px;line-height:0;font-family:'Open Sans';-inkscape-font-specification:'Open Sans, Semi-Bold';text-align:end;writing-mode:lr-tb;direction:ltr;text-anchor:end;fill:#000000;stroke:#241212;stroke-width:0.2;stroke-linejoin:bevel;stroke-opacity:1"
x="204.76865"
- y="256.40359"
+ y="262.22452"
id="text4">Freie TestzeitFreie Testzeit
in Sekunden
+ y="271.33279">in Sekunden
€ Laserzeit€ Laserzeit in Sekunden
Sekunden
+ x="-218.73421"
+ y="225.72589"
+ sodipodi:role="line">umlaufend
+ y="126.91018" />
Error
-
+
diff --git a/include/mqtt_client.h b/include/mqtt_client.h
index 48912da..eb099e4 100644
--- a/include/mqtt_client.h
+++ b/include/mqtt_client.h
@@ -94,6 +94,14 @@ private:
// Verbindungsaufbau (blockierend - NUR aus MQTT-Task auf Core 0 aufrufen!)
bool reconnect();
+ // TLS-Client komplett abreissen und neu aufbauen (Fix: Heap-Korruption bei
+ // unerwartetem SSL-Verbindungsabbruch durch kaputten mbedTLS-Kontext)
+ void _rebuildClient();
+
+ // Gecachte Broker-Einstellungen fuer _rebuildClient()
+ char _broker[64];
+ uint16_t _port;
+
// Heartbeat-Publish (lasercutter/status)
void publishHeartbeat();
diff --git a/platformio.ini b/platformio.ini
index d028053..0726972 100644
--- a/platformio.ini
+++ b/platformio.ini
@@ -23,7 +23,7 @@ build_flags =
-DCORE_DEBUG_LEVEL=1 ; 0=keine, 1=Fehler, 3=Info, 5=Verbose
-DARDUINO_LOOP_STACK_SIZE=8192
-DELEGANTOTA_USE_ASYNC_WEBSERVER=1
- -DFIRMWARE_VERSION='"1.4.0"' ; Semantic Versioning – hier erhöhen bei neuem Release
+ -DFIRMWARE_VERSION='"1.4.1"' ; Semantic Versioning – hier erhöhen bei neuem Release
lib_deps =
majicDesigns/MD_Parola @ ^3.7.3
majicDesigns/MD_MAX72XX @ ^3.5.1
diff --git a/src/mqtt_client.cpp b/src/mqtt_client.cpp
index b0fe08f..98d12f4 100644
--- a/src/mqtt_client.cpp
+++ b/src/mqtt_client.cpp
@@ -46,6 +46,8 @@ MqttClient::MqttClient()
{
_clientId[0] = '\0';
_resetReason[0] = '\0';
+ _broker[0] = '\0';
+ _port = 0;
}
// --------------------------------------------------------------------------
@@ -200,6 +202,43 @@ void MqttClient::publishHeartbeat() {
LOG_I("MQTT", "Heartbeat: %s -> %s", buf, ok ? "OK" : "FEHLER");
}
+// --------------------------------------------------------------------------
+// _rebuildClient() - TLS-Client sauber abreissen und neu aufbauen
+// MUSS auf Core 0 aufgerufen werden (mbedtls Heap-Kontext-Binding)
+// --------------------------------------------------------------------------
+void MqttClient::_rebuildClient() {
+ // Alten Client sauber schliessen und freigeben
+ if (_client) {
+ _client->disconnect();
+ delete _client;
+ _client = nullptr;
+ }
+ if (_secureClient) {
+ delete _secureClient;
+ _secureClient = nullptr;
+ }
+ if (_wifiClient) {
+ delete _wifiClient;
+ _wifiClient = nullptr;
+ }
+
+ // Frische Objekte auf Core 0 anlegen
+ _wifiClient = new WiFiClient();
+ _secureClient = new WiFiClientSecure();
+
+ if (_port == 8883) {
+ _secureClient->setInsecure();
+ _client = new PubSubClient(*_secureClient);
+ } else {
+ _client = new PubSubClient(*_wifiClient);
+ }
+ _client->setServer(_broker, _port);
+ _client->setCallback(MqttClient::onMessage);
+ _client->setKeepAlive(60);
+ _client->setBufferSize(512);
+ LOG_I("MQTT", "Client neu aufgebaut (sauberer TLS-Kontext)");
+}
+
// --------------------------------------------------------------------------
// reconnect() - Verbindungsaufbau, non-blocking
// --------------------------------------------------------------------------
@@ -255,6 +294,11 @@ void MqttClient::_taskLoop() {
const char* broker = cfg.mqttBroker[0] != '\0' ? cfg.mqttBroker : DEFAULT_MQTT_BROKER;
uint16_t port = cfg.mqttPort > 0 ? cfg.mqttPort : DEFAULT_MQTT_PORT;
+ // Broker/Port für _rebuildClient() sichern
+ strncpy(_broker, broker, sizeof(_broker) - 1);
+ _broker[sizeof(_broker) - 1] = '\0';
+ _port = port;
+
// Netzwerk-Objekte auf Core 0 anlegen (mbedtls/WiFiClientSecure bindet
// Heap-Kontexte an den erstellenden Task/Core -> kein Cross-Core-Zugriff).
_wifiClient = new WiFiClient();
@@ -284,6 +328,9 @@ void MqttClient::_taskLoop() {
uint32_t now = millis();
if (now - _lastReconnectMs >= MQTT_RECONNECT_MS) {
_lastReconnectMs = now;
+ // Sicherstellen, dass kein korrupter mbedTLS-Kontext vom letzten
+ // Verbindungsabbruch uebrig bleibt (Fix FR-011: Heap-Korruption)
+ _rebuildClient();
reconnect(); // blockiert hier (TLS); Core 1 laeuft unbehelligt
}
vTaskDelay(pdMS_TO_TICKS(200));