diff --git a/src/nimble/NimbleBluetooth.cpp b/src/nimble/NimbleBluetooth.cpp index 647c1bef8..bd3de1601 100644 --- a/src/nimble/NimbleBluetooth.cpp +++ b/src/nimble/NimbleBluetooth.cpp @@ -637,6 +637,21 @@ class NimbleBluetoothFromRadioCallback : public BLECharacteristicCallbacks } }; +// One log notify per log line shares the msys_1 mbuf pool with the fromNum doorbell and ATT +// responses, so a logging burst starves them; back off on rejection until the pool refills. +static constexpr uint32_t LOG_NOTIFY_BACKOFF_MS = 250; +static std::atomic lastLogNotifyFailureMs{0}; + +class NimbleBluetoothLogRadioCallback : public BLECharacteristicCallbacks +{ + void onStatus(BLECharacteristic *, Status s, uint32_t) override + { + // ERROR_GATT is the only status meaning the host refused it; the rest never allocated. + if (s == Status::ERROR_GATT) + lastLogNotifyFailureMs.store(millis()); + } +}; + class NimbleBluetoothSecurityCallback : public BLESecurityCallbacks { void onPassKeyNotify(uint32_t passkey) override @@ -1005,6 +1020,9 @@ void NimbleBluetooth::setupService() static NimbleBluetoothFromRadioCallback fromRadioCallbacks; FromRadioCharacteristic->setCallbacks(&fromRadioCallbacks); + static NimbleBluetoothLogRadioCallback logRadioCallbacks; + logRadioCharacteristic->setCallbacks(&logRadioCallbacks); + bleService->start(); // Setup the battery service @@ -1056,6 +1074,11 @@ void NimbleBluetooth::sendLog(const uint8_t *logMessage, size_t length) if (!isConnected() || length > 512) { return; } + if (!logRadioCharacteristic) // BLE may have been torn down; never notify a freed characteristic + return; + // Pool still under pressure; drop this line rather than spend a buffer fromNum needs. + if (Throttle::isWithinTimespanMs(lastLogNotifyFailureMs.load(), LOG_NOTIFY_BACKOFF_MS)) + return; logRadioCharacteristic->setValue(logMessage, length); logRadioCharacteristic->notify(); } diff --git a/variants/esp32/esp32-common.ini b/variants/esp32/esp32-common.ini index 72cc1966e..ea7d8d15c 100644 --- a/variants/esp32/esp32-common.ini +++ b/variants/esp32/esp32-common.ini @@ -291,7 +291,9 @@ custom_sdkconfig = CONFIG_BT_CTRL_BLE_MAX_ACT=2 CONFIG_BT_CTRL_SCAN_DUPL_CACHE_SIZE=10 CONFIG_BT_NIMBLE_WHITELIST_SIZE=1 - CONFIG_BT_NIMBLE_MSYS_1_BLOCK_COUNT=8 + # msys_1 is the only pool GATT allocates from, and 8 blocks left no headroom for a notify burst + # (#11245); back to the IDF default of 12 for +1KB. Raising msys_2 would not help. + CONFIG_BT_NIMBLE_MSYS_1_BLOCK_COUNT=12 CONFIG_BT_NIMBLE_MSYS_2_BLOCK_COUNT=8 CONFIG_BT_NIMBLE_TRANSPORT_ACL_FROM_LL_COUNT=8 CONFIG_BT_NIMBLE_TRANSPORT_EVT_COUNT=12