fix(nimble): Improve safety, fix bugs, and update docs across NimBLE examples

- Remove unused headers from examples
- Improve periodic adv/sync example functionality and readability
- Use ble_hs_id_infer_auto() instead of hardcoded BLE_OWN_ADDR_PUBLIC/RANDOM
- Add ble_hs_util_ensure_addr() calls in on_sync for proper address setup
- Use correct ext adv instance (0 instead of 1) in phy_prph, l2cap_coc, multi_conn
- Fix struct name: ble_gap_periodic_adv_enable_params -> ble_gap_periodic_adv_start_params
- Add CONFIG_BT_NIMBLE_GAP_SERVICE guards around ble_svc_gap_device_name_set
- Fix unsafe AD data parsing with bounds checks in central examples
- Fix UUID matching bugs (off-by-one loop condition and byte order) in
  phy_cent, htp_cent, and proximity_sensor_cent
- Fix ble_multi_conn_cent address type to use dynamic inference
- Remove contradictory sm_sc=0 after sm_sc=1 in ble_multi_adv
- Add CONFIG_BT_NIMBLE_EXT_ADV=y to ble_multi_adv sdkconfig defaults
- Check return values for ble_gap_set_host_feat, nimble_port_init
- Update tutorials and READMEs to match code changes
This commit is contained in:
Rahul Tank
2026-03-20 11:06:13 +05:30
parent 77462b9f65
commit 745b7bea38
69 changed files with 1054 additions and 2091 deletions
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2015-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -35,6 +35,7 @@ static void ble_cent_advertise(void);
static void ble_cent_scan(void);
static void ble_cent_connect(void *disc);
static uint8_t own_addr_type;
static uint8_t s_ble_multi_conn_num = 0;
/**
@@ -234,7 +235,7 @@ ble_cent_advertise(void)
/* Enable connectable advertising */
params.connectable = 1;
params.own_addr_type = BLE_OWN_ADDR_PUBLIC;
params.own_addr_type = own_addr_type;
params.primary_phy = BLE_HCI_LE_PHY_1M;
params.secondary_phy = BLE_HCI_LE_PHY_1M;
params.tx_power = 127;
@@ -257,8 +258,6 @@ ble_cent_advertise(void)
/* Start advertising */
rc = ble_gap_ext_adv_start(instance, 0, 0);
assert(rc == 0);
if (rc) {
ESP_LOGE(TAG, "Failed to enable advertisement; rc=%d\n", rc);
return;
@@ -294,7 +293,7 @@ ble_cent_scan(void)
/* Tell the controller to filter duplicates; we don't want to process
* repeated advertisements from the same device.
*/
rc = ble_gap_ext_disc(BLE_OWN_ADDR_PUBLIC, 0, 0, 1, 0, 0, &uncoded_disc_params,
rc = ble_gap_ext_disc(own_addr_type, 0, 0, 1, 0, 0, &uncoded_disc_params,
&coded_disc_params, ble_cent_client_gap_event, NULL);
if (rc != 0) {
ESP_LOGE(TAG, "Error initiating GAP discovery procedure; rc=%d\n", rc);
@@ -411,6 +410,13 @@ blecent_on_sync(void)
rc = ble_hs_util_ensure_addr(0);
assert(rc == 0);
/* Figure out address to use for advertising and scanning */
rc = ble_hs_id_infer_auto(0, &own_addr_type);
if (rc != 0) {
ESP_LOGE(TAG, "error determining address type; rc=%d\n", rc);
return;
}
/* We will function as both the central and peripheral device, connecting to all peripherals
* with the name of BLE_PEER_NAME. Meanwhile, a connectable advertising will be enabled.
* In this example, we register two gap callback functions.
@@ -466,7 +472,9 @@ app_main(void)
#if MYNEWT_VAL(BLE_GATTS)
rc = gatt_svr_init();
assert(rc == 0);
#endif
#if CONFIG_BT_NIMBLE_GAP_SERVICE
/* Set the default device name. We will act as both central and peripheral. */
rc = ble_svc_gap_device_name_set("esp-ble-role-coex");
assert(rc == 0);
@@ -60,15 +60,25 @@ app_main(void)
ble_hs_cfg.store_status_cb = ble_store_util_status_rr;
/* Initialize data structures to track connected peers. */
#if MYNEWT_VAL(BLE_INCL_SVC_DISCOVERY) || MYNEWT_VAL(BLE_GATT_CACHING_INCLUDE_SERVICES)
rc = peer_init(BLE_PEER_MAX_NUM, BLE_PEER_MAX_NUM, BLE_PEER_MAX_NUM, BLE_PEER_MAX_NUM, BLE_PEER_MAX_NUM);
assert(rc == 0);
#else
rc = peer_init(BLE_PEER_MAX_NUM, BLE_PEER_MAX_NUM, BLE_PEER_MAX_NUM, BLE_PEER_MAX_NUM);
assert(rc == 0);
#endif
#if MYNEWT_VAL(BLE_GATTS)
rc = gatt_svr_init();
assert(rc == 0);
#endif
#if CONFIG_BT_NIMBLE_GAP_SERVICE
/* Set the default device name. We will act as both central and peripheral. */
rc = ble_svc_gap_device_name_set("esp-ble-role-coex");
assert(rc == 0);
rc = gatt_svr_init();
assert(rc == 0);
#endif
/* XXX Need to have template for store */
ble_store_config_init();
@@ -144,9 +154,8 @@ esp_err_t esp_nimble_init(void)
The host is configured by setting up the callbacks on Stack-reset, Stack-sync, registration of each GATT resource, and storage status.
```c
ble_hs_cfg.reset_cb = ble_multi_adv_on_reset;
ble_hs_cfg.sync_cb = ble_multi_adv_on_sync;
ble_hs_cfg.gatts_register_cb = gatt_svr_register_cb;
ble_hs_cfg.reset_cb = blecent_on_reset;
ble_hs_cfg.sync_cb = blecent_on_sync;
ble_hs_cfg.store_status_cb = ble_store_util_status_rr;
```
@@ -166,11 +175,43 @@ ble_store_config_init();
The main function ends by creating a task where nimble will run using `nimble_port_freertos_init()`. This enables the nimble stack by using `esp_nimble_enable()`.
```c
nimble_port_freertos_init(ble_multi_adv_host_task);
nimble_port_freertos_init(blecent_host_task);
```
`esp_nimble_enable()` create a task where the nimble host will run. It is not strictly necessary to have a separate task for the nimble host, but since something needs to handle the default queue, it is easier to create a separate task.
## Sync Callback
When the BLE host and controller are synced, the `blecent_on_sync` callback is invoked. It sets up the connection interval common factor, ensures a valid identity address, determines the address type dynamically, and starts both advertising and scanning:
```c
static void
blecent_on_sync(void)
{
int rc;
rc = ble_gap_common_factor_set(true, (BLE_PREF_CONN_ITVL_MS * 1000) / 625);
assert(rc == 0);
/* Make sure we have proper identity address set (public preferred) */
rc = ble_hs_util_ensure_addr(0);
assert(rc == 0);
/* Figure out address to use for advertising and scanning */
rc = ble_hs_id_infer_auto(0, &own_addr_type);
if (rc != 0) {
ESP_LOGE(TAG, "error determining address type; rc=%d\n", rc);
return;
}
ble_cent_advertise();
ble_cent_scan();
}
```
- `ble_hs_util_ensure_addr(0)`: Ensures the device has a valid identity address configured (prefers public address).
- `ble_hs_id_infer_auto(0, &own_addr_type)`: Dynamically determines the best address type to use. The result is stored in the global `own_addr_type` variable, which is then used by `ble_cent_advertise()` and `ble_cent_scan()`.
## Multiple Connections
This example will be executed according to the following steps:
@@ -217,7 +258,7 @@ This example will be executed according to the following steps:
multi_conn_params.scheduling_len_us = BLE_PREF_EVT_LEN_MS * 1000;
multi_conn_params.own_addr_type = BLE_OWN_ADDR_RANDOM;
multi_conn_params.peer_addr = peer_addr;
multi_conn_params.duration_ms = 3000;
multi_conn_params.duration_ms = 8000;
multi_conn_params.phy_mask = BLE_GAP_LE_PHY_1M_MASK | BLE_GAP_LE_PHY_2M_MASK |
BLE_GAP_LE_PHY_CODED_MASK;
multi_conn_params.phy_1m_conn_params = &uncoded_conn_param;
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2015-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -10,6 +10,7 @@
#include "nimble/nimble_port.h"
#include "nimble/nimble_port_freertos.h"
#include "host/ble_hs.h"
#include "host/util/util.h"
#include "services/gap/ble_svc_gap.h"
#include "ble_multi_conn_prph.h"
@@ -90,7 +91,6 @@ ble_prph_advertise(void)
/* start advertising */
rc = ble_gap_ext_adv_start(instance, 0, 0);
assert(rc == 0);
#else
struct ble_gap_adv_params adv_params;
struct ble_hs_adv_fields fields;
@@ -131,10 +131,12 @@ ble_prph_advertise(void)
fields.tx_pwr_lvl_is_present = 1;
fields.tx_pwr_lvl = BLE_HS_ADV_TX_PWR_LVL_AUTO;
#if CONFIG_BT_NIMBLE_GAP_SERVICE
name = ble_svc_gap_device_name();
fields.name = (uint8_t *)name;
fields.name_len = strlen(name);
fields.name_is_complete = 1;
#endif
rc = ble_gap_adv_set_fields(&fields);
if (rc != 0) {
@@ -247,6 +249,12 @@ bleprph_on_reset(int reason)
static void
bleprph_on_sync(void)
{
int rc;
/* Make sure we have proper identity address set (public preferred) */
rc = ble_hs_util_ensure_addr(0);
assert(rc == 0);
/* Begin advertising. */
ble_prph_advertise();
}
@@ -290,7 +298,9 @@ app_main(void)
#if MYNEWT_VAL(BLE_GATTS)
rc = gatt_svr_init();
assert(rc == 0);
#endif
#if CONFIG_BT_NIMBLE_GAP_SERVICE
/* Set the default device name. */
rc = ble_svc_gap_device_name_set("esp-multi-conn");
assert(rc == 0);
@@ -18,6 +18,7 @@ This example is located in the examples folder of the ESP-IDF under the [ble_mul
#include "nimble/nimble_port.h"
#include "nimble/nimble_port_freertos.h"
#include "host/ble_hs.h"
#include "host/util/util.h"
#include "services/gap/ble_svc_gap.h"
#include "ble_multi_conn_prph.h"
```
@@ -62,12 +63,16 @@ app_main(void)
ble_hs_cfg.gatts_register_cb = gatt_svr_register_cb;
ble_hs_cfg.store_status_cb = ble_store_util_status_rr;
#if MYNEWT_VAL(BLE_GATTS)
rc = gatt_svr_init();
assert(rc == 0);
#endif
#if CONFIG_BT_NIMBLE_GAP_SERVICE
/* Set the default device name. */
rc = ble_svc_gap_device_name_set("esp-multi-conn");
assert(rc == 0);
#endif
/* XXX Need to have template for store */
ble_store_config_init();
@@ -88,7 +93,7 @@ app_main(void)
vTaskDelay(pdMS_TO_TICKS(delay_ms));
ble_prph_advertise();
} else {
ESP_LOGE(TAG, "Failed to take Semaphor");
ESP_LOGE(TAG, "Failed to take Semaphore");
}
}
#endif // CONFIG_EXAMPLE_RESTART_ADV_AFTER_CONNECTED
@@ -162,16 +167,19 @@ esp_err_t esp_nimble_init(void)
The host is configured by setting up the callbacks on Stack-reset, Stack-sync, registration of each GATT resource, and storage status.
```c
ble_hs_cfg.reset_cb = ble_multi_adv_on_reset;
ble_hs_cfg.sync_cb = ble_multi_adv_on_sync;
ble_hs_cfg.reset_cb = bleprph_on_reset;
ble_hs_cfg.sync_cb = bleprph_on_sync;
ble_hs_cfg.gatts_register_cb = gatt_svr_register_cb;
ble_hs_cfg.store_status_cb = ble_store_util_status_rr;
```
The main function calls `ble_svc_gap_device_name_set()` to set the default device name. 'esp-multi-conn' is passed as the default device name to this function.
The main function calls `ble_svc_gap_device_name_set()` to set the default device name, guarded by `CONFIG_BT_NIMBLE_GAP_SERVICE` for cases where the GAP service may be disabled:
```c
rc = ble_svc_gap_device_name_set("esp-multi-conn");
#if CONFIG_BT_NIMBLE_GAP_SERVICE
rc = ble_svc_gap_device_name_set("esp-multi-conn");
assert(rc == 0);
#endif
```
main function calls `ble_store_config_init()` to configure the host by setting up the storage callbacks which handle the read, write, and deletion of security material.
@@ -184,11 +192,32 @@ ble_store_config_init();
The main function ends by creating a task where nimble will run using `nimble_port_freertos_init()`. This enables the nimble stack by using `esp_nimble_enable()`.
```c
nimble_port_freertos_init(ble_multi_adv_host_task);
nimble_port_freertos_init(bleprph_host_task);
```
`esp_nimble_enable()` create a task where the nimble host will run. It is not strictly necessary to have a separate task for the nimble host, but since something needs to handle the default queue, it is easier to create a separate task.
## Sync Callback
When the BLE host and controller are synced, the `bleprph_on_sync` callback is invoked. It ensures a valid identity address is set before starting advertising:
```c
static void
bleprph_on_sync(void)
{
int rc;
/* Make sure we have proper identity address set (public preferred) */
rc = ble_hs_util_ensure_addr(0);
assert(rc == 0);
/* Begin advertising. */
ble_prph_advertise();
}
```
- `ble_hs_util_ensure_addr(0)`: Ensures the device has a valid identity address configured (prefers public address).
## Multiple Connections
This example will be executed according to the following steps:
@@ -217,7 +246,7 @@ This example will be executed according to the following steps:
vTaskDelay(pdMS_TO_TICKS(delay_ms));
ble_prph_advertise();
} else {
ESP_LOGE(TAG, "Failed to take Semaphor");
ESP_LOGE(TAG, "Failed to take Semaphore");
}
}
```