From 3f4e7cc6222eb2206e48d88e7a1e95ca4fc2be32 Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Sat, 25 Jul 2026 06:20:04 -0500 Subject: [PATCH] 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 --- src/platform/nrf52/NRF52Bluetooth.cpp | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/platform/nrf52/NRF52Bluetooth.cpp b/src/platform/nrf52/NRF52Bluetooth.cpp index 357c1484c..f1d9c6845 100644 --- a/src/platform/nrf52/NRF52Bluetooth.cpp +++ b/src/platform/nrf52/NRF52Bluetooth.cpp @@ -7,6 +7,7 @@ #include "error.h" #include "main.h" #include "mesh/PhoneAPI.h" +#include "mesh/Throttle.h" #include "mesh/mesh-pb-constants.h" #include #include @@ -466,17 +467,23 @@ bool NRF52Bluetooth::onUnwantedPairing(uint16_t conn_handle, uint8_t const passk // Disconnect any BLE connections void NRF52Bluetooth::disconnect() { + static constexpr uint32_t DISCONNECT_TIMEOUT_MSEC = 1000; uint8_t connection_num = Bluefruit.connected(); if (connection_num) { // Close all connections. We're only expecting one. for (uint8_t i = 0; i < connection_num; i++) Bluefruit.disconnect(i); - // Wait for disconnection - while (Bluefruit.connected()) - yield(); + // Best-effort wait: on Bluefruit's BLE event task the DISCONNECTED event can't be processed + // until this callback returns, so an unbounded wait would deadlock until the watchdog fires. + 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"); } }