fix(mqtt): an explicit bridge stop cancels a pending late-ack resume

setBridgeState(false) returned early when the bridge was already down,
so `set bridge.enabled off` after a timed-out restart left the resume
pending and MQTT restarted against the operator's choice once the task
acknowledged. Disabling now clears the pending resume before that early
return, and the resume also requires bridge_enabled. The StopUnproven
log and `get mqtt.status` no longer say only "reboot to recover", since
a late ack now recovers on its own.

Hardware (Heltec V4, 5 live slots, 1 s test stop deadline): a restart
went StopUnproven, the task acknowledged 27 s later and the bridge
resumed with all slots reconnecting; with `set bridge.enabled off` in
between it stayed stopped until `set bridge.enabled on`.
This commit is contained in:
agessaman committed 2026-09-19 13:58:49 -07:00
1 parent f72bdbad48
commit d180f097ea
5 files changed
+13 -5

No files matched your search

+2 -1
View File
@@ -1702,7 +1702,8 @@ void MyMesh::loop() {
#ifdef WITH_MQTT_BRIDGE
// A timed-out stop keeps the bridge down until the MQTT task acknowledges it;
// once that late ack lands, restart the bridge that was meant to be running.
if (_bridge_resume_pending && bridge && bridge->stopAcknowledgedLate()) {
if (_bridge_resume_pending && _prefs.bridge_enabled && bridge &&
bridge->stopAcknowledgedLate()) {
Serial.println("MQTT: stop acknowledged late - resuming bridge");
setBridgeState(true);
}
+3
View File
@@ -353,6 +353,9 @@ public:
#endif
if (!bridge) return;
}
#ifdef WITH_MQTT_BRIDGE
if (!enable) _bridge_resume_pending = false; // an explicit stop cancels a pending resume
#endif
if (enable == bridge->isRunning()) return;
if (enable)
{
+2 -1
View File
@@ -1518,7 +1518,8 @@ void MyMesh::loop() {
#ifdef WITH_MQTT_BRIDGE
// A timed-out stop keeps the bridge down until the MQTT task acknowledges it;
// once that late ack lands, restart the bridge that was meant to be running.
if (_bridge_resume_pending && bridge && bridge->stopAcknowledgedLate()) {
if (_bridge_resume_pending && _prefs.bridge_enabled && bridge &&
bridge->stopAcknowledgedLate()) {
Serial.println("MQTT: stop acknowledged late - resuming bridge");
setBridgeState(true);
}
+3
View File
@@ -364,6 +364,9 @@ public:
#endif
if (!bridge) return;
}
#ifdef WITH_MQTT_BRIDGE
if (!enable) _bridge_resume_pending = false; // an explicit stop cancels a pending resume
#endif
if (enable == bridge->isRunning()) return;
if (enable)
{
+3 -3
View File
@@ -272,7 +272,7 @@ void MQTTBridge::formatMqttStatusReply(char* buf, size_t bufsize, const MQTTPref
const char* msgs = (obs && obs->mqtt_status_enabled) ? "on" : "off";
if (s_mqtt_bridge_instance == nullptr || !s_mqtt_bridge_instance->_initialized) {
snprintf(buf, bufsize, "> msgs: %s (bridge %s)", msgs,
s_stop_unproven ? "stopped: previous stop unproven, reboot to recover"
s_stop_unproven ? "stopped: waiting for the previous stop to finish; reboot if it persists"
: "not running");
return;
}
@@ -903,7 +903,7 @@ void MQTTBridge::begin() {
// refuse. Recovery is the task finishing, or a reboot.
pollLateStopAck();
if (!_lifecycle.mayRestart()) {
MQTT_DEBUG_PRINTLN("MQTT Bridge start refused: previous stop unproven (%s) - reboot to recover",
MQTT_DEBUG_PRINTLN("MQTT Bridge start refused: previous stop unproven (%s) - waiting for the task to acknowledge",
MQTTLifecycle::stateName(_lifecycle.state()));
return;
}
@@ -1230,7 +1230,7 @@ void MQTTBridge::end() {
s_stop_unproven = _lifecycle.isStopUnproven();
if (_lifecycle.isStopUnproven()) {
MQTT_DEBUG_PRINTLN("MQTT Bridge stop UNPROVEN after %lu ms: task did not acknowledge. "
"Nothing released, restart refused, OTA blocked - reboot to recover.",
"Nothing released, OTA blocked; restart waits for its late ack (reboot if it never comes).",
(unsigned long)_lifecycle.stopTimeoutMs());
} else {
MQTT_DEBUG_PRINTLN("MQTT Bridge stopped (clean)");