diff --git a/config/esp32/components/chip/Kconfig b/config/esp32/components/chip/Kconfig index d176d6571ef205..47ceceffe2f94d 100644 --- a/config/esp32/components/chip/Kconfig +++ b/config/esp32/components/chip/Kconfig @@ -726,12 +726,13 @@ menu "CHIP Device Layer" should not start advertising automatically after power-up. config USE_BLE_ONLY_FOR_COMMISSIONING - bool "Use BLE only for commissioning" - default y - help - Disable this flag if BLE is used for any other purpose than commissioning. - When enabled, it deinitialized the BLE on successful commissioning, and on - bootup do not initialize the BLE if device is already provisioned with Wi-Fi/Thread credentials. + depends on BT_ENABLED + bool "Use BLE only for commissioning" + default y + help + Disable this flag if BLE is used for any other purpose than commissioning. + When enabled, it deinitialized the BLE on successful commissioning, and on + bootup do not initialize the BLE if device is already provisioned with Wi-Fi/Thread credentials. endmenu diff --git a/examples/platform/esp32/common/CommonDeviceCallbacks.cpp b/examples/platform/esp32/common/CommonDeviceCallbacks.cpp index ede4e154483154..763340cee45e45 100644 --- a/examples/platform/esp32/common/CommonDeviceCallbacks.cpp +++ b/examples/platform/esp32/common/CommonDeviceCallbacks.cpp @@ -51,7 +51,6 @@ void CommonDeviceCallbacks::DeviceEventCallback(const ChipDeviceEvent * event, i case DeviceEventType::kCHIPoBLEConnectionClosed: ESP_LOGI(TAG, "CHIPoBLE disconnected"); - Esp32AppServer::DeInitBLEIfCommissioned(); break; case DeviceEventType::kDnssdInitialized: @@ -67,6 +66,7 @@ void CommonDeviceCallbacks::DeviceEventCallback(const ChipDeviceEvent * event, i case DeviceEventType::kCommissioningComplete: { ESP_LOGI(TAG, "Commissioning complete"); + Esp32AppServer::DeInitBLEIfCommissioned(); } break; diff --git a/examples/platform/esp32/common/Esp32AppServer.cpp b/examples/platform/esp32/common/Esp32AppServer.cpp index 873115c25ab1af..4f854c518bd01f 100644 --- a/examples/platform/esp32/common/Esp32AppServer.cpp +++ b/examples/platform/esp32/common/Esp32AppServer.cpp @@ -111,47 +111,12 @@ static size_t hex_string_to_binary(const char * hex_string, uint8_t * buf, size_ void Esp32AppServer::DeInitBLEIfCommissioned(void) { -#if CONFIG_BT_ENABLED && CONFIG_USE_BLE_ONLY_FOR_COMMISSIONING +#ifdef CONFIG_USE_BLE_ONLY_FOR_COMMISSIONING if (chip::Server::GetInstance().GetFabricTable().FabricCount() > 0) { - esp_err_t err = ESP_OK; - -#if CONFIG_BT_NIMBLE_ENABLED - if (!ble_hs_is_enabled()) - { - ESP_LOGI(TAG, "BLE already deinited"); - return; - } - if (nimble_port_stop() != 0) - { - ESP_LOGE(TAG, "nimble_port_stop() failed"); - return; - } - vTaskDelay(100); - nimble_port_deinit(); - -#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0, 0) - err = esp_nimble_hci_and_controller_deinit(); -#endif -#endif /* CONFIG_BT_NIMBLE_ENABLED */ - -#if CONFIG_IDF_TARGET_ESP32 - err |= esp_bt_mem_release(ESP_BT_MODE_BTDM); -#elif CONFIG_IDF_TARGET_ESP32C2 || CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S3 || CONFIG_IDF_TARGET_ESP32H2 || \ - CONFIG_IDF_TARGET_ESP32C6 - err |= esp_bt_mem_release(ESP_BT_MODE_BLE); -#endif - - if (err != ESP_OK) - { - ESP_LOGE(TAG, "BLE deinit failed"); - } - else - { - ESP_LOGI(TAG, "BLE deinit successful and memory reclaimed"); - } + chip::DeviceLayer::Internal::BLEMgr().Shutdown(); } -#endif /* CONFIG_BT_ENABLED && CONFIG_USE_BLE_ONLY_FOR_COMMISSIONING */ +#endif /* CONFIG_USE_BLE_ONLY_FOR_COMMISSIONING */ } void Esp32AppServer::Init(AppDelegate * sAppDelegate) @@ -162,7 +127,7 @@ void Esp32AppServer::Init(AppDelegate * sAppDelegate) if (hex_string_to_binary(CONFIG_TEST_EVENT_TRIGGER_ENABLE_KEY, sTestEventTriggerEnableKey, sizeof(sTestEventTriggerEnableKey)) == 0) { - ESP_LOGE(TAG, "Failed to convert the EnableKey string to octstr type value"); + ChipLogError(DeviceLayer, "Failed to convert the EnableKey string to octstr type value"); memset(sTestEventTriggerEnableKey, 0, sizeof(sTestEventTriggerEnableKey)); } static SimpleTestEventTriggerDelegate sTestEventTriggerDelegate{}; @@ -190,7 +155,7 @@ void Esp32AppServer::Init(AppDelegate * sAppDelegate) if (chip::DeviceLayer::ConnectivityMgr().IsThreadProvisioned() && (chip::Server::GetInstance().GetFabricTable().FabricCount() != 0)) { - ESP_LOGI(TAG, "Thread has been provisioned, publish the dns service now"); + ChipLogProgress(DeviceLayer, "Thread has been provisioned, publish the dns service now"); chip::app::DnssdServer::Instance().StartServer(); } #endif diff --git a/src/platform/ESP32/BLEManagerImpl.h b/src/platform/ESP32/BLEManagerImpl.h index 39cde57dc0c262..9364ead642c525 100644 --- a/src/platform/ESP32/BLEManagerImpl.h +++ b/src/platform/ESP32/BLEManagerImpl.h @@ -157,7 +157,7 @@ class BLEManagerImpl final : public BLEManager, // ===== Members that implement the BLEManager internal interface. CHIP_ERROR _Init(void); - void _Shutdown() {} + void _Shutdown(); bool _IsAdvertisingEnabled(void); CHIP_ERROR _SetAdvertisingEnabled(bool val); bool _IsAdvertising(void); @@ -298,6 +298,7 @@ class BLEManagerImpl final : public BLEManager, void DriveBLEState(void); CHIP_ERROR InitESPBleLayer(void); + void DeinitESPBleLayer(void); CHIP_ERROR ConfigureAdvertisingData(void); CHIP_ERROR StartAdvertising(void); void StartBleAdvTimeoutTimer(uint32_t aTimeoutInMs); @@ -328,6 +329,9 @@ class BLEManagerImpl final : public BLEManager, static void HandleGAPEvent(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t * param); #elif CONFIG_BT_NIMBLE_ENABLED + CHIP_ERROR DeinitBLE(); + static void ClaimBLEMemory(System::Layer *, void *); + void HandleRXCharRead(struct ble_gatt_char_context * param); void HandleRXCharWrite(struct ble_gatt_char_context * param); void HandleTXCharWrite(struct ble_gatt_char_context * param); diff --git a/src/platform/ESP32/nimble/BLEManagerImpl.cpp b/src/platform/ESP32/nimble/BLEManagerImpl.cpp index edff1666e6311a..67b5f9bc4b9530 100644 --- a/src/platform/ESP32/nimble/BLEManagerImpl.cpp +++ b/src/platform/ESP32/nimble/BLEManagerImpl.cpp @@ -211,14 +211,6 @@ CHIP_ERROR BLEManagerImpl::_Init() { CHIP_ERROR err; - // Initialize the Chip BleLayer. -#if CONFIG_ENABLE_ESP32_BLE_CONTROLLER - err = BleLayer::Init(this, this, this, &DeviceLayer::SystemLayer()); -#else - err = BleLayer::Init(this, this, &DeviceLayer::SystemLayer()); -#endif - SuccessOrExit(err); - // Create FreeRTOS sw timer for BLE timeouts and interval change. sbleAdvTimeoutTimer = xTimerCreate("BleAdvTimer", // Just a text name, not used by the RTOS kernel 1, // == default timer period @@ -227,6 +219,16 @@ CHIP_ERROR BLEManagerImpl::_Init() BleAdvTimeoutHandler // timer callback handler ); + VerifyOrReturnError(sbleAdvTimeoutTimer != nullptr, CHIP_ERROR_NO_MEMORY); + + // Initialize the Chip BleLayer. +#if CONFIG_ENABLE_ESP32_BLE_CONTROLLER + err = BleLayer::Init(this, this, this, &DeviceLayer::SystemLayer()); +#else + err = BleLayer::Init(this, this, &DeviceLayer::SystemLayer()); +#endif + SuccessOrExit(err); + mRXCharAttrHandle = 0; #if CHIP_ENABLE_ADDITIONAL_DATA_ADVERTISING mC3CharAttrHandle = 0; @@ -252,6 +254,25 @@ CHIP_ERROR BLEManagerImpl::_Init() return err; } +void BLEManagerImpl::_Shutdown() +{ + VerifyOrReturn(sbleAdvTimeoutTimer != nullptr); + xTimerDelete(sbleAdvTimeoutTimer, portMAX_DELAY); + sbleAdvTimeoutTimer = nullptr; + + BleLayer::Shutdown(); + + // selectively setting kGATTServiceStarted flag, in order to notify the state machine to stop the CHIPoBLE GATT service + mFlags.ClearAll().Set(Flags::kGATTServiceStarted); + mServiceMode = ConnectivityManager::kCHIPoBLEServiceMode_Disabled; + +#if CONFIG_ENABLE_ESP32_BLE_CONTROLLER + OnChipBleConnectReceived = nullptr; +#endif // CONFIG_ENABLE_ESP32_BLE_CONTROLLER + + PlatformMgr().ScheduleWork(DriveBLEState, 0); +} + CHIP_ERROR BLEManagerImpl::_SetAdvertisingEnabled(bool val) { CHIP_ERROR err = CHIP_NO_ERROR; @@ -707,6 +728,8 @@ CHIP_ERROR BLEManagerImpl::MapBLEError(int bleErr) } void BLEManagerImpl::CancelBleAdvTimeoutTimer(void) { + VerifyOrReturn(sbleAdvTimeoutTimer != nullptr); + if (xTimerStop(sbleAdvTimeoutTimer, pdMS_TO_TICKS(0)) == pdFAIL) { ChipLogError(DeviceLayer, "Failed to stop BledAdv timeout timer"); @@ -714,6 +737,8 @@ void BLEManagerImpl::CancelBleAdvTimeoutTimer(void) } void BLEManagerImpl::StartBleAdvTimeoutTimer(uint32_t aTimeoutInMs) { + VerifyOrReturn(sbleAdvTimeoutTimer != nullptr); + if (xTimerIsTimerActive(sbleAdvTimeoutTimer)) { CancelBleAdvTimeoutTimer(); @@ -843,7 +868,8 @@ void BLEManagerImpl::DriveBLEState(void) // Stop the CHIPoBLE GATT service if needed. if (mServiceMode != ConnectivityManager::kCHIPoBLEServiceMode_Enabled && mFlags.Has(Flags::kGATTServiceStarted)) { - // TODO: Not supported + DeinitESPBleLayer(); + mFlags.ClearAll(); } exit: @@ -969,6 +995,56 @@ CHIP_ERROR BLEManagerImpl::InitESPBleLayer(void) return err; } +void BLEManagerImpl::DeinitESPBleLayer() +{ + VerifyOrReturn(DeinitBLE() == CHIP_NO_ERROR); + BLEManagerImpl::ClaimBLEMemory(nullptr, nullptr); +} + +void BLEManagerImpl::ClaimBLEMemory(System::Layer *, void *) +{ + TaskHandle_t handle = xTaskGetHandle("nimble_host"); + if (handle) + { + ChipLogDetail(DeviceLayer, "Schedule ble memory reclaiming since nimble host is still running"); + + // Rescheduling it for later, 2 seconds is an arbitrary value, keeping it a bit more so that + // we dont have to reschedule it again + SystemLayer().StartTimer(System::Clock::Seconds32(2), ClaimBLEMemory, nullptr); + } + else + { + // Free up all the space occupied by ble and add it to heap + esp_err_t err = ESP_OK; + +#if CONFIG_IDF_TARGET_ESP32 + err = esp_bt_mem_release(ESP_BT_MODE_BTDM); +#elif CONFIG_IDF_TARGET_ESP32C2 || CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S3 || CONFIG_IDF_TARGET_ESP32H2 || \ + CONFIG_IDF_TARGET_ESP32C6 + err = esp_bt_mem_release(ESP_BT_MODE_BLE); +#endif + + VerifyOrReturn(err == ESP_OK, ChipLogError(DeviceLayer, "BLE deinit failed")); + ChipLogProgress(DeviceLayer, "BLE deinit successful and memory reclaimed"); + // TODO: post an event when ble is deinitialized and memory is added to heap + } +} + +CHIP_ERROR BLEManagerImpl::DeinitBLE() +{ + VerifyOrReturnError(ble_hs_is_enabled(), CHIP_ERROR_INCORRECT_STATE, ChipLogProgress(DeviceLayer, "BLE already deinited")); + VerifyOrReturnError(0 == nimble_port_stop(), MapBLEError(ESP_FAIL), ChipLogError(DeviceLayer, "nimble_port_stop() failed")); + + esp_err_t err = nimble_port_deinit(); + VerifyOrReturnError(err == ESP_OK, MapBLEError(err)); + +#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0, 0) + err = esp_nimble_hci_and_controller_deinit(); +#endif + + return MapBLEError(err); +} + CHIP_ERROR BLEManagerImpl::ConfigureAdvertisingData(void) { CHIP_ERROR err;