Fix nRF52 freeze + watchdog reset when saving config over BLE (#11202)

* Fix nRF52 freeze + watchdog reset when saving config over BLE

Since #10967 phone-originated admin messages are handled synchronously on
Bluefruit's BLE event task. NRF52Bluetooth::disconnect() busy-waited for
BLE_GAP_EVT_DISCONNECTED, which only that same task can process, so any
config save that requires a reboot (e.g. position) deadlocked the device
until the 90s watchdog fired. Bound the wait to 1s and sleep instead of
spinning so lower-priority tasks (including the watchdog feed) keep
running; the SoftDevice completes the link termination on its own.

* Address review: use Throttle helper, tighten comment

* Name the disconnect timeout constant

* Log unconfirmed BLE disconnect at WARN with elapsed time
This commit is contained in:
Ben Meadors
2026-07-25 11:20:04 +00:00
committed by GitHub
co-authored by GitHub
parent b46c8c9f80
commit 3f4e7cc622
+11 -4
View File
@@ -7,6 +7,7 @@
#include "error.h" #include "error.h"
#include "main.h" #include "main.h"
#include "mesh/PhoneAPI.h" #include "mesh/PhoneAPI.h"
#include "mesh/Throttle.h"
#include "mesh/mesh-pb-constants.h" #include "mesh/mesh-pb-constants.h"
#include <bluefruit.h> #include <bluefruit.h>
#include <utility/bonding.h> #include <utility/bonding.h>
@@ -466,17 +467,23 @@ bool NRF52Bluetooth::onUnwantedPairing(uint16_t conn_handle, uint8_t const passk
// Disconnect any BLE connections // Disconnect any BLE connections
void NRF52Bluetooth::disconnect() void NRF52Bluetooth::disconnect()
{ {
static constexpr uint32_t DISCONNECT_TIMEOUT_MSEC = 1000;
uint8_t connection_num = Bluefruit.connected(); uint8_t connection_num = Bluefruit.connected();
if (connection_num) { if (connection_num) {
// Close all connections. We're only expecting one. // Close all connections. We're only expecting one.
for (uint8_t i = 0; i < connection_num; i++) for (uint8_t i = 0; i < connection_num; i++)
Bluefruit.disconnect(i); Bluefruit.disconnect(i);
// Wait for disconnection // Best-effort wait: on Bluefruit's BLE event task the DISCONNECTED event can't be processed
while (Bluefruit.connected()) // until this callback returns, so an unbounded wait would deadlock until the watchdog fires.
yield(); uint32_t start = millis();
while (Bluefruit.connected() && Throttle::isWithinTimespanMs(start, DISCONNECT_TIMEOUT_MSEC))
delay(1);
LOG_INFO("Ended BLE connection"); if (Bluefruit.connected())
LOG_WARN("BLE disconnect unconfirmed after %ums, continuing shutdown", millis() - start);
else
LOG_INFO("Ended BLE connection");
} }
} }