fix(mqtt): Netzwerk-Objekte per new auf Core 0 anlegen - Heap-Korruption behoben

- WiFiClient, WiFiClientSecure, PubSubClient als Pointer deklariert (nullptr init)
- Objekte werden erst in _taskLoop() auf Core 0 per 'new' erstellt
- mbedtls bindet Heap-Allokationen an den erstellenden Core/Task
  -> Anlegen auf Core 1 (globaler Konstruktor) + Nutzung auf Core 0 = Heap-Korruption
  -> Fix: Anlegen UND Nutzung ausschliesslich auf Core 0
- isConnected(), printToSerial(): Null-Checks ergaenzt
This commit is contained in:
MaPaLo76 2026-02-28 19:05:09 +01:00
parent eeede50f1c
commit b91b3ca96f
2 changed files with 38 additions and 31 deletions

View File

@ -52,9 +52,13 @@ public:
void printToSerial(); void printToSerial();
private: private:
WiFiClient _wifiClient; // WICHTIG: Diese Objekte werden als nullptr initialisiert und erst in
WiFiClientSecure _secureClient; // fuer TLS (Port 8883) // _taskLoop() auf Core 0 per 'new' angelegt. mbedtls/WiFiClientSecure
PubSubClient _client; // bindet Heap-Kontexte an den erstellenden Task/Core daher KEIN
// Anlegen im globalen Konstruktor (Core 1)!
WiFiClient* _wifiClient;
WiFiClientSecure* _secureClient;
PubSubClient* _client;
uint32_t _lastReconnectMs; uint32_t _lastReconnectMs;
uint32_t _lastHeartbeatMs; uint32_t _lastHeartbeatMs;

View File

@ -16,7 +16,9 @@ MqttClient mqttClient;
// Konstruktor // Konstruktor
// -------------------------------------------------------------------------- // --------------------------------------------------------------------------
MqttClient::MqttClient() MqttClient::MqttClient()
: _client(_wifiClient) : _wifiClient(nullptr)
, _secureClient(nullptr)
, _client(nullptr)
, _lastReconnectMs(0) , _lastReconnectMs(0)
, _lastHeartbeatMs(0) , _lastHeartbeatMs(0)
, _taskHandle(nullptr) , _taskHandle(nullptr)
@ -80,7 +82,7 @@ void MqttClient::publishSession(int lastSessionSec, int gratisSec) {
// _doPublishSession() - eigentlicher Publish, wird aus MQTT-Task aufgerufen // _doPublishSession() - eigentlicher Publish, wird aus MQTT-Task aufgerufen
// -------------------------------------------------------------------------- // --------------------------------------------------------------------------
void MqttClient::_doPublishSession(int lastSessionSec, int gratisSec) { void MqttClient::_doPublishSession(int lastSessionSec, int gratisSec) {
if (!_client.connected()) { if (!_client->connected()) {
LOG_E("MQTT", "_doPublishSession: nicht verbunden, uebersprungen"); LOG_E("MQTT", "_doPublishSession: nicht verbunden, uebersprungen");
return; return;
} }
@ -106,7 +108,7 @@ void MqttClient::_doPublishSession(int lastSessionSec, int gratisSec) {
char buf[128]; char buf[128];
serializeJson(doc, buf, sizeof(buf)); serializeJson(doc, buf, sizeof(buf));
bool ok = _client.publish(MQTT_TOPIC_SESSION, buf, /*retained=*/false); bool ok = _client->publish(MQTT_TOPIC_SESSION, buf, /*retained=*/false);
LOG_I("MQTT", "publishSession: %s -> %s", buf, ok ? "OK" : "FEHLER"); LOG_I("MQTT", "publishSession: %s -> %s", buf, ok ? "OK" : "FEHLER");
} }
@ -125,7 +127,7 @@ void MqttClient::publishHeartbeat() {
char buf[220]; char buf[220];
serializeJson(doc, buf, sizeof(buf)); serializeJson(doc, buf, sizeof(buf));
bool ok = _client.publish(MQTT_TOPIC_STATUS, buf, /*retained=*/true); bool ok = _client->publish(MQTT_TOPIC_STATUS, buf, /*retained=*/true);
LOG_I("MQTT", "Heartbeat: %s -> %s", buf, ok ? "OK" : "FEHLER"); LOG_I("MQTT", "Heartbeat: %s -> %s", buf, ok ? "OK" : "FEHLER");
} }
@ -133,38 +135,34 @@ void MqttClient::publishHeartbeat() {
// reconnect() - Verbindungsaufbau, non-blocking // reconnect() - Verbindungsaufbau, non-blocking
// -------------------------------------------------------------------------- // --------------------------------------------------------------------------
bool MqttClient::reconnect() { bool MqttClient::reconnect() {
if (_client.connected()) return true; if (_client->connected()) return true;
const auto& cfg = settings.get(); const auto& cfg = settings.get();
const char* user = cfg.mqttUser[0] != '\0' ? cfg.mqttUser : nullptr; const char* user = cfg.mqttUser[0] != '\0' ? cfg.mqttUser : nullptr;
const char* pass = cfg.mqttPassword[0] != '\0' ? cfg.mqttPassword : nullptr; const char* pass = cfg.mqttPassword[0] != '\0' ? cfg.mqttPassword : nullptr;
// Offline-LWT (Last Will and Testament)
const char* lwtPayload = "{\"online\":false}"; const char* lwtPayload = "{\"online\":false}";
LOG_I("MQTT", "Verbinde als '%s'...", _clientId); LOG_I("MQTT", "Verbinde als '%s'...", _clientId);
// TLS-Handshake kann 2-15 s dauern. Laeuft auf Core 0 (MQTT-Task),
// blockiert Core 1 (loop/LaserTracker/Display) nicht mehr.
bool ok; bool ok;
if (user && pass) { if (user && pass) {
ok = _client.connect(_clientId, user, pass, ok = _client->connect(_clientId, user, pass,
MQTT_TOPIC_STATUS, 0, true, lwtPayload); MQTT_TOPIC_STATUS, 0, true, lwtPayload);
} else { } else {
ok = _client.connect(_clientId, ok = _client->connect(_clientId,
nullptr, nullptr, nullptr, nullptr,
MQTT_TOPIC_STATUS, 0, true, lwtPayload); MQTT_TOPIC_STATUS, 0, true, lwtPayload);
} }
if (ok) { if (ok) {
LOG_I("MQTT", "Verbunden!"); LOG_I("MQTT", "Verbunden!");
_client.subscribe(MQTT_TOPIC_RESET); _client->subscribe(MQTT_TOPIC_RESET);
LOG_I("MQTT", "Abonniert: %s", MQTT_TOPIC_RESET); LOG_I("MQTT", "Abonniert: %s", MQTT_TOPIC_RESET);
// Sofortigen Heartbeat senden
publishHeartbeat(); publishHeartbeat();
_lastHeartbeatMs = millis(); _lastHeartbeatMs = millis();
} else { } else {
LOG_E("MQTT", "Verbindung fehlgeschlagen, rc=%d", _client.state()); LOG_E("MQTT", "Verbindung fehlgeschlagen, rc=%d", _client->state());
} }
return ok; return ok;
@ -188,17 +186,22 @@ void MqttClient::_taskLoop() {
const char* broker = cfg.mqttBroker[0] != '\0' ? cfg.mqttBroker : DEFAULT_MQTT_BROKER; const char* broker = cfg.mqttBroker[0] != '\0' ? cfg.mqttBroker : DEFAULT_MQTT_BROKER;
uint16_t port = cfg.mqttPort > 0 ? cfg.mqttPort : DEFAULT_MQTT_PORT; uint16_t port = cfg.mqttPort > 0 ? cfg.mqttPort : DEFAULT_MQTT_PORT;
// Netzwerk-Objekte auf Core 0 anlegen (mbedtls/WiFiClientSecure bindet
// Heap-Kontexte an den erstellenden Task/Core -> kein Cross-Core-Zugriff).
_wifiClient = new WiFiClient();
_secureClient = new WiFiClientSecure();
if (port == 8883) { if (port == 8883) {
_secureClient.setInsecure(); _secureClient->setInsecure();
_client.setClient(_secureClient); _client = new PubSubClient(*_secureClient);
LOG_I("MQTT", "TLS aktiv (setInsecure, Core 0)"); LOG_I("MQTT", "TLS aktiv (setInsecure, Core 0)");
} else { } else {
_client.setClient(_wifiClient); _client = new PubSubClient(*_wifiClient);
} }
_client.setServer(broker, port); _client->setServer(broker, port);
_client.setCallback(MqttClient::onMessage); _client->setCallback(MqttClient::onMessage);
_client.setKeepAlive(60); _client->setKeepAlive(60);
_client.setBufferSize(512); _client->setBufferSize(512);
for (;;) { for (;;) {
// Kein WiFi: warten // Kein WiFi: warten
@ -208,7 +211,7 @@ void MqttClient::_taskLoop() {
} }
// Nicht verbunden: Reconnect-Intervall abwarten, dann versuchen // Nicht verbunden: Reconnect-Intervall abwarten, dann versuchen
if (!_client.connected()) { if (!_client->connected()) {
uint32_t now = millis(); uint32_t now = millis();
if (now - _lastReconnectMs >= MQTT_RECONNECT_MS) { if (now - _lastReconnectMs >= MQTT_RECONNECT_MS) {
_lastReconnectMs = now; _lastReconnectMs = now;
@ -219,7 +222,7 @@ void MqttClient::_taskLoop() {
} }
// PubSubClient-interne Verarbeitung (eingehende Nachrichten) // PubSubClient-interne Verarbeitung (eingehende Nachrichten)
_client.loop(); _client->loop();
// Pending Session-Publish (von Core 1 via volatile Flag gesetzt) // Pending Session-Publish (von Core 1 via volatile Flag gesetzt)
if (_pendingSession) { if (_pendingSession) {
@ -299,7 +302,7 @@ void MqttClient::onMessage(const char* topic, byte* payload, unsigned int length
// isConnected() // isConnected()
// -------------------------------------------------------------------------- // --------------------------------------------------------------------------
bool MqttClient::isConnected() { bool MqttClient::isConnected() {
return _client.connected(); return _client != nullptr && _client->connected();
} }
// -------------------------------------------------------------------------- // --------------------------------------------------------------------------
@ -311,7 +314,7 @@ void MqttClient::printToSerial() {
LOG_I("MQTT", " Broker : %s", cfg.mqttBroker[0] ? cfg.mqttBroker : DEFAULT_MQTT_BROKER); LOG_I("MQTT", " Broker : %s", cfg.mqttBroker[0] ? cfg.mqttBroker : DEFAULT_MQTT_BROKER);
LOG_I("MQTT", " Port : %u", cfg.mqttPort > 0 ? cfg.mqttPort : DEFAULT_MQTT_PORT); LOG_I("MQTT", " Port : %u", cfg.mqttPort > 0 ? cfg.mqttPort : DEFAULT_MQTT_PORT);
LOG_I("MQTT", " ClientID : %s", _clientId[0] ? _clientId : MQTT_CLIENT_ID); LOG_I("MQTT", " ClientID : %s", _clientId[0] ? _clientId : MQTT_CLIENT_ID);
LOG_I("MQTT", " Verbunden: %s", _client.connected() ? "JA" : "NEIN"); LOG_I("MQTT", " Verbunden: %s", (_client && _client->connected()) ? "JA" : "NEIN");
LOG_I("MQTT", " rc : %d", _client.state()); LOG_I("MQTT", " rc : %d", _client ? _client->state() : -1);
LOG_I("MQTT", "=================="); LOG_I("MQTT", "==================");
} }