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));