mirror of
https://github.com/espressif/esp-idf.git
synced 2026-10-02 03:00:34 +03:00
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:
@@ -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);
|
||||
|
||||
+49
-8
@@ -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);
|
||||
|
||||
+36
-7
@@ -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");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Reference in New Issue
Block a user