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();
private:
WiFiClient _wifiClient;
WiFiClientSecure _secureClient; // fuer TLS (Port 8883)
PubSubClient _client;
// WICHTIG: Diese Objekte werden als nullptr initialisiert und erst in
// _taskLoop() auf Core 0 per 'new' angelegt. mbedtls/WiFiClientSecure
// 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 _lastHeartbeatMs;

View File

@ -16,7 +16,9 @@ MqttClient mqttClient;
// Konstruktor
// --------------------------------------------------------------------------
MqttClient::MqttClient()
: _client(_wifiClient)
: _wifiClient(nullptr)
, _secureClient(nullptr)
, _client(nullptr)
, _lastReconnectMs(0)
, _lastHeartbeatMs(0)
, _taskHandle(nullptr)
@ -80,7 +82,7 @@ void MqttClient::publishSession(int lastSessionSec, int gratisSec) {
// _doPublishSession() - eigentlicher Publish, wird aus MQTT-Task aufgerufen
// --------------------------------------------------------------------------
void MqttClient::_doPublishSession(int lastSessionSec, int gratisSec) {
if (!_client.connected()) {
if (!_client->connected()) {
LOG_E("MQTT", "_doPublishSession: nicht verbunden, uebersprungen");
return;
}
@ -106,7 +108,7 @@ void MqttClient::_doPublishSession(int lastSessionSec, int gratisSec) {
char buf[128];
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");
}
@ -125,7 +127,7 @@ void MqttClient::publishHeartbeat() {
char buf[220];
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");
}
@ -133,38 +135,34 @@ void MqttClient::publishHeartbeat() {
// reconnect() - Verbindungsaufbau, non-blocking
// --------------------------------------------------------------------------
bool MqttClient::reconnect() {
if (_client.connected()) return true;
if (_client->connected()) return true;
const auto& cfg = settings.get();
const char* user = cfg.mqttUser[0] != '\0' ? cfg.mqttUser : nullptr;
const char* pass = cfg.mqttPassword[0] != '\0' ? cfg.mqttPassword : nullptr;
// Offline-LWT (Last Will and Testament)
const char* lwtPayload = "{\"online\":false}";
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;
if (user && pass) {
ok = _client.connect(_clientId, user, pass,
MQTT_TOPIC_STATUS, 0, true, lwtPayload);
ok = _client->connect(_clientId, user, pass,
MQTT_TOPIC_STATUS, 0, true, lwtPayload);
} else {
ok = _client.connect(_clientId,
nullptr, nullptr,
MQTT_TOPIC_STATUS, 0, true, lwtPayload);
ok = _client->connect(_clientId,
nullptr, nullptr,
MQTT_TOPIC_STATUS, 0, true, lwtPayload);
}
if (ok) {
LOG_I("MQTT", "Verbunden!");
_client.subscribe(MQTT_TOPIC_RESET);
_client->subscribe(MQTT_TOPIC_RESET);
LOG_I("MQTT", "Abonniert: %s", MQTT_TOPIC_RESET);
// Sofortigen Heartbeat senden
publishHeartbeat();
_lastHeartbeatMs = millis();
} else {
LOG_E("MQTT", "Verbindung fehlgeschlagen, rc=%d", _client.state());
LOG_E("MQTT", "Verbindung fehlgeschlagen, rc=%d", _client->state());
}
return ok;
@ -188,17 +186,22 @@ 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;
// 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) {
_secureClient.setInsecure();
_client.setClient(_secureClient);
_secureClient->setInsecure();
_client = new PubSubClient(*_secureClient);
LOG_I("MQTT", "TLS aktiv (setInsecure, Core 0)");
} else {
_client.setClient(_wifiClient);
_client = new PubSubClient(*_wifiClient);
}
_client.setServer(broker, port);
_client.setCallback(MqttClient::onMessage);
_client.setKeepAlive(60);
_client.setBufferSize(512);
_client->setServer(broker, port);
_client->setCallback(MqttClient::onMessage);
_client->setKeepAlive(60);
_client->setBufferSize(512);
for (;;) {
// Kein WiFi: warten
@ -208,7 +211,7 @@ void MqttClient::_taskLoop() {
}
// Nicht verbunden: Reconnect-Intervall abwarten, dann versuchen
if (!_client.connected()) {
if (!_client->connected()) {
uint32_t now = millis();
if (now - _lastReconnectMs >= MQTT_RECONNECT_MS) {
_lastReconnectMs = now;
@ -219,7 +222,7 @@ void MqttClient::_taskLoop() {
}
// PubSubClient-interne Verarbeitung (eingehende Nachrichten)
_client.loop();
_client->loop();
// Pending Session-Publish (von Core 1 via volatile Flag gesetzt)
if (_pendingSession) {
@ -299,7 +302,7 @@ void MqttClient::onMessage(const char* topic, byte* payload, unsigned int length
// 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", " Port : %u", cfg.mqttPort > 0 ? cfg.mqttPort : DEFAULT_MQTT_PORT);
LOG_I("MQTT", " ClientID : %s", _clientId[0] ? _clientId : MQTT_CLIENT_ID);
LOG_I("MQTT", " Verbunden: %s", _client.connected() ? "JA" : "NEIN");
LOG_I("MQTT", " rc : %d", _client.state());
LOG_I("MQTT", " Verbunden: %s", (_client && _client->connected()) ? "JA" : "NEIN");
LOG_I("MQTT", " rc : %d", _client ? _client->state() : -1);
LOG_I("MQTT", "==================");
}