From 68d39df2862afae5ff9650079c3e126b50d091d5 Mon Sep 17 00:00:00 2001 From: Lucas Saavedra Vaz <32426024+lucasssvaz@users.noreply.github.com> Date: Wed, 10 Sep 2025 09:06:16 -0300 Subject: [PATCH 1/2] feat(simpleBLE): Add support for NimBLE --- .../BluetoothSerial/src/BluetoothSerial.h | 2 +- .../SimpleBleDevice/SimpleBleDevice.ino | 8 +- libraries/SimpleBLE/src/SimpleBLE.cpp | 289 +++++++++++++++++- libraries/SimpleBLE/src/SimpleBLE.h | 14 +- 4 files changed, 297 insertions(+), 16 deletions(-) diff --git a/libraries/BluetoothSerial/src/BluetoothSerial.h b/libraries/BluetoothSerial/src/BluetoothSerial.h index 8cb6edb876c..68ddd3c747a 100644 --- a/libraries/BluetoothSerial/src/BluetoothSerial.h +++ b/libraries/BluetoothSerial/src/BluetoothSerial.h @@ -35,7 +35,7 @@ typedef std::function KeyRequestCb; typedef std::function AuthCompleteCb; typedef std::function BTAdvertisedDeviceCb; -class BluetoothSerial : public Stream { +class [[deprecated("BluetoothSerial won't be supported in version 4.0.0 by default")]] BluetoothSerial : public Stream { public: BluetoothSerial(void); ~BluetoothSerial(void); diff --git a/libraries/SimpleBLE/examples/SimpleBleDevice/SimpleBleDevice.ino b/libraries/SimpleBLE/examples/SimpleBleDevice/SimpleBleDevice.ino index a92e576196d..69536bed7e2 100644 --- a/libraries/SimpleBLE/examples/SimpleBleDevice/SimpleBleDevice.ino +++ b/libraries/SimpleBLE/examples/SimpleBleDevice/SimpleBleDevice.ino @@ -18,10 +18,6 @@ #include "SimpleBLE.h" -#if !defined(CONFIG_BT_ENABLED) || !defined(CONFIG_BLUEDROID_ENABLED) -#error Bluetooth is not enabled! Please run `make menuconfig` to and enable it -#endif - SimpleBLE ble; void onButton() { @@ -34,7 +30,7 @@ void onButton() { void setup() { Serial.begin(115200); Serial.setDebugOutput(true); - pinMode(0, INPUT_PULLUP); + pinMode(BOOT_PIN, INPUT_PULLUP); Serial.print("ESP32 SDK: "); Serial.println(ESP.getSdkVersion()); ble.begin("ESP32 SimpleBLE"); @@ -43,7 +39,7 @@ void setup() { void loop() { static uint8_t lastPinState = 1; - uint8_t pinState = digitalRead(0); + uint8_t pinState = digitalRead(BOOT_PIN); if (!pinState && lastPinState) { onButton(); } diff --git a/libraries/SimpleBLE/src/SimpleBLE.cpp b/libraries/SimpleBLE/src/SimpleBLE.cpp index 3f1f2bbd1c1..78f639c58fe 100644 --- a/libraries/SimpleBLE/src/SimpleBLE.cpp +++ b/libraries/SimpleBLE/src/SimpleBLE.cpp @@ -12,20 +12,56 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "sdkconfig.h" #include "soc/soc_caps.h" - -#if SOC_BT_SUPPORTED && defined(CONFIG_BT_ENABLED) && defined(CONFIG_BLUEDROID_ENABLED) +#include "sdkconfig.h" +#if defined(SOC_BLE_SUPPORTED) || defined(CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE) +#if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED) #include "SimpleBLE.h" #include "esp32-hal-log.h" +#if defined(SOC_BLE_SUPPORTED) #include "esp_bt.h" +#endif + +/*************************************************************************** + * Bluedroid includes * + ***************************************************************************/ +#if defined(CONFIG_BLUEDROID_ENABLED) #include "esp_gap_ble_api.h" #include "esp_gatts_api.h" #include "esp_bt_defs.h" #include "esp_bt_main.h" +#endif + +/*************************************************************************** + * NimBLE includes * + ***************************************************************************/ +#if defined(CONFIG_NIMBLE_ENABLED) +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_BT_NIMBLE_LEGACY_VHCI_ENABLE +#include +#endif + +// Forward declaration +extern "C" void ble_store_config_init(void); +#endif + +#if defined(CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE) +#include "esp32-hal-hosted.h" +#endif +/*************************************************************************** + * Bluedroid data structures * + ***************************************************************************/ +#if defined(CONFIG_BLUEDROID_ENABLED) static esp_ble_adv_data_t _adv_config = { .set_scan_rsp = false, .include_name = true, @@ -55,14 +91,90 @@ static esp_ble_adv_params_t _adv_params = { .channel_map = ADV_CHNL_ALL, .adv_filter_policy = ADV_FILTER_ALLOW_SCAN_ANY_CON_ANY, }; +#endif + +/*************************************************************************** + * NimBLE data structures * + ***************************************************************************/ +#if defined(CONFIG_NIMBLE_ENABLED) +static struct ble_hs_adv_fields _nimble_adv_fields; +static struct ble_gap_adv_params _nimble_adv_params = { + .conn_mode = BLE_GAP_CONN_MODE_NON, + .disc_mode = BLE_GAP_DISC_MODE_GEN, + .itvl_min = 512, + .itvl_max = 1024, + .channel_map = 0, + .filter_policy = 0, + .high_duty_cycle = 0, +}; +// Global variables for NimBLE synchronization +static bool _nimble_synced = false; +#endif + +// Global state tracking +static bool _ble_initialized = false; + +/*************************************************************************** + * Bluedroid callbacks * + ***************************************************************************/ +#if defined(CONFIG_BLUEDROID_ENABLED) static void _on_gap(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) { if (event == ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT) { esp_ble_gap_start_advertising(&_adv_params); } } +#endif + +/*************************************************************************** + * NimBLE callbacks * + ***************************************************************************/ +#if defined(CONFIG_NIMBLE_ENABLED) +static void _nimble_host_task(void *param) { + // This function will be called to run the BLE host + nimble_port_run(); + // Should never reach here unless nimble_port_stop() is called + nimble_port_freertos_deinit(); +} + +static void _nimble_on_reset(int reason) { + log_i("NimBLE reset; reason=%d", reason); +} + +static void _nimble_on_sync(void) { + log_i("NimBLE sync complete"); + _nimble_synced = true; +} + +static int _nimble_gap_event(struct ble_gap_event *event, void *arg) { + switch (event->type) { + case BLE_GAP_EVENT_ADV_COMPLETE: + log_d("BLE_GAP_EVENT_ADV_COMPLETE"); + break; + default: + break; + } + return 0; +} +#endif +/*************************************************************************** + * Forward declarations * + ***************************************************************************/ +static bool _init_gap(const char *name); +static bool _stop_gap(); +static bool _update_advertising(const char *name); + +/*************************************************************************** + * Initialization functions * + ***************************************************************************/ static bool _init_gap(const char *name) { + if (_ble_initialized) { + log_d("BLE already initialized, skipping"); + return true; + } + +#if defined(CONFIG_BLUEDROID_ENABLED) if (!btStarted() && !btStart()) { log_e("btStart failed"); return false; @@ -92,16 +204,178 @@ static bool _init_gap(const char *name) { log_e("gap_register_callback failed"); return false; } + _ble_initialized = true; return true; +#elif defined(CONFIG_NIMBLE_ENABLED) +#if defined(CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE) + // Initialize esp-hosted transport for BLE HCI when explicitly enabled + if (!hostedInitBLE()) { + log_e("Failed to initialize ESP-Hosted for BLE"); + return false; + } +#endif + + esp_err_t errRc = nimble_port_init(); + if (errRc != ESP_OK) { + log_e("nimble_port_init: rc=%d", errRc); + return false; + } + + // Configure NimBLE host + ble_hs_cfg.reset_cb = _nimble_on_reset; + ble_hs_cfg.sync_cb = _nimble_on_sync; + ble_hs_cfg.sm_io_cap = BLE_HS_IO_NO_INPUT_OUTPUT; + ble_hs_cfg.sm_bonding = 0; + ble_hs_cfg.sm_mitm = 0; + ble_hs_cfg.sm_sc = 1; + + // Set device name + errRc = ble_svc_gap_device_name_set(name); + if (errRc != ESP_OK) { + log_e("ble_svc_gap_device_name_set: rc=%d", errRc); + return false; + } + + // Configure advertising data + memset(&_nimble_adv_fields, 0, sizeof(_nimble_adv_fields)); + _nimble_adv_fields.flags = BLE_HS_ADV_F_DISC_GEN | BLE_HS_ADV_F_BREDR_UNSUP; + _nimble_adv_fields.name = (uint8_t *)name; + _nimble_adv_fields.name_len = strlen(name); + _nimble_adv_fields.name_is_complete = 1; + _nimble_adv_fields.tx_pwr_lvl_is_present = 1; + + // Initialize store configuration + ble_store_config_init(); + + // Start the host task + nimble_port_freertos_init(_nimble_host_task); + + // Wait for sync + int sync_timeout = 1000; // 10 seconds timeout + while (!_nimble_synced && sync_timeout > 0) { + vTaskDelay(pdMS_TO_TICKS(10)); + sync_timeout--; + } + + if (!_nimble_synced) { + log_e("NimBLE sync timeout"); + return false; + } + + // Set advertising data + errRc = ble_gap_adv_set_fields(&_nimble_adv_fields); + if (errRc != ESP_OK) { + log_e("ble_gap_adv_set_fields: rc=%d", errRc); + return false; + } + + // Start advertising + errRc = ble_gap_adv_start(BLE_OWN_ADDR_PUBLIC, NULL, BLE_HS_FOREVER, &_nimble_adv_params, _nimble_gap_event, NULL); + if (errRc != ESP_OK) { + log_e("ble_gap_adv_start: rc=%d", errRc); + return false; + } + + _ble_initialized = true; + return true; +#else + log_e("No BLE stack enabled"); + return false; +#endif } static bool _stop_gap() { + if (!_ble_initialized) { + log_d("BLE not initialized, nothing to stop"); + return true; + } + +#if defined(CONFIG_BLUEDROID_ENABLED) if (btStarted()) { esp_bluedroid_disable(); esp_bluedroid_deinit(); btStop(); } + _ble_initialized = false; + return true; +#elif defined(CONFIG_NIMBLE_ENABLED) + // Stop advertising + ble_gap_adv_stop(); + + // Stop NimBLE + int rc = nimble_port_stop(); + if (rc != ESP_OK) { + log_e("nimble_port_stop: rc=%d", rc); + } + + nimble_port_deinit(); + _nimble_synced = false; + _ble_initialized = false; + return true; +#else + return true; +#endif +} + +static bool _update_advertising(const char *name) { + if (!_ble_initialized) { + log_e("BLE not initialized"); + return false; + } + +#if defined(CONFIG_BLUEDROID_ENABLED) + // Stop current advertising + esp_ble_gap_stop_advertising(); + + // Set new device name + if (esp_ble_gap_set_device_name(name)) { + log_e("gap_set_device_name failed"); + return false; + } + + // Restart advertising with new name + if (esp_ble_gap_config_adv_data(&_adv_config)) { + log_e("gap_config_adv_data failed"); + return false; + } + + return true; +#elif defined(CONFIG_NIMBLE_ENABLED) + // Stop current advertising + ble_gap_adv_stop(); + + // Set new device name + int errRc = ble_svc_gap_device_name_set(name); + if (errRc != ESP_OK) { + log_e("ble_svc_gap_device_name_set: rc=%d", errRc); + return false; + } + + // Update advertising fields with new name + _nimble_adv_fields.name = (uint8_t *)name; + _nimble_adv_fields.name_len = strlen(name); + _nimble_adv_fields.name_is_complete = 1; + + // Set new advertising data + errRc = ble_gap_adv_set_fields(&_nimble_adv_fields); + if (errRc != ESP_OK) { + log_e("ble_gap_adv_set_fields: rc=%d", errRc); + return false; + } + + // Restart advertising + errRc = ble_gap_adv_start(BLE_OWN_ADDR_PUBLIC, NULL, BLE_HS_FOREVER, &_nimble_adv_params, _nimble_gap_event, NULL); + if (errRc != ESP_OK) { + log_e("ble_gap_adv_start: rc=%d", errRc); + return false; + } + + return true; +#else + log_e("No BLE stack enabled"); + return false; +#endif } /* @@ -121,6 +395,12 @@ bool SimpleBLE::begin(String localName) { if (localName.length()) { local_name = localName; } + + // If already initialized, just update advertising data + if (_ble_initialized) { + return _update_advertising(local_name.c_str()); + } + return _init_gap(local_name.c_str()); } @@ -128,4 +408,5 @@ void SimpleBLE::end() { _stop_gap(); } -#endif +#endif // CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED +#endif // SOC_BLE_SUPPORTED || CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE diff --git a/libraries/SimpleBLE/src/SimpleBLE.h b/libraries/SimpleBLE/src/SimpleBLE.h index df1ee751f10..cf17f16fd36 100644 --- a/libraries/SimpleBLE/src/SimpleBLE.h +++ b/libraries/SimpleBLE/src/SimpleBLE.h @@ -15,10 +15,10 @@ #ifndef _SIMPLE_BLE_H_ #define _SIMPLE_BLE_H_ -#include "sdkconfig.h" #include "soc/soc_caps.h" - -#if SOC_BT_SUPPORTED && defined(CONFIG_BT_ENABLED) && defined(CONFIG_BLUEDROID_ENABLED) +#include "sdkconfig.h" +#if defined(SOC_BLE_SUPPORTED) || defined(CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE) +#if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED) #include #include @@ -26,7 +26,10 @@ #include #include "freertos/FreeRTOS.h" #include "freertos/task.h" + +#if defined(SOC_BLE_SUPPORTED) #include "esp_bt.h" +#endif #include "Arduino.h" @@ -60,6 +63,7 @@ class SimpleBLE { private: }; -#endif +#endif // SOC_BLE_SUPPORTED || CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE +#endif // CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED -#endif +#endif // _SIMPLE_BLE_H_ From f4bb3aee3d643980accc6183b47cd15490041cae Mon Sep 17 00:00:00 2001 From: "pre-commit-ci-lite[bot]" <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com> Date: Thu, 11 Sep 2025 07:54:09 +0000 Subject: [PATCH 2/2] ci(pre-commit): Apply automatic fixes --- libraries/SimpleBLE/src/SimpleBLE.cpp | 13 +++++-------- libraries/SimpleBLE/src/SimpleBLE.h | 6 +++--- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/libraries/SimpleBLE/src/SimpleBLE.cpp b/libraries/SimpleBLE/src/SimpleBLE.cpp index 78f639c58fe..670d371ca9e 100644 --- a/libraries/SimpleBLE/src/SimpleBLE.cpp +++ b/libraries/SimpleBLE/src/SimpleBLE.cpp @@ -148,11 +148,8 @@ static void _nimble_on_sync(void) { static int _nimble_gap_event(struct ble_gap_event *event, void *arg) { switch (event->type) { - case BLE_GAP_EVENT_ADV_COMPLETE: - log_d("BLE_GAP_EVENT_ADV_COMPLETE"); - break; - default: - break; + case BLE_GAP_EVENT_ADV_COMPLETE: log_d("BLE_GAP_EVENT_ADV_COMPLETE"); break; + default: break; } return 0; } @@ -251,7 +248,7 @@ static bool _init_gap(const char *name) { nimble_port_freertos_init(_nimble_host_task); // Wait for sync - int sync_timeout = 1000; // 10 seconds timeout + int sync_timeout = 1000; // 10 seconds timeout while (!_nimble_synced && sync_timeout > 0) { vTaskDelay(pdMS_TO_TICKS(10)); sync_timeout--; @@ -408,5 +405,5 @@ void SimpleBLE::end() { _stop_gap(); } -#endif // CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED -#endif // SOC_BLE_SUPPORTED || CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE +#endif // CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED +#endif // SOC_BLE_SUPPORTED || CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE diff --git a/libraries/SimpleBLE/src/SimpleBLE.h b/libraries/SimpleBLE/src/SimpleBLE.h index cf17f16fd36..41a06b0b1aa 100644 --- a/libraries/SimpleBLE/src/SimpleBLE.h +++ b/libraries/SimpleBLE/src/SimpleBLE.h @@ -63,7 +63,7 @@ class SimpleBLE { private: }; -#endif // SOC_BLE_SUPPORTED || CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE -#endif // CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED +#endif // SOC_BLE_SUPPORTED || CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE +#endif // CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED -#endif // _SIMPLE_BLE_H_ +#endif // _SIMPLE_BLE_H_