From 4d6e9ad20e734404f2ffd4cc216331aed04c19eb Mon Sep 17 00:00:00 2001 From: Jakub <106778416+MarekZegare4@users.noreply.github.com> Date: Mon, 28 Sep 2026 14:52:43 +0200 Subject: [PATCH] fix(esp32-ble): stay findable after a NimBLE host reset or a link that never pairs While nothing is connected, every 5 s: if advertising has stopped (a host reset ends it with no disconnect to restart it), start it again. A link up for 60 s without pairing is dropped, so advertising comes back. Co-Authored-By: Claude Opus 5.5 --- src/helpers/esp32/SerialBLEInterface.cpp | 32 +++++++++++++++++++++++- src/helpers/esp32/SerialBLEInterface.h | 3 +++ 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/src/helpers/esp32/SerialBLEInterface.cpp b/src/helpers/esp32/SerialBLEInterface.cpp index c71784f5..d0dcc92a 100644 --- a/src/helpers/esp32/SerialBLEInterface.cpp +++ b/src/helpers/esp32/SerialBLEInterface.cpp @@ -18,6 +18,14 @@ #define ADV_SLOW 244 #define ADV_FAST_MS 30000 +// While nothing is connected, every few seconds: is it still advertising? A +// NimBLE host reset (controller error, timeout) ends advertising with no +// disconnect to restart it, and it stayed unfindable until a reboot. A link +// that never pairs (the phone's OS connecting on its own, a PIN left +// unanswered) is dropped after a while: it stops advertising too. +#define ADV_CHECK_MS 5000 +#define AUTH_TIMEOUT_MS 60000 + void SerialBLEInterface::begin(const char* prefix, char* name, uint32_t pin_code) { _pin_code = pin_code; @@ -113,6 +121,7 @@ void SerialBLEInterface::authDone(bool ok) { if (ok) { BLE_DEBUG_PRINTLN(" - SecurityCallback - Authentication Success"); deviceConnected = true; + conn_since = 0; } else { BLE_DEBUG_PRINTLN(" - SecurityCallback - Authentication Failure*"); @@ -131,6 +140,7 @@ void SerialBLEInterface::onConnect(BLEServer* pServer) { void SerialBLEInterface::onConnect(BLEServer* pServer, ble_gap_conn_desc* desc) { BLE_DEBUG_PRINTLN("onConnect(), conn_handle=%d", desc->conn_handle); last_conn_id = desc->conn_handle; + conn_since = millis() | 1; } void SerialBLEInterface::onMtuChanged(BLEServer* pServer, ble_gap_conn_desc* desc, uint16_t mtu) { @@ -140,6 +150,7 @@ void SerialBLEInterface::onMtuChanged(BLEServer* pServer, ble_gap_conn_desc* des void SerialBLEInterface::onConnect(BLEServer* pServer, esp_ble_gatts_cb_param_t *param) { BLE_DEBUG_PRINTLN("onConnect(), conn_id=%d, mtu=%d", param->connect.conn_id, pServer->getPeerMTU(param->connect.conn_id)); last_conn_id = param->connect.conn_id; + conn_since = millis() | 1; } void SerialBLEInterface::onMtuChanged(BLEServer* pServer, esp_ble_gatts_cb_param_t* param) { @@ -150,6 +161,7 @@ void SerialBLEInterface::onMtuChanged(BLEServer* pServer, esp_ble_gatts_cb_param void SerialBLEInterface::onDisconnect(BLEServer* pServer) { BLE_DEBUG_PRINTLN("onDisconnect()"); deviceConnected = false; + conn_since = 0; if (_isEnabled) { adv_restart_time = millis() + ADVERT_RESTART_DELAY; } @@ -216,7 +228,7 @@ void SerialBLEInterface::disable() { pServer->disconnect(last_conn_id); pService->stop(); oldDeviceConnected = deviceConnected = false; - adv_restart_time = adv_slow_time = 0; + adv_restart_time = adv_slow_time = conn_since = 0; } size_t SerialBLEInterface::writeFrame(const uint8_t src[], size_t len) { @@ -298,6 +310,24 @@ size_t SerialBLEInterface::checkRecvFrame(uint8_t dest[]) { adv_slow_time = 0; if (_isEnabled && pServer->getConnectedCount() == 0) startAdvertising(false); } + + if (_isEnabled && !deviceConnected && (long)(millis() - adv_check_time) >= 0) { + adv_check_time = millis() + ADV_CHECK_MS; + unsigned long since = conn_since; + if (pServer->getConnectedCount() > 0) { + if (since && millis() - since > AUTH_TIMEOUT_MS) { + BLE_DEBUG_PRINTLN("SerialBLEInterface -> dropping a link that never paired"); + conn_since = 0; + pServer->disconnect(last_conn_id); // onDisconnect() restarts advertising + } + } +#if defined(CONFIG_NIMBLE_ENABLED) + else if (!adv_restart_time && !pServer->getAdvertising()->isAdvertising()) { + BLE_DEBUG_PRINTLN("SerialBLEInterface -> advertising had stopped, restarting"); + startAdvertising(true); + } +#endif + } return 0; } diff --git a/src/helpers/esp32/SerialBLEInterface.h b/src/helpers/esp32/SerialBLEInterface.h index e0cce5f3..6032eadc 100644 --- a/src/helpers/esp32/SerialBLEInterface.h +++ b/src/helpers/esp32/SerialBLEInterface.h @@ -20,6 +20,8 @@ class SerialBLEInterface : public BaseSerialInterface, BLESecurityCallbacks, BLE unsigned long _last_write; unsigned long adv_restart_time; unsigned long adv_slow_time; // when fast advertising drops to the slow interval (0 = not pending) + unsigned long adv_check_time; // next look at whether it's still findable + unsigned long conn_since; // a link up but not yet paired: since when (0 = none) struct Frame { uint8_t len; @@ -75,6 +77,7 @@ public: oldDeviceConnected = false; adv_restart_time = 0; adv_slow_time = 0; + adv_check_time = conn_since = 0; _isEnabled = false; _last_write = 0; last_conn_id = 0;