From e4d127db67713b8c5bb99d2b975fb4724d3960a4 Mon Sep 17 00:00:00 2001 From: proddy Date: Sun, 9 Aug 2026 09:58:57 +0200 Subject: [PATCH] mqtt disconnect on non-psram boards --- src/core/mqtt.cpp | 25 +++++++++++++++++++++++++ src/core/mqtt.h | 11 ++++------- src/core/system.cpp | 2 +- 3 files changed, 30 insertions(+), 8 deletions(-) diff --git a/src/core/mqtt.cpp b/src/core/mqtt.cpp index 13ccdb81b..7067cdd99 100644 --- a/src/core/mqtt.cpp +++ b/src/core/mqtt.cpp @@ -313,6 +313,31 @@ void Mqtt::on_publish(uint16_t packetId) const { LOG_DEBUG("Packet %d sent successful", packetId); } +// gracefully disconnect from the broker by sending a proper MQTT DISCONNECT packet. +// this is called just before a restart so the broker ends our session cleanly +// (avoids a lingering session that shows up as "session taken over" on reconnect). +// +// the disconnect is deferred inside espMqttClient - the DISCONNECT packet is only +// queued and actually sent when loop() runs the state machine. On PSRAM boards the +// client has its own task that keeps pumping loop(), so it flushes on its own. On +// non-PSRAM boards (UseInternalTask::NO) nothing drives loop() once we're mid-restart, +// so we pump it here until the client is fully disconnected, bounded by a timeout. +void Mqtt::disconnect() { + if (!mqttClient_) { + return; + } + + mqttClient_->disconnect(); // graceful - queues the DISCONNECT packet + + if (EMSESP::system_.PSram() == 0) { + uint32_t start = uuid::get_uptime(); + while (!mqttClient_->disconnected() && (uuid::get_uptime() - start) < MQTT_DISCONNECT_TIMEOUT) { + mqttClient_->loop(); + delay(2); + } + } +} + // called when MQTT settings have changed via the MQTT Settings or Application Settings Web pages void Mqtt::reset_mqtt() { if (!enabled()) { diff --git a/src/core/mqtt.h b/src/core/mqtt.h index 21a1cfcc7..46e69478c 100644 --- a/src/core/mqtt.h +++ b/src/core/mqtt.h @@ -61,8 +61,9 @@ class Mqtt { enum Operation : uint8_t { PUBLISH, SUBSCRIBE, UNSUBSCRIBE }; enum NestedFormat : uint8_t { NESTED = 1, SINGLE }; - static constexpr uint8_t MQTT_TOPIC_MAX_SIZE = 128; // fixed, not a user setting anymore - static constexpr uint16_t MQTT_QUEUE_MAX_SIZE = 300; + static constexpr uint8_t MQTT_TOPIC_MAX_SIZE = 128; // fixed, not a user setting anymore + static constexpr uint16_t MQTT_QUEUE_MAX_SIZE = 300; + static constexpr uint32_t MQTT_DISCONNECT_TIMEOUT = 1000; // max ms to flush a graceful disconnect on non-PSRAM boards static void on_connect(); static void on_disconnect(espMqttClientTypes::DisconnectReason reason); @@ -132,11 +133,7 @@ class Mqtt { return mqttClient_ ? mqttClient_->connected() : false; } - static void disconnect() { - if (mqttClient_) { - mqttClient_->disconnect(); - }; - } + static void disconnect(); static bool enabled() { return mqtt_enabled_; diff --git a/src/core/system.cpp b/src/core/system.cpp index bee672f33..214de9ac5 100644 --- a/src/core/system.cpp +++ b/src/core/system.cpp @@ -586,7 +586,7 @@ void System::system_restart(const char * partitionname) { } Serial.flush(); // wait for hardware TX buffer to drain - Mqtt::disconnect(); // gracefully disconnect MQTT, needed for QOS1 + Mqtt::disconnect(); // gracefully disconnect MQTT (flushes the DISCONNECT before reboot, needed for QOS1) EMSuart::stop(); // stop UART so there is no interference #ifndef EMSESP_STANDALONE delay(1000); // wait 1 second