diff --git a/examples/bluetooth/nimble/ble_ancs/main/main.c b/examples/bluetooth/nimble/ble_ancs/main/main.c index 1cc501ca5c..6a5c688cc6 100644 --- a/examples/bluetooth/nimble/ble_ancs/main/main.c +++ b/examples/bluetooth/nimble/ble_ancs/main/main.c @@ -644,6 +644,8 @@ ble_ancs_gap_event(struct ble_gap_event *event, void *arg) if (event->passkey.params.action == BLE_SM_IOACT_DISP) { pkey.action = event->passkey.params.action; + /* WARNING: Hardcoded passkey for demonstration only. + * In production, generate a random passkey per pairing. */ pkey.passkey = 123456; // This is the passkey to be entered on peer ESP_LOGI(tag, "Enter passkey %" PRIu32 "on the peer side", pkey.passkey); rc = ble_sm_inject_io(event->passkey.conn_handle, &pkey); diff --git a/examples/bluetooth/nimble/ble_chan_sound_initiator/main/main.c b/examples/bluetooth/nimble/ble_chan_sound_initiator/main/main.c index 899b7a4ff9..deaf7012b9 100644 --- a/examples/bluetooth/nimble/ble_chan_sound_initiator/main/main.c +++ b/examples/bluetooth/nimble/ble_chan_sound_initiator/main/main.c @@ -683,6 +683,9 @@ blecent_on_sync(void) /* Make sure we have set Host feature bit for Channel Sounding*/ int rc; rc = ble_gap_set_host_feat(47,0x01); + if (rc != 0) { + MODLOG_DFLT(ERROR, "error setting host feature; rc=%d\n", rc); + } /* Make sure we have proper identity address set (public preferred) */ rc = ble_hs_util_ensure_addr(0); assert(rc == 0); diff --git a/examples/bluetooth/nimble/ble_chan_sound_initiator/tutorial/blecent_walkthrough.md b/examples/bluetooth/nimble/ble_chan_sound_initiator/tutorial/blecent_walkthrough.md index 3192c30d92..fc5d9b9340 100644 --- a/examples/bluetooth/nimble/ble_chan_sound_initiator/tutorial/blecent_walkthrough.md +++ b/examples/bluetooth/nimble/ble_chan_sound_initiator/tutorial/blecent_walkthrough.md @@ -1,12 +1,14 @@ -# BLE Central Example Walkthrough +# BLE Channel Sounding Initiator Example Walkthrough ## Introduction -In this tutorial, we will explore the blecent example code provided by Espressif's ESP-IDF framework. The primary goal of the blecent example is to illustrate how a BLE Central device can interact with multiple BLE Peripheral devices in the vicinity. The Central device initiates the communication by scanning for nearby Peripherals and establishing connections with them. By the end of this tutorial, you will have a comprehensive understanding of how the blecent example code operates as a BLE Central application. +In this tutorial, the ble_chan_sound_initiator example code is reviewed. This example demonstrates how a BLE Central device can perform Channel Sounding (CS) procedures with a BLE Peripheral. The initiator scans for a device advertising the Ranging Service (0x185B), establishes a connection, performs service discovery for the RAS (Ranging and Sounding) service, and initiates the CS procedure to exchange ranging data. + +**Note:** This example currently requires an external Bluetooth controller supporting BLE Channel Sounding functionality. ## Includes -This example is located in the examples folder of the ESP-IDF under the [blecent/main](../main). The [main.c](../main/main.c) file located in the main folder contains all the functionality that we are going to review. The header files contained in [main.c](../main/main.c) are: +This example is located in the examples folder of the ESP-IDF under the [ble_chan_sound_initiator/main](../main). The [main.c](../main/main.c) file located in the main folder contains all the functionality that we are going to review. The key header files are: ```c #include "esp_log.h" @@ -15,981 +17,113 @@ This example is located in the examples folder of the ESP-IDF under the [blecent #include "nimble/nimble_port.h" #include "nimble/nimble_port_freertos.h" #include "host/ble_hs.h" +#include "host/ble_cs.h" #include "host/util/util.h" #include "console/console.h" #include "services/gap/ble_svc_gap.h" -#include "blecent.h" +#include "services/hid/ble_svc_hid.h" +#include "services/ras/ble_svc_ras.h" +#include "ble_chan_initiator.h" ``` -These `includes` are required for the FreeRTOS and underlying system components to run, including the logging functionality and a library to store data in non-volatile flash memory. We are interested in `“nimble_port.h”`, `“nimble_port_freertos.h”`, `"ble_hs.h"`, `“ble_svc_gap.h”` and `“blecent.h”` which expose the BLE APIs required to implement this example. -* `nimble_port.h`: Includes the declaration of functions required for the initialization of the nimble stack. -* `nimble_port_freertos.h`: Initializes and enables nimble host task. -* `ble_hs.h`: Defines the functionalities to handle the host event. -* `ble_svc_gap.h`: Defines the macros for device name, and device appearance and declares the function to set them. -* `blecent.h`: Provides necessary definitions and forward declarations for the blecent example's functionality, specifically for interacting with BLE services and characteristics related to Alert Notifications. +* `ble_cs.h`: Defines Channel Sounding APIs for initiating CS procedures. +* `ble_svc_ras.h`: Defines the Ranging and Sounding (RAS) service APIs and structures. +* `ble_chan_initiator.h`: Contains forward declarations and definitions specific to this example. ## Main Entry Point -The program's entry point is the app_main() function: +The `app_main()` function initializes NVS, the NimBLE stack, configures host callbacks, initializes peer tracking structures, sets the device name, and starts the NimBLE host task: + ```c void app_main(void) { - int rc; - /* Initialize NVS — it is used to store PHY calibration data */ - esp_err_t ret = nvs_flash_init(); - if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) { - ESP_ERROR_CHECK(nvs_flash_erase()); - ret = nvs_flash_init(); - } - ESP_ERROR_CHECK(ret); - + ... ret = nimble_port_init(); - if (ret != ESP_OK) { - ESP_LOGE(tag, "Failed to init nimble %d ", ret); - return; - } - - /* Configure the host. */ + ... 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; - /* Initialize data structures to track connected peers. */ rc = peer_init(MYNEWT_VAL(BLE_MAX_CONNECTIONS), 64, 64, 64); assert(rc == 0); - /* Set the default device name. */ - rc = ble_svc_gap_device_name_set("nimble-blecent"); + rc = ble_svc_gap_device_name_set("nimble_ble_channel_sounding"); assert(rc == 0); - /* XXX Need to have template for store */ ble_store_config_init(); - nimble_port_freertos_init(blecent_host_task); - -#if CONFIG_EXAMPLE_INIT_DEINIT_LOOP - stack_init_deinit(); -#endif - -} -``` -The main function starts by initializing the non-volatile storage library. This library allows us to save the key-value pairs in flash memory. `nvs_flash_init()` stores the PHY calibration data. In a Bluetooth Low Energy (BLE) device, cryptographic keys used for encryption and authentication are often stored in Non-Volatile Storage (NVS). BLE stores the peer keys, CCCD keys, peer records, etc on NVS. By storing these keys in NVS, the BLE device can quickly retrieve them when needed, without the need for time-consuming key generations. -```c -esp_err_t ret = nvs_flash_init(); -if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) { - ESP_ERROR_CHECK(nvs_flash_erase()); - ret = nvs_flash_init(); -} -ESP_ERROR_CHECK(ret); -``` - -## BT Controller and Stack Initialization - -The main function calls `nimble_port_init()` to initialize BT Controller and nimble stack. This function initializes the BT controller by first creating its configuration structure named `esp_bt_controller_config_t` with default settings generated by the `BT_CONTROLLER_INIT_CONFIG_DEFAULT()` macro. It implements the Host Controller Interface (HCI) on the controller side, the Link Layer (LL), and the Physical Layer (PHY). The BT Controller is invisible to the user applications and deals with the lower layers of the BLE stack. The controller configuration includes setting the BT controller stack size, priority, and HCI baud rate. With the settings created, the BT controller is initialized and enabled with the `esp_bt_controller_init()` and `esp_bt_controller_enable()` functions: - -```c -esp_bt_controller_config_t config_opts = BT_CONTROLLER_INIT_CONFIG_DEFAULT(); -ret = esp_bt_controller_init(&config_opts); -``` -Next, the controller is enabled in BLE Mode. - -```c -ret = esp_bt_controller_enable(ESP_BT_MODE_BLE); -``` -The controller should be enabled in `ESP_BT_MODE_BLE` if you want to use the BLE mode. - -There are four Bluetooth modes supported: - -1. `ESP_BT_MODE_IDLE`: Bluetooth not running -2. `ESP_BT_MODE_BLE`: BLE mode -3. `ESP_BT_MODE_CLASSIC_BT`: BT Classic mode -4. `ESP_BT_MODE_BTDM`: Dual mode (BLE + BT Classic) - -After the initialization of the BT controller, the nimble stack, which includes the common definitions and APIs for BLE, is initialized by using `esp_nimble_init()`: - -```c -esp_err_t esp_nimble_init(void) -{ -#if !SOC_ESP_NIMBLE_CONTROLLER - /* Initialize the function pointers for OS porting */ - npl_freertos_funcs_init(); - - npl_freertos_mempool_init(); - - if(esp_nimble_hci_init() != ESP_OK) { - ESP_LOGE(NIMBLE_PORT_LOG_TAG, "hci inits failed\n"); - return ESP_FAIL; - } - - /* Initialize default event queue */ - ble_npl_eventq_init(&g_eventq_dflt); - /* Initialize the global memory pool */ - os_mempool_module_init(); - os_msys_init(); - -#endif - /* Initialize the host */ - ble_transport_hs_init(); - - return ESP_OK; } ``` -The host is configured by setting up the callbacks for Stack-reset, Stack-sync, and Storage status. -```c -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; -``` +## Host Sync Callback -The main function invokes `peer_init()` to initialize memory pools to manage peer, service, characteristics, and descriptor objects in BLE. -```c -rc = peer_init(MYNEWT_VAL(BLE_MAX_CONNECTIONS), 64, 64, 64); -``` - -The main function calls `ble_svc_gap_device_name_set()` to set the default device name. -```c -rc = ble_svc_gap_device_name_set("nimble-blecent"); -``` - -The 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. -```c -/* XXX Need to have a template for store */ - 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(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 to handle the default queue, it is easier to create a separate task. - - -## CONFIG_EXAMPLE_INIT_DEINIT_LOOP - -```c -#if CONFIG_EXAMPLE_INIT_DEINIT_LOOP -/* This function showcases stack init and deinit procedure. */ -static void stack_init_deinit(void) -{ - int rc; - while(1) { - - vTaskDelay(1000); - - ESP_LOGI(tag, "Deinit host"); - - rc = nimble_port_stop(); - if (rc == 0) { - nimble_port_deinit(); - } else { - ESP_LOGI(tag, "Nimble port stop failed, rc = %d", rc); - break; - } - - vTaskDelay(1000); - - ESP_LOGI(tag, "Init host"); - - rc = nimble_port_init(); - if (rc != ESP_OK) { - ESP_LOGI(tag, "Failed to init nimble %d ", rc); - break; - } - - nimble_port_freertos_init(blecent_host_task); - - ESP_LOGI(tag, "Waiting for 1 second"); - } -} -#endif -``` - -The `stack_init_deinit` function provides a demonstration of the initialization and deinitialization procedure for the NimBLE stack. It operates within a loop that showcases the following steps: - -* **Delay**: The function starts by introducing a delay of 1000 tick periods using the vTaskDelay function. This allows time for certain operations to complete before moving on. - -* **Deinitialization**: The function logs that the host deinitialization process is beginning. It calls `nimble_port_stop` to halt the NimBLE stack's operation. If the stop operation is successful (returning 0), the function then calls `nimble_port_deinit` to deinitialize the NimBLE stack. If the stop operation fails, an error message is logged, and the loop breaks. - -* **Delay**: Another 1000 tick periods delay is introduced before proceeding with initialization. - -* **Initialization**: The function logs that the host initialization process is starting. It calls `nimble_port_init` to initialize the NimBLE stack. If the initialization fails (returning anything other than `ESP_OK`), an error message is logged, and the loop breaks. - -* **FreeRTOS Initialization**: After successful NimBLE stack initialization, the function calls `nimble_port_freertos_init` and provides the `blecent_host_task` as a parameter. This step sets up the NimBLE stack to work within the FreeRTOS environment. - - -## blecent_scan() +When the NimBLE host syncs with the controller, `blecent_on_sync()` is called. It sets the Channel Sounding host feature bit, ensures a valid identity address, and begins scanning: ```c static void -blecent_scan(void) +blecent_on_sync(void) { - uint8_t own_addr_type; - struct ble_gap_disc_params disc_params; int rc; - - /* Figure out address to use while advertising (no privacy for now) */ - rc = ble_hs_id_infer_auto(0, &own_addr_type); + rc = ble_gap_set_host_feat(47, 0x01); if (rc != 0) { - MODLOG_DFLT(ERROR, "error determining address type; rc=%d\n", rc); - return; + MODLOG_DFLT(ERROR, "error setting host feature; rc=%d\n", rc); } + rc = ble_hs_util_ensure_addr(0); + assert(rc == 0); - /* Tell the controller to filter duplicates; we don't want to process - * repeated advertisements from the same device. - */ - disc_params.filter_duplicates = 1; - - /** - * Perform a passive scan. I.e., don't send follow-up scan requests to - * each advertiser. - */ - disc_params.passive = 1; - - /* Use defaults for the rest of the parameters. */ - disc_params.itvl = 0; - disc_params.window = 0; - disc_params.filter_policy = 0; - disc_params.limited = 0; - - rc = ble_gap_disc(own_addr_type, BLE_HS_FOREVER, &disc_params, - blecent_gap_event, NULL); - if (rc != 0) { - MODLOG_DFLT(ERROR, "Error initiating GAP discovery procedure; rc=%d\n", - rc); - } + blecent_scan(); } ``` -The function `blecent_scan()` initiates the General Discovery Procedure for scanning nearby BLE devices. The function starts by declaring several variables used in the scanning process. These variable include: -* `own_addr_type`: A uint8_t variable that stores the type of address (public or random) that the device will use while scanning. -* `disc_params`: A struct of type `ble_gap_disc_params` that holds the parameters for the GAP (Generic Access Profile) discovery procedure. +The `ble_gap_set_host_feat(47, 0x01)` call enables the Channel Sounding host feature (bit 47) as required by the BLE specification for CS support. -The function uses `ble_hs_id_infer_auto()` to determine the address type (public or random) that the device should use for scanning. The result is stored in the own_addr_type variable. +## Scanning and Connection -Configure Discovery Parameters: The function configures the disc_params struct with the following settings: +The `blecent_scan()` function performs passive scanning using `ble_gap_disc()`. When an advertisement is received, `blecent_connect_if_interesting()` checks if the advertising data contains the Ranging Service UUID (0x185B). If found, the initiator connects to the peripheral. -* `filter_duplicates`: Set to 1, indicating that the controller should filter out duplicate advertisements from the same device. This reduces unnecessary processing of repeated advertisements. -* `passive`: Set to 1, indicating that the scan will be a passive scan. In a passive scan, the scanning device only listens for advertisements without sending any follow-up scan requests to advertisers. It's used for general device discovery. +## Service Discovery and RAS Subscription -The function sets some other parameters in the disc_params struct to their default values: +After connection and encryption setup (`BLE_GAP_EVENT_ENC_CHANGE`), the initiator performs GATT service discovery via `peer_disc_all()`. Upon discovery completion, `blecent_on_disc_complete()` locates the RAS service characteristics: -* `itvl`: The scan interval is set to 0, using the default value. -* `window`: The scan window is set to 0, using the default value. -* `filter_policy`: The filter policy is set to 0, using the default value. -* `limited`: The limited discovery mode is set to 0, using the default value. +- **RAS Features** (0x2C14) - Read to determine supported features +- **RAS Control Point** (0x2C17) - For sending ranging commands +- **Ranging Data Ready** (0x2C18) - Notification of available ranging data +- **Ranging Data Overwritten** (0x2C19) - Notification when data is overwritten +- **On-demand Ranging Data** (0x2C16) - For receiving ranging data on demand -The function then calls `ble_gap_disc()` to initiate the BLE scanning procedure. It passes the following parameters: +The initiator reads the RAS Features characteristic to determine if real-time ranging data is supported, then subscribes to the appropriate characteristics accordingly. -* `own_addr_type`: The address type to use for scanning (determined earlier). -* `BLE_HS_FOREVER`: The duration for which the scan should continue (in this case, indefinitely). -* `&disc_params`: A pointer to the ble_gap_disc_params struct containing the scan parameters. -* `blecent_gap_event`: The callback function to handle the scan events (such as receiving advertisements from nearby devices). -* `NULL`: The argument for the callback context, which is not used in this example. +## Channel Sounding Procedure -If an error occurs during the initiation of the scanning procedure, the function prints an error message. - - -## blecent_gap_event - -The function `blecent_gap_event` is in responsible of managing various GAP (Generic Access Profile) events that arise during the BLE communication. - -The function employs a switch statement to manage diverse types of GAP events that can be received. - -* `BLE_GAP_EVENT_DISC`: This case is activated when a new advertisement report is detected during scanning. The function extracts the advertisement data using `ble_hs_adv_parse_fields` and then displays the advertisement fields using the print_adv_fields function. It subsequently verifies if the discovered device is of interest and attempts to establish a connection with it. - -* `BLE_GAP_EVENT_CONNECT`: This case is triggered when a new connection is established or when a connection attempt fails. If the Connection was established then the connection descriptor is initiated using the `ble_gap_conn_find()` method else advertisement is resumed. If the connection is successful, it displays the connection descriptor through `print_conn_desc` and stores the peer information. Additionally, it handles optional features such as BLE power control, vendor-specific commands, and security initiation. In the event of a connection attempt failure, it resumes scanning. - -* `BLE_GAP_EVENT_DISCONNECT`: This case is activated when a connection is terminated. It prints the reason for disconnection and the connection descriptor before removing information about the peer and resuming scanning. - -* `BLE_GAP_EVENT_DISC_COMPLETE`: This case is triggered upon the completion of the GAP discovery process. It displays the reason for the discovery's completion. - -* `BLE_GAP_EVENT_ENC_CHANGE`: This case is activated when encryption is enabled or disabled for a connection. It displays the status of the encryption change and the connection descriptor. If encryption is enabled (when CONFIG_EXAMPLE_ENCRYPTION is defined), it initiates service discovery. - -* `BLE_GAP_EVENT_NOTIFY_RX`: This case is triggered when the Central device receives a notification or indication from the Peripheral device. It displays information about the received data. - -* `BLE_GAP_EVENT_MTU`: This case is activated when the Maximum Transmission Unit (MTU) is updated for a connection. It prints the new MTU value and related information. - -* `BLE_GAP_EVENT_REPEAT_PAIRING`: This case is triggered when the Peripheral device attempts to establish a new secure link with the Central despite an existing bond. The app prioritizes convenience over security and deletes the old bond, accepting the new link. - - So, event handler function effectively manages various GAP events, handles connection-related tasks, initiates security procedures, performs service discovery, and accommodates optional features based on the configuration settings. - - -## blecent_should_connect +Once all four required subscriptions are complete (`subscribe_all == 4`), the CS procedure is initiated: ```c -static int -blecent_should_connect(const struct ble_gap_disc_desc *disc) -{ - struct ble_hs_adv_fields fields; - int rc; - int i; - - /* The device has to be advertising connectability. */ - if (disc->event_type != BLE_HCI_ADV_RPT_EVTYPE_ADV_IND && - disc->event_type != BLE_HCI_ADV_RPT_EVTYPE_DIR_IND) { - - return 0; - } - - rc = ble_hs_adv_parse_fields(&fields, disc->data, disc->length_data); - if (rc != 0) { - return 0; - } - - if (strlen(CONFIG_EXAMPLE_PEER_ADDR) && (strncmp(CONFIG_EXAMPLE_PEER_ADDR, "ADDR_ANY", strlen("ADDR_ANY")) != 0)) { - ESP_LOGI(tag, "Peer address from menuconfig: %s", CONFIG_EXAMPLE_PEER_ADDR); - /* Convert string to address */ - sscanf(CONFIG_EXAMPLE_PEER_ADDR, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx", - &peer_addr[5], &peer_addr[4], &peer_addr[3], - &peer_addr[2], &peer_addr[1], &peer_addr[0]); - if (memcmp(peer_addr, disc->addr.val, sizeof(disc->addr.val)) != 0) { - return 0; - } - } - - /* The device has to advertise support for the Alert Notification - * service (0x1811). - */ - for (i = 0; i < fields.num_uuids16; i++) { - if (ble_uuid_u16(&fields.uuids16[i].u) == BLECENT_SVC_ALERT_UUID) { - return 1; - } - } - - return 0; -} +struct ble_cs_initiator_procedure_start_params param; +memset(¶m, 0, sizeof param); +param.conn_handle = conn_handle; +param.cb = blecs_gap_event; +param.cb_arg = NULL; +ble_cs_initiator_procedure_start(¶m); ``` -This function is responsible for determining whether the Central device should establish a connection with a discovered Peripheral device based on the advertisement data received during scanning. Let's break down the code step by step: +CS subevent results are handled in the `blecs_gap_event()` callback, which processes: +- `BLE_CS_EVENT_CS_PROCEDURE_COMPLETE`: Procedure completion notification +- `BLE_CS_EVENT_SUBEVET_RESULT`: Individual subevent results containing CS step data +- `BLE_CS_EVENT_SUBEVET_RESULT_CONTINUE`: Continuation of subevent results -* **Local Variables**: The function declares several local variables, including `fields` of type `struct ble_hs_adv_fields` to hold the parsed advertisement data, `rc` to store the return code of function calls, and `i` for loop iteration. +## Ranging Data Exchange -* **Checking Advertisement Event Type**: The function begins by checking the event type of the advertisement report stored in the `disc` structure. It verifies whether the event type indicates that the device is advertising its connectability. Specifically, it checks for two event types: `BLE_HCI_ADV_RPT_EVTYPE_ADV_IND` (Connectable Undirected Advertising) and `BLE_HCI_ADV_RPT_EVTYPE_DIR_IND` (Directed Advertising). If the event type is neither of these, it means the device is not connectable, and the function returns 0, indicating that the Central device should not attempt to connect. - -* **Parsing Advertisement Data**: The function then attempts to parse the advertisement data using the `ble_hs_adv_parse_fields` function. The advertisement data is stored in the `disc->data` buffer, and its length is specified by `disc->length_data`. The parsed data is then stored in the `fields` struct. - -* **Checking Peer Address**: This section of the code handles an optional feature where the Central device can specify a specific peer address it wants to connect to. It checks whether the configuration option `CONFIG_EXAMPLE_PEER_ADDR` is set and not equal to `ADDR_ANY` (a special value). If this configuration is set, it means the Central device wants to connect to a specific device based on its address. The function converts the specified peer address from a string representation to a byte array `peer_addr` using the `sscanf` function. If the received device's address (in `disc->addr.val`) does not match the specified peer address, the function returns 0, indicating that the Central device should not connect to this device. - -* **Checking for Alert Notification Service**: Next, the function examines the parsed advertisement data to check for the presence of the Alert Notification service (UUID: 0x1811). The Alert Notification service is used to notify the Central device about alerts or notifications from the Peripheral device. If the advertised service is found in the advertisement data, the function returns 1, indicating that the Central device should proceed with connecting to this device. - -* **Returning 0**: If none of the conditions mentioned above are met (i.e., the event type is not connectable, the peer address check is not enabled, and the Alert Notification service is not advertised), the function returns 0, signaling that the Central device should not establish a connection with this device. - - -## blecent_connect_if_interesting - -```c -/** - * Connects to the sender of the specified advertisement of it looks - * interesting. A device is "interesting" if it advertises connectability and - * support for the Alert Notification service. - */ -static void -blecent_connect_if_interesting(void *disc) -{ - uint8_t own_addr_type; - int rc; - ble_addr_t *addr; - - /* Don't do anything if we don't care about this advertiser. */ -#if CONFIG_EXAMPLE_EXTENDED_ADV - if (!ext_blecent_should_connect((struct ble_gap_ext_disc_desc *)disc)) { - return; - } -#else - if (!blecent_should_connect((struct ble_gap_disc_desc *)disc)) { - return; - } -#endif - - /* Scanning must be stopped before a connection can be initiated. */ - rc = ble_gap_disc_cancel(); - if (rc != 0) { - MODLOG_DFLT(DEBUG, "Failed to cancel scan; rc=%d\n", rc); - return; - } - - /* Figure out address to use for connect (no privacy for now) */ - rc = ble_hs_id_infer_auto(0, &own_addr_type); - if (rc != 0) { - MODLOG_DFLT(ERROR, "error determining address type; rc=%d\n", rc); - return; - } - - /* Try to connect the the advertiser. Allow 30 seconds (30000 ms) for - * timeout. - */ -#if CONFIG_EXAMPLE_EXTENDED_ADV - addr = &((struct ble_gap_ext_disc_desc *)disc)->addr; -#else - addr = &((struct ble_gap_disc_desc *)disc)->addr; -#endif - - rc = ble_gap_connect(own_addr_type, addr, 30000, NULL, - blecent_gap_event, NULL); - if (rc != 0) { - MODLOG_DFLT(ERROR, "Error: Failed to connect to device; addr_type=%d " - "addr=%s; rc=%d\n", - addr->type, addr_str(addr->val), rc); - return; - } -} -``` - -Function `blecent_connect_if_interesting` is to handle the connection process to a BLE Peripheral device if certain criteria are met, making the device interesting for the Central to connect to. Let's summarize the code step by step: - -* **Local Variables**: The function declares local variables, including `own_addr_type` of type `uint8_t` to store the own address type of the Central device, `rc` to store the return code of function calls, and `addr` of type `ble_addr_t*` to store the address of the discovered advertiser. - -* **Checking Device Interest**: The function checks if the advertiser meets the criteria for an interesting device to connect to. The criteria are evaluated using either `blecent_should_connect` or `ext_blecent_should_connect`, depending on the presence of the `CONFIG_EXAMPLE_EXTENDED_ADV` configuration. If the advertiser doesn't meet the criteria, the function returns without attempting a connection. - -* **Stopping Scanning**: Before initiating a connection, the function cancels any ongoing BLE scanning using `ble_gap_disc_cancel()`. Scanning must be halted to establish a connection. - -* **Determining Address Type**: The function infers the address type of the Central device for the connection by calling `ble_hs_id_infer_auto`, and the inferred type is stored in `own_addr_type`. If there's an error during address type determination, the function exits without attempting a connection. - -* **Initiating Connection**: With scanning stopped and the address type determined, the function is ready to initiate the connection. It retrieves the advertiser's address from the provided `disc` argument (of type `struct ble_gap_disc_desc*` or `struct ble_gap_ext_disc_desc*`, depending on configuration). - -* **Connecting to the Advertiser**: The function uses `ble_gap_connect` to establish the connection with the advertiser device. It provides the Central's own address type, the advertiser's address, and sets a timeout of 30 seconds (30000 ms) for the connection attempt. Additionally, it designates the `blecent_gap_event` function as the event callback to manage connection-related events. - -* **Error Handling**: If the connection attempt fails (`rc != 0`), the function logs an error message with details about the failed connection attempt and returns. - - -## blecent_on_disc_complete - -```c -/** - * Called when service discovery of the specified peer has completed. - */ -static void -blecent_on_disc_complete(const struct peer *peer, int status, void *arg) -{ - - if (status != 0) { - /* Service discovery failed. Terminate the connection. */ - MODLOG_DFLT(ERROR, "Error: Service discovery failed; status=%d " - "conn_handle=%d\n", status, peer->conn_handle); - ble_gap_terminate(peer->conn_handle, BLE_ERR_REM_USER_CONN_TERM); - return; - } - - /* Service discovery has completed successfully. Now we have a complete - * list of services, characteristics, and descriptors that the peer - * supports. - */ - MODLOG_DFLT(INFO, "Service discovery complete; status=%d " - "conn_handle=%d\n", status, peer->conn_handle); - - /* Now perform three GATT procedures against the peer: read, - * write, and subscribe to notifications for the ANS service. - */ - blecent_read_write_subscribe(peer); -} -``` - -The function `blecent_on_disc_complete` is essential for managing service discovery completion and GATT procedures. It's invoked after service discovery on a peer device concludes. The function takes three arguments: a pointer to the `peer` structure (containing peer information), an integer `status` indicating success or failure, and a generic `arg` pointer for potential extra data (not used here). - -* **Handling Discovery Status**: Upon initiation, the function checks the `status` of the service discovery operation. Non-zero status indicates failure. In such cases, an error message is logged using `MODLOG_DFLT(ERROR, ...)` and the connection with the peer is terminated via `ble_gap_terminate()`, assigning the error reason code `BLE_ERR_REM_USER_CONN_TERM`. This ensures a proper connection termination due to the unsuccessful service discovery. - -* **Success Path**: For a status of zero, denoting successful service discovery, a log message is generated, signifying the completion of service discovery. It includes the `status` and the peer's connection handle (`conn_handle`). - -* **GATT Procedures**: Upon successful service discovery, the function proceeds with three GATT (Generic Attribute Profile) procedures performed on the peer. These encompass reading, writing, and subscribing to notifications for the Alert Notification Service (ANS). These specific GATT actions are typically implemented within the `blecent_read_write_subscribe()` function. - - -## blecent_read_write_subscribe - -```c -/** - * Performs three GATT operations against the specified peer: - * 1. Reads the ANS Supported New Alert Category characteristic. - * 2. After read is completed, writes the ANS Alert Notification Control Point characteristic. - * 3. After write is completed, subscribes to notifications for the ANS Unread Alert Status - * characteristic. - * - * If the peer does not support a required service, characteristic, or - * descriptor, then the peer lied when it claimed support for the alert - * notification service! When this happens, or if a GATT procedure fails, - * this function immediately terminates the connection. - */ -static void -blecent_read_write_subscribe(const struct peer *peer) -{ - const struct peer_chr *chr; - int rc; - - /* Read the supported-new-alert-category characteristic. */ - chr = peer_chr_find_uuid(peer, - BLE_UUID16_DECLARE(BLECENT_SVC_ALERT_UUID), - BLE_UUID16_DECLARE(BLECENT_CHR_SUP_NEW_ALERT_CAT_UUID)); - if (chr == NULL) { - MODLOG_DFLT(ERROR, "Error: Peer doesn't support the Supported New " - "Alert Category characteristic\n"); - goto err; - } - - rc = ble_gattc_read(peer->conn_handle, chr->chr.val_handle, - blecent_on_read, NULL); - if (rc != 0) { - MODLOG_DFLT(ERROR, "Error: Failed to read characteristic; rc=%d\n", - rc); - goto err; - } - - return; -err: - /* Terminate the connection. */ - ble_gap_terminate(peer->conn_handle, BLE_ERR_REM_USER_CONN_TERM); -} -``` - -This function executes the following sequence of actions: - -* **Reading Supported New Alert Category Characteristic:** It initiates by attempting to read the *"Supported New Alert Category"* characteristic within the *"Alert Notification Service"* (ANS). To accomplish this, it employs the `peer_chr_find_uuid` function, which searches for the specified characteristic using its UUID. If the characteristic isn't located within the peer's attributes, the function logs an error message and discontinues the connection. - -* **Carrying Out Characteristic Read**: If the *"Supported New Alert Category"* characteristic is successfully identified, the function endeavors to read its value utilizing the `ble_gattc_read` function. In the event of a failed read operation, an error is logged, and the connection is terminated. - - -## blecent_on_custom_read - -```c -/** - * Application Callback. Called when the custom subscribable chatacteristic - * in the remote GATT server is read. - * Expect to get the recently written data. - **/ -static int -blecent_on_custom_read(uint16_t conn_handle, - const struct ble_gatt_error *error, - struct ble_gatt_attr *attr, - void *arg) -{ - MODLOG_DFLT(INFO, - "Read complete for the subscribable characteristic; " - "status=%d conn_handle=%d", error->status, conn_handle); - if (error->status == 0) { - MODLOG_DFLT(INFO, " attr_handle=%d value=", attr->handle); - print_mbuf(attr->om); - } - MODLOG_DFLT(INFO, "\n"); - - return 0; -} -``` - -`blecent_on_custom_read` function is triggered when data is read from a custom subscribable characteristic in a distant Generic Attribute Profile (GATT) server. - -* **Function Definition**: The callback function `blecent_on_custom_read` is defined with specific parameters. These parameters are supplied by the BLE stack when the callback is invoked: - - `uint16_t conn_handle`: The BLE connection handle associated with the read operation. - - `const struct ble_gatt_error *error`: A pointer to a structure that holds error information, if any, during the read. - - `struct ble_gatt_attr *attr`: A pointer to the read attribute, encompassing its handle and data. - - `void *arg`: An optional argument that can be included when registering the callback (not used in this example). - -* **Logging Usage**: The function utilizes the `MODLOG_DFLT` macro to log insights regarding the read operation. This includes details such as the connection handle, operation status (via `error->status`), and attribute handle (via `attr->handle`). - -* **Successful Read Check**: The code assesses the read operation's success by inspecting the status stored in `error->status`. A status of `0` indicates a successful read with no errors. - -* **Value Logging**: If the read operation succeeds, the function logs the attribute's value. This is achieved by employing the `print_mbuf` function, which prints the content of `attr->om` (mbuf) data, representing the read attribute's value. - - -## blecent_on_custom_write - -```c -/** - * Application Callback. Called when the custom subscribable characteristic - * in the remote GATT server is written to. - * Client has previously subscribed to this characeteristic, - * so expect a notification from the server. - **/ -static int -blecent_on_custom_write(uint16_t conn_handle, - const struct ble_gatt_error *error, - struct ble_gatt_attr *attr, - void *arg) -{ - const struct peer_chr *chr; - const struct peer *peer; - int rc; - - MODLOG_DFLT(INFO, - "Write to the custom subscribable characteristic complete; " - "status=%d conn_handle=%d attr_handle=%d\n", - error->status, conn_handle, attr->handle); - - peer = peer_find(conn_handle); - chr = peer_chr_find_uuid(peer, - remote_svc_uuid, - remote_chr_uuid); - if (chr == NULL) { - MODLOG_DFLT(ERROR, - "Error: Peer doesn't have the custom subscribable characteristic\n"); - goto err; - } - - /*** Performs a read on the characteristic, the result is handled in blecent_on_new_read callback ***/ - rc = ble_gattc_read(conn_handle, chr->chr.val_handle, - blecent_on_custom_read, NULL); - if (rc != 0) { - MODLOG_DFLT(ERROR, - "Error: Failed to read the custom subscribable characteristic; " - "rc=%d\n", rc); - goto err; - } - - return 0; -err: - /* Terminate the connection */ - return ble_gap_terminate(peer->conn_handle, BLE_ERR_REM_USER_CONN_TERM); -} -``` - -`blecent_on_custom_write` function is invoked when a remote GATT server's (Generic Attribute Profile) custom subscribable characteristic is successfully written to by the Central device. - -* **Parameters**: - - `conn_handle`: BLE connection handle where the write operation took place. - - `error`: Contains write operation details, including the status code. - - `attr`: Represents the written attribute (characteristic). - - `arg`: Optional user-defined argument (unused here). - -* **Logging**: - This function logs write operation completion, displaying write status, connection handle, and attribute handle. - -* **Locating Peer and Characteristic**: - - The function seeks the relevant `peer` using the connection handle through `peer_find`. - - It also searches for a `chr` (characteristic) within the peer's attributes using `remote_svc_uuid` and `remote_chr_uuid`. - - If the characteristic isn't found, an error message is logged, and the function proceeds to the error handling section. - -* **Initiating Characteristic Read**: - - Upon locating the characteristic, the function begins a read operation using `ble_gattc_read`. - - It supplies connection handle, characteristic's value handle (`chr->chr.val_handle`), read handler (`blecent_on_custom_read`), and no user-defined argument (`NULL`). - -* **Error Handling**: - - If the read operation faces an issue (non-zero return value), an error message is logged. - - The function then navigates to the error handling section (`err`), ending the BLE connection with the remote device via `ble_gap_terminate`. - - The termination rationale is marked as `BLE_ERR_REM_USER_CONN_TERM`. - -* **Return Value**: - - Successful write and subsequent read return `0`. - - Write or read errors lead to connection termination, with the result of `ble_gap_terminate`. - - -## blecent_on_custom_subscribe - -```c -/** - * Application Callback. Called when the custom subscribable characteristic - * is subscribed to. - **/ -static int -blecent_on_custom_subscribe(uint16_t conn_handle, - const struct ble_gatt_error *error, - struct ble_gatt_attr *attr, - void *arg) -{ - const struct peer_chr *chr; - uint8_t value; - int rc; - const struct peer *peer; - - MODLOG_DFLT(INFO, - "Subscribe to the custom subscribable characteristic complete; " - "status=%d conn_handle=%d", error->status, conn_handle); - - if (error->status == 0) { - MODLOG_DFLT(INFO, " attr_handle=%d value=", attr->handle); - print_mbuf(attr->om); - } - MODLOG_DFLT(INFO, "\n"); - - peer = peer_find(conn_handle); - chr = peer_chr_find_uuid(peer, - remote_svc_uuid, - remote_chr_uuid); - if (chr == NULL) { - MODLOG_DFLT(ERROR, "Error: Peer doesn't have the subscribable characteristic\n"); - goto err; - } - - /* Write 1 byte to the new characteristic to test if it notifies after subscribing */ - value = 0x19; - rc = ble_gattc_write_flat(conn_handle, chr->chr.val_handle, - &value, sizeof(value), blecent_on_custom_write, NULL); - if (rc != 0) { - MODLOG_DFLT(ERROR, - "Error: Failed to write to the subscribable characteristic; " - "rc=%d\n", rc); - goto err; - } - - return 0; -err: - /* Terminate the connection */ - return ble_gap_terminate(peer->conn_handle, BLE_ERR_REM_USER_CONN_TERM); -} -``` - -`blecent_on_custom_subscribe` function is triggered when a custom subscribable characteristic is subscribed to. Here's a concise breakdown of the code's key aspects: - -* **Function and Parameters**: The function receives parameters including the connection handle `conn_handle`, an error structure `error` for status details, a GATT attribute structure `attr` representing the subscribed attribute, and a generic argument `arg`. - -* **Subscription Log**: Information about the subscription process is logged, including the subscription status, connection handle, and attribute handle. If the subscription is successful (`error->status == 0`), the subscribed attribute's value is printed using the `print_mbuf` function. - -* **Peer and Characteristic Search**: The function searches for a peer structure using `conn_handle` and attempts to locate a specific characteristic within that peer. The desired characteristic is identified by `remote_svc_uuid` and `remote_chr_uuid`. - -* **Characteristic Validation**: If the characteristic isn't found within the peer, an error message is logged to indicate the absence of the expected subscribable characteristic. - -* **Characteristic Write**: Assuming the characteristic is found, the function tries to write a single byte (0x19) to it. This test is performed to determine if the characteristic will notify the Central device after subscribing. The `ble_gattc_write_flat` function handles this operation. - -* **Write Outcome Check**: In case the write operation fails (`rc != 0`), an error message is logged to indicate the failure in writing to the subscribable characteristic. - -* **Return Value**: The function returns `0` to signal the completion of processing. In case of errors, connection termination is initiated within the error-handling segments. - - -## blecent_custom_gatt_operations -```c -/** - * Performs 3 operations on the remote GATT server. - * 1. Subscribes to a characteristic by writing 0x10 to it's CCCD. - * 2. Writes to the characteristic and expect a notification from remote. - * 3. Reads the characteristic and expect to get the recently written information. - **/ -static void -blecent_custom_gatt_operations(const struct peer* peer) -{ - const struct peer_dsc *dsc; - int rc; - uint8_t value[2]; - - dsc = peer_dsc_find_uuid(peer, - remote_svc_uuid, - remote_chr_uuid, - BLE_UUID16_DECLARE(BLE_GATT_DSC_CLT_CFG_UUID16)); - if (dsc == NULL) { - MODLOG_DFLT(ERROR, "Error: Peer lacks a CCCD for the subscribable characteristic\n"); - goto err; - } - - /*** Write 0x00 and 0x01 (The subscription code) to the CCCD ***/ - value[0] = 1; - value[1] = 0; - rc = ble_gattc_write_flat(peer->conn_handle, dsc->dsc.handle, - value, sizeof(value), blecent_on_custom_subscribe, NULL); - if (rc != 0) { - MODLOG_DFLT(ERROR, - "Error: Failed to subscribe to the subscribable characteristic; " - "rc=%d\n", rc); - goto err; - } - - return; -err: - /* Terminate the connection */ - ble_gap_terminate(peer->conn_handle, BLE_ERR_REM_USER_CONN_TERM); -} -``` - -`blecent_custom_gatt_operations` function demonstrates various GATT interactions, including subscribing to a characteristic, writing to it, and reading from it. - -* **Finding CCCD Descriptor**: The function begins by searching for a specific Client Characteristic Configuration Descriptor (CCCD) associated with a subscribable characteristic. This search employs the `peer_dsc_find_uuid` function using provided UUIDs. - -* **Checking CCCD Availability**: If the CCCD is not found (`dsc` is `NULL`), an error message is logged, and the connection is terminated. - -* **Subscribing to Characteristic**: The function assembles a two-byte value array with `value[0]` indicating subscription (set to 1) and `value[1]` set to 0. It utilizes `ble_gattc_write_flat` to write this value to the CCCD, subscribing to characteristic notifications. If the write operation fails (returns non-zero), an error message is logged, and the connection is terminated. - -* **Successful Execution**: If the subscription write succeeds, the function returns without further actions. - -* **Error Handling**: In case of any failures, the code proceeds to the `err` label, terminating the BLE connection using `ble_gap_terminate` with the relevant error code. - - -## blecent_on_subscribe - -```c -/** - * Application callback. Called when the attempt to subscribe to notifications - * for the ANS Unread Alert Status characteristic has completed. - */ -static int -blecent_on_subscribe(uint16_t conn_handle, - const struct ble_gatt_error *error, - struct ble_gatt_attr *attr, - void *arg) -{ - struct peer *peer; - - MODLOG_DFLT(INFO, "Subscribe complete; status=%d conn_handle=%d " - "attr_handle=%d\n", - error->status, conn_handle, attr->handle); - - peer = peer_find(conn_handle); - if (peer == NULL) { - MODLOG_DFLT(ERROR, "Error in finding peer, aborting..."); - ble_gap_terminate(conn_handle, BLE_ERR_REM_USER_CONN_TERM); - } - /* Subscribe to, write to, and read the custom characteristic*/ - blecent_custom_gatt_operations(peer); - - return 0; -} -``` - -`blecent_on_subscribe` function's purpose is to handle the completion of subscribing to notifications for a specific characteristic in a BLE Peripheral device. - -* **Parameters**: - - `conn_handle`: Corresponds to the BLE connection's handle. - - `error`: Points to a `struct ble_gatt_error` indicating the outcome of the subscription attempt. - - `attr`: Points to the GATT attribute linked with the subscribed characteristic. - - `arg`: Represents a user-defined argument passed into the callback. - -* **Logging**: The function logs subscription completion details utilizing `MODLOG_DFLT(INFO, ...)`. It reports subscription status, connection handle, and attribute handle. - -* **Peer Identification**: The function endeavors to locate the associated `peer` (Peripheral device) linked with the provided `conn_handle`. If the peer can't be located, it logs an error message and terminates the BLE connection with the peripheral using `ble_gap_terminate`, coupled with error code `BLE_ERR_REM_USER_CONN_TERM`. - -* **Custom GATT Operations**: Presuming the peer is successfully identified, the code implies the existence of a function named `blecent_custom_gatt_operations`. This function likely performs extra custom GATT (Generic Attribute Profile) operations on the peripheral. Such operations could encompass writing to or reading from custom characteristics or attributes. - - -## blecent_on_write - -```c -/** - * Application callback. Called when the write to the ANS Alert Notification - * Control Point characteristic has completed. - */ -static int -blecent_on_write(uint16_t conn_handle, - const struct ble_gatt_error *error, - struct ble_gatt_attr *attr, - void *arg) -{ - MODLOG_DFLT(INFO, - "Write complete; status=%d conn_handle=%d attr_handle=%d\n", - error->status, conn_handle, attr->handle); - - /* Subscribe to notifications for the Unread Alert Status characteristic. - * A central enables notifications by writing two bytes (1, 0) to the - * characteristic's client-characteristic-configuration-descriptor (CCCD). - */ - const struct peer_dsc *dsc; - uint8_t value[2]; - int rc; - const struct peer *peer = peer_find(conn_handle); - - dsc = peer_dsc_find_uuid(peer, - BLE_UUID16_DECLARE(BLECENT_SVC_ALERT_UUID), - BLE_UUID16_DECLARE(BLECENT_CHR_UNR_ALERT_STAT_UUID), - BLE_UUID16_DECLARE(BLE_GATT_DSC_CLT_CFG_UUID16)); - if (dsc == NULL) { - MODLOG_DFLT(ERROR, "Error: Peer lacks a CCCD for the Unread Alert " - "Status characteristic\n"); - goto err; - } - - value[0] = 1; - value[1] = 0; - rc = ble_gattc_write_flat(conn_handle, dsc->dsc.handle, - value, sizeof value, blecent_on_subscribe, NULL); - if (rc != 0) { - MODLOG_DFLT(ERROR, "Error: Failed to subscribe to characteristic; " - "rc=%d\n", rc); - goto err; - } - - return 0; -err: - /* Terminate the connection. */ - return ble_gap_terminate(peer->conn_handle, BLE_ERR_REM_USER_CONN_TERM); -} -``` - -This function gets triggered upon the completion of a write operation to the ANS (Alert Notification Service) Alert Notification Control Point characteristic. Here's a breakdown of the code's components: - -* **Subscription to Notifications**: The primary objective of the function is to subscribe to notifications for the Unread Alert Status characteristic. Notifications are enabled by writing two bytes (1, 0) to the Client Characteristic Configuration Descriptor (CCCD) of the characteristic. - -* **Locating CCCD Descriptor**: The `peer_dsc_find_uuid` function is employed to find the CCCD descriptor associated with the Unread Alert Status characteristic. This entails locating a specific descriptor with a particular UUID linked to CCCD. In case the descriptor isn't found, an error message is logged, and the code proceeds to the `err` label. - -* **Preparing Subscription Value**: The code prepares the subscription value as an array of two bytes: [1, 0]. This value's purpose is to enable notifications for the characteristic. - -* **Initiating Write Operation**: The function then uses `ble_gattc_write_flat` to execute a flat write operation. It writes the subscription value to the CCCD descriptor's handle. The `blecent_on_subscribe` callback function is designated to manage the completion of the subscription write operation. - -* **Error Handling for Write**: If the write operation encounters a failure (as indicated by a non-zero return value from `ble_gattc_write_flat`), an error message is logged, and the code proceeds to the `err` label. - - -## blecent_on_read - -```c -/** - * Application callback. Called when the read of the ANS Supported New Alert - * Category characteristic has completed. - */ -static int -blecent_on_read(uint16_t conn_handle, - const struct ble_gatt_error *error, - struct ble_gatt_attr *attr, - void *arg) -{ - MODLOG_DFLT(INFO, "Read complete; status=%d conn_handle=%d", error->status, - conn_handle); - if (error->status == 0) { - MODLOG_DFLT(INFO, " attr_handle=%d value=", attr->handle); - print_mbuf(attr->om); - } - MODLOG_DFLT(INFO, "\n"); - - /* Write two bytes (99, 100) to the alert-notification-control-point - * characteristic. - */ - const struct peer_chr *chr; - uint8_t value[2]; - int rc; - const struct peer *peer = peer_find(conn_handle); - - chr = peer_chr_find_uuid(peer, - BLE_UUID16_DECLARE(BLECENT_SVC_ALERT_UUID), - BLE_UUID16_DECLARE(BLECENT_CHR_ALERT_NOT_CTRL_PT)); - if (chr == NULL) { - MODLOG_DFLT(ERROR, "Error: Peer doesn't support the Alert " - "Notification Control Point characteristic\n"); - goto err; - } - - value[0] = 99; - value[1] = 100; - rc = ble_gattc_write_flat(conn_handle, chr->chr.val_handle, - value, sizeof value, blecent_on_write, NULL); - if (rc != 0) { - MODLOG_DFLT(ERROR, "Error: Failed to write characteristic; rc=%d\n", - rc); - goto err; - } - - return 0; -err: - /* Terminate the connection. */ - return ble_gap_terminate(peer->conn_handle, BLE_ERR_REM_USER_CONN_TERM); -} -``` - -The `blecent_on_read` function is called when a read operation for a certain characteristic is completed. - -* **Logging Read Status**: The function logs information regarding the read operation, including its status and the connection handle. If the read operation is successful (status equals `0`), it also logs the attribute handle and the value read from the characteristic using the `print_mbuf` function. - -* **Writing to a Characteristic**: The code proceeds to write two bytes (`99` and `100`) to the Alert Notification Control Point characteristic. It locates the characteristic using `peer_chr_find_uuid` based on its UUID values. If the characteristic is not found (i.e., `chr` is `NULL`), it logs an error message and proceeds to the `err` label. - -* **Write Operation**: When the Alert Notification Control Point characteristic is found, the code prepares the data to be written (an array called `value` containing two bytes) and employs `ble_gattc_write_flat` to execute the write operation. The completion of the write operation triggers the `blecent_on_write` callback function. If the write operation encounters an error (return code `rc` is non-zero), the code logs an error message and proceeds to the `err` label. - -* **Handling Errors**: If an error occurs (such as not finding the characteristic or a failed write operation), the code terminates the connection using `ble_gap_terminate` with the reason code `BLE_ERR_REM_USER_CONN_TERM`. - -In essence, this callback function handles reading a characteristic's value, logs the result, and potentially performs a subsequent write operation to another characteristic. +The initiator handles RAS Control Point and Data Ready notifications to exchange ranging data with the reflector: +1. When a **Ranging Data Ready** indication arrives, the initiator sends a `GET_RD` (Get Ranging Data) command via the RAS Control Point. +2. The reflector responds with ranging data segments through On-demand Ranging Data notifications. +3. When the data transfer is complete (`COMPLETE_RD_RSP`), the initiator sends an `ACK_RD` acknowledgment. ## Conclusion -In this example code, we've explored how to initiate BLE scanning, discover nearby Peripheral devices, establish connections, and interact with their services and characteristics. - -**Key Takeaways**: - -- **Initialization**: The code initializes the BLE stack and sets up event handlers. -- **Scanning**: The Central device scans for nearby BLE Peripheral devices and filters interesting devices based on criteria like service UUIDs or peer addresses. -- **Connection Management**: When a suitable Peripheral is found, the Central initiates a connection and handles connection events. -- **Service Discovery**: After connection, the Central performs service discovery to identify the services and characteristics supported by the Peripheral. -- **GATT Procedures**: The code showcases read, write, and subscription procedures with the discovered services and characteristics. -- **Error Handling**: Proper error handling ensures termination of connections in case of failures. +This example demonstrates: +1. BLE Channel Sounding procedure initiation from a Central device. +2. RAS (Ranging and Sounding) service discovery and characteristic subscription. +3. Exchange of ranging data between initiator and reflector using the RAS protocol. +4. CS subevent result handling for processing step data. diff --git a/examples/bluetooth/nimble/ble_chan_sound_reflector/main/main.c b/examples/bluetooth/nimble/ble_chan_sound_reflector/main/main.c index 12dee1c02f..3c0d6f2f24 100644 --- a/examples/bluetooth/nimble/ble_chan_sound_reflector/main/main.c +++ b/examples/bluetooth/nimble/ble_chan_sound_reflector/main/main.c @@ -40,6 +40,7 @@ static uint8_t ext_adv_pattern_1[] = { #endif static const char *tag = "NimBLE_BLE_CHAN_REFLECTOR"; static int bleprph_gap_event(struct ble_gap_event *event, void *arg); +static uint8_t own_addr_type; void ble_store_config_init(void); struct ble_cs_event ranging_subevent; @@ -199,8 +200,8 @@ ext_bleprph_advertise(void) memset (¶ms, 0, sizeof(params)); /* enable connectable advertising */ params.connectable = 1; - /* advertise using random addr */ - params.own_addr_type = BLE_OWN_ADDR_PUBLIC; + /* advertise using the inferred address type */ + params.own_addr_type = own_addr_type; params.primary_phy = BLE_HCI_LE_PHY_1M; params.secondary_phy = BLE_HCI_LE_PHY_2M; params.sid = 1; @@ -259,10 +260,12 @@ bleprph_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 fields.uuids16 = (ble_uuid16_t[]) { BLE_UUID16_INIT(BLE_UUID_RANGING_SERVICE_VAL) }; @@ -277,7 +280,7 @@ bleprph_advertise(void) memset(&adv_params, 0, sizeof adv_params); adv_params.conn_mode = BLE_GAP_CONN_MODE_UND; adv_params.disc_mode = BLE_GAP_DISC_MODE_GEN; - rc = ble_gap_adv_start(0, NULL, BLE_HS_FOREVER, + rc = ble_gap_adv_start(own_addr_type, NULL, BLE_HS_FOREVER, &adv_params, bleprph_gap_event, NULL); if (rc != 0) { MODLOG_DFLT(ERROR, "error enabling advertisement; rc=%d\n", rc); @@ -412,12 +415,13 @@ static void bleprph_on_sync(void) { int rc; - uint8_t own_addr_type = 0; /* Make sure we have set Host feature bit for Channel Sounding*/ rc = ble_gap_set_host_feat(47,0x01); + if (rc != 0) { + MODLOG_DFLT(ERROR, "error setting host feature; rc=%d\n", rc); + } /* 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 while advertising (no privacy for now) */ rc = ble_hs_id_infer_auto(0, &own_addr_type); @@ -490,9 +494,11 @@ app_main(void) #endif rc = custom_gatt_svr_init(); assert(rc == 0); +#if CONFIG_BT_NIMBLE_GAP_SERVICE /* Set the default device name. */ rc = ble_svc_gap_device_name_set("nimble-ble_chan_reflector"); assert(rc == 0); +#endif /* XXX Need to have template for store */ ble_store_config_init(); nimble_port_freertos_init(bleprph_host_task); diff --git a/examples/bluetooth/nimble/ble_cte/ble_periodic_adv_with_cte/main/main.c b/examples/bluetooth/nimble/ble_cte/ble_periodic_adv_with_cte/main/main.c index 24fbb2beff..21c701170f 100644 --- a/examples/bluetooth/nimble/ble_cte/ble_periodic_adv_with_cte/main/main.c +++ b/examples/bluetooth/nimble/ble_cte/ble_periodic_adv_with_cte/main/main.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2025-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: CC0-1.0 */ @@ -10,9 +10,7 @@ #include "nimble/nimble_port_freertos.h" #include "host/ble_hs.h" #include "host/util/util.h" -#include "console/console.h" #include "services/gap/ble_svc_gap.h" -#include "periodic_adv.h" #include "host/ble_gap.h" #include "host/ble_hs_adv.h" #include "cte_config.h" @@ -21,25 +19,17 @@ static const char *TAG = "CTE_ADV_EXAMPLE"; static uint8_t s_periodic_adv_raw_data[] = {0x0D, BLE_HS_ADV_TYPE_COMP_NAME, 'C','T','E',' ','P','e','r','i','o','d','i','c'}; -/* Configuration based on Kconfig settings */ -#if CONFIG_EXAMPLE_RANDOM_ADDR -static uint8_t s_own_addr_type = BLE_OWN_ADDR_RANDOM; -#else -static uint8_t s_own_addr_type; -#endif - - /** * @brief Configure and start periodic advertising with CTE */ -static void start_periodic_adv_cte(void) +static void start_periodic_adv_cte(uint8_t own_addr_type) { int rc; - uint8_t instance = 1; + uint8_t instance = 0; ble_addr_t addr; + uint8_t addr_type = own_addr_type == BLE_OWN_ADDR_RANDOM ? BLE_ADDR_RANDOM : BLE_ADDR_PUBLIC; - /* Generate random address for instance */ - rc = ble_hs_id_gen_rnd(1, &addr); + rc = ble_hs_id_copy_addr(addr_type, addr.val, NULL); assert(rc == 0); ESP_LOGI(TAG, "Device Address: "); @@ -48,26 +38,23 @@ static void start_periodic_adv_cte(void) /* Configure extended advertising parameters */ struct ble_gap_ext_adv_params ext_adv_params = { - .own_addr_type = BLE_OWN_ADDR_RANDOM, + .own_addr_type = own_addr_type, .primary_phy = BLE_HCI_LE_PHY_1M, .secondary_phy = BLE_HCI_LE_PHY_1M, .sid = 2, - .tx_power = 0 + .tx_power = 0x7f }; rc = ble_gap_ext_adv_configure(instance, &ext_adv_params, NULL, NULL, NULL); assert(rc == 0); - rc = ble_gap_ext_adv_set_addr(instance, &addr); - assert(rc == 0); - /* Configure advertising data */ struct ble_hs_adv_fields adv_fields = { .name = (const uint8_t *)"CTE_Periodic_Adv", .name_len = strlen((char *)adv_fields.name) }; - struct os_mbuf *data = os_msys_get_pkthdr(BLE_HCI_MAX_ADV_DATA_LEN, 0); + struct os_mbuf *data = os_msys_get_pkthdr(BLE_HS_ADV_MAX_FIELD_SZ, 0); assert(data); rc = ble_hs_adv_set_fields_mbuf(&adv_fields, data); @@ -79,8 +66,8 @@ static void start_periodic_adv_cte(void) /* Configure periodic advertising parameters */ struct ble_gap_periodic_adv_params pparams = { .include_tx_power = 0, - .itvl_min = BLE_GAP_ADV_ITVL_MS(200), - .itvl_max = BLE_GAP_ADV_ITVL_MS(400) + .itvl_min = BLE_GAP_PERIODIC_ITVL_MS(200), + .itvl_max = BLE_GAP_PERIODIC_ITVL_MS(400) }; rc = ble_gap_periodic_adv_configure(instance, &pparams); @@ -98,11 +85,15 @@ static void start_periodic_adv_cte(void) /* Configure CTE parameters */ #if defined(CONFIG_EXAMPLE_ADV_DIRECTION_FINDING_AOA) - /* Configure CTE parameters */ + /* Configure CTE parameters for AoA (receiver does antenna switching). + * A minimal switching pattern is provided to satisfy the NimBLE host + * API; the controller ignores it for AoA transmissions. */ struct ble_gap_periodic_adv_cte_params cte_params = { .cte_length = 0x14, .cte_type = BLE_CTE_TYPE_AOA, .cte_count = 1, + .switching_pattern_length = 2, + .antenna_ids = (uint8_t[]){0, 0}, }; #elif defined(CONFIG_EXAMPLE_ADV_DIRECTION_FINDING_AOD) struct ble_gap_periodic_adv_cte_params cte_params = { @@ -151,27 +142,19 @@ static void periodic_adv_on_reset(int reason) static void periodic_sync_cb(void) { int rc; + uint8_t own_addr_type; #if CONFIG_EXAMPLE_RANDOM_ADDR - ble_addr_t addr; - if (ble_hs_id_gen_rnd(0, &addr) == 0) { - ble_hs_id_set_rnd(addr.val); - } - /* Ensure proper identity address */ rc = ble_hs_util_ensure_addr(1); #else rc = ble_hs_util_ensure_addr(0); #endif assert(rc == 0); - /* Infer address type */ - rc = ble_hs_id_infer_auto(0, &s_own_addr_type); - if (rc != 0) { - ESP_LOGE(TAG, "Failed to infer address type (rc=%d)", rc); - return; - } + rc = ble_hs_id_infer_auto(0, &own_addr_type); + assert(rc == 0); /* Start advertising */ - start_periodic_adv_cte(); + start_periodic_adv_cte(own_addr_type); } /** @@ -210,7 +193,7 @@ void app_main(void) ESP_LOGI(TAG, "%s", direction_finding_logo); #if defined(CONFIG_EXAMPLE_ADV_DIRECTION_FINDING_AOD) ESP_LOGI(TAG, "DIRECTION_FINDING Example Periodic Adv AOD Mode"); - ble_direction_finding_antenna_init(antenna_use_gpio,CONFIG_EXAMPLE_AOD_GPIO_BIT_COUNT); + ble_direction_finding_antenna_init(antenna_use_gpio,CONFIG_EXAMPLE_ANT_GPIO_BIT_COUNT); #elif defined(CONFIG_EXAMPLE_ADV_DIRECTION_FINDING_AOA) ESP_LOGI(TAG, "DIRECTION_FINDING Example Periodic Adv AOA Mode"); #endif @@ -221,8 +204,10 @@ void app_main(void) ble_hs_cfg.sync_cb = periodic_sync_cb; /* Set device name */ +#if CONFIG_BT_NIMBLE_GAP_SERVICE rc = ble_svc_gap_device_name_set("Periodic ADV with CTE"); assert(rc == 0); +#endif /* Start BLE host task */ nimble_port_freertos_init(periodic_adv_host_task); diff --git a/examples/bluetooth/nimble/ble_cte/ble_periodic_sync_with_cte/main/main.c b/examples/bluetooth/nimble/ble_cte/ble_periodic_sync_with_cte/main/main.c index c94ba0a2a5..2117fdf7ba 100644 --- a/examples/bluetooth/nimble/ble_cte/ble_periodic_sync_with_cte/main/main.c +++ b/examples/bluetooth/nimble/ble_cte/ble_periodic_sync_with_cte/main/main.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2025-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: CC0-1.0 */ @@ -13,9 +13,6 @@ #include "host/ble_hs.h" #include "host/util/util.h" #include "services/gap/ble_svc_gap.h" -#include "console/console.h" - -#include "periodic_sync.h" #include "cte_config.h" static const char *TAG = "CTE_SYNC_EXAMPLE"; @@ -36,20 +33,17 @@ static struct ble_gap_cte_sampling_params sync_cte_sampling_params = { static void periodic_sync_scan(void) { uint8_t own_addr_type; - struct ble_gap_disc_params disc_params = {0}; + struct ble_gap_ext_disc_params disc_params = {0}; int rc = ble_hs_id_infer_auto(0, &own_addr_type); if (rc != 0) { ESP_LOGE(TAG, "Failed to determine address type; rc=%d", rc); return; } - /** - * Perform a passive scan. I.e., don't send follow-up scan requests to - * each advertiser. - */ disc_params.passive = 1; - rc = ble_gap_disc(own_addr_type, BLE_HS_FOREVER, &disc_params, - periodic_sync_gap_event, NULL); + rc = ble_gap_ext_disc(own_addr_type, 0, 0, + 0, 0, 0, &disc_params, &disc_params, + periodic_sync_gap_event, NULL); if (rc != 0) { ESP_LOGE(TAG, "GAP discovery failed; rc=%d", rc); } @@ -146,8 +140,7 @@ static int periodic_sync_gap_event(struct ble_gap_event *event, void *arg) { switch (event->type) { case BLE_GAP_EVENT_EXT_DISC: { - const struct ble_gap_ext_disc_desc *disc = ((struct ble_gap_ext_disc_desc *)(&event->disc)); - + const struct ble_gap_ext_disc_desc *disc = &event->ext_disc; if (is_synced) { return 0; } @@ -216,6 +209,11 @@ static int periodic_sync_gap_event(struct ble_gap_event *event, void *arg) } case BLE_GAP_EVENT_PERIODIC_SYNC: + if (event->periodic_sync.status != 0) { + ESP_LOGE(TAG, "Periodic Sync Failed; status=%d", event->periodic_sync.status); + is_synced = 0; + return 0; + } ESP_LOGI(TAG, "Periodic Sync Established"); print_periodic_sync_data(event); ble_gap_disc_cancel(); @@ -260,7 +258,7 @@ static void periodic_sync_on_sync(void) rc = ble_hs_util_ensure_addr(0); assert(rc == 0); - /* Begin scanning for a peripheral to connect to. */ + /* Begin scanning for a periodic advertiser to sync with. */ periodic_sync_scan(); } @@ -296,8 +294,10 @@ void app_main(void) ble_hs_cfg.sync_cb = periodic_sync_on_sync; /* Set the default device name. */ +#if CONFIG_BT_NIMBLE_GAP_SERVICE rc = ble_svc_gap_device_name_set("periodic_sync_CTE"); assert(rc == 0); +#endif #if MYNEWT_VAL(BLE_AOA_AOD) diff --git a/examples/bluetooth/nimble/ble_enc_adv_data/enc_adv_data_cent/main/main.c b/examples/bluetooth/nimble/ble_enc_adv_data/enc_adv_data_cent/main/main.c index 9a8c9e4453..a31f144e12 100644 --- a/examples/bluetooth/nimble/ble_enc_adv_data/enc_adv_data_cent/main/main.c +++ b/examples/bluetooth/nimble/ble_enc_adv_data/enc_adv_data_cent/main/main.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2021-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Unlicense OR CC0-1.0 */ @@ -11,6 +11,7 @@ #include "nimble/nimble_port.h" #include "nimble/nimble_port_freertos.h" #include "host/ble_hs.h" +#include "host/ble_ead.h" #include "host/util/util.h" #include "console/console.h" #include "services/gap/ble_svc_gap.h" @@ -253,37 +254,50 @@ enc_adv_data_cent_decrypt(uint8_t length_data, const uint8_t *data, const uint8_ uint8_t op; uint8_t len, offset = 0; uint8_t *enc_data; + uint8_t enc_payload_len; int rc; uint8_t dec_data_len; + uint8_t temp[BLE_EAD_DECRYPTED_PAYLOAD_SIZE(UINT8_MAX)]; struct ble_store_key_ead key_ead = {0}; struct ble_store_value_ead value_ead = {0}; while (offset < length_data) { len = data[offset]; + + /* Bounds check: ensure we can read the type byte and the full AD field */ + if (offset + 1 >= length_data) { + break; + } op = data[offset + 1]; - uint8_t temp[len]; + + if (len == 0 || offset + 1 + len > length_data) { + break; + } switch (op) { case BLE_GAP_ENC_ADV_DATA: - enc_data = (uint8_t *) malloc (sizeof(uint8_t) * len); + /* Encrypted payload is AD value (len - 1 bytes, excluding the type byte) */ + enc_payload_len = len - 1; + enc_data = (uint8_t *) malloc (sizeof(uint8_t) * enc_payload_len); if (enc_data == NULL) { MODLOG_DFLT(ERROR, "Failed to allocate enc_data"); return 0; } - memcpy(enc_data, data + offset + 2, len); + memcpy(enc_data, data + offset + 2, enc_payload_len); memcpy(&key_ead.peer_addr.val, peer_addr, PEER_ADDR_VAL_SIZE); rc = ble_store_read_ead(&key_ead, &value_ead); if (rc != 0 || !value_ead.km_present) { MODLOG_DFLT(INFO, "Reading of session key and iv from NVS failed rc = %d", rc); + free(enc_data); return 0; } else { MODLOG_DFLT(INFO, "Read session key and iv from NVS successfully"); } - rc = ble_ead_decrypt(value_ead.km->session_key, value_ead.km->iv, enc_data, len, - temp); + rc = ble_ead_decrypt(value_ead.km->session_key, value_ead.km->iv, enc_data, + enc_payload_len, temp); if (rc == 0) { MODLOG_DFLT(INFO, "Decryption of adv data done successfully"); } else { @@ -593,6 +607,8 @@ enc_adv_data_cent_gap_event(struct ble_gap_event *event, void *arg) if (event->passkey.params.action == BLE_SM_IOACT_INPUT) { pkey.action = event->passkey.params.action; + /* WARNING: Hardcoded passkey for demonstration only. + * In production, generate a random passkey per pairing. */ pkey.passkey = 123456; ESP_LOGI(tag, "Entering passkey %" PRIu32, pkey.passkey); rc = ble_sm_inject_io(event->passkey.conn_handle, &pkey); diff --git a/examples/bluetooth/nimble/ble_enc_adv_data/enc_adv_data_prph/main/main.c b/examples/bluetooth/nimble/ble_enc_adv_data/enc_adv_data_prph/main/main.c index 53e0302299..1d162ef869 100644 --- a/examples/bluetooth/nimble/ble_enc_adv_data/enc_adv_data_prph/main/main.c +++ b/examples/bluetooth/nimble/ble_enc_adv_data/enc_adv_data_prph/main/main.c @@ -247,6 +247,8 @@ enc_adv_data_prph_gap_event(struct ble_gap_event *event, void *arg) /** For now only BLE_SM_IOACT_DISP is handled */ if (event->passkey.params.action == BLE_SM_IOACT_DISP) { pkey.action = event->passkey.params.action; + /* WARNING: Hardcoded passkey for demonstration only. + * In production, generate a random passkey per pairing. */ pkey.passkey = 123456; ESP_LOGI(tag, "Enter passkey %" PRIu32 " on the peer side", pkey.passkey); rc = ble_sm_inject_io(event->passkey.conn_handle, &pkey); diff --git a/examples/bluetooth/nimble/ble_gattc_gatts_coex/main/main.c b/examples/bluetooth/nimble/ble_gattc_gatts_coex/main/main.c index e33df0579f..fc052080ff 100644 --- a/examples/bluetooth/nimble/ble_gattc_gatts_coex/main/main.c +++ b/examples/bluetooth/nimble/ble_gattc_gatts_coex/main/main.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2025-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Unlicense OR CC0-1.0 */ @@ -491,9 +491,11 @@ ble_coex_advertise(void) fields.tx_pwr_lvl = BLE_HS_ADV_TX_PWR_LVL_AUTO; name = ble_svc_gap_device_name(); - fields.name = (uint8_t *)name; - fields.name_len = strlen(name); - fields.name_is_complete = 1; + if (name) { + fields.name = (uint8_t *)name; + fields.name_len = strlen(name); + fields.name_is_complete = 1; + } fields.uuids16 = (ble_uuid16_t[]) { BLE_UUID16_INIT(BLECOEX_SVC_ALERT_UUID) @@ -523,16 +525,19 @@ static void on_sync(void) { int rc; - ble_hs_util_ensure_addr(0); - - ble_hs_id_infer_auto(0, &own_addr_type); - ble_svc_gap_device_name_set("NimBLE Coex"); - ble_coex_advertise(); - - // Start scanning as a client rc = ble_hs_util_ensure_addr(0); assert(rc == 0); + rc = ble_hs_id_infer_auto(0, &own_addr_type); + assert(rc == 0); + +#if CONFIG_BT_NIMBLE_GAP_SERVICE + rc = ble_svc_gap_device_name_set("NimBLE Coex"); + assert(rc == 0); +#endif + ble_coex_advertise(); + + /* Start scanning as a client */ ble_coex_scan(); } @@ -557,7 +562,11 @@ void app_main(void) } ESP_ERROR_CHECK(ret); - nimble_port_init(); + ret = nimble_port_init(); + if (ret != ESP_OK) { + MODLOG_DFLT(ERROR, "Failed to init nimble %d \n", ret); + return; + } ble_hs_cfg.sync_cb = on_sync; #if MYNEWT_VAL(BLE_INCL_SVC_DISCOVERY) || MYNEWT_VAL(BLE_GATT_CACHING_INCLUDE_SERVICES) diff --git a/examples/bluetooth/nimble/ble_htp/htp_cent/main/main.c b/examples/bluetooth/nimble/ble_htp/htp_cent/main/main.c index 58c9ae5426..bc2668d530 100644 --- a/examples/bluetooth/nimble/ble_htp/htp_cent/main/main.c +++ b/examples/bluetooth/nimble/ble_htp/htp_cent/main/main.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2017-2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2017-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ diff --git a/examples/bluetooth/nimble/ble_l2cap_coc/coc_bleprph/main/main.c b/examples/bluetooth/nimble/ble_l2cap_coc/coc_bleprph/main/main.c index 1eda92dab1..a8e5f3b8df 100644 --- a/examples/bluetooth/nimble/ble_l2cap_coc/coc_bleprph/main/main.c +++ b/examples/bluetooth/nimble/ble_l2cap_coc/coc_bleprph/main/main.c @@ -1,10 +1,9 @@ /* - * SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2021-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Unlicense OR CC0-1.0 */ -#include "esp_log.h" #include "nvs_flash.h" /* BLE */ #include "nimble/nimble_port.h" @@ -80,7 +79,7 @@ ext_bleprph_advertise(void) { struct ble_gap_ext_adv_params params; struct os_mbuf *data; - uint8_t instance = 1; + uint8_t instance = 0; int rc; /* use defaults for non-set params */ @@ -89,8 +88,8 @@ ext_bleprph_advertise(void) /* enable connectable advertising */ params.connectable = 1; - /* advertise using random addr */ - params.own_addr_type = BLE_OWN_ADDR_PUBLIC; + /* advertise using the inferred address type */ + params.own_addr_type = own_addr_type; params.primary_phy = BLE_HCI_LE_PHY_1M; params.secondary_phy = BLE_HCI_LE_PHY_2M; diff --git a/examples/bluetooth/nimble/ble_l2cap_coc/coc_bleprph/tutorial/coc_bleprph_walkthrough.md b/examples/bluetooth/nimble/ble_l2cap_coc/coc_bleprph/tutorial/coc_bleprph_walkthrough.md index 4c215f6789..67fe151a97 100644 --- a/examples/bluetooth/nimble/ble_l2cap_coc/coc_bleprph/tutorial/coc_bleprph_walkthrough.md +++ b/examples/bluetooth/nimble/ble_l2cap_coc/coc_bleprph/tutorial/coc_bleprph_walkthrough.md @@ -150,8 +150,8 @@ esp_err_t esp_nimble_init(void) ``` The host is configured by setting up the callbacks for Stack-reset, Stack-sync, and Storage status. ```c -ble_hs_cfg.reset_cb = blecent_on_reset; -ble_hs_cfg.sync_cb = blecent_on_sync; +ble_hs_cfg.reset_cb = bleprph_on_reset; +ble_hs_cfg.sync_cb = bleprph_on_sync; ble_hs_cfg.store_status_cb = ble_store_util_status_rr; ``` ## Security Manager Configuration diff --git a/examples/bluetooth/nimble/ble_multi_adv/main/gatt_svr.c b/examples/bluetooth/nimble/ble_multi_adv/main/gatt_svr.c index 2b8539ee87..175d160dd6 100644 --- a/examples/bluetooth/nimble/ble_multi_adv/main/gatt_svr.c +++ b/examples/bluetooth/nimble/ble_multi_adv/main/gatt_svr.c @@ -4,14 +4,9 @@ * SPDX-License-Identifier: Apache-2.0 */ -#include -#include -#include #include "host/ble_hs.h" -#include "host/ble_uuid.h" #include "services/gap/ble_svc_gap.h" #include "services/gatt/ble_svc_gatt.h" -#include "multi_adv.h" static uint8_t gatt_svr_chr_val = 0x01; /* Example characteristic value */ #define GATT_SVR_UUID16_1 (0xCDAB) diff --git a/examples/bluetooth/nimble/ble_multi_adv/main/main.c b/examples/bluetooth/nimble/ble_multi_adv/main/main.c index a0da020f89..b234034c54 100644 --- a/examples/bluetooth/nimble/ble_multi_adv/main/main.c +++ b/examples/bluetooth/nimble/ble_multi_adv/main/main.c @@ -4,7 +4,6 @@ * SPDX-License-Identifier: Apache-2.0 */ -#include "esp_log.h" #include "nvs_flash.h" /* BLE */ @@ -12,7 +11,6 @@ #include "nimble/nimble_port_freertos.h" #include "host/ble_hs.h" #include "host/util/util.h" -#include "console/console.h" #include "services/gap/ble_svc_gap.h" #include "multi_adv.h" @@ -512,7 +510,6 @@ app_main(void) ble_hs_cfg.sm_bonding = 1; ble_hs_cfg.sm_mitm = 1; ble_hs_cfg.sm_sc = 1; - ble_hs_cfg.sm_sc = 0; /* Enable the appropriate bit masks to make sure the keys * that are needed are exchanged */ diff --git a/examples/bluetooth/nimble/ble_multi_adv/sdkconfig.defaults.esp32c2 b/examples/bluetooth/nimble/ble_multi_adv/sdkconfig.defaults.esp32c2 index 1da03222de..b53ef8656a 100644 --- a/examples/bluetooth/nimble/ble_multi_adv/sdkconfig.defaults.esp32c2 +++ b/examples/bluetooth/nimble/ble_multi_adv/sdkconfig.defaults.esp32c2 @@ -4,4 +4,6 @@ CONFIG_IDF_TARGET="esp32c2" CONFIG_BT_ENABLED=y CONFIG_BT_NIMBLE_ENABLED=y -CONFIG_BT_NIMBLE_HCI_EVT_BUF_SIZE=70 +CONFIG_BT_NIMBLE_TRANSPORT_EVT_SIZE=70 +CONFIG_BT_NIMBLE_EXT_ADV=y +CONFIG_BT_NIMBLE_MAX_EXT_ADV_INSTANCES=4 diff --git a/examples/bluetooth/nimble/ble_multi_adv/sdkconfig.defaults.esp32c6 b/examples/bluetooth/nimble/ble_multi_adv/sdkconfig.defaults.esp32c6 index ad0515b3cc..12fb0c1528 100644 --- a/examples/bluetooth/nimble/ble_multi_adv/sdkconfig.defaults.esp32c6 +++ b/examples/bluetooth/nimble/ble_multi_adv/sdkconfig.defaults.esp32c6 @@ -3,4 +3,6 @@ # CONFIG_BT_ENABLED=y CONFIG_BT_NIMBLE_ENABLED=y -CONFIG_BT_NIMBLE_HCI_EVT_BUF_SIZE=70 +CONFIG_BT_NIMBLE_TRANSPORT_EVT_SIZE=70 +CONFIG_BT_NIMBLE_EXT_ADV=y +CONFIG_BT_NIMBLE_MAX_EXT_ADV_INSTANCES=4 diff --git a/examples/bluetooth/nimble/ble_multi_adv/tutorial/Ble_Multi_Adv_Example_Walkthrough.md b/examples/bluetooth/nimble/ble_multi_adv/tutorial/Ble_Multi_Adv_Example_Walkthrough.md index 3f5a99009b..4e68d81c94 100644 --- a/examples/bluetooth/nimble/ble_multi_adv/tutorial/Ble_Multi_Adv_Example_Walkthrough.md +++ b/examples/bluetooth/nimble/ble_multi_adv/tutorial/Ble_Multi_Adv_Example_Walkthrough.md @@ -9,7 +9,6 @@ In this tutorial, the ble_multi_adv example code for the espressif chipsets with This example is located in the examples folder of the ESP-IDF under the [ble_multi_adv/main](../main/). The [main.c](../main/main.c) file located in the main folder contains all the functionality that we are going to review. The header files contained in [main.c](../main/main.c) are: ```c -#include "esp_log.h" #include "nvs_flash.h" /* BLE */ @@ -17,7 +16,6 @@ This example is located in the examples folder of the ESP-IDF under the [ble_mul #include "nimble/nimble_port_freertos.h" #include "host/ble_hs.h" #include "host/util/util.h" -#include "console/console.h" #include "services/gap/ble_svc_gap.h" #include "multi_adv.h" ``` @@ -38,8 +36,6 @@ The program’s entry point is the app_main() function: void app_main(void) { - int rc; - /* Initialize NVS — it is used to store PHY calibration data */ esp_err_t ret = nvs_flash_init(); if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) { @@ -50,10 +46,9 @@ app_main(void) ret = nimble_port_init(); if (ret != ESP_OK) { - MODLOG_DFLT(ERROR, "Failed to init nimble %d \n", ret); + ESP_LOGE(tag, "Failed to init nimble %d ", ret); return; } - /* Initialize the NimBLE host configuration. */ ble_hs_cfg.reset_cb = ble_multi_adv_on_reset; ble_hs_cfg.sync_cb = ble_multi_adv_on_sync; @@ -62,8 +57,7 @@ app_main(void) ble_hs_cfg.sm_bonding = 1; ble_hs_cfg.sm_mitm = 1; - ble_hs_cfg.sm_sc = 0; - + ble_hs_cfg.sm_sc = 1; /* Enable the appropriate bit masks to make sure the keys * that are needed are exchanged */ @@ -75,12 +69,17 @@ app_main(void) ble_instance_cb[i].cb = NULL; } +#if MYNEWT_VAL(BLE_GATTS) + int rc; rc = gatt_svr_init(); assert(rc == 0); +#if CONFIG_BT_NIMBLE_GAP_SERVICE /* Set the default device name. */ rc = ble_svc_gap_device_name_set("nimble-multi-adv"); assert(rc == 0); +#endif +#endif /* XXX Need to have template for store */ ble_store_config_init(); @@ -172,8 +171,7 @@ The Security Manager is configured by setting up the following SM's flag and att ```c ble_hs_cfg.sm_bonding = 1; ble_hs_cfg.sm_mitm = 1; - ble_hs_cfg.sm_sc = 0; - + ble_hs_cfg.sm_sc = 1; /* Enable the appropriate bit masks to make sure the keys * that are needed are exchanged */ @@ -181,10 +179,12 @@ The Security Manager is configured by setting up the following SM's flag and att ble_hs_cfg.sm_their_key_dist = BLE_SM_PAIR_KEY_DIST_ENC; ``` -The main function calls `ble_svc_gap_device_name_set()` to set the default device name. 'nimble-multi-adv' 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. 'nimble-multi-adv' is passed as the default device name to this function. This is wrapped with `CONFIG_BT_NIMBLE_GAP_SERVICE` guard as the GAP service may be disabled. ```c +#if CONFIG_BT_NIMBLE_GAP_SERVICE rc = ble_svc_gap_device_name_set("nimble-multi-adv"); +#endif ``` main function calls `ble_store_config_init()` to configure the host by setting up the storage callbacks that handle the read, write, and deletion of security material. @@ -218,7 +218,7 @@ It provides the following benefits over legacy advertisement. * Non connectable extended * Connectable extended * Scannable legacy -* Legacy withe specified duration(5 sec) +* Legacy with specified duration(5 sec) For each instance: * A random address is generated which is associated with the advertising instance diff --git a/examples/bluetooth/nimble/ble_multi_conn/ble_multi_conn_cent/main/main.c b/examples/bluetooth/nimble/ble_multi_conn/ble_multi_conn_cent/main/main.c index d2e626f1af..a03a6bac9b 100644 --- a/examples/bluetooth/nimble/ble_multi_conn/ble_multi_conn_cent/main/main.c +++ b/examples/bluetooth/nimble/ble_multi_conn/ble_multi_conn_cent/main/main.c @@ -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); diff --git a/examples/bluetooth/nimble/ble_multi_conn/ble_multi_conn_cent/tutorial/Ble_Multiple_Connections_Central_Example_Walkthrough.md b/examples/bluetooth/nimble/ble_multi_conn/ble_multi_conn_cent/tutorial/Ble_Multiple_Connections_Central_Example_Walkthrough.md index 5a69870230..278f3c4132 100644 --- a/examples/bluetooth/nimble/ble_multi_conn/ble_multi_conn_cent/tutorial/Ble_Multiple_Connections_Central_Example_Walkthrough.md +++ b/examples/bluetooth/nimble/ble_multi_conn/ble_multi_conn_cent/tutorial/Ble_Multiple_Connections_Central_Example_Walkthrough.md @@ -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; diff --git a/examples/bluetooth/nimble/ble_multi_conn/ble_multi_conn_prph/main/main.c b/examples/bluetooth/nimble/ble_multi_conn/ble_multi_conn_prph/main/main.c index e697bfff3f..2cf93be2d1 100644 --- a/examples/bluetooth/nimble/ble_multi_conn/ble_multi_conn_prph/main/main.c +++ b/examples/bluetooth/nimble/ble_multi_conn/ble_multi_conn_prph/main/main.c @@ -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); diff --git a/examples/bluetooth/nimble/ble_multi_conn/ble_multi_conn_prph/tutorial/Ble_Multiple_Connections_Peripheral_Example_Walkthrough.md b/examples/bluetooth/nimble/ble_multi_conn/ble_multi_conn_prph/tutorial/Ble_Multiple_Connections_Peripheral_Example_Walkthrough.md index 3e15818237..70a95dc4c7 100644 --- a/examples/bluetooth/nimble/ble_multi_conn/ble_multi_conn_prph/tutorial/Ble_Multiple_Connections_Peripheral_Example_Walkthrough.md +++ b/examples/bluetooth/nimble/ble_multi_conn/ble_multi_conn_prph/tutorial/Ble_Multiple_Connections_Peripheral_Example_Walkthrough.md @@ -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"); } } ``` diff --git a/examples/bluetooth/nimble/ble_pawr_adv/ble_pawr_adv/main/main.c b/examples/bluetooth/nimble/ble_pawr_adv/ble_pawr_adv/main/main.c index 06f104f8d4..a10b3d552e 100644 --- a/examples/bluetooth/nimble/ble_pawr_adv/ble_pawr_adv/main/main.c +++ b/examples/bluetooth/nimble/ble_pawr_adv/ble_pawr_adv/main/main.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2024-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Unlicense OR CC0-1.0 */ @@ -9,6 +9,7 @@ #include "nimble/nimble_port.h" #include "nimble/nimble_port_freertos.h" #include "host/ble_hs.h" +#include "host/util/util.h" #define BLE_PAWR_EVENT_INTERVAL (600) #define BLE_PAWR_PERIODIC_EVENT_INTERVAL_MS (3000) @@ -85,7 +86,7 @@ gap_event_cb(struct ble_gap_event *event, void *arg) } static void -start_periodic_adv(void) +start_periodic_adv(uint8_t own_addr_type) { int rc; uint8_t addr[6]; @@ -96,12 +97,13 @@ start_periodic_adv(void) uint8_t instance = 0; #if MYNEWT_VAL(BLE_PERIODIC_ADV_ENH) - struct ble_gap_periodic_adv_enable_params eparams; + struct ble_gap_periodic_adv_start_params eparams; memset(&eparams, 0, sizeof(eparams)); #endif - /* Get the local public address. */ - rc = ble_hs_id_copy_addr(BLE_ADDR_PUBLIC, addr, NULL); + /* Get the local address. */ + uint8_t addr_type = own_addr_type == BLE_OWN_ADDR_RANDOM ? BLE_ADDR_RANDOM : BLE_ADDR_PUBLIC; + rc = ble_hs_id_copy_addr(addr_type, addr, NULL); assert (rc == 0); ESP_LOGI(TAG, "Device Address %02x:%02x:%02x:%02x:%02x:%02x", addr[5], addr[4], addr[3], @@ -109,7 +111,7 @@ start_periodic_adv(void) /* For periodic we use instance with non-connectable advertising */ memset (¶ms, 0, sizeof(params)); - params.own_addr_type = BLE_OWN_ADDR_PUBLIC; + params.own_addr_type = own_addr_type; params.primary_phy = BLE_HCI_LE_PHY_CODED; params.secondary_phy = BLE_HCI_LE_PHY_1M; params.sid = 0; @@ -173,8 +175,18 @@ on_reset(int reason) static void on_sync(void) { + int rc; + uint8_t own_addr_type; + + /* Make sure we have proper identity address set (public preferred) */ + rc = ble_hs_util_ensure_addr(0); + assert(rc == 0); + + rc = ble_hs_id_infer_auto(0, &own_addr_type); + assert(rc == 0); + /* Begin advertising. */ - start_periodic_adv(); + start_periodic_adv(own_addr_type); } void pawr_host_task(void *param) diff --git a/examples/bluetooth/nimble/ble_pawr_adv/ble_pawr_adv/tutorial/BLE_pawr_adv_walkthrough.md b/examples/bluetooth/nimble/ble_pawr_adv/ble_pawr_adv/tutorial/BLE_pawr_adv_walkthrough.md index e609cbd24a..57145d957a 100644 --- a/examples/bluetooth/nimble/ble_pawr_adv/ble_pawr_adv/tutorial/BLE_pawr_adv_walkthrough.md +++ b/examples/bluetooth/nimble/ble_pawr_adv/ble_pawr_adv/tutorial/BLE_pawr_adv_walkthrough.md @@ -15,6 +15,7 @@ This example is located in the examples folder of the ESP-IDF under the [ble_paw #include "nimble/nimble_port.h" #include "nimble/nimble_port_freertos.h" #include "host/ble_hs.h" +#include "host/util/util.h" ``` These includes provide: @@ -26,6 +27,8 @@ These includes provide: - BLE host stack functionality +- BLE utility functions for address management (util.h) + ## Main Entry Point The program’s entry point is the app_main() function: @@ -156,10 +159,10 @@ These parameters control: ## PAwR Advertisement -The start_periodic_adv() function configures and starts PAwR: +The start_periodic_adv() function configures and starts PAwR. It takes `own_addr_type` as a parameter, which is determined dynamically via `ble_hs_id_infer_auto()` in the `on_sync` callback: ```c static void -start_periodic_adv(void) +start_periodic_adv(uint8_t own_addr_type) { int rc; uint8_t addr[6]; @@ -170,12 +173,13 @@ start_periodic_adv(void) uint8_t instance = 0; #if MYNEWT_VAL(BLE_PERIODIC_ADV_ENH) - struct ble_gap_periodic_adv_enable_params eparams; + struct ble_gap_periodic_adv_start_params eparams; memset(&eparams, 0, sizeof(eparams)); #endif - /* Get the local public address. */ - rc = ble_hs_id_copy_addr(BLE_ADDR_PUBLIC, addr, NULL); + /* Get the local address. */ + uint8_t addr_type = own_addr_type == BLE_OWN_ADDR_RANDOM ? BLE_ADDR_RANDOM : BLE_ADDR_PUBLIC; + rc = ble_hs_id_copy_addr(addr_type, addr, NULL); assert (rc == 0); ESP_LOGI(TAG, "Device Address %02x:%02x:%02x:%02x:%02x:%02x", addr[5], addr[4], addr[3], @@ -183,7 +187,7 @@ start_periodic_adv(void) /* For periodic we use instance with non-connectable advertising */ memset (¶ms, 0, sizeof(params)); - params.own_addr_type = BLE_OWN_ADDR_PUBLIC; + params.own_addr_type = own_addr_type; params.primary_phy = BLE_HCI_LE_PHY_CODED; params.secondary_phy = BLE_HCI_LE_PHY_1M; params.sid = 0; @@ -276,6 +280,32 @@ It processes two main events: - BLE_GAP_EVENT_PER_SUBEV_RESP: Triggered when responses are received from scanners +## Sync Callback + +When the BLE host and controller are synced, the `on_sync` callback is invoked. It ensures a valid identity address is set and determines the appropriate address type before starting PAwR: + +```c +static void +on_sync(void) +{ + int rc; + uint8_t own_addr_type; + + /* Make sure we have proper identity address set (public preferred) */ + rc = ble_hs_util_ensure_addr(0); + assert(rc == 0); + + rc = ble_hs_id_infer_auto(0, &own_addr_type); + assert(rc == 0); + + /* Begin advertising. */ + start_periodic_adv(own_addr_type); +} +``` + +- `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)`: Automatically determines the best address type to use based on what is available on the device. + ## Host Task The pawr_host_task runs the NimBLE stack: diff --git a/examples/bluetooth/nimble/ble_pawr_adv/ble_pawr_sync/main/main.c b/examples/bluetooth/nimble/ble_pawr_adv/ble_pawr_sync/main/main.c index 12b517af51..1ae6f65600 100644 --- a/examples/bluetooth/nimble/ble_pawr_adv/ble_pawr_sync/main/main.c +++ b/examples/bluetooth/nimble/ble_pawr_adv/ble_pawr_sync/main/main.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2024-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Unlicense OR CC0-1.0 */ @@ -108,7 +108,7 @@ gap_event_cb(struct ble_gap_event *event, void *arg) // choose subevents in range 0 to (num_subevents - 1) uint8_t subevents[] = {0, 1, 2, 3, 4}; int result = ble_gap_periodic_adv_sync_subev(event->periodic_sync.sync_handle, 0, sizeof(subevents), subevents); - if (result == ESP_OK) { + if (result == 0) { ESP_LOGI(TAG, "[Subevent Sync OK] sync handle:%d, sync_subevents:%d", event->periodic_sync.sync_handle, sizeof(subevents)); } else { ESP_LOGE(TAG, "Failed to sync subevents, rc = 0x%x", result); @@ -172,7 +172,14 @@ start_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, NULL, &disc_params, + uint8_t own_addr_type; + int rc_addr = ble_hs_id_infer_auto(0, &own_addr_type); + if (rc_addr != 0) { + ESP_LOGE(TAG, "error determining address type; rc=%d\n", rc_addr); + return; + } + + rc = ble_gap_ext_disc(own_addr_type, 0, 0, 1, 0, 0, NULL, &disc_params, gap_event_cb, NULL); if (rc != 0) { ESP_LOGE(TAG, "Error initiating GAP discovery procedure; rc=%d\n", rc); diff --git a/examples/bluetooth/nimble/ble_pawr_adv/ble_pawr_sync/tutorial/BLE_pawr_sync_walkthrough.md b/examples/bluetooth/nimble/ble_pawr_adv/ble_pawr_sync/tutorial/BLE_pawr_sync_walkthrough.md index 5543acaa7e..6b63dffcd0 100644 --- a/examples/bluetooth/nimble/ble_pawr_adv/ble_pawr_sync/tutorial/BLE_pawr_sync_walkthrough.md +++ b/examples/bluetooth/nimble/ble_pawr_adv/ble_pawr_sync/tutorial/BLE_pawr_sync_walkthrough.md @@ -187,14 +187,25 @@ static int gap_event_cb(struct ble_gap_event *event, void *arg) ```c static void start_scan(void) { - struct ble_gap_ext_disc_params disc_params = { - .itvl = BLE_GAP_SCAN_ITVL_MS(600), - .window = BLE_GAP_SCAN_ITVL_MS(300), - .passive = 1 - }; + struct ble_gap_ext_disc_params disc_params; - ble_gap_ext_disc(BLE_OWN_ADDR_PUBLIC, 0, 0, 1, 0, 0, NULL, - &disc_params, gap_event_cb, NULL); + memset(&disc_params, 0, sizeof(disc_params)); + disc_params.itvl = BLE_GAP_SCAN_ITVL_MS(600); + disc_params.window = BLE_GAP_SCAN_ITVL_MS(300); + disc_params.passive = 1; + + uint8_t own_addr_type; + int rc_addr = ble_hs_id_infer_auto(0, &own_addr_type); + if (rc_addr != 0) { + ESP_LOGE(TAG, "error determining address type; rc=%d\n", rc_addr); + return; + } + + int rc = ble_gap_ext_disc(own_addr_type, 0, 0, 1, 0, 0, NULL, &disc_params, + gap_event_cb, NULL); + if (rc != 0) { + ESP_LOGE(TAG, "Error initiating GAP discovery procedure; rc=%d\n", rc); + } } ``` Key parameters: diff --git a/examples/bluetooth/nimble/ble_pawr_adv_conn/ble_pawr_adv_conn/main/main.c b/examples/bluetooth/nimble/ble_pawr_adv_conn/ble_pawr_adv_conn/main/main.c index 4286cf7be6..d5a6fe0fc8 100644 --- a/examples/bluetooth/nimble/ble_pawr_adv_conn/ble_pawr_adv_conn/main/main.c +++ b/examples/bluetooth/nimble/ble_pawr_adv_conn/ble_pawr_adv_conn/main/main.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2024-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Unlicense OR CC0-1.0 */ @@ -9,6 +9,7 @@ #include "nimble/nimble_port.h" #include "nimble/nimble_port_freertos.h" #include "host/ble_hs.h" +#include "host/util/util.h" #define BLE_PAWR_EVENT_PERIODIC_INTERVAL_MS (3000) #define BLE_PAWR_NUM_SUBEVTS (10) @@ -23,6 +24,7 @@ static struct ble_gap_set_periodic_adv_subev_data_params sub_data_params[BLE_PAWR_NUM_SUBEVTS]; static uint8_t sub_data_pattern[BLE_PAWR_SUB_DATA_LEN] = {0}; static uint8_t conn; +static uint8_t own_addr_type; static struct ble_gap_conn_desc desc; char * addr_str(const void *addr) @@ -148,7 +150,7 @@ gap_event_cb(struct ble_gap_event *event, void *arg) ESP_LOGI(TAG, "data: 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x", data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7], data[8], data[9]); - peer_addr.type=0; + peer_addr.type = event->periodic_adv_response.data[8]; memcpy(peer_addr.val,&event->periodic_adv_response.data[2],6); adv_handle = event->periodic_adv_response.adv_handle; @@ -156,7 +158,7 @@ gap_event_cb(struct ble_gap_event *event, void *arg) phy_mask = 0x01; if (conn == 0) { - rc = ble_gap_connect_with_synced(0,adv_handle,subevent,&peer_addr,30000,phy_mask,NULL,NULL,NULL,gap_event_cb,NULL); + rc = ble_gap_connect_with_synced(own_addr_type,adv_handle,subevent,&peer_addr,30000,phy_mask,NULL,NULL,NULL,gap_event_cb,NULL); if (rc != 0 ) { ESP_LOGI(TAG,"Error: Failed to connect to device , rc = %d\n",rc); }else { @@ -179,7 +181,7 @@ gap_event_cb(struct ble_gap_event *event, void *arg) } static void -start_periodic_adv(void) +start_periodic_adv(uint8_t own_addr_type) { int rc; uint8_t addr[6]; @@ -190,12 +192,13 @@ start_periodic_adv(void) uint8_t instance = 0; #if MYNEWT_VAL(BLE_PERIODIC_ADV_ENH) - struct ble_gap_periodic_adv_enable_params eparams; + struct ble_gap_periodic_adv_start_params eparams; memset(&eparams, 0, sizeof(eparams)); #endif - /* Get the local public address. */ - rc = ble_hs_id_copy_addr(BLE_ADDR_PUBLIC, addr, NULL); + /* Get the local address. */ + uint8_t addr_type = own_addr_type == BLE_OWN_ADDR_RANDOM ? BLE_ADDR_RANDOM : BLE_ADDR_PUBLIC; + rc = ble_hs_id_copy_addr(addr_type, addr, NULL); assert (rc == 0); ESP_LOGI(TAG, "Device Address %02x:%02x:%02x:%02x:%02x:%02x", addr[5], addr[4], addr[3], @@ -203,7 +206,7 @@ start_periodic_adv(void) /* For periodic we use instance with non-connectable advertising */ memset (¶ms, 0, sizeof(params)); - params.own_addr_type = BLE_OWN_ADDR_PUBLIC; + params.own_addr_type = own_addr_type; params.primary_phy = BLE_HCI_LE_PHY_CODED; params.secondary_phy = BLE_HCI_LE_PHY_1M; params.sid = 0; @@ -267,8 +270,17 @@ on_reset(int reason) static void 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); + + rc = ble_hs_id_infer_auto(0, &own_addr_type); + assert(rc == 0); + /* Begin advertising. */ - start_periodic_adv(); + start_periodic_adv(own_addr_type); } void pawr_host_task(void *param) diff --git a/examples/bluetooth/nimble/ble_pawr_adv_conn/ble_pawr_adv_conn/tutorial/BLE_pawr_adv_conn_walkthrough.md b/examples/bluetooth/nimble/ble_pawr_adv_conn/ble_pawr_adv_conn/tutorial/BLE_pawr_adv_conn_walkthrough.md index 0cd708f4a3..11404e3f43 100644 --- a/examples/bluetooth/nimble/ble_pawr_adv_conn/ble_pawr_adv_conn/tutorial/BLE_pawr_adv_conn_walkthrough.md +++ b/examples/bluetooth/nimble/ble_pawr_adv_conn/ble_pawr_adv_conn/tutorial/BLE_pawr_adv_conn_walkthrough.md @@ -14,7 +14,8 @@ This example is located in the examples folder of the ESP-IDF under the [ble_paw /* BLE */ #include "nimble/nimble_port.h" #include "nimble/nimble_port_freertos.h" -#include "host/ble_hs.h +#include "host/ble_hs.h" +#include "host/util/util.h" ``` These includes provide: @@ -26,6 +27,8 @@ These includes provide: - BLE host stack functionality +- BLE utility functions for address management (util.h) + ## Main Entry Point The program’s entry point is the app_main() function: @@ -122,7 +125,6 @@ esp_err_t esp_nimble_init(void) ## PAwR Configuration ```c -#define BLE_PAWR_EVENT_INTERVAL (520) #define BLE_PAWR_EVENT_PERIODIC_INTERVAL_MS (3000) #define BLE_PAWR_NUM_SUBEVTS (10) #define BLE_PAWR_SUB_INTERVAL (52) @@ -133,7 +135,7 @@ esp_err_t esp_nimble_init(void) ``` These parameters control: -- The interval between periodic advertising events +- The periodic advertising interval in milliseconds - Number of subevents per periodic interval @@ -145,20 +147,20 @@ These parameters control: - num_subevents: Number of subevents per periodic interval (10) -- subevent_interval: Time between subevents (44 × 1.25ms = 55ms) +- subevent_interval: Time between subevents (52 × 1.25ms = 65ms) -- response_slot_delay: First response slot delay (20 × 1.25ms = 25ms) +- response_slot_delay: First response slot delay (5 × 1.25ms = 6.25ms) -- response_slot_spacing: Time between slots (32 × 0.125ms = 4ms) +- response_slot_spacing: Time between slots (10 × 0.125ms = 1.25ms) -- num_response_slots: Number of response slots per subevent (5) +- num_response_slots: Number of response slots per subevent (25) ## PAwR Advertisement -The start_periodic_adv() function configures and starts PAwR: +The start_periodic_adv() function configures and starts PAwR. It takes `own_addr_type` as a parameter, which is determined dynamically via `ble_hs_id_infer_auto()` in the `on_sync` callback: ```c static void -start_periodic_adv(void) +start_periodic_adv(uint8_t own_addr_type) { int rc; uint8_t addr[6]; @@ -169,12 +171,13 @@ start_periodic_adv(void) uint8_t instance = 0; #if MYNEWT_VAL(BLE_PERIODIC_ADV_ENH) - struct ble_gap_periodic_adv_enable_params eparams; + struct ble_gap_periodic_adv_start_params eparams; memset(&eparams, 0, sizeof(eparams)); #endif - /* Get the local public address. */ - rc = ble_hs_id_copy_addr(BLE_ADDR_PUBLIC, addr, NULL); + /* Get the local address. */ + uint8_t addr_type = own_addr_type == BLE_OWN_ADDR_RANDOM ? BLE_ADDR_RANDOM : BLE_ADDR_PUBLIC; + rc = ble_hs_id_copy_addr(addr_type, addr, NULL); assert (rc == 0); ESP_LOGI(TAG, "Device Address %02x:%02x:%02x:%02x:%02x:%02x", addr[5], addr[4], addr[3], @@ -182,7 +185,7 @@ start_periodic_adv(void) /* For periodic we use instance with non-connectable advertising */ memset (¶ms, 0, sizeof(params)); - params.own_addr_type = BLE_OWN_ADDR_PUBLIC; + params.own_addr_type = own_addr_type; params.primary_phy = BLE_HCI_LE_PHY_CODED; params.secondary_phy = BLE_HCI_LE_PHY_1M; params.sid = 0; @@ -271,12 +274,12 @@ The API ble_gap_connect_with_synced() is a NimBLE API used by a PAwR Advertiser This is especially useful in use cases where on-demand, peer-to-peer data exchange is needed. ```c ble_gap_connect_with_synced( - BLE_OWN_ADDR_PUBLIC, + own_addr_type, adv_handle, subevent, &peer_addr, 30000, - BLE_GAP_LE_PHY_1M_MASK, + phy_mask, NULL, NULL, NULL, gap_event_cb, NULL); ``` @@ -287,7 +290,32 @@ Highlights: - Avoids scanning (direct connection) - Enables faster, deterministic connection -📌 Tip: Choose connection interval as a multiple of subevent interval for optimal scheduling. +> Tip: Choose connection interval as a multiple of subevent interval for optimal scheduling. + +## Sync Callback + +When the BLE host and controller are synced, the `on_sync` callback is invoked. It ensures a valid identity address is set and determines the appropriate address type before starting PAwR: + +```c +static void +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); + + rc = ble_hs_id_infer_auto(0, &own_addr_type); + assert(rc == 0); + + /* Begin advertising. */ + start_periodic_adv(own_addr_type); +} +``` + +- `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)`: Automatically determines the best address type to use based on what is available on the device. ## Host Task ```c diff --git a/examples/bluetooth/nimble/ble_pawr_adv_conn/ble_pawr_sync_conn/main/main.c b/examples/bluetooth/nimble/ble_pawr_adv_conn/ble_pawr_sync_conn/main/main.c index 539d7b540a..d00c37fc32 100644 --- a/examples/bluetooth/nimble/ble_pawr_adv_conn/ble_pawr_sync_conn/main/main.c +++ b/examples/bluetooth/nimble/ble_pawr_adv_conn/ble_pawr_sync_conn/main/main.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2024-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Unlicense OR CC0-1.0 */ @@ -126,9 +126,22 @@ gap_event_cb(struct ble_gap_event *event, void *arg) // create a special data for checking manually in ADV side sub_data_pattern[0] = event->periodic_report.subevent; - rc = ble_hs_id_copy_addr(BLE_ADDR_PUBLIC, device_addr, NULL); + uint8_t addr_type; + rc = ble_hs_id_infer_auto(0, &addr_type); + if (rc != 0) { + ESP_LOGE(TAG, "Failed to infer address type; rc=%d", rc); + os_mbuf_free_chain(data); + return 0; + } + rc = ble_hs_id_copy_addr(addr_type, device_addr, NULL); + if (rc != 0) { + ESP_LOGE(TAG, "Failed to copy address; rc=%d", rc); + os_mbuf_free_chain(data); + return 0; + } sub_data_pattern[1] = param.response_slot; memcpy(&sub_data_pattern[2],device_addr,BLE_DEV_ADDR_LEN); + sub_data_pattern[8] = addr_type; os_mbuf_append(data, sub_data_pattern, BLE_PAWR_RSP_DATA_LEN); @@ -165,7 +178,7 @@ gap_event_cb(struct ble_gap_event *event, void *arg) // choose subevents in range 0 to (num_subevents - 1) uint8_t subevents[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; int result = ble_gap_periodic_adv_sync_subev(event->periodic_sync.sync_handle, 0, sizeof(subevents), subevents); - if (result == ESP_OK) { + if (result == 0) { ESP_LOGI(TAG, "[Subevent Sync OK] sync handle:%d, sync_subevents:%d\n", event->periodic_sync.sync_handle, sizeof(subevents)); } else { ESP_LOGE(TAG, "Failed to sync subevents, rc = 0x%x", result); @@ -227,7 +240,14 @@ start_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, NULL, &disc_params, + uint8_t own_addr_type; + int rc_addr = ble_hs_id_infer_auto(0, &own_addr_type); + if (rc_addr != 0) { + ESP_LOGE(TAG, "error determining address type; rc=%d\n", rc_addr); + return; + } + + rc = ble_gap_ext_disc(own_addr_type, 0, 0, 1, 0, 0, NULL, &disc_params, gap_event_cb, NULL); if (rc != 0) { ESP_LOGE(TAG, "Error initiating GAP discovery procedure; rc=%d\n", rc); diff --git a/examples/bluetooth/nimble/ble_pawr_adv_conn/ble_pawr_sync_conn/tutorial/BLE_pawr_sync_conn_walkthrough.md b/examples/bluetooth/nimble/ble_pawr_adv_conn/ble_pawr_sync_conn/tutorial/BLE_pawr_sync_conn_walkthrough.md index 1f608d67ec..dd988eb1cf 100644 --- a/examples/bluetooth/nimble/ble_pawr_adv_conn/ble_pawr_sync_conn/tutorial/BLE_pawr_sync_conn_walkthrough.md +++ b/examples/bluetooth/nimble/ble_pawr_adv_conn/ble_pawr_sync_conn/tutorial/BLE_pawr_sync_conn_walkthrough.md @@ -112,18 +112,26 @@ esp_err_t esp_nimble_init(void) ### Start Scanning -Configures a passive extended scan to detect periodic advertisers: +Configures a passive extended scan to detect periodic advertisers. The address type is determined dynamically using `ble_hs_id_infer_auto()`: ```c memset(&disc_params, 0, sizeof(disc_params)); disc_params.itvl = BLE_GAP_SCAN_ITVL_MS(600); disc_params.window = BLE_GAP_SCAN_ITVL_MS(300); disc_params.passive = 1; - rc = ble_gap_ext_disc(BLE_OWN_ADDR_PUBLIC, 0, 0, 1, 0, 0, NULL, &disc_params, + uint8_t own_addr_type; + int rc_addr = ble_hs_id_infer_auto(0, &own_addr_type); + if (rc_addr != 0) { + ESP_LOGE(TAG, "error determining address type; rc=%d\n", rc_addr); + return; + } + + rc = ble_gap_ext_disc(own_addr_type, 0, 0, 1, 0, 0, NULL, &disc_params, gap_event_cb, NULL); ``` -- gap_event_cb: Processes discovery events (EXT_DISC) to find our target.` +- `ble_hs_id_infer_auto()`: Dynamically determines the best address type to use instead of hardcoding `BLE_OWN_ADDR_PUBLIC`. +- gap_event_cb: Processes discovery events (EXT_DISC) to find our target. ## Create Periodic Sync @@ -153,13 +161,13 @@ static int create_periodic_sync(struct ble_gap_ext_disc_desc *disc) { After sync establishment, sync to configurable subevents: ```c -// Choose subevents to listen to -uint8_t subevents[] = {0, 1, 2, 3, 4}; +// choose subevents in range 0 to (num_subevents - 1) +uint8_t subevents[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; int result = ble_gap_periodic_adv_sync_subev( event->periodic_sync.sync_handle, 0, sizeof(subevents), subevents); ``` -The subevents sync selection depends on the subevent number of the Periodic Advertising device. +The subevents sync selection depends on the subevent number of the Periodic Advertising device. In this example, all 10 subevents are synced. ## Sending Response Data @@ -187,9 +195,22 @@ case BLE_GAP_EVENT_PERIODIC_REPORT: // create a special data for checking manually in ADV side sub_data_pattern[0] = event->periodic_report.subevent; - rc = ble_hs_id_copy_addr(BLE_ADDR_PUBLIC, device_addr, NULL); + uint8_t addr_type; + rc = ble_hs_id_infer_auto(0, &addr_type); + if (rc != 0) { + ESP_LOGE(TAG, "Failed to infer address type; rc=%d", rc); + os_mbuf_free_chain(data); + return 0; + } + rc = ble_hs_id_copy_addr(addr_type, device_addr, NULL); + if (rc != 0) { + ESP_LOGE(TAG, "Failed to copy address; rc=%d", rc); + os_mbuf_free_chain(data); + return 0; + } sub_data_pattern[1] = param.response_slot; memcpy(&sub_data_pattern[2],device_addr,BLE_DEV_ADDR_LEN); + sub_data_pattern[8] = addr_type; os_mbuf_append(data, sub_data_pattern, BLE_PAWR_RSP_DATA_LEN); @@ -197,7 +218,9 @@ case BLE_GAP_EVENT_PERIODIC_REPORT: ``` - os_msys_get_pkthdr: Allocates memory for the response. -- Payload layout: [subevent, 6-byte address, slot index]. +- The device address is obtained using `ble_hs_id_infer_auto()` to dynamically determine the correct address type, followed by `ble_hs_id_copy_addr()` to copy the address. Return values are checked to prevent sending uninitialized data. This ensures compatibility across all ESP32 variants. + +- Payload layout: [subevent, slot index, 6-byte address, address type]. - ble_gap_periodic_adv_set_response_data: Transmits response in the next slot. @@ -237,6 +260,26 @@ gap_event_cb() covers: - CONNECT/DISCONNECT → Handle connection lifecycle. +## Sync Callback + +When the BLE host and controller are synced, the `on_sync` callback is invoked. It ensures a valid identity address is set before starting the scan: + +```c +static void +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); + + start_scan(); +} +``` + +- `ble_hs_util_ensure_addr(0)`: Ensures the device has a valid identity address configured (prefers public address). + ## Host Task ```c void pawr_host_task(void *param) { diff --git a/examples/bluetooth/nimble/ble_periodic_adv/README.md b/examples/bluetooth/nimble/ble_periodic_adv/README.md index 2841fb0569..e5f88e084d 100644 --- a/examples/bluetooth/nimble/ble_periodic_adv/README.md +++ b/examples/bluetooth/nimble/ble_periodic_adv/README.md @@ -28,18 +28,6 @@ Before project configuration and build, be sure to set the correct chip target u idf.py set-target ``` -### Configure the project - -Open the project configuration menu: - -```bash -idf.py menuconfig -``` - -In the `Example Configuration` menu: - -* Select I/O capabilities of device from `Example Configuration --> I/O Capability`, default is `Just_works`. - ### Build and Flash Run `idf.py -p PORT flash monitor` to build, flash and monitor the project. @@ -64,7 +52,7 @@ I (373) NimBLE: Device Address: I (373) NimBLE: d0:42:3a:95:84:05 I (373) NimBLE: -I (383) NimBLE: instance 1 started (periodic) +I (383) NimBLE: instance 0 started (periodic) ``` ## Note diff --git a/examples/bluetooth/nimble/ble_periodic_adv/main/Kconfig.projbuild b/examples/bluetooth/nimble/ble_periodic_adv/main/Kconfig.projbuild index fd4037d16a..b996e2fb90 100644 --- a/examples/bluetooth/nimble/ble_periodic_adv/main/Kconfig.projbuild +++ b/examples/bluetooth/nimble/ble_periodic_adv/main/Kconfig.projbuild @@ -1,16 +1,5 @@ menu "Example Configuration" - config EXAMPLE_EXTENDED_ADV - bool - depends on SOC_BLE_50_SUPPORTED && BT_NIMBLE_50_FEATURE_SUPPORT - default y if SOC_ESP_NIMBLE_CONTROLLER - select BT_NIMBLE_EXT_ADV - prompt "Enable Extended Adv" - help - Use this option to enable extended advertising in the example. - If you disable this option, ensure config BT_NIMBLE_EXT_ADV is - also disabled from Nimble stack menuconfig. - config EXAMPLE_RANDOM_ADDR bool prompt "Advertise RANDOM Address" diff --git a/examples/bluetooth/nimble/ble_periodic_adv/main/main.c b/examples/bluetooth/nimble/ble_periodic_adv/main/main.c index 3bc2a19ff9..83c2e67f84 100644 --- a/examples/bluetooth/nimble/ble_periodic_adv/main/main.c +++ b/examples/bluetooth/nimble/ble_periodic_adv/main/main.c @@ -1,80 +1,71 @@ /* - * SPDX-FileCopyrightText: 2021-2023 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2021-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Unlicense OR CC0-1.0 */ +#include +#include #include "esp_log.h" #include "nvs_flash.h" /* BLE */ #include "nimble/nimble_port.h" #include "nimble/nimble_port_freertos.h" #include "host/ble_hs.h" -#include "host/util/util.h" -#include "console/console.h" -#include "services/gap/ble_svc_gap.h" -#include "periodic_adv.h" #include "host/ble_gap.h" #include "host/ble_hs_adv.h" -#include "patterns.h" +#include "host/util/util.h" +#include "services/gap/ble_svc_gap.h" +#include "modlog/modlog.h" +#include "esp_peripheral.h" -#if CONFIG_EXAMPLE_EXTENDED_ADV static uint8_t periodic_adv_raw_data[] = {'E', 'S', 'P', '_', 'P', 'E', 'R', 'I', 'O', 'D', 'I', 'C', '_', 'A', 'D', 'V'}; -#endif static const char *tag = "NimBLE_BLE_PERIODIC_ADV"; -#if CONFIG_EXAMPLE_RANDOM_ADDR -static uint8_t own_addr_type = BLE_OWN_ADDR_RANDOM; -#else -static uint8_t own_addr_type; -#endif void ble_store_config_init(void); -#if CONFIG_EXAMPLE_EXTENDED_ADV /** - * Enables advertising with the following parameters: - * o General discoverable mode. - * o Undirected connectable mode. + * Configures and starts periodic advertising with extended + * non-connectable advertising on instance 0. */ static void -start_periodic_adv(void) +start_periodic_adv(uint8_t own_addr_type) { int rc; struct ble_gap_periodic_adv_params pparams; struct ble_gap_ext_adv_params params; struct ble_hs_adv_fields adv_fields; struct os_mbuf *data; - uint8_t instance = 1; + uint8_t instance = 0; ble_addr_t addr; + uint8_t addr_type; #if MYNEWT_VAL(BLE_PERIODIC_ADV_ENH) - struct ble_gap_periodic_adv_enable_params eparams; + struct ble_gap_periodic_adv_start_params eparams; memset(&eparams, 0, sizeof(eparams)); #endif - /* set random (NRPA) address for instance */ - rc = ble_hs_id_gen_rnd(1, &addr); - assert (rc == 0); - - MODLOG_DFLT(INFO, "Device Address: "); - print_addr(addr.val); - MODLOG_DFLT(INFO, "\n"); - /* For periodic we use instance with non-connectable advertising */ memset (¶ms, 0, sizeof(params)); - /* advertise using random addr */ - params.own_addr_type = BLE_OWN_ADDR_RANDOM; + params.own_addr_type = own_addr_type; params.primary_phy = BLE_HCI_LE_PHY_1M; params.secondary_phy = BLE_HCI_LE_PHY_2M; + /* Use controller-selected default TX power. */ + params.tx_power = 0x7f; params.sid = 2; - /* configure instance 1 */ + /* configure instance 0 */ rc = ble_gap_ext_adv_configure(instance, ¶ms, NULL, NULL, NULL); assert (rc == 0); - rc = ble_gap_ext_adv_set_addr(instance, &addr ); - assert (rc == 0); + addr_type = params.own_addr_type == BLE_OWN_ADDR_RANDOM ? BLE_ADDR_RANDOM : BLE_ADDR_PUBLIC; + rc = ble_hs_id_copy_addr(addr_type, addr.val, NULL); + assert(rc == 0); + + MODLOG_DFLT(INFO, "Device Address: "); + print_addr(addr.val); + MODLOG_DFLT(INFO, "\n"); memset(&adv_fields, 0, sizeof(adv_fields)); adv_fields.name = (const uint8_t *)"Periodic ADV"; @@ -82,7 +73,7 @@ start_periodic_adv(void) /* Default to legacy PDUs size, mbuf chain will be increased if needed */ - data = os_msys_get_pkthdr(BLE_HCI_MAX_ADV_DATA_LEN, 0); + data = os_msys_get_pkthdr(BLE_HS_ADV_MAX_FIELD_SZ, 0); assert(data); rc = ble_hs_adv_set_fields_mbuf(&adv_fields, data); @@ -129,7 +120,6 @@ start_periodic_adv(void) MODLOG_DFLT(INFO, "instance %u started (periodic)\n", instance); } -#endif static void periodic_adv_on_reset(int reason) @@ -137,50 +127,24 @@ periodic_adv_on_reset(int reason) MODLOG_DFLT(ERROR, "Resetting state; reason=%d\n", reason); } -#if CONFIG_EXAMPLE_RANDOM_ADDR -static void -periodic_adv_set_addr(void) -{ - ble_addr_t addr; - int rc; - - /* generate new non-resolvable private address */ - rc = ble_hs_id_gen_rnd(0, &addr); - assert(rc == 0); - - /* set generated address */ - rc = ble_hs_id_set_rnd(addr.val); - - assert(rc == 0); -} -#endif - static void periodic_adv_on_sync(void) { int rc; + uint8_t own_addr_type; #if CONFIG_EXAMPLE_RANDOM_ADDR - /* Generate a non-resolvable private address. */ - periodic_adv_set_addr(); - /* Make sure we have proper identity address set (public preferred) */ rc = ble_hs_util_ensure_addr(1); #else rc = ble_hs_util_ensure_addr(0); #endif assert(rc == 0); - /* Figure out address to use while advertising (no privacy for now) */ rc = ble_hs_id_infer_auto(0, &own_addr_type); - if (rc != 0) { - MODLOG_DFLT(ERROR, "error determining address type; rc=%d\n", rc); - return; - } + assert(rc == 0); /* Begin advertising. */ -#if CONFIG_EXAMPLE_EXTENDED_ADV - start_periodic_adv(); -#endif + start_periodic_adv(own_addr_type); } void periodic_adv_host_task(void *param) @@ -195,7 +159,6 @@ void periodic_adv_host_task(void *param) void app_main(void) { - int rc; /* Initialize NVS — it is used to store PHY calibration data */ esp_err_t ret = nvs_flash_init(); if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) { @@ -214,9 +177,11 @@ app_main(void) ble_hs_cfg.reset_cb = periodic_adv_on_reset; ble_hs_cfg.sync_cb = periodic_adv_on_sync; ble_hs_cfg.store_status_cb = ble_store_util_status_rr; +#if CONFIG_BT_NIMBLE_GAP_SERVICE /* Set the default device name. */ - rc = ble_svc_gap_device_name_set("nimble_periodic_adv"); + int rc = ble_svc_gap_device_name_set("nimble_periodic_adv"); assert(rc == 0); +#endif /* XXX Need to have template for store */ ble_store_config_init(); diff --git a/examples/bluetooth/nimble/ble_periodic_adv/main/patterns.h b/examples/bluetooth/nimble/ble_periodic_adv/main/patterns.h deleted file mode 100644 index 94b85f72c6..0000000000 --- a/examples/bluetooth/nimble/ble_periodic_adv/main/patterns.h +++ /dev/null @@ -1,173 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD - * - * SPDX-License-Identifier: Unlicense OR CC0-1.0 - */ - -static const uint8_t ext_adv_pattern_1[] = { - 0x00, 0x02, 0x00, 0x04, 0x00, 0x06, 0x00, 0x08, 0x00, 0x0a, - 0x00, 0x0c, 0x00, 0x0e, 0x00, 0x10, 0x00, 0x12, 0x00, 0x14, - 0x00, 0x16, 0x00, 0x18, 0x00, 0x1a, 0x00, 0x1c, 0x00, 0x1e, - 0x00, 0x20, 0x00, 0x22, 0x00, 0x24, 0x00, 0x26, 0x00, 0x28, - 0x00, 0x2a, 0x00, 0x2c, 0x00, 0x2e, 0x00, 0x30, 0x00, 0x32, - 0x00, 0x34, 0x00, 0x36, 0x00, 0x38, 0x00, 0x3a, 0x00, 0x3c, - 0x00, 0x3e, 0x00, 0x40, 0x00, 0x42, 0x00, 0x44, 0x00, 0x46, - 0x00, 0x48, 0x00, 0x4a, 0x00, 0x4c, 0x00, 0x4e, 0x00, 0x50, - 0x00, 0x52, 0x00, 0x54, 0x00, 0x56, 0x00, 0x58, 0x00, 0x5a, - 0x00, 0x5c, 0x00, 0x5e, 0x00, 0x60, 0x00, 0x62, 0x00, 0x64, - 0x00, 0x66, 0x00, 0x68, 0x00, 0x6a, 0x00, 0x6c, 0x00, 0x6e, - 0x00, 0x70, 0x00, 0x72, 0x00, 0x74, 0x00, 0x76, 0x00, 0x78, - 0x00, 0x7a, 0x00, 0x7c, 0x00, 0x7e, 0x00, 0x80, 0x00, 0x82, - 0x00, 0x84, 0x00, 0x86, 0x00, 0x88, 0x00, 0x8a, 0x00, 0x8c, - 0x00, 0x8e, 0x00, 0x90, 0x00, 0x92, 0x00, 0x94, 0x00, 0x96, - 0x00, 0x98, 0x00, 0x9a, 0x00, 0x9c, 0x00, 0x9e, 0x00, 0xa0, - 0x00, 0xa2, 0x00, 0xa4, 0x00, 0xa6, 0x00, 0xa8, 0x00, 0xaa, - 0x00, 0xac, 0x00, 0xae, 0x00, 0xb0, 0x00, 0xb2, 0x00, 0xb4, - 0x00, 0xb6, 0x00, 0xb8, 0x00, 0xba, 0x00, 0xbc, 0x00, 0xbe, - 0x00, 0xc0, 0x00, 0xc2, 0x00, 0xc4, 0x00, 0xc6, 0x00, 0xc8, - 0x00, 0xca, 0x00, 0xcc, 0x00, 0xce, 0x00, 0xd0, 0x00, 0xd2, - 0x00, 0xd4, 0x00, 0xd6, 0x00, 0xd8, 0x00, 0xda, 0x00, 0xdc, - 0x00, 0xde, 0x00, 0xe0, 0x00, 0xe2, 0x00, 0xe4, 0x00, 0xe6, - 0x00, 0xe8, 0x00, 0xea, 0x00, 0xec, 0x00, 0xee, 0x00, 0xf0, - 0x00, 0xf2, 0x00, 0xf4, 0x00, 0xf6, 0x00, 0xf8, 0x00, 0xfa, - 0x00, 0xfc, 0x00, 0xfe, 0x01, 0x01, 0x01, 0x03, 0x01, 0x05, - 0x01, 0x07, 0x01, 0x09, 0x01, 0x0b, 0x01, 0x0d, 0x01, 0x0f, - 0x01, 0x11, 0x01, 0x13, 0x01, 0x15, 0x01, 0x17, 0x01, 0x19, - 0x01, 0x1b, 0x01, 0x1d, 0x01, 0x1f, 0x01, 0x21, 0x01, 0x23, - 0x01, 0x25, 0x01, 0x27, 0x01, 0x29, 0x01, 0x2b, 0x01, 0x2d, - 0x01, 0x2f, 0x01, 0x31, 0x01, 0x33, 0x01, 0x35, 0x01, 0x37, - 0x01, 0x39, 0x01, 0x3b, 0x01, 0x3d, 0x01, 0x3f, 0x01, 0x41, - 0x01, 0x43, 0x01, 0x45, 0x01, 0x47, 0x01, 0x49, 0x01, 0x4b, - 0x01, 0x4d, 0x01, 0x4f, 0x01, 0x51, 0x01, 0x53, 0x01, 0x55, - 0x01, 0x57, 0x01, 0x59, 0x01, 0x5b, 0x01, 0x5d, 0x01, 0x5f, - 0x01, 0x61, 0x01, 0x63, 0x01, 0x65, 0x01, 0x67, 0x01, 0x69, - 0x01, 0x6b, 0x01, 0x6d, 0x01, 0x6f, 0x01, 0x71, 0x01, 0x73, - 0x01, 0x75, 0x01, 0x77, 0x01, 0x79, 0x01, 0x7b, 0x01, 0x7d, - 0x01, 0x7f, 0x01, 0x81, 0x01, 0x83, 0x01, 0x85, 0x01, 0x87, - 0x01, 0x89, 0x01, 0x8b, 0x01, 0x8d, 0x01, 0x8f, 0x01, 0x91, - 0x01, 0x93, 0x01, 0x95, 0x01, 0x97, 0x01, 0x99, 0x01, 0x9b, - 0x01, 0x9d, 0x01, 0x9f, 0x01, 0xa1, 0x01, 0xa3, 0x01, 0xa5, - 0x01, 0xa7, 0x01, 0xa9, 0x01, 0xab, 0x01, 0xad, 0x01, 0xaf, - 0x01, 0xb1, 0x01, 0xb3, 0x01, 0xb5, 0x01, 0xb7, 0x01, 0xb9, - 0x01, 0xbb, 0x01, 0xbd, 0x01, 0xbf, 0x01, 0xc1, 0x01, 0xc3, - 0x01, 0xc5, 0x01, 0xc7, 0x01, 0xc9, 0x01, 0xcb, 0x01, 0xcd, - 0x01, 0xcf, 0x01, 0xd1, 0x01, 0xd3, 0x01, 0xd5, 0x01, 0xd7, - 0x01, 0xd9, 0x01, 0xdb, 0x01, 0xdd, 0x01, 0xdf, 0x01, 0xe1, - 0x01, 0xe3, 0x01, 0xe5, 0x01, 0xe7, 0x01, 0xe9, 0x01, 0xeb, - 0x01, 0xed, 0x01, 0xef, 0x01, 0xf1, 0x01, 0xf3, 0x01, 0xf5, - 0x01, 0xf7, 0x01, 0xf9, 0x01, 0xfb, 0x01, 0xfd, 0x02, 0x00, - 0x02, 0x02, 0x02, 0x04, 0x02, 0x06, 0x02, 0x08, 0x02, 0x0a, - 0x02, 0x0c, 0x02, 0x0e, 0x02, 0x10, 0x02, 0x12, 0x02, 0x14, - 0x02, 0x16, 0x02, 0x18, 0x02, 0x1a, 0x02, 0x1c, 0x02, 0x1e, - 0x02, 0x20, 0x02, 0x22, 0x02, 0x24, 0x02, 0x26, 0x02, 0x28, - 0x02, 0x2a, 0x02, 0x2c, 0x02, 0x2e, 0x02, 0x30, 0x02, 0x32, - 0x02, 0x34, 0x02, 0x36, 0x02, 0x38, 0x02, 0x3a, 0x02, 0x3c, - 0x02, 0x3e, 0x02, 0x40, 0x02, 0x42, 0x02, 0x44, 0x02, 0x46, - 0x02, 0x48, 0x02, 0x4a, 0x02, 0x4c, 0x02, 0x4e, 0x02, 0x50, - 0x02, 0x52, 0x02, 0x54, 0x02, 0x56, 0x02, 0x58, 0x02, 0x5a, - 0x02, 0x5c, 0x02, 0x5e, 0x02, 0x60, 0x02, 0x62, 0x02, 0x64, - 0x02, 0x66, 0x02, 0x68, 0x02, 0x6a, 0x02, 0x6c, 0x02, 0x6e, - 0x02, 0x70, 0x02, 0x72, 0x02, 0x74, 0x02, 0x76, 0x02, 0x78, - 0x02, 0x7a, 0x02, 0x7c, 0x02, 0x7e, 0x02, 0x80, 0x02, 0x82, - 0x02, 0x84, 0x02, 0x86, 0x02, 0x88, 0x02, 0x8a, 0x02, 0x8c, - 0x02, 0x8e, 0x02, 0x90, 0x02, 0x92, 0x02, 0x94, 0x02, 0x96, - 0x02, 0x98, 0x02, 0x9a, 0x02, 0x9c, 0x02, 0x9e, 0x02, 0xa0, - 0x02, 0xa2, 0x02, 0xa4, 0x02, 0xa6, 0x02, 0xa8, 0x02, 0xaa, - 0x02, 0xac, 0x02, 0xae, 0x02, 0xb0, 0x02, 0xb2, 0x02, 0xb4, - 0x02, 0xb6, 0x02, 0xb8, 0x02, 0xba, 0x02, 0xbc, 0x02, 0xbe, - 0x02, 0xc0, 0x02, 0xc2, 0x02, 0xc4, 0x02, 0xc6, 0x02, 0xc8, - 0x02, 0xca, 0x02, 0xcc, 0x02, 0xce, 0x02, 0xd0, 0x02, 0xd2, - 0x02, 0xd4, 0x02, 0xd6, 0x02, 0xd8, 0x02, 0xda, 0x02, 0xdc, - 0x02, 0xde, 0x02, 0xe0, 0x02, 0xe2, 0x02, 0xe4, 0x02, 0xe6, - 0x02, 0xe8, 0x02, 0xea, 0x02, 0xec, 0x02, 0xee, 0x02, 0xf0, - 0x02, 0xf2, 0x02, 0xf4, 0x02, 0xf6, 0x02, 0xf8, 0x02, 0xfa, - 0x02, 0xfc, 0x02, 0xfe, 0x03, 0x01, 0x03, 0x03, 0x03, 0x05, - 0x03, 0x07, 0x03, 0x09, 0x03, 0x0b, 0x03, 0x0d, 0x03, 0x0f, - 0x03, 0x11, 0x03, 0x13, 0x03, 0x15, 0x03, 0x17, 0x03, 0x19, - 0x03, 0x1b, 0x03, 0x1d, 0x03, 0x1f, 0x03, 0x21, 0x03, 0x23, - 0x03, 0x25, 0x03, 0x27, 0x03, 0x29, 0x03, 0x2b, 0x03, 0x2d, - 0x03, 0x2f, 0x03, 0x31, 0x03, 0x33, 0x03, 0x35, 0x03, 0x37, - 0x03, 0x39, 0x03, 0x3b, 0x03, 0x3d, 0x03, 0x3f, 0x03, 0x41, - 0x03, 0x43, 0x03, 0x45, 0x03, 0x47, 0x03, 0x49, 0x03, 0x4b, - 0x03, 0x4d, 0x03, 0x4f, 0x03, 0x51, 0x03, 0x53, 0x03, 0x55, - 0x03, 0x57, 0x03, 0x59, 0x03, 0x5b, 0x03, 0x5d, 0x03, 0x5f, - 0x03, 0x61, 0x03, 0x63, 0x03, 0x65, 0x03, 0x67, 0x03, 0x69, - 0x03, 0x6b, 0x03, 0x6d, 0x03, 0x6f, 0x03, 0x71, 0x03, 0x73, - 0x03, 0x75, 0x03, 0x77, 0x03, 0x79, 0x03, 0x7b, 0x03, 0x7d, - 0x03, 0x7f, 0x03, 0x81, 0x03, 0x83, 0x03, 0x85, 0x03, 0x87, - 0x03, 0x89, 0x03, 0x8b, 0x03, 0x8d, 0x03, 0x8f, 0x03, 0x91, - 0x03, 0x93, 0x03, 0x95, 0x03, 0x97, 0x03, 0x99, 0x03, 0x9b, - 0x03, 0x9d, 0x03, 0x9f, 0x03, 0xa1, 0x03, 0xa3, 0x03, 0xa5, - 0x03, 0xa7, 0x03, 0xa9, 0x03, 0xab, 0x03, 0xad, 0x03, 0xaf, - 0x03, 0xb1, 0x03, 0xb3, 0x03, 0xb5, 0x03, 0xb7, 0x03, 0xb9, - 0x03, 0xbb, 0x03, 0xbd, 0x03, 0xbf, 0x03, 0xc1, 0x03, 0xc3, - 0x03, 0xc5, 0x03, 0xc7, 0x03, 0xc9, 0x03, 0xcb, 0x03, 0xcd, - 0x03, 0xcf, 0x03, 0xd1, 0x03, 0xd3, 0x03, 0xd5, 0x03, 0xd7, - 0x03, 0xd9, 0x03, 0xdb, 0x03, 0xdd, 0x03, 0xdf, 0x03, 0xe1, - 0x03, 0xe3, 0x03, 0xe5, 0x03, 0xe7, 0x03, 0xe9, 0x03, 0xeb, - 0x03, 0xed, 0x03, 0xef, 0x03, 0xf1, 0x03, 0xf3, 0x03, 0xf5, - 0x03, 0xf7, 0x03, 0xf9, 0x03, 0xfb, 0x03, 0xfd, 0x04, 0x00, - 0x04, 0x02, 0x04, 0x04, 0x04, 0x06, 0x04, 0x08, 0x04, 0x0a, - 0x04, 0x0c, 0x04, 0x0e, 0x04, 0x10, 0x04, 0x12, 0x04, 0x14, - 0x04, 0x16, 0x04, 0x18, 0x04, 0x1a, 0x04, 0x1c, 0x04, 0x1e, - 0x04, 0x20, 0x04, 0x22, 0x04, 0x24, 0x04, 0x26, 0x04, 0x28, - 0x04, 0x2a, 0x04, 0x2c, 0x04, 0x2e, 0x04, 0x30, 0x04, 0x32, - 0x04, 0x34, 0x04, 0x36, 0x04, 0x38, 0x04, 0x3a, 0x04, 0x3c, - 0x04, 0x3e, 0x04, 0x40, 0x04, 0x42, 0x04, 0x44, 0x04, 0x46, - 0x04, 0x48, 0x04, 0x4a, 0x04, 0x4c, 0x04, 0x4e, 0x04, 0x50, - 0x04, 0x52, 0x04, 0x54, 0x04, 0x56, 0x04, 0x58, 0x04, 0x5a, - 0x04, 0x5c, 0x04, 0x5e, 0x04, 0x60, 0x04, 0x62, 0x04, 0x64, - 0x04, 0x66, 0x04, 0x68, 0x04, 0x6a, 0x04, 0x6c, 0x04, 0x6e, - 0x04, 0x70, 0x04, 0x72, 0x04, 0x74, 0x04, 0x76, 0x04, 0x78, - 0x04, 0x7a, 0x04, 0x7c, 0x04, 0x7e, 0x04, 0x80, 0x04, 0x82, - 0x04, 0x84, 0x04, 0x86, 0x04, 0x88, 0x04, 0x8a, 0x04, 0x8c, - 0x04, 0x8e, 0x04, 0x90, 0x04, 0x92, 0x04, 0x94, 0x04, 0x96, - 0x04, 0x98, 0x04, 0x9a, 0x04, 0x9c, 0x04, 0x9e, 0x04, 0xa0, - 0x04, 0xa2, 0x04, 0xa4, 0x04, 0xa6, 0x04, 0xa8, 0x04, 0xaa, - 0x04, 0xac, 0x04, 0xae, 0x04, 0xb0, 0x04, 0xb2, 0x04, 0xb4, - 0x04, 0xb6, 0x04, 0xb8, 0x04, 0xba, 0x04, 0xbc, 0x04, 0xbe, - 0x04, 0xc0, 0x04, 0xc2, 0x04, 0xc4, 0x04, 0xc6, 0x04, 0xc8, - 0x04, 0xca, 0x04, 0xcc, 0x04, 0xce, 0x04, 0xd0, 0x04, 0xd2, - 0x04, 0xd4, 0x04, 0xd6, 0x04, 0xd8, 0x04, 0xda, 0x04, 0xdc, - 0x04, 0xde, 0x04, 0xe0, 0x04, 0xe2, 0x04, 0xe4, 0x04, 0xe6, - 0x04, 0xe8, 0x04, 0xea, 0x04, 0xec, 0x04, 0xee, 0x04, 0xf0, - 0x04, 0xf2, 0x04, 0xf4, 0x04, 0xf6, 0x04, 0xf8, 0x04, 0xfa, - 0x04, 0xfc, 0x04, 0xfe, 0x05, 0x01, 0x05, 0x03, 0x05, 0x05, - 0x05, 0x07, 0x05, 0x09, 0x05, 0x0b, 0x05, 0x0d, 0x05, 0x0f, - 0x05, 0x11, 0x05, 0x13, 0x05, 0x15, 0x05, 0x17, 0x05, 0x19, - 0x05, 0x1b, 0x05, 0x1d, 0x05, 0x1f, 0x05, 0x21, 0x05, 0x23, - 0x05, 0x25, 0x05, 0x27, 0x05, 0x29, 0x05, 0x2b, 0x05, 0x2d, - 0x05, 0x2f, 0x05, 0x31, 0x05, 0x33, 0x05, 0x35, 0x05, 0x37, - 0x05, 0x39, 0x05, 0x3b, 0x05, 0x3d, 0x05, 0x3f, 0x05, 0x41, - 0x05, 0x43, 0x05, 0x45, 0x05, 0x47, 0x05, 0x49, 0x05, 0x4b, - 0x05, 0x4d, 0x05, 0x4f, 0x05, 0x51, 0x05, 0x53, 0x05, 0x55, - 0x05, 0x57, 0x05, 0x59, 0x05, 0x5b, 0x05, 0x5d, 0x05, 0x5f, - 0x05, 0x61, 0x05, 0x63, 0x05, 0x65, 0x05, 0x67, 0x05, 0x69, - 0x05, 0x6b, 0x05, 0x6d, 0x05, 0x6f, 0x05, 0x71, 0x05, 0x73, - 0x05, 0x75, 0x05, 0x77, 0x05, 0x79, 0x05, 0x7b, 0x05, 0x7d, - 0x05, 0x7f, 0x05, 0x81, 0x05, 0x83, 0x05, 0x85, 0x05, 0x87, - 0x05, 0x89, 0x05, 0x8b, 0x05, 0x8d, 0x05, 0x8f, 0x05, 0x91, - 0x05, 0x93, 0x05, 0x95, 0x05, 0x97, 0x05, 0x99, 0x05, 0x9b, - 0x05, 0x9d, 0x05, 0x9f, 0x05, 0xa1, 0x05, 0xa3, 0x05, 0xa5, - 0x05, 0xa7, 0x05, 0xa9, 0x05, 0xab, 0x05, 0xad, 0x05, 0xaf, - 0x05, 0xb1, 0x05, 0xb3, 0x05, 0xb5, 0x05, 0xb7, 0x05, 0xb9, - 0x05, 0xbb, 0x05, 0xbd, 0x05, 0xbf, 0x05, 0xc1, 0x05, 0xc3, - 0x05, 0xc5, 0x05, 0xc7, 0x05, 0xc9, 0x05, 0xcb, 0x05, 0xcd, - 0x05, 0xcf, 0x05, 0xd1, 0x05, 0xd3, 0x05, 0xd5, 0x05, 0xd7, - 0x05, 0xd9, 0x05, 0xdb, 0x05, 0xdd, 0x05, 0xdf, 0x05, 0xe1, - 0x05, 0xe3, 0x05, 0xe5, 0x05, 0xe7, 0x05, 0xe9, 0x05, 0xeb, - 0x05, 0xed, 0x05, 0xef, 0x05, 0xf1, 0x05, 0xf3, 0x05, 0xf5, - 0x05, 0xf7, 0x05, 0xf9, 0x05, 0xfb, 0x05, 0xfd, 0x06, 0x00, - 0x06, 0x02, 0x06, 0x04, 0x06, 0x06, 0x06, 0x08, 0x06, 0x0a, - 0x06, 0x0c, 0x06, 0x0e, 0x06, 0x10, 0x06, 0x12, 0x06, 0x14, - 0x06, 0x16, 0x06, 0x18, 0x06, 0x1a, 0x06, 0x1c, 0x06, 0x1e, - 0x06, 0x20, 0x06, 0x22, 0x06, 0x24, 0x06, 0x26, 0x06, 0x28, - 0x06, 0x2a, 0x06, 0x2c, 0x06, 0x2e, 0x06, 0x30, 0x06, 0x32, - 0x06, 0x34, 0x06, 0x36, 0x06, 0x38, 0x06, 0x3a, 0x06, 0x3c, - 0x06, 0x3e, 0x06, 0x40, 0x06, 0x42, 0x06, 0x44, 0x06, 0x46, - 0x06, 0x48, 0x06, 0x4a, 0x06, 0x4c, 0x06, 0x4e, 0x06, 0x50, - 0x06, 0x52, 0x06, 0x54, 0x06, 0x56, 0x06, 0x58, 0x06, 0x5a, - 0x06, 0x5c, 0x06, 0x5e, 0x06, 0x60, 0x06, 0x62, 0x06, 0x64, - 0x06, 0x66, 0x06, 0x68, 0x06, 0x6a, 0x06, 0x6c, 0x06, 0x6e, - 0x06, 0x70, 0x06, 0x72, 0x06, 0x74, 0x06, 0x76, 0x06, 0x78 -}; diff --git a/examples/bluetooth/nimble/ble_periodic_adv/main/periodic_adv.h b/examples/bluetooth/nimble/ble_periodic_adv/main/periodic_adv.h deleted file mode 100644 index bb1bdad298..0000000000 --- a/examples/bluetooth/nimble/ble_periodic_adv/main/periodic_adv.h +++ /dev/null @@ -1,25 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD - * - * SPDX-License-Identifier: Unlicense OR CC0-1.0 - */ - -#ifndef H_BLE_PERIODIC_ADV_ -#define H_BLE_PERIODIC_ADV_ - -#include -#include "nimble/ble.h" -#include "modlog/modlog.h" -#include "esp_peripheral.h" -#ifdef __cplusplus -extern "C" { -#endif - -struct ble_hs_cfg; -struct ble_gatt_register_ctxt; - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/examples/bluetooth/nimble/ble_periodic_adv/sdkconfig.defaults b/examples/bluetooth/nimble/ble_periodic_adv/sdkconfig.defaults index 60f9e68480..cd48c8bec7 100644 --- a/examples/bluetooth/nimble/ble_periodic_adv/sdkconfig.defaults +++ b/examples/bluetooth/nimble/ble_periodic_adv/sdkconfig.defaults @@ -10,6 +10,5 @@ CONFIG_BTDM_CTRL_MODE_BR_EDR_ONLY=n CONFIG_BTDM_CTRL_MODE_BTDM=n CONFIG_BT_BLUEDROID_ENABLED=n CONFIG_BT_NIMBLE_ENABLED=y -CONFIG_EXAMPLE_EXTENDED_ADV=y CONFIG_BT_NIMBLE_EXT_ADV=y CONFIG_BT_NIMBLE_MAX_PERIODIC_SYNCS=1 diff --git a/examples/bluetooth/nimble/ble_periodic_adv/sdkconfig.defaults.dram_optimised b/examples/bluetooth/nimble/ble_periodic_adv/sdkconfig.defaults.dram_optimised index b42d28d378..b6342b0637 100644 --- a/examples/bluetooth/nimble/ble_periodic_adv/sdkconfig.defaults.dram_optimised +++ b/examples/bluetooth/nimble/ble_periodic_adv/sdkconfig.defaults.dram_optimised @@ -10,7 +10,6 @@ CONFIG_BTDM_CTRL_MODE_BR_EDR_ONLY=n CONFIG_BTDM_CTRL_MODE_BTDM=n CONFIG_BT_BLUEDROID_ENABLED=n CONFIG_BT_NIMBLE_ENABLED=y -CONFIG_EXAMPLE_EXTENDED_ADV=y CONFIG_BT_NIMBLE_EXT_ADV=y CONFIG_BT_NIMBLE_MAX_PERIODIC_SYNCS=1 diff --git a/examples/bluetooth/nimble/ble_periodic_adv/sdkconfig.defaults.mini b/examples/bluetooth/nimble/ble_periodic_adv/sdkconfig.defaults.mini index 4c9ca092ce..c795bebb8b 100644 --- a/examples/bluetooth/nimble/ble_periodic_adv/sdkconfig.defaults.mini +++ b/examples/bluetooth/nimble/ble_periodic_adv/sdkconfig.defaults.mini @@ -10,7 +10,6 @@ CONFIG_BTDM_CTRL_MODE_BR_EDR_ONLY=n CONFIG_BTDM_CTRL_MODE_BTDM=n CONFIG_BT_BLUEDROID_ENABLED=n CONFIG_BT_NIMBLE_ENABLED=y -CONFIG_EXAMPLE_EXTENDED_ADV=y CONFIG_BT_NIMBLE_EXT_ADV=y CONFIG_BT_NIMBLE_MAX_PERIODIC_SYNCS=1 CONFIG_BT_NIMBLE_MAX_CONNECTIONS=1 diff --git a/examples/bluetooth/nimble/ble_periodic_adv/tutorial/BLE_periodic_adv_walkthrough.md b/examples/bluetooth/nimble/ble_periodic_adv/tutorial/BLE_periodic_adv_walkthrough.md index 0ec674fb48..9e275b16f8 100644 --- a/examples/bluetooth/nimble/ble_periodic_adv/tutorial/BLE_periodic_adv_walkthrough.md +++ b/examples/bluetooth/nimble/ble_periodic_adv/tutorial/BLE_periodic_adv_walkthrough.md @@ -2,43 +2,46 @@ ## Introduction -In this tutorial, the ble_periodic_adv example code for the espressif chipsets with BLE5.0 support is reviewed. This example aims at understanding periodic advertisements and related NimBLE APIs.This code implements the periodic advertisement functionality along with extended advertisement by generating a non-resolvable private address. +In this tutorial, the ble_periodic_adv example code for the Espressif chipsets with BLE 5.0 support is reviewed. This example demonstrates periodic advertising using the NimBLE stack. It configures extended non-connectable advertising on instance 0, sets up periodic advertising parameters, and begins transmitting periodic advertisement data. ## Includes This example is located in the examples folder of the ESP-IDF under the [ble_periodic_adv/main](../main). The [main.c](../main/main.c) file located in the main folder contains all the functionality that we are going to review. The header files contained in [main.c](../main/main.c) are: ```c +#include +#include #include "esp_log.h" #include "nvs_flash.h" /* BLE */ #include "nimble/nimble_port.h" #include "nimble/nimble_port_freertos.h" #include "host/ble_hs.h" -#include "host/util/util.h" -#include "console/console.h" -#include "services/gap/ble_svc_gap.h" -#include "periodic_adv.h" #include "host/ble_gap.h" #include "host/ble_hs_adv.h" -#include "patterns.h" +#include "host/util/util.h" +#include "services/gap/ble_svc_gap.h" +#include "modlog/modlog.h" +#include "esp_peripheral.h" ``` -These `includes` are required for the FreeRTOS and underlying system components to run, including the logging functionality and a library to store data in non-volatile flash memory. We are interested in `“nimble_port.h”`, `“nimble_port_freertos.h”`, `"ble_hs.h"` and `“ble_svc_gap.h”`, `“periodic_adv.h”` which expose the BLE APIs required to implement this example. +These `includes` are required for the FreeRTOS and underlying system components to run, including the logging functionality and a library to store data in non-volatile flash memory. We are interested in `"nimble_port.h"`, `"nimble_port_freertos.h"`, `"ble_hs.h"`, `"ble_gap.h"`, `"ble_hs_adv.h"` and `"ble_svc_gap.h"` which expose the BLE APIs required to implement this example. * `nimble_port.h`: Includes the declaration of functions required for the initialization of the nimble stack. * `nimble_port_freertos.h`: Initializes and enables nimble host task. -* `ble_hs.h`: Defines the functionalities to handle the host event -* `ble_svc_gap.h`:Defines the macros for device name, and device appearance and declares the function to set them. -* `periodic_adv.h`:It includes the code containing forward declarations of 2 structs `ble_hs_cfg` , and `ble_gatt_register_ctxt` based on weather macro `H_BLE_PERIODIC_ADV_` is defined. +* `ble_hs.h`: Defines the functionalities to handle the host event. +* `ble_gap.h`: Defines GAP (Generic Access Profile) related APIs for advertising and scanning. +* `ble_hs_adv.h`: Defines advertisement data field manipulation APIs. +* `ble_svc_gap.h`: Defines the macros for device name, and device appearance and declares the function to set them. +* `modlog/modlog.h`: Provides logging macros used throughout the example. +* `esp_peripheral.h`: Provides the `print_addr` utility for printing BLE addresses. ## Main Entry Point -The program’s entry point is the app_main() function: +The program's entry point is the app_main() function: ```c void app_main(void) { - int rc; /* Initialize NVS — it is used to store PHY calibration data */ esp_err_t ret = nvs_flash_init(); if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) { @@ -57,17 +60,19 @@ app_main(void) ble_hs_cfg.reset_cb = periodic_adv_on_reset; ble_hs_cfg.sync_cb = periodic_adv_on_sync; ble_hs_cfg.store_status_cb = ble_store_util_status_rr; +#if CONFIG_BT_NIMBLE_GAP_SERVICE /* Set the default device name. */ - rc = ble_svc_gap_device_name_set("nimble_periodic_adv"); + int rc = ble_svc_gap_device_name_set("nimble_periodic_adv"); assert(rc == 0); +#endif - /* XXX Need to have a template for store */ + /* XXX Need to have template for store */ ble_store_config_init(); nimble_port_freertos_init(periodic_adv_host_task); } ``` -The main function starts by initializing the non-volatile storage library. This library allows us to save the key-value pairs in flash memory.`nvs_flash_init()` stores the PHY calibration data. In a Bluetooth Low Energy (BLE) device, cryptographic keys used for encryption and authentication are often stored in Non-Volatile Storage (NVS).BLE stores the peer keys, CCCD keys, peer records, etc on NVS. By storing these keys in NVS, the BLE device can quickly retrieve them when needed, without the need for time-consuming key generations. +The main function starts by initializing the non-volatile storage library. This library allows us to save the key-value pairs in flash memory.`nvs_flash_init()` stores the PHY calibration data. In a Bluetooth Low Energy (BLE) device, cryptographic keys used for encryption and authentication are often stored in Non-Volatile Storage (NVS). BLE stores the peer keys, CCCD keys, peer records, etc on NVS. By storing these keys in NVS, the BLE device can quickly retrieve them when needed, without the need for time-consuming key generations. ```c esp_err_t ret = nvs_flash_init(); if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) { @@ -79,60 +84,9 @@ ESP_ERROR_CHECK( ret ); ## BT Controller and Stack Initialization -The main function calls `nimble_port_init()` to initialize BT Controller and nimble stack. This function initializes the BT controller by first creating its configuration structure named `esp_bt_controller_config_t` with default settings generated by the `BT_CONTROLLER_INIT_CONFIG_DEFAULT()` macro. It implements the Host Controller Interface (HCI) on the controller side, the Link Layer (LL), and the Physical Layer (PHY). The BT Controller is invisible to the user applications and deals with the lower layers of the BLE stack. The controller configuration includes setting the BT controller stack size, priority, and HCI baud rate. With the settings created, the BT controller is initialized and enabled with the `esp_bt_controller_init()` and `esp_bt_controller_enable()` functions: +The main function calls `nimble_port_init()` to initialize BT Controller and nimble stack. This function initializes the BT controller by first creating its configuration structure named `esp_bt_controller_config_t` with default settings generated by the `BT_CONTROLLER_INIT_CONFIG_DEFAULT()` macro. It implements the Host Controller Interface (HCI) on the controller side, the Link Layer (LL), and the Physical Layer (PHY). The BT Controller is invisible to the user applications and deals with the lower layers of the BLE stack. The controller configuration includes setting the BT controller stack size, priority, and HCI baud rate. With the settings created, the BT controller is initialized and enabled with the `esp_bt_controller_init()` and `esp_bt_controller_enable()` functions. -```c -esp_bt_controller_config_t config_opts = BT_CONTROLLER_INIT_CONFIG_DEFAULT(); -ret = esp_bt_controller_init(&config_opts); -``` -Next, the controller is enabled in BLE Mode. - -```c -ret = esp_bt_controller_enable(ESP_BT_MODE_BLE); -``` -The controller should be enabled in `ESP_BT_MODE_BLE` if you want to use the BLE mode. - -There are four Bluetooth modes supported: - -1. `ESP_BT_MODE_IDLE`: Bluetooth not running -2. `ESP_BT_MODE_BLE`: BLE mode -3. `ESP_BT_MODE_CLASSIC_BT`: BT Classic mode -4. `ESP_BT_MODE_BTDM`: Dual mode (BLE + BT Classic) - -After the initialization of the BT controller, the nimble stack, which includes the common definitions and APIs for BLE, is initialized by using `esp_nimble_init()`: - -```c -esp_err_t esp_nimble_init(void) -{ - -#if !SOC_ESP_NIMBLE_CONTROLLER - /* Initialize the function pointers for OS porting */ - npl_freertos_funcs_init(); - - npl_freertos_mempool_init(); - - if(esp_nimble_hci_init() != ESP_OK) { - ESP_LOGE(NIMBLE_PORT_LOG_TAG, "hci inits failed\n"); - return ESP_FAIL; - } - - /* Initialize default event queue */ - ble_npl_eventq_init(&g_eventq_dflt); - - os_msys_init(); - - void ble_store_ram_init(void); - /* XXX Need to have a template for store */ - ble_store_ram_init(); -#endif - - /* Initialize the host */ - ble_hs_init(); - return ESP_OK; -} -``` - -The host is configured by setting up the callbacks for Stack-reset, Stack-sync, and Storage status +The host is configured by setting up the callbacks for Stack-reset, Stack-sync, and Storage status: ```c ble_hs_cfg.reset_cb = periodic_adv_on_reset; @@ -140,102 +94,138 @@ ble_hs_cfg.sync_cb = periodic_adv_on_sync; 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. 'blecent_phy' 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. This call is guarded by `CONFIG_BT_NIMBLE_GAP_SERVICE` to handle cases where the GAP service is optionally disabled: ```c +#if CONFIG_BT_NIMBLE_GAP_SERVICE rc = ble_svc_gap_device_name_set("nimble_periodic_adv"); -``` -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. -```c -/* XXX Need to have a template for store */ - ble_store_config_init(); +assert(rc == 0); +#endif ``` -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()`. +The 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. + +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(periodic_adv_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 to handle the default queue, it is easier to create a separate task. -## Generation of non-resolvable private address +## Host Sync Callback - Address Inference -In Bluetooth Low Energy (BLE), a non-resolvable private address is a type of Bluetooth device address that is used for privacy purposes. It is a randomly generated address that changes periodically to prevent long-term tracking of a device. The API call to `ble_hs_id_gen_rnd()` is responsible for generating a non-resolvable private address. NRPA is a 48-bit address that is stored in `addr.val`. +When the NimBLE host and controller are synced, the `periodic_adv_on_sync()` callback is invoked. This function ensures a valid identity address is available and infers the appropriate address type before starting periodic advertising: ```c -#if CONFIG_EXAMPLE_RANDOM_ADDR static void -periodic_adv_set_addr(void) +periodic_adv_on_sync(void) { - ble_addr_t addr; int rc; + uint8_t own_addr_type; - /* generate new non-resolvable private address */ - rc = ble_hs_id_gen_rnd(0, &addr); - assert(rc == 0); - - /* set generated address */ - rc = ble_hs_id_set_rnd(addr.val); - - assert(rc == 0); -} +#if CONFIG_EXAMPLE_RANDOM_ADDR + rc = ble_hs_util_ensure_addr(1); +#else + rc = ble_hs_util_ensure_addr(0); #endif + assert(rc == 0); + + rc = ble_hs_id_infer_auto(0, &own_addr_type); + assert(rc == 0); + + /* Begin advertising. */ + start_periodic_adv(own_addr_type); +} ``` +`ble_hs_util_ensure_addr()` ensures the device has a valid identity address. The argument `1` requests a random address, while `0` requests a public address. `ble_hs_id_infer_auto()` determines the best address type to use (public or random) based on what is available on the device. The inferred `own_addr_type` is passed to `start_periodic_adv()` so that the advertising instance uses the correct address type. + ## Periodic Advertisement -Periodic advertisement start by creating instances of structures `ble_gap_periodic_adv_params`, `ble_gap_ext_adv_params`, `ble_hs_adv_fields`, and `os_mbuf`. Advertising parameters such as connecting modes, advertising intervals, peer address, advertising-filter policy, etc are defined in these structures for periodic and extended advertisements. `pparams` and `params` instances have parameters for periodic advertisement and extended advertisement respectively. -Bluetooth device address is given by the structure ble_aadr_t which contains the fields for address type and address value. +Periodic advertisement starts by creating instances of structures `ble_gap_periodic_adv_params`, `ble_gap_ext_adv_params`, `ble_hs_adv_fields`, and `os_mbuf`. Advertising parameters such as connecting modes, advertising intervals, own address type, PHYs, etc. are defined in these structures for periodic and extended advertisements. -## Need of Extended Advertisement in Periodic Advertisement +### Need of Extended Advertisement in Periodic Advertisement Non-connectable and non-scannable advertising events containing synchronization information about a periodic advertising train are necessary for the scanner device to sync with the periodic advertising train. The periodic advertising will utilize the same physical layer (PHY) as the auxiliary packet, which is part of the extended advertisement. - -Below is the implementation to start periodic advertisement. +Below is the implementation to start periodic advertisement: ```c static void -start_periodic_adv(void) +start_periodic_adv(uint8_t own_addr_type) { int rc; struct ble_gap_periodic_adv_params pparams; struct ble_gap_ext_adv_params params; struct ble_hs_adv_fields adv_fields; struct os_mbuf *data; - uint8_t instance = 1; + uint8_t instance = 0; ble_addr_t addr; + uint8_t addr_type; +#if MYNEWT_VAL(BLE_PERIODIC_ADV_ENH) + struct ble_gap_periodic_adv_start_params eparams; + memset(&eparams, 0, sizeof(eparams)); +#endif - /* set random (NRPA) address for instance */ - rc = ble_hs_id_gen_rnd(1, &addr); + /* For periodic we use instance with non-connectable advertising */ + memset (¶ms, 0, sizeof(params)); + + params.own_addr_type = own_addr_type; + params.primary_phy = BLE_HCI_LE_PHY_1M; + params.secondary_phy = BLE_HCI_LE_PHY_2M; + /* Use controller-selected default TX power. */ + params.tx_power = 0x7f; + params.sid = 2; + + /* configure instance 0 */ + rc = ble_gap_ext_adv_configure(instance, ¶ms, NULL, NULL, NULL); assert (rc == 0); + addr_type = params.own_addr_type == BLE_OWN_ADDR_RANDOM ? BLE_ADDR_RANDOM : BLE_ADDR_PUBLIC; + rc = ble_hs_id_copy_addr(addr_type, addr.val, NULL); + assert(rc == 0); + MODLOG_DFLT(INFO, "Device Address: "); print_addr(addr.val); MODLOG_DFLT(INFO, "\n"); - /* For periodic we use instance with non-connectable advertising */ - memset (¶ms, 0, sizeof(params)); + ... +``` - /* advertise using random addr */ - params.own_addr_type = BLE_OWN_ADDR_RANDOM; - params.primary_phy = BLE_HCI_LE_PHY_1M; - params.secondary_phy = BLE_HCI_LE_PHY_2M; - params.sid = 2; +The function takes `own_addr_type` as a parameter (inferred in `periodic_adv_on_sync`). Key aspects: - /* configure instance 1 */ - rc = ble_gap_ext_adv_configure(instance, ¶ms, NULL, NULL, NULL); - assert (rc == 0); +1. **Instance 0** is used for the advertising instance. +2. The address type is set dynamically from the inferred `own_addr_type` rather than being hardcoded. This ensures the example works correctly across all ESP32 variants, including those that only have a random address (e.g., some ESP32-C2, ESP32-H2 configurations). +3. After configuring the instance, the device address is retrieved using `ble_hs_id_copy_addr()` based on the address type. +4. `params.tx_power = 0x7f` tells the controller to use its default TX power. - rc = ble_gap_ext_adv_set_addr(instance, &addr ); - assert (rc == 0); +### Parameter Configuration - memset(&adv_fields, 0, sizeof(adv_fields)); - adv_fields.name = (const uint8_t *)"Periodic ADV"; - adv_fields.name_len = strlen((char *)adv_fields.name); +#### For Extended Advertisement - /* Default to legacy PDUs size, mbuf chain will be increased if needed - */ - data = os_msys_get_pkthdr(BLE_HCI_MAX_ADV_DATA_LEN, 0); +```c + params.own_addr_type = own_addr_type; + params.primary_phy = BLE_HCI_LE_PHY_1M; // Primary advertising PHY is set to 1M + params.secondary_phy = BLE_HCI_LE_PHY_2M; // Secondary advertising PHY is set to 2M + params.tx_power = 0x7f; // Use controller-selected default TX power + params.sid = 2; // Advertising set ID is assigned value 2 +``` + +The periodic advertisement uses a non-connectable advertising mode. `memset (¶ms, 0, sizeof(params))` initialises params to 0. This also sets `params.connectable` to 0. + +#### For Periodic Advertisement + +```c + memset(&pparams, 0, sizeof(pparams)); + pparams.include_tx_power = 0; // TX power is not included in advertising PDU + pparams.itvl_min = BLE_GAP_PERIODIC_ITVL_MS(120); // Minimum periodic advertising interval of 120ms + pparams.itvl_max = BLE_GAP_PERIODIC_ITVL_MS(240); // Maximum periodic advertising interval of 240ms +``` + +### Setting Advertisement Data + +The extended advertisement data (device name) is set using `ble_hs_adv_set_fields_mbuf()`: + +```c + data = os_msys_get_pkthdr(BLE_HS_ADV_MAX_FIELD_SZ, 0); assert(data); rc = ble_hs_adv_set_fields_mbuf(&adv_fields, data); @@ -243,78 +233,54 @@ start_periodic_adv(void) rc = ble_gap_ext_adv_set_data(instance, data); assert(rc == 0); +``` - /* configure periodic advertising */ - memset(&pparams, 0, sizeof(pparams)); - pparams.include_tx_power = 0; - pparams.itvl_min = BLE_GAP_ADV_ITVL_MS(120); - pparams.itvl_max = BLE_GAP_ADV_ITVL_MS(240); - - rc = ble_gap_periodic_adv_configure(instance, &pparams); - assert(rc == 0); +The periodic advertising data is set separately as raw data: +```c data = os_msys_get_pkthdr(sizeof(periodic_adv_raw_data), 0); assert(data); rc = os_mbuf_append(data, periodic_adv_raw_data, sizeof(periodic_adv_raw_data)); assert(rc == 0); +#if MYNEWT_VAL(BLE_PERIODIC_ADV_ENH) + rc = ble_gap_periodic_adv_set_data(instance, data, NULL); +#else rc = ble_gap_periodic_adv_set_data(instance, data); +#endif assert (rc == 0); +``` +### Starting Periodic and Extended Advertising + +Periodic advertising is started first, then extended advertising. When `BLE_PERIODIC_ADV_ENH` is enabled, the enhanced API is used which accepts additional parameters (e.g., `include_adi`): + +```c /* start periodic advertising */ - assert (rc == 0 rc = ble_gap_periodic_adv_start(instance); -); +#if MYNEWT_VAL(BLE_PERIODIC_ADV_ENH) +#if CONFIG_EXAMPLE_PERIODIC_ADV_ENH + eparams.include_adi = 1; +#endif + rc = ble_gap_periodic_adv_start(instance, &eparams); +#else + rc = ble_gap_periodic_adv_start(instance); +#endif + assert (rc == 0); /* start advertising */ rc = ble_gap_ext_adv_start(instance, 0, 0); assert (rc == 0); MODLOG_DFLT(INFO, "instance %u started (periodic)\n", instance); -} ``` -The periodic advertisement uses a non-connectable advertising mode. `memset (¶ms, 0, sizeof(params))` initialises params to 0. This also sets `params.connectable` to 0. - -## Parameter Configuration - -The below snippets represent the parameter configuration for extended and periodic advertisement. - -### For Extended Advertisement - -```c - params.own_addr_type = BLE_OWN_ADDR_RANDOM; //Own address type is set to Random - params.primary_phy = BLE_HCI_LE_PHY_1M; // Primary advertising PHY is set to 1M - params.secondary_phy = BLE_HCI_LE_PHY_2M; // Secondary advertising PHY is set to 2M - params.sid = 2; // Advertising set Id is assigned with value 2. -``` - -### For Periodic Advertisment - -```c - memset(&pparams, 0, sizeof(pparams)); - pparams.include_tx_power = 0; // Indicates that TX power is not included in advertising PDU - pparams.itvl_min = BLE_GAP_ADV_ITVL_MS(120); // Minimum advertising interval of 240ms - pparams.itvl_max = BLE_GAP_ADV_ITVL_MS(240); //Maximum advertising interval of 480ms -``` - -Periodic advertisement is started for a particular advertisement instance by calling the API `ble_gap_periodic_adv_start(instance)`. This function takes instance-id as an input parameter. It defines the hci command by initializing the command parameters which are represented in the following lines. - -```c - struct ble_hci_le_set_periodic_adv_enable_cp cmd; - cmd.enable = 0x01; - cmd.adv_handle = instance; -``` - -Extended advertising is invoked for a particular instance using the API call `ble_gap_ext_adv_start(instance, 0, 0)`.Instance-id, duration, and max_events are input parameters for this API call respectively. - -Duration represents the time for which the adverteiment will take place. Upon expiration, the advertising procedure ends, and the BLE_GAP_EVENT_ADV_COMPLETE event is reported.0 value is used for no expiration. - -max_events Number of advertising events that should be sent before advertising ends and a BLE_GAP_EVENT_ADV_COMPLETE event is reported.0 value is used for no limit. +`ble_gap_ext_adv_start(instance, 0, 0)` starts extended advertising with no duration limit and no maximum event count. ## Conclusion -This Walkthrough covers the code explanation of the BLE_PERIODIC_ADV. The following points are concluded through this walkthrough. +This walkthrough covers the code explanation of the BLE_PERIODIC_ADV example. The following points are concluded: -1. Periodic advertising allows the scanner to sync with the advertiser so the scanner and advertiser would wake up at the same time. -2. Periodic advertisment uses NRPA (Non Resolvable private adress). It is a randomly generated address that changes periodically to prevent long-term tracking of a device. -3. Extended advertising is used to indicate to the scanner that the advertiser is utilizing periodic advertising. Therefore, periodic advertising is started before extended advertising so that the scanner and advertiser can synchronize their actions and operate at the same time. \ No newline at end of file +1. Periodic advertising allows the scanner to sync with the advertiser so the scanner and advertiser wake up at the same time. +2. The example uses `ble_hs_id_infer_auto()` to dynamically determine the correct address type, ensuring compatibility across all supported ESP32 variants. +3. Extended advertising is used to indicate to the scanner that the advertiser is utilizing periodic advertising. Therefore, periodic advertising is started before extended advertising so that the scanner and advertiser can synchronize their actions. +4. When `BLE_PERIODIC_ADV_ENH` is enabled, the enhanced periodic advertising API allows additional features such as ADI (Advertising Data Info) inclusion. diff --git a/examples/bluetooth/nimble/ble_periodic_sync/README.md b/examples/bluetooth/nimble/ble_periodic_sync/README.md index f42796b1e0..2d9cd490e8 100644 --- a/examples/bluetooth/nimble/ble_periodic_sync/README.md +++ b/examples/bluetooth/nimble/ble_periodic_sync/README.md @@ -8,11 +8,11 @@ This example performs passive scan for non-connectable non-scannable extended advertisement, it then establishes the periodic sync with the advertiser and then listens to the periodic advertisements. -It uses ESP32C3's Bluetooth controller and NimBLE stack based BLE host. +It uses Bluetooth controller and NimBLE stack based BLE host. This example aims at understanding BLE periodic sync establishment and periodic advertisement reports. -To test this demo, use any periodic advertiser with uses extended adv data as "ESP_PERIODIC_ADV". +To test this demo, use the `ble_periodic_adv` example or any periodic advertiser with SID 2. Note : @@ -30,23 +30,11 @@ idf.py set-target ### Hardware Required -* A development board with ESP32/ESP32-C3 SoC (e.g., ESP32-DevKitC, ESP-WROVER-KIT, etc.) +* A development board with a supported SoC (e.g., ESP32-C3, ESP32-C6, ESP32-H2, ESP32-S3, etc.) * A USB cable for Power supply and programming See [Development Boards](https://www.espressif.com/en/products/devkits) for more information about it. -### Configure the Project - -Open the project configuration menu: - -```bash -idf.py menuconfig -``` - -In the `Example Configuration` menu: - -* Change the `Peer Address` option if needed. - ### Build and Flash Run `idf.py -p PORT flash monitor` to build, flash and monitor the project. diff --git a/examples/bluetooth/nimble/ble_periodic_sync/main/CMakeLists.txt b/examples/bluetooth/nimble/ble_periodic_sync/main/CMakeLists.txt index cf2c455cb5..fc2fa0dd33 100644 --- a/examples/bluetooth/nimble/ble_periodic_sync/main/CMakeLists.txt +++ b/examples/bluetooth/nimble/ble_periodic_sync/main/CMakeLists.txt @@ -1,2 +1,3 @@ idf_component_register(SRCS "main.c" - INCLUDE_DIRS ".") + INCLUDE_DIRS "." + PRIV_REQUIRES bt nvs_flash) diff --git a/examples/bluetooth/nimble/ble_periodic_sync/main/Kconfig.projbuild b/examples/bluetooth/nimble/ble_periodic_sync/main/Kconfig.projbuild index f5315592b1..d30f44b046 100644 --- a/examples/bluetooth/nimble/ble_periodic_sync/main/Kconfig.projbuild +++ b/examples/bluetooth/nimble/ble_periodic_sync/main/Kconfig.projbuild @@ -1,14 +1,4 @@ menu "Example Configuration" - config EXAMPLE_EXTENDED_ADV - bool - depends on SOC_BLE_50_SUPPORTED && BT_NIMBLE_50_FEATURE_SUPPORT - default y if SOC_ESP_NIMBLE_CONTROLLER - select BT_NIMBLE_EXT_ADV - prompt "Enable Extended Adv" - help - Use this option to enable extended advertising in the example. - If this option is disabled, ensure config BT_NIMBLE_EXT_ADV is - also disabled from NimBLE stack menuconfig config EXAMPLE_PERIODIC_ADV_ENH bool diff --git a/examples/bluetooth/nimble/ble_periodic_sync/main/idf_component.yml b/examples/bluetooth/nimble/ble_periodic_sync/main/idf_component.yml index db8886afea..d4e300f177 100644 --- a/examples/bluetooth/nimble/ble_periodic_sync/main/idf_component.yml +++ b/examples/bluetooth/nimble/ble_periodic_sync/main/idf_component.yml @@ -1,3 +1,2 @@ -dependencies: - nimble_central_utils: - path: ${IDF_PATH}/examples/bluetooth/nimble/common/nimble_central_utils +## IDF Component Manager Manifest File +dependencies: {} diff --git a/examples/bluetooth/nimble/ble_periodic_sync/main/main.c b/examples/bluetooth/nimble/ble_periodic_sync/main/main.c index daf2ee4e3e..234e48d711 100644 --- a/examples/bluetooth/nimble/ble_periodic_sync/main/main.c +++ b/examples/bluetooth/nimble/ble_periodic_sync/main/main.c @@ -1,23 +1,28 @@ /* - * SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2021-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Unlicense OR CC0-1.0 */ +#include +#include +#include +#include #include "esp_log.h" #include "nvs_flash.h" /* BLE */ #include "nimble/nimble_port.h" #include "nimble/nimble_port_freertos.h" #include "host/ble_hs.h" -#include "host/util/util.h" -#include "console/console.h" -#include "services/gap/ble_svc_gap.h" -#include "periodic_sync.h" #include "host/ble_gap.h" +#include "host/util/util.h" +#include "services/gap/ble_svc_gap.h" +#include "modlog/modlog.h" static const char *tag = "NimBLE_BLE_PERIODIC_SYNC"; static int synced = 0; + + static int periodic_sync_gap_event(struct ble_gap_event *event, void *arg); void ble_store_config_init(void); @@ -26,7 +31,7 @@ static void periodic_sync_scan(void) { uint8_t own_addr_type; - struct ble_gap_disc_params disc_params = {0}; + struct ble_gap_ext_disc_params disc_params = {0}; int rc; /* Figure out address to use while advertising (no privacy for now) */ @@ -36,25 +41,11 @@ periodic_sync_scan(void) return; } - /* Tell the controller to filter duplicates; we don't want to process - * repeated advertisements from the same device. - */ - disc_params.filter_duplicates = 0; - - /** - * Perform a passive scan. I.e., don't send follow-up scan requests to - * each advertiser. - */ disc_params.passive = 1; - /* Use defaults for the rest of the parameters. */ - disc_params.itvl = 0; - disc_params.window = 0; - disc_params.filter_policy = 0; - disc_params.limited = 0; - - rc = ble_gap_disc(own_addr_type, BLE_HS_FOREVER, &disc_params, - periodic_sync_gap_event, NULL); + rc = ble_gap_ext_disc(own_addr_type, 0, 0, + 0, 0, 0, &disc_params, &disc_params, + periodic_sync_gap_event, NULL); if (rc != 0) { MODLOG_DFLT(ERROR, "Error initiating GAP discovery procedure; rc=%d\n", rc); @@ -63,32 +54,31 @@ periodic_sync_scan(void) void print_periodic_sync_data(struct ble_gap_event *event) { - MODLOG_DFLT(DEBUG, "status : %d\nperiodic_sync_handle : %d\nsid : %d\n", event->periodic_sync.status, event->periodic_sync.sync_handle, event->periodic_sync.sid); - MODLOG_DFLT(DEBUG, "adv addr : "); - for (int i = 0; i < 6; i++) { - MODLOG_DFLT(DEBUG, "%d ", event->periodic_sync.adv_addr.val[i]); - } - MODLOG_DFLT(DEBUG, "\nadv_phy : %s\n", event->periodic_sync.adv_phy == 1 ? "1m" : (event->periodic_sync.adv_phy == 2 ? "2m" : "coded")); - MODLOG_DFLT(DEBUG, "per_adv_ival : %d\n", event->periodic_sync.per_adv_ival); - MODLOG_DFLT(DEBUG, "adv_clk_accuracy : %d\n", event->periodic_sync.adv_clk_accuracy); + MODLOG_DFLT(INFO, "status : %d\nperiodic_sync_handle : %d\nsid : %d\n", + event->periodic_sync.status, event->periodic_sync.sync_handle, event->periodic_sync.sid); + MODLOG_DFLT(INFO, "adv addr : "); + ESP_LOG_BUFFER_HEX("NimBLE", event->periodic_sync.adv_addr.val, 6); + MODLOG_DFLT(INFO, "adv_phy : %s\n", event->periodic_sync.adv_phy == 1 ? "1m" : (event->periodic_sync.adv_phy == 2 ? "2m" : "coded")); + MODLOG_DFLT(INFO, "per_adv_ival : %d\n", event->periodic_sync.per_adv_ival); + MODLOG_DFLT(INFO, "adv_clk_accuracy : %d\n", event->periodic_sync.adv_clk_accuracy); } + void print_periodic_adv_data(struct ble_gap_event *event) { - MODLOG_DFLT(DEBUG, "sync_handle : %d\n", event->periodic_report.sync_handle); - MODLOG_DFLT(DEBUG, "tx_power : %d\n", event->periodic_report.tx_power); - MODLOG_DFLT(DEBUG, "rssi : %d\n", event->periodic_report.rssi); - MODLOG_DFLT(DEBUG, "data_status : %d\n", event->periodic_report.data_status); - MODLOG_DFLT(DEBUG, "data_length : %d\n", event->periodic_report.data_length); - MODLOG_DFLT(DEBUG, "data : "); - for (int i = 0; i < event->periodic_report.data_length; i++) { - MODLOG_DFLT(DEBUG, "%c", ((char *)event->periodic_report.data)[i]); - } - MODLOG_DFLT(DEBUG, "\n"); + MODLOG_DFLT(INFO, "sync_handle : %d\n", event->periodic_report.sync_handle); + MODLOG_DFLT(INFO, "tx_power : %d\n", event->periodic_report.tx_power); + MODLOG_DFLT(INFO, "rssi : %d\n", event->periodic_report.rssi); + MODLOG_DFLT(INFO, "data_status : %d\n", event->periodic_report.data_status); + MODLOG_DFLT(INFO, "data_length : %d\n", event->periodic_report.data_length); + MODLOG_DFLT(INFO, "data : "); + ESP_LOG_BUFFER_HEX("NimBLE", event->periodic_report.data, event->periodic_report.data_length); + MODLOG_DFLT(INFO, "\n"); } + void print_periodic_sync_lost_data(struct ble_gap_event *event) { - MODLOG_DFLT(DEBUG, "sync_handle : %d\n", event->periodic_sync_lost.sync_handle); - MODLOG_DFLT(DEBUG, "reason : %s\n", event->periodic_sync_lost.reason == 13 ? "timeout" : (event->periodic_sync_lost.reason == 14 ? "terminated locally" : "Unknown reason")); + MODLOG_DFLT(INFO, "sync_handle : %d\n", event->periodic_sync_lost.sync_handle); + MODLOG_DFLT(INFO, "reason : %s\n", event->periodic_sync_lost.reason == 13 ? "timeout" : (event->periodic_sync_lost.reason == 14 ? "terminated locally" : "Unknown reason")); } /** * The nimble host executes this callback when a GAP event occurs. The @@ -107,19 +97,16 @@ void print_periodic_sync_lost_data(struct ble_gap_event *event) static int periodic_sync_gap_event(struct ble_gap_event *event, void *arg) { + (void)arg; + switch (event->type) { -#if CONFIG_EXAMPLE_EXTENDED_ADV - case BLE_GAP_EVENT_EXT_DISC: + case BLE_GAP_EVENT_EXT_DISC: { /* An advertisement report was received during GAP discovery. */ - struct ble_gap_ext_disc_desc *disc = ((struct ble_gap_ext_disc_desc *)(&event->ext_disc)); + const struct ble_gap_ext_disc_desc *disc = &event->ext_disc; if (disc->sid == 2 && synced == 0) { - synced++; - ble_addr_t addr; - uint8_t adv_sid; - struct ble_gap_periodic_sync_params params; + struct ble_gap_periodic_sync_params params = {0}; int rc; - memcpy(&addr, &disc->addr, sizeof(disc->addr)); - memcpy(&adv_sid, &disc->sid, sizeof(disc->sid)); + synced++; params.skip = 10; params.sync_timeout = 1000; @@ -129,10 +116,11 @@ periodic_sync_gap_event(struct ble_gap_event *event, void *arg) or the Data-Id is updated by the advertiser */ params.filter_duplicates = 1; #endif - rc = ble_gap_periodic_adv_sync_create(&addr, adv_sid, ¶ms, periodic_sync_gap_event, NULL); + rc = ble_gap_periodic_adv_sync_create(&disc->addr, disc->sid, ¶ms, periodic_sync_gap_event, NULL); assert(rc == 0); } return 0; + } case BLE_GAP_EVENT_PERIODIC_REPORT: MODLOG_DFLT(INFO, "Periodic adv report event: \n"); print_periodic_adv_data(event); @@ -141,12 +129,19 @@ periodic_sync_gap_event(struct ble_gap_event *event, void *arg) MODLOG_DFLT(INFO, "Periodic sync lost\n"); print_periodic_sync_lost_data(event); synced = 0; + /* Restart scanning to re-sync */ + periodic_sync_scan(); return 0; - case BLE_GAP_EVENT_PERIODIC_SYNC: + case BLE_GAP_EVENT_PERIODIC_SYNC: MODLOG_DFLT(INFO, "Periodic sync event : \n"); print_periodic_sync_data(event); + if (event->periodic_sync.status != 0) { + synced = 0; + } else { + /* Cancel scanning since sync is established */ + ble_gap_disc_cancel(); + } return 0; -#endif default: return 0; } @@ -166,7 +161,7 @@ periodic_sync_on_sync(void) rc = ble_hs_util_ensure_addr(0); assert(rc == 0); - /* Begin scanning for a peripheral to connect to. */ + /* Begin scanning for a periodic advertiser to sync with. */ periodic_sync_scan(); } @@ -182,7 +177,6 @@ void periodic_sync_host_task(void *param) void app_main(void) { - int rc; /* Initialize NVS — it is used to store PHY calibration data */ esp_err_t ret = nvs_flash_init(); if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) { @@ -202,17 +196,9 @@ app_main(void) ble_hs_cfg.sync_cb = periodic_sync_on_sync; 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(MYNEWT_VAL(BLE_MAX_CONNECTIONS), 64, 64, 64, 64); - assert(rc == 0); -#else - rc = peer_init(MYNEWT_VAL(BLE_MAX_CONNECTIONS), 64, 64, 64); - assert(rc == 0); -#endif #if CONFIG_BT_NIMBLE_GAP_SERVICE /* Set the default device name. */ - rc = ble_svc_gap_device_name_set("nimble_periodic_sync"); + int rc = ble_svc_gap_device_name_set("nimble_periodic_sync"); assert(rc == 0); #endif diff --git a/examples/bluetooth/nimble/ble_periodic_sync/main/periodic_sync.h b/examples/bluetooth/nimble/ble_periodic_sync/main/periodic_sync.h deleted file mode 100644 index 5da9f33399..0000000000 --- a/examples/bluetooth/nimble/ble_periodic_sync/main/periodic_sync.h +++ /dev/null @@ -1,26 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD - * - * SPDX-License-Identifier: Unlicense OR CC0-1.0 - */ - -#ifndef H_BLE_PERIODIC_SYNC_ -#define H_BLE_PERIODIC_SYNC_ - -#include "modlog/modlog.h" -#include "esp_central.h" -#ifdef __cplusplus -extern "C" { -#endif - -struct ble_hs_adv_fields; -struct ble_gap_conn_desc; -struct ble_hs_cfg; -union ble_store_value; -union ble_store_key; - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/examples/bluetooth/nimble/ble_periodic_sync/sdkconfig.defaults b/examples/bluetooth/nimble/ble_periodic_sync/sdkconfig.defaults index 60f9e68480..cd48c8bec7 100644 --- a/examples/bluetooth/nimble/ble_periodic_sync/sdkconfig.defaults +++ b/examples/bluetooth/nimble/ble_periodic_sync/sdkconfig.defaults @@ -10,6 +10,5 @@ CONFIG_BTDM_CTRL_MODE_BR_EDR_ONLY=n CONFIG_BTDM_CTRL_MODE_BTDM=n CONFIG_BT_BLUEDROID_ENABLED=n CONFIG_BT_NIMBLE_ENABLED=y -CONFIG_EXAMPLE_EXTENDED_ADV=y CONFIG_BT_NIMBLE_EXT_ADV=y CONFIG_BT_NIMBLE_MAX_PERIODIC_SYNCS=1 diff --git a/examples/bluetooth/nimble/ble_periodic_sync/sdkconfig.defaults.dram_optimised b/examples/bluetooth/nimble/ble_periodic_sync/sdkconfig.defaults.dram_optimised index b42d28d378..b6342b0637 100644 --- a/examples/bluetooth/nimble/ble_periodic_sync/sdkconfig.defaults.dram_optimised +++ b/examples/bluetooth/nimble/ble_periodic_sync/sdkconfig.defaults.dram_optimised @@ -10,7 +10,6 @@ CONFIG_BTDM_CTRL_MODE_BR_EDR_ONLY=n CONFIG_BTDM_CTRL_MODE_BTDM=n CONFIG_BT_BLUEDROID_ENABLED=n CONFIG_BT_NIMBLE_ENABLED=y -CONFIG_EXAMPLE_EXTENDED_ADV=y CONFIG_BT_NIMBLE_EXT_ADV=y CONFIG_BT_NIMBLE_MAX_PERIODIC_SYNCS=1 diff --git a/examples/bluetooth/nimble/ble_periodic_sync/sdkconfig.defaults.mini b/examples/bluetooth/nimble/ble_periodic_sync/sdkconfig.defaults.mini index a718452e62..d3423e6b6a 100644 --- a/examples/bluetooth/nimble/ble_periodic_sync/sdkconfig.defaults.mini +++ b/examples/bluetooth/nimble/ble_periodic_sync/sdkconfig.defaults.mini @@ -10,7 +10,6 @@ CONFIG_BTDM_CTRL_MODE_BR_EDR_ONLY=n CONFIG_BTDM_CTRL_MODE_BTDM=n CONFIG_BT_BLUEDROID_ENABLED=n CONFIG_BT_NIMBLE_ENABLED=y -CONFIG_EXAMPLE_EXTENDED_ADV=y CONFIG_BT_NIMBLE_EXT_ADV=y CONFIG_BT_NIMBLE_MAX_PERIODIC_SYNCS=1 CONFIG_BT_NIMBLE_MAX_CONNECTIONS=1 diff --git a/examples/bluetooth/nimble/ble_periodic_sync/tutorial/BLE_periodic_sync_walkthrough.md b/examples/bluetooth/nimble/ble_periodic_sync/tutorial/BLE_periodic_sync_walkthrough.md index f6dd3e23af..ec141ea7b3 100644 --- a/examples/bluetooth/nimble/ble_periodic_sync/tutorial/BLE_periodic_sync_walkthrough.md +++ b/examples/bluetooth/nimble/ble_periodic_sync/tutorial/BLE_periodic_sync_walkthrough.md @@ -2,42 +2,44 @@ ## Introduction -In this tutorial, the ble_periodic_sync example code for the espressif chipsets with BLE5.0 support is reviewed. This example aims at understanding BLE periodic sync establishment and periodic advertisement reports.It supports the chips like ESP32-C2, -ESP32-C3, ESP32-C6,ESP32-H2, and ESP32-S3. +In this tutorial, the ble_periodic_sync example code for the Espressif chipsets with BLE 5.0 support is reviewed. This example demonstrates BLE periodic sync establishment and periodic advertisement reports. It performs a passive extended scan for non-connectable non-scannable extended advertisements, establishes periodic sync with the advertiser, and then listens to the periodic advertisement data. ## Includes This example is located in the examples folder of the ESP-IDF under the [ble_periodic_sync/main](../main). The [main.c](../main/main.c) file located in the main folder contains all the functionality that we are going to review. The header files contained in [main.c](../main/main.c) are: ```c +#include +#include +#include +#include #include "esp_log.h" #include "nvs_flash.h" /* BLE */ #include "nimble/nimble_port.h" #include "nimble/nimble_port_freertos.h" #include "host/ble_hs.h" -#include "host/util/util.h" -#include "console/console.h" -#include "services/gap/ble_svc_gap.h" -#include "periodic_sync.h" #include "host/ble_gap.h" +#include "host/util/util.h" +#include "services/gap/ble_svc_gap.h" +#include "modlog/modlog.h" ``` -These `includes` are required for the FreeRTOS and underlying system components to run, including the logging functionality and a library to store data in non-volatile flash memory. We are interested in `“nimble_port.h”`, `“nimble_port_freertos.h”`, `"ble_hs.h"` and `“ble_svc_gap.h”`, `“periodic_sync.h”` which expose the BLE APIs required to implement this example. +These `includes` are required for the FreeRTOS and underlying system components to run, including the logging functionality and a library to store data in non-volatile flash memory. We are interested in `"nimble_port.h"`, `"nimble_port_freertos.h"`, `"ble_hs.h"`, `"ble_gap.h"`, and `"ble_svc_gap.h"` which expose the BLE APIs required to implement this example. * `nimble_port.h`: Includes the declaration of functions required for the initialization of the nimble stack. * `nimble_port_freertos.h`: Initializes and enables nimble host task. -* `ble_hs.h`: Defines the functionalities to handle the host event -* `ble_svc_gap.h`:Defines the macros for device name, and device appearance and declares the function to set them. -* `periodic_sync.h`:It includes the code containing forward declarations of structures for storing host_advertsing_fileds, host configrations and connection descriptors based on weather macro `H_BLE_PERIODIC_SYNC_` is defined.Also, it includes the unions for storing CCCD, Security values, and Keys for their lookups. +* `ble_hs.h`: Defines the functionalities to handle the host event. +* `ble_gap.h`: Defines GAP related APIs for advertising, scanning, and periodic sync. +* `ble_svc_gap.h`: Defines the macros for device name, and device appearance and declares the function to set them. +* `modlog/modlog.h`: Provides logging macros used throughout the example. ## Main Entry Point -The program’s entry point is the app_main() function: +The program's entry point is the app_main() function: ```c void app_main(void) { - int rc; /* Initialize NVS — it is used to store PHY calibration data */ esp_err_t ret = nvs_flash_init(); if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) { @@ -57,88 +59,24 @@ app_main(void) ble_hs_cfg.sync_cb = periodic_sync_on_sync; ble_hs_cfg.store_status_cb = ble_store_util_status_rr; - /* Initialize data structures to track connected peers. */ - rc = peer_init(MYNEWT_VAL(BLE_MAX_CONNECTIONS), 64, 64, 64); - assert(rc == 0); - +#if CONFIG_BT_NIMBLE_GAP_SERVICE /* Set the default device name. */ - rc = ble_svc_gap_device_name_set("nimble_periodic_sync"); + int rc = ble_svc_gap_device_name_set("nimble_periodic_sync"); assert(rc == 0); +#endif /* XXX Need to have template for store */ ble_store_config_init(); nimble_port_freertos_init(periodic_sync_host_task); } ``` -The main function starts by initializing the non-volatile storage library. This library allows us to save the key-value pairs in flash memory.`nvs_flash_init()` stores the PHY calibration data. In a Bluetooth Low Energy (BLE) device, cryptographic keys used for encryption and authentication are often stored in Non-Volatile Storage (NVS).BLE stores the peer keys, CCCD keys, peer records, etc on NVS. By storing these keys in NVS, the BLE device can quickly retrieve them when needed, without the need for time-consuming key -generations. - -```c -esp_err_t ret = nvs_flash_init(); -if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) { - ESP_ERROR_CHECK(nvs_flash_erase()); - ret = nvs_flash_init(); -} -ESP_ERROR_CHECK( ret ); -``` +The main function starts by initializing the non-volatile storage library. This library allows us to save the key-value pairs in flash memory.`nvs_flash_init()` stores the PHY calibration data. In a Bluetooth Low Energy (BLE) device, cryptographic keys used for encryption and authentication are often stored in Non-Volatile Storage (NVS). BLE stores the peer keys, CCCD keys, peer records, etc on NVS. By storing these keys in NVS, the BLE device can quickly retrieve them when needed, without the need for time-consuming key generations. ## BT Controller and Stack Initialization -The main function calls `nimble_port_init()` to initialize BT Controller and nimble stack. This function initializes the BT controller by first creating its configuration structure named `esp_bt_controller_config_t` with default settings generated by the `BT_CONTROLLER_INIT_CONFIG_DEFAULT()` macro. It implements the Host Controller Interface (HCI) on the controller side, the Link Layer (LL), and the Physical Layer (PHY). The BT Controller is invisible to the user applications and deals with the lower layers of the BLE stack. The controller configuration includes setting the BT controller stack size, priority, and HCI baud rate. With the settings created, the BT controller is initialized and enabled with the `esp_bt_controller_init()` and `esp_bt_controller_enable()` functions: +The main function calls `nimble_port_init()` to initialize BT Controller and nimble stack. This function initializes the BT controller by first creating its configuration structure named `esp_bt_controller_config_t` with default settings generated by the `BT_CONTROLLER_INIT_CONFIG_DEFAULT()` macro. It implements the Host Controller Interface (HCI) on the controller side, the Link Layer (LL), and the Physical Layer (PHY). -```c -esp_bt_controller_config_t config_opts = BT_CONTROLLER_INIT_CONFIG_DEFAULT(); -ret = esp_bt_controller_init(&config_opts); -``` - -Next, the controller is enabled in BLE Mode. - -```c -ret = esp_bt_controller_enable(ESP_BT_MODE_BLE); -``` -The controller should be enabled in `ESP_BT_MODE_BLE` if you want to use the BLE mode. - -There are four Bluetooth modes supported: - -1. `ESP_BT_MODE_IDLE`: Bluetooth not running -2. `ESP_BT_MODE_BLE`: BLE mode -3. `ESP_BT_MODE_CLASSIC_BT`: BT Classic mode -4. `ESP_BT_MODE_BTDM`: Dual mode (BLE + BT Classic) - -After the initialization of the BT controller, the nimble stack, which includes the common definitions and APIs for BLE, is initialized by using `esp_nimble_init()`: - -```c -esp_err_t esp_nimble_init(void) -{ - -#if !SOC_ESP_NIMBLE_CONTROLLER - /* Initialize the function pointers for OS porting */ - npl_freertos_funcs_init(); - - npl_freertos_mempool_init(); - - if(esp_nimble_hci_init() != ESP_OK) { - ESP_LOGE(NIMBLE_PORT_LOG_TAG, "hci inits failed\n"); - return ESP_FAIL; - } - - /* Initialize default event queue */ - ble_npl_eventq_init(&g_eventq_dflt); - - os_msys_init(); - - void ble_store_ram_init(void); - /* XXX Need to have a template for store */ - ble_store_ram_init(); -#endif - - /* Initialize the host */ - ble_hs_init(); - return ESP_OK; -} -``` - -The host is configured by setting up the callbacks for Stack-reset, Stack-sync, and Storage status +The host is configured by setting up the callbacks for Stack-reset, Stack-sync, and Storage status: ```c ble_hs_cfg.reset_cb = periodic_sync_on_reset; @@ -146,181 +84,111 @@ ble_hs_cfg.sync_cb = periodic_sync_on_sync; ble_hs_cfg.store_status_cb = ble_store_util_status_rr; ``` -Further Data Structures are created and initialized to track connected peers using `peer_init()`. This function creates memory buffers to generate the memory pools like `peer_pool`, `peer_svc_pool`, `peer_chr_pool`, and `peer_dsc_pool`. -```c - rc = peer_init(MYNEWT_VAL(BLE_MAX_CONNECTIONS), 64, 64, 64); -``` - -## Structure of Peer - -The structure of a peer includes fields such as its connection handle, a pointer to the next peer, a list of discovered gatt services, tracking parameters for the service discovery process, and the callbacks that get executed when service discovery completes. - -```c -struct peer { - SLIST_ENTRY(peer) next; - uint16_t conn_handle; - struct peer_svc_list svcs; - uint16_t disc_prev_chr_val; - struct peer_svc *cur_svc; - peer_disc_fn *disc_cb; - void *disc_cb_arg; -}; -``` -The main function calls `ble_svc_gap_device_name_set()` to set the default device name. 'blecent_phy' is passed as the default device name to this function. +The device name is set using `ble_svc_gap_device_name_set()`, guarded by `CONFIG_BT_NIMBLE_GAP_SERVICE` for cases where the GAP service may be disabled: ```c +#if CONFIG_BT_NIMBLE_GAP_SERVICE rc = ble_svc_gap_device_name_set("nimble_periodic_sync"); -``` -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. -```c -/* XXX Need to have a template for store */ - ble_store_config_init(); +assert(rc == 0); +#endif ``` -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()`. +The 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. + +The main function ends by creating a task where nimble will run using `nimble_port_freertos_init()`: ```c nimble_port_freertos_init(periodic_sync_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 to handle the default queue, it is easier to create a separate task. -## Periodic Synchronisation scanning +## Host Sync Callback + +When the NimBLE host and controller are synced, the `periodic_sync_on_sync()` callback is invoked. It ensures a valid identity address is set and begins scanning: -This example performs a passive scan for non-connectable non-scannable extended advertisements, it then establishes the periodic sync with the advertiser and then listens to the periodic advertisements. -variable `own_addr_type` refers to the address type used by a BLE device to identify itself during communication. Its valid values are : ```c -BLE_OWN_ADDR_PUBLIC +static void +periodic_sync_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); -BLE_OWN_ADDR_RANDOM - -BLE_OWN_ADDR_RPA_PUBLIC_DEFAULT - -BLE_OWN_ADDR_RPA_RANDOM_DEFAULT + /* Begin scanning for a periodic advertiser to sync with. */ + periodic_sync_scan(); +} ``` -Discovery parameters are defined in the object disc_params which includes particulars of the discovery procedure such as scan interval, scan window, filter_policy, and flags to decide whether to use a limited discovery procedure, passive scanning, and enables duplicate filtering. +## Periodic Synchronisation Scanning + +This example performs a passive extended scan for non-connectable non-scannable extended advertisements. It uses `ble_gap_ext_disc()` (extended discovery) rather than the legacy `ble_gap_disc()` API to receive extended advertisement reports that contain periodic advertising synchronization information. ```c static void periodic_sync_scan(void) { uint8_t own_addr_type; - struct ble_gap_disc_params disc_params; + struct ble_gap_ext_disc_params disc_params = {0}; int rc; - /* Figure out address to use while advertising (no privacy for now) */ + /* Figure out address to use while scanning (no privacy for now) */ rc = ble_hs_id_infer_auto(0, &own_addr_type); if (rc != 0) { MODLOG_DFLT(ERROR, "error determining address type; rc=%d\n", rc); return; } - /* Tell the controller to filter duplicates; we don't want to process - * repeated advertisements from the same device. - */ - disc_params.filter_duplicates = 0; - - /** - * Perform a passive scan. I.e., don't send follow-up scan requests to - * each advertiser. - */ disc_params.passive = 1; - /* Use defaults for the rest of the parameters. */ - disc_params.itvl = 0; - disc_params.window = 0; - disc_params.filter_policy = 0; - disc_params.limited = 0; - - rc = ble_gap_disc(own_addr_type, BLE_HS_FOREVER, &disc_params, - periodic_sync_gap_event, NULL); + rc = ble_gap_ext_disc(own_addr_type, 0, 0, + 0, 0, 0, &disc_params, &disc_params, + periodic_sync_gap_event, NULL); if (rc != 0) { MODLOG_DFLT(ERROR, "Error initiating GAP discovery procedure; rc=%d\n", rc); } } ``` -## Address Generation To establish the connection -```c - /* Figure out address to use while advertising (no privacy for now) */ - rc = ble_hs_id_infer_auto(0, &own_addr_type); - if (rc != 0) { - MODLOG_DFLT(ERROR, "error determining address type; rc=%d\n", rc); - return; - } -``` -The above function call `ble_hs_id_infer_auto(0,own_addr_type)` figures out the address to use while scanning. Depending on privacy parameters public address or private address can be assigned. Public address types are `BLE_OWN_ADDR_RANDOM`, `BLE_OWN_ADDR_PUBLIC`, and private address types are `BLE_OWN_ADDR_RPA_RANDOM_DEFAULT`, `BLE_OWN_ADDR_RPA_PUBLIC_DEFAULT`. -1st parameter of the function `ble_hs_id_infer_auto` defines the privacy.0 value indicates that privacy is not used. -## Configuration of discovery parameters - -`disc_params.filter_duplicates = 0;` -Here we are telling the controller to filter the repeated advertisements from the same device. - -`disc_params.passive = 1;` -To perform passive scanning `passive` field is set to 1. Other discovery parameters are set to default as follows. - -```c - /* Use defaults for the rest of the parameters. */ - disc_params.itvl = 0; - disc_params.window = 0; - disc_params.filter_policy = 0; - disc_params.limited = 0; -``` - -## Perform Discovery Procedures - -By utilizing the ble_gap_disc function and associated callbacks, you can initiate and manage the discovery procedure to scan for nearby BLE devices and gather information about their presence, capabilities, and services. - -```c -rc = ble_gap_disc(own_addr_type, BLE_HS_FOREVER, &disc_params, - periodic_sync_gap_event, NULL); -``` -Above mentioned function `ble_gap_disc` performs the limited or general discovery procedures. -It received the following parameter. - -1. `own_addr_type`: This refers to the address type that the stack should utilize when sending scan requests. - -2. `BLE_HS_FOREVER`: This refers to the duration of the discovery procedure. Once the expiration time is reached, the procedure concludes, and a BLE_GAP_EVENT_DISC_COMPLETE event is generated and reported. The duration is measured in milliseconds. If you want the procedure to have no expiration, you can specify BLE_HS_FOREVER. Alternatively, if you wish to use the default expiration time defined by the stack, you can specify 0. - -3. `disc_params`: This refers to discovery arguments. - -4. `periodic_sync_gap_event`:You can assign a callback function to be associated with the discovery procedure. This callback function will be responsible for handling and processing advertising reports received during the discovery process, as well as any events related to the termination of the discovery procedure. - -5. `NULL`: The optional argument to pass to the callback function. +Key aspects: +- `ble_hs_id_infer_auto()` determines the best address type (public or random) to use for scanning. +- `ble_gap_ext_disc_params` is used instead of `ble_gap_disc_params` since we need extended discovery to receive `BLE_GAP_EVENT_EXT_DISC` events. +- `disc_params.passive = 1` sets passive scanning — the scanner only listens for advertisements without sending scan requests. +- `ble_gap_ext_disc()` initiates the extended discovery procedure with the configured parameters. ## GAP Events -The nimble host executes this callback when a GAP event occurs. The application associates a GAP event callback with each connection that is established. periodic_sync uses the same callback for all connections. -4 types of gap events are handled in the function`periodic_sync_gap_event`. Events are the following: +The nimble host executes the `periodic_sync_gap_event` callback when a GAP event occurs. Four types of gap events are handled: -1. BLE_GAP_EVENT_EXT_DISC -2. BLE_GAP_EVENT_PERIODIC_REPORT -3. BLE_GAP_EVENT_PERIODIC_SYNC_LOST -4. BLE_GAP_EVENT_PERIODIC_SYNC +1. `BLE_GAP_EVENT_EXT_DISC` +2. `BLE_GAP_EVENT_PERIODIC_REPORT` +3. `BLE_GAP_EVENT_PERIODIC_SYNC_LOST` +4. `BLE_GAP_EVENT_PERIODIC_SYNC` ```c static int periodic_sync_gap_event(struct ble_gap_event *event, void *arg) { + (void)arg; + switch (event->type) { -#if CONFIG_EXAMPLE_EXTENDED_ADV - case BLE_GAP_EVENT_EXT_DISC: - /* An advertisment report was received during GAP discovery. */ - struct ble_gap_ext_disc_desc *disc = ((struct ble_gap_ext_disc_desc *)(&event->disc)); + case BLE_GAP_EVENT_EXT_DISC: { + /* An advertisement report was received during GAP discovery. */ + const struct ble_gap_ext_disc_desc *disc = &event->ext_disc; if (disc->sid == 2 && synced == 0) { - synced++; - const ble_addr_t addr; - uint8_t adv_sid; - struct ble_gap_periodic_sync_params params; + struct ble_gap_periodic_sync_params params = {0}; int rc; - memcpy((void *)&addr, (void *)&disc->addr, sizeof(disc->addr)); - memcpy(&adv_sid, &disc->sid, sizeof(disc->sid)); + synced++; params.skip = 10; params.sync_timeout = 1000; - rc = ble_gap_periodic_adv_sync_create(&addr, adv_sid, ¶ms, periodic_sync_gap_event, NULL); + +#if CONFIG_EXAMPLE_PERIODIC_ADV_ENH + params.filter_duplicates = 1; +#endif + rc = ble_gap_periodic_adv_sync_create(&disc->addr, disc->sid, ¶ms, periodic_sync_gap_event, NULL); assert(rc == 0); } return 0; + } case BLE_GAP_EVENT_PERIODIC_REPORT: MODLOG_DFLT(INFO, "Periodic adv report event: \n"); print_periodic_adv_data(event); @@ -329,12 +197,19 @@ periodic_sync_gap_event(struct ble_gap_event *event, void *arg) MODLOG_DFLT(INFO, "Periodic sync lost\n"); print_periodic_sync_lost_data(event); synced = 0; + /* Restart scanning to re-sync */ + periodic_sync_scan(); return 0; - case BLE_GAP_EVENT_PERIODIC_SYNC: + case BLE_GAP_EVENT_PERIODIC_SYNC: MODLOG_DFLT(INFO, "Periodic sync event : \n"); print_periodic_sync_data(event); + if (event->periodic_sync.status != 0) { + synced = 0; + } else { + /* Cancel scanning since sync is established */ + ble_gap_disc_cancel(); + } return 0; -#endif default: return 0; } @@ -343,25 +218,28 @@ periodic_sync_gap_event(struct ble_gap_event *event, void *arg) ### BLE_GAP_EVENT_EXT_DISC -An extended advertising report is received during this event. Once the event is received it is handled by calling the function `ble_gap_periodic_adv_sync_create()`.This method performs the synchronization procedure with periodic advertisers. +An extended advertising report is received during this event. The handler checks if the advertisement's SID (Set ID) matches the expected value (2) and if we haven't already synced. If both conditions are met, it creates a periodic sync using `ble_gap_periodic_adv_sync_create()` with the advertiser's address and SID directly from the extended discovery descriptor. When `CONFIG_EXAMPLE_PERIODIC_ADV_ENH` is enabled, duplicate filtering is turned on so periodic reports are only delivered when the advertising data changes or the Data-Id is updated. ### BLE_GAP_EVENT_PERIODIC_REPORT -Periodic advertisement report is printed in this case.`print_periodic_adv_data()` performs the required printing task. It includes a sync handle, transmit power, data status, data length, and data itself. +Periodic advertisement report data is printed. `print_periodic_adv_data()` displays the sync handle, transmit power, RSSI, data status, data length, and data contents using `ESP_LOG_BUFFER_HEX`. -### BLE_GAP_EVENT_PERIODIC_SYNC_LOST: +### BLE_GAP_EVENT_PERIODIC_SYNC_LOST -Periodic synchronization lost data is printed in this case.`print_periodic_sync_lost_data()` prints the data which includes the sync handle and reason for data loss. here are 2 codes used for data loss reasons. - 1. 13: Timeout - 2. 14: Terminated locally +Periodic synchronization lost data is printed. `print_periodic_sync_lost_data()` prints the sync handle and reason for sync loss: +- 13: Timeout +- 14: Terminated locally + +The `synced` flag is reset to 0 and `periodic_sync_scan()` is called to restart scanning, allowing the device to discover and re-sync with a periodic advertiser. ### BLE_GAP_EVENT_PERIODIC_SYNC -Periodic synchronization data is printed in this. `print_periodic_sync_data()` prints the required data. It includes a periodic sync handle, advertising address, advertising physical channel, periodic advertising interval, and advertiser clock accuracy. +Periodic synchronization establishment data is printed. `print_periodic_sync_data()` prints the status, periodic sync handle, SID, advertising address, advertising PHY, periodic advertising interval, and advertiser clock accuracy. If the sync status is non-zero (failure), the `synced` flag is reset to allow retry. On successful sync establishment (status == 0), `ble_gap_disc_cancel()` is called to stop scanning since a periodic sync has been established and further scanning is unnecessary. ## Conclusion -1. This Walkthrough covers the code explanation for the BLE_PERIODIC_SYNC example. -2. In this example, a passive scan is conducted to detect non-connectable and non-scannable extended advertisements from nearby devices. -3. Once such an advertisement is found, the example establishes a periodic sync with the advertising device. After successfully establishing the periodic sync, the example starts listening to the periodic advertisements transmitted by the advertiser. -4. Also walkthrough includes the periodic synchronization gap events and their handling. \ No newline at end of file +1. This walkthrough covers the code explanation for the BLE_PERIODIC_SYNC example. +2. In this example, an extended passive scan is conducted to detect non-connectable and non-scannable extended advertisements from nearby devices. +3. Once such an advertisement with a matching SID is found, the example establishes a periodic sync with the advertising device. After successfully establishing the periodic sync, the example starts listening to the periodic advertisements transmitted by the advertiser. +4. The example uses `ble_hs_id_infer_auto()` to dynamically determine the correct address type, ensuring compatibility across all supported ESP32 variants. +5. The walkthrough also covers the periodic synchronization gap events and their handling. diff --git a/examples/bluetooth/nimble/ble_phy/phy_cent/main/main.c b/examples/bluetooth/nimble/ble_phy/phy_cent/main/main.c index c05dc3edc9..7181eaedc5 100644 --- a/examples/bluetooth/nimble/ble_phy/phy_cent/main/main.c +++ b/examples/bluetooth/nimble/ble_phy/phy_cent/main/main.c @@ -267,12 +267,10 @@ ext_blecent_should_connect(const struct ble_gap_ext_disc_desc *disc) if (!ad_struct_len || (offset + ad_struct_len + 1 > disc->length_data)) { break; } - /* Search if LE PHY UUID is advertised */ if (disc->data[offset] == 0x03 && disc->data[offset + 1] == 0x03) { if ( disc->data[offset + 2] == 0xAB && disc->data[offset + 3] == 0xF2 ) { return 1; - } } offset += ad_struct_len + 1; diff --git a/examples/bluetooth/nimble/ble_phy/phy_cent/tutorial/Ble_Phy_Central_Example_Walkthrough.md b/examples/bluetooth/nimble/ble_phy/phy_cent/tutorial/Ble_Phy_Central_Example_Walkthrough.md index 4431d3e84c..847c282836 100644 --- a/examples/bluetooth/nimble/ble_phy/phy_cent/tutorial/Ble_Phy_Central_Example_Walkthrough.md +++ b/examples/bluetooth/nimble/ble_phy/phy_cent/tutorial/Ble_Phy_Central_Example_Walkthrough.md @@ -27,7 +27,7 @@ These `includes` are required for the FreeRTOS and underlying system components * `nimble_port.h`: Includes the declaration of functions required for the initialization of the nimble stack. * `nimble_port_freertos.h`: Initializes and enables nimble host task. * `ble_hs.h`: Defines the functionalities to handle the host event -* `ble_svc_gap.h`:Defines the macros for device name ,device apperance and declare the function to set them. +* `ble_svc_gap.h`:Defines the macros for device name ,device appearance and declare the function to set them. * `phy_cent.h`: Defines the macro name `LE_PHY_UUID16` and `LE_PHY_CHR_UUID16`. @@ -48,19 +48,30 @@ app_main(void) } ESP_ERROR_CHECK(ret); - nimble_port_init(); + ret = nimble_port_init(); + if (ret != ESP_OK) { + MODLOG_DFLT(ERROR, "Failed to init nimble %d \n", ret); + return; + } + /* Configure the host. */ 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; /* Initialize data structures to track connected peers. */ +#if MYNEWT_VAL(BLE_INCL_SVC_DISCOVERY) || MYNEWT_VAL(BLE_GATT_CACHING_INCLUDE_SERVICES) + rc = peer_init(MYNEWT_VAL(BLE_MAX_CONNECTIONS), 64, 64, 64, 64); + assert(rc == 0); +#else rc = peer_init(MYNEWT_VAL(BLE_MAX_CONNECTIONS), 64, 64, 64); assert(rc == 0); - +#endif +#if CONFIG_BT_NIMBLE_GAP_SERVICE /* Set the default device name. */ rc = ble_svc_gap_device_name_set("blecent-phy"); assert(rc == 0); +#endif /* XXX Need to have template for store */ ble_store_config_init(); @@ -191,10 +202,10 @@ nimble_port_freertos_init(blecent_host_task); ## Intializaion of LE PHY to Default 1M PHY . - 1M PHY is the default PHY for BLE devices which enables it to provide a data rate of 1 Mbps. It is used while establishing the connection between devices and maintains backward compatibility with all those devices that don't have BLE5.0 support.`set_default_le_phy_before_conn()` function set default LE PHY before establishing a connection. + 1M PHY is the default PHY for BLE devices which enables it to provide a data rate of 1 Mbps. It is used while establishing the connection between devices and maintains backward compatibility with all those devices that don't have BLE5.0 support.`set_default_le_phy()` function set default LE PHY before establishing a connection. ```c -void set_default_le_phy_before_conn(uint8_t tx_phys_mask, uint8_t rx_phys_mask) +void set_default_le_phy(uint8_t tx_phys_mask, uint8_t rx_phys_mask) { int rc = ble_gap_set_prefered_default_le_phy(tx_phys_mask, rx_phys_mask); if (rc == 0) @@ -207,32 +218,11 @@ void set_default_le_phy_before_conn(uint8_t tx_phys_mask, uint8_t rx_phys_mask) } ``` -## Setting LE PHY to preferred LE PHY. +## Connecting on Different PHYs -2M PHY is introduced in BLE5.0 to increase the symbol rate at the physical layer. It provides a symbol rate of 2 Mega symbols per second where each symbol corresponds to a single bit. This allows the user to double the number of bits sent over the air during a given period, or conversely reduce energy consumption for a given amount of data by having the necessary transmit time. +2M PHY is introduced in BLE5.0 to increase the symbol rate at the physical layer. It provides a symbol rate of 2 Mega symbols per second where each symbol corresponds to a single bit. This allows the user to double the number of bits sent over the air during a given period, or conversely reduce energy consumption for a given amount of data by having the necessary transmit time. -The following lines change the default LE PHY to 2M PHY. - -` tx_phys_mask = BLE_HCI_LE_PHY_2M_PREF_MASK` -` rx_phys_mask = BLE_HCI_LE_PHY_2M_PREF_MASK` - -```c -void set_prefered_le_phy_after_conn(uint16_t conn_handle) - { - uint8_t tx_phys_mask = 0, rx_phys_mask = 0; - - tx_phys_mask = BLE_HCI_LE_PHY_2M_PREF_MASK; - - rx_phys_mask = BLE_HCI_LE_PHY_2M_PREF_MASK; - - int rc = ble_gap_set_prefered_le_phy(conn_handle, tx_phys_mask, rx_phys_mask,0); - if (rc == 0) { - MODLOG_DFLT(INFO, "Prefered LE PHY set to LE_PHY_2M successfully"); - } else { - MODLOG_DFLT(ERROR, "Failed to set prefered LE_PHY_2M"); - } - } -``` +Instead of changing the PHY after connection establishment, this example uses `ble_gap_ext_connect()` to initiate connections directly on the preferred PHY. After the first connection (on 1M PHY) is disconnected, the central connects directly on 2M PHY, and then on Coded PHY. ## Read Operation @@ -255,7 +245,7 @@ blecent_read(const struct peer *peer) } rc = ble_gattc_read(peer->conn_handle, chr->chr.val_handle, - NULL, NULL); + blecent_on_read, NULL); if (rc != 0) { MODLOG_DFLT(ERROR, "Error: Failed to read characteristic; rc=%d\n", rc); @@ -271,17 +261,31 @@ blecent_read(const struct peer *peer) ## BLE GAP Connect Event -Once the connection is established `BLE_GAP_EVENT_CONNECT` event occurs. This event is handled by checking the current LE PHY equals 1M PHY and updating it to 2M PHY followed by performing the service discovery. If the connection attempt is failed then central start scanning again using the `blecent_scan` function. +Once the connection is established `BLE_GAP_EVENT_CONNECT` event occurs. This event is handled by logging which PHY the connection was established on, performing the service discovery (if GATT client is enabled), and adding the peer. If the connection attempt failed, the central resumes scanning using the `blecent_scan` function. ```c case BLE_GAP_EVENT_CONNECT: /* A new connection was established or a connection attempt failed. */ if (event->connect.status == 0) { /* Connection successfully established. */ - MODLOG_DFLT(INFO, "Connection established "); + + switch (s_current_phy) { + case BLE_HCI_LE_PHY_1M_PREF_MASK: + MODLOG_DFLT(INFO,"Connection established on 1M Phy"); + break; + + case BLE_HCI_LE_PHY_2M_PREF_MASK: + case BLE_HCI_LE_PHY_1M_PREF_MASK | BLE_HCI_LE_PHY_2M_PREF_MASK: + MODLOG_DFLT(INFO,"Connection established on 2M Phy"); + break; + + case BLE_HCI_LE_PHY_CODED_PREF_MASK: + MODLOG_DFLT(INFO,"Connection established on Coded Phy"); + break; + } rc = ble_gap_conn_find(event->connect.conn_handle, &desc); - assert(rc == 0); + assert(rc == 0); print_conn_desc(&desc); MODLOG_DFLT(INFO, "\n"); @@ -292,10 +296,7 @@ case BLE_GAP_EVENT_CONNECT: return 0; } - if (s_current_phy == BLE_HCI_LE_PHY_1M_PREF_MASK) { - /* Update LE PHY from 1M to 2M */ - set_prefered_le_phy_after_conn(event->connect.conn_handle); - } +#if MYNEWT_VAL(BLE_GATTC) /* Perform service discovery. */ rc = peer_disc_all(event->connect.conn_handle, blecent_on_disc_complete, NULL); @@ -303,6 +304,7 @@ case BLE_GAP_EVENT_CONNECT: MODLOG_DFLT(ERROR, "Failed to discover services; rc=%d\n", rc); return 0; } +#endif } else { /* Connection attempt failed; resume scanning. */ MODLOG_DFLT(ERROR, "Error: Connection failed; status=%d\n", @@ -315,8 +317,8 @@ case BLE_GAP_EVENT_CONNECT: ## BLE GAP Disconnect Event -The connection between Central and Peripheral is terminated when the service discovery is failed or the GATT procedure is completed. `ble_gap_terminate` function is used to terminate the connection which results in the event called `BLE_GAP_EVENT_DISCONNECT`. As the connection is terminated so the event is handled by setting up the default LE PHY to 1M followed by calling the `blecent_scan` function. - +The connection between Central and Peripheral is terminated when the service discovery is failed or the GATT procedure is completed. `ble_gap_terminate` function is used to terminate the connection which results in the event called `BLE_GAP_EVENT_DISCONNECT`. As the connection is terminated, the current PHY is advanced to the next one, and a direct connection is initiated using `ble_gap_ext_connect()` on the preferred PHY. + ```c case BLE_GAP_EVENT_DISCONNECT: /* Connection terminated. */ @@ -341,8 +343,20 @@ The connection between Central and Peripheral is terminated when the service dis case BLE_HCI_LE_PHY_CODED_PREF_MASK: return 0; } - set_default_le_phy_before_conn(s_current_phy, s_current_phy); - blecent_scan(); + + vTaskDelay(200); + + /* Attempt direct connection on 2M or Coded phy now */ + if (s_current_phy == BLE_HCI_LE_PHY_CODED_PREF_MASK) { + MODLOG_DFLT(INFO, " Attempting to initiate connection on Coded PHY \n"); + ble_gap_ext_connect(0, &conn_addr, 30000, BLE_HCI_LE_PHY_CODED_PREF_MASK, + NULL, NULL, NULL, blecent_gap_event, NULL); + } + else if (s_current_phy == BLE_HCI_LE_PHY_2M_PREF_MASK) { + MODLOG_DFLT(INFO, " Attempting to initiate connection on 2M PHY \n"); + ble_gap_ext_connect(0, &conn_addr, 30000, (BLE_HCI_LE_PHY_1M_PREF_MASK | BLE_HCI_LE_PHY_2M_PREF_MASK), + NULL, NULL, NULL, blecent_gap_event, NULL); + } return 0; case BLE_GAP_EVENT_DISC_COMPLETE: @@ -354,7 +368,7 @@ The connection between Central and Peripheral is terminated when the service dis ## Conclusion This Walkthrough covers the code explanation of the BLE_PHY_CENTRAL example. The following points are concluded through this walkthrough. - 1. As the nimble stack is initialized default LE PHY is set to 1M in the `blecent_on_sync` function. - 2. Once the connection is established default LE PHY is updated to 2M PHY and service discovery is performed. - 3. Once the Service discovery completes or fails, a connection is terminated. - 4. Once the connection is terminated, depending on the value current LE PHY it is updated to the preferred LE PHY. \ No newline at end of file + 1. As the nimble stack is initialized, default LE PHY is set to all PHYs (1M, 2M, Coded) in the `blecent_on_sync` function, and scanning begins on 1M PHY. + 2. Once the connection is established, the current PHY is logged and service discovery is performed. + 3. Once the service discovery completes or fails, a connection is terminated. + 4. Once the connection is terminated, a direct connection is initiated on the next PHY (2M, then Coded) using `ble_gap_ext_connect()`. \ No newline at end of file diff --git a/examples/bluetooth/nimble/ble_phy/phy_prph/README.md b/examples/bluetooth/nimble/ble_phy/phy_prph/README.md index e1e08e7271..68ed3e3cc2 100644 --- a/examples/bluetooth/nimble/ble_phy/phy_prph/README.md +++ b/examples/bluetooth/nimble/ble_phy/phy_prph/README.md @@ -58,7 +58,7 @@ I (459) NimBLE: 60:55:f9:f7:3e:23 I (469) NimBLE: I (469) NimBLE: Default LE PHY set successfully -I (479) NimBLE: GAP procedure initiated: extended advertise; instance=1 +I (479) NimBLE: GAP procedure initiated: extended advertise; instance=0 I (479) uart: queue free spaces: 8 I (489) main_task: Returned from app_main() @@ -89,7 +89,7 @@ I (1779) NimBLE: conn_itvl=40 conn_latency=0 supervision_timeout=256 encrypted= I (1789) NimBLE: -I (1789) NimBLE: GAP procedure initiated: extended advertise; instance=1 +I (1789) NimBLE: GAP procedure initiated: extended advertise; instance=0 I (1809) NimBLE: connection established; status=0 I (1809) NimBLE: handle=1 our_ota_addr_type=0 our_ota_addr= @@ -118,7 +118,7 @@ I (3039) NimBLE: conn_itvl=40 conn_latency=0 supervision_timeout=256 encrypted= I (3049) NimBLE: -I (3059) NimBLE: GAP procedure initiated: extended advertise; instance=1 +I (3059) NimBLE: GAP procedure initiated: extended advertise; instance=0 I (3079) NimBLE: connection established; status=0 I (3079) NimBLE: handle=1 our_ota_addr_type=0 our_ota_addr= diff --git a/examples/bluetooth/nimble/ble_phy/phy_prph/main/main.c b/examples/bluetooth/nimble/ble_phy/phy_prph/main/main.c index 915f2b90d1..81ff95800c 100644 --- a/examples/bluetooth/nimble/ble_phy/phy_prph/main/main.c +++ b/examples/bluetooth/nimble/ble_phy/phy_prph/main/main.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2021-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Unlicense OR CC0-1.0 */ @@ -104,7 +104,7 @@ ext_bleprph_advertise(void) { struct ble_gap_ext_adv_params params; struct os_mbuf *data = NULL; - uint8_t instance = 1; + uint8_t instance = 0; int rc; /* use defaults for non-set params */ @@ -118,8 +118,8 @@ ext_bleprph_advertise(void) /*enable connectable advertising for all Phy*/ params.connectable = 1; - /* advertise using random addr */ - params.own_addr_type = BLE_OWN_ADDR_PUBLIC; + /* advertise using the inferred address type */ + params.own_addr_type = own_addr_type; /* Set current phy; get mbuf for scan rsp data; fill mbuf with scan rsp data */ switch (s_current_phy) { diff --git a/examples/bluetooth/nimble/ble_phy/phy_prph/tutorial/Ble_Phy_Peripheral_Example_Walkthrough.md b/examples/bluetooth/nimble/ble_phy/phy_prph/tutorial/Ble_Phy_Peripheral_Example_Walkthrough.md index 49557acee3..16a3d76c3a 100644 --- a/examples/bluetooth/nimble/ble_phy/phy_prph/tutorial/Ble_Phy_Peripheral_Example_Walkthrough.md +++ b/examples/bluetooth/nimble/ble_phy/phy_prph/tutorial/Ble_Phy_Peripheral_Example_Walkthrough.md @@ -78,8 +78,10 @@ app_main(void) ble_hs_cfg.sm_their_key_dist = 1; #endif +#if MYNEWT_VAL(BLE_GATTS) rc = gatt_svr_init_le_phy(); assert(rc == 0); +#endif /* Set the default device name. */ rc = ble_svc_gap_device_name_set("bleprph-phy"); @@ -166,8 +168,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 = blecent_on_reset; - ble_hs_cfg.sync_cb = blecent_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; ``` @@ -221,10 +223,10 @@ nimble_port_freertos_init(bleprph_host_task); ## Intializaion of LE PHY to Default 1M PHY. - 1M PHY is the default PHY for BLE devices which enables it to provide a data rate of 1 Mbps. It is used while establishing the connection between devices and maintains backward compatibility with all those devices that don't have BLE5.0 support.`set_default_le_phy_before_conn()` function set default LE PHY before establishing a connection. + 1M PHY is the default PHY for BLE devices which enables it to provide a data rate of 1 Mbps. It is used while establishing the connection between devices and maintains backward compatibility with all those devices that don't have BLE5.0 support.`set_default_le_phy()` function set default LE PHY before establishing a connection. ```c -void set_default_le_phy_before_conn(uint8_t tx_phys_mask, uint8_t rx_phys_mask) +void set_default_le_phy(uint8_t tx_phys_mask, uint8_t rx_phys_mask) { int rc = ble_gap_set_prefered_default_le_phy(tx_phys_mask, rx_phys_mask); if (rc == 0) @@ -252,15 +254,14 @@ As the phy_cent disconnects the connection link with the peripheral, `BLE_GAP_EV /* Connection terminated; resume advertising. */ -#if CONFIG_EXAMPLE_EXTENDED_ADV switch (s_current_phy) { case BLE_HCI_LE_PHY_1M_PREF_MASK: - /* Setting current phy to create a connection on 2M PHY */ + /* Setting current phy to create connection on 2M PHY */ s_current_phy = BLE_HCI_LE_PHY_2M_PREF_MASK; break; case BLE_HCI_LE_PHY_2M_PREF_MASK: - /* Setting current phy to create a connection on CODED PHY */ + /* Setting current phy to create connection on CODED PHY */ s_current_phy = BLE_HCI_LE_PHY_CODED_PREF_MASK; break; @@ -270,11 +271,8 @@ As the phy_cent disconnects the connection link with the peripheral, `BLE_GAP_EV default: return 0; } - set_default_le_phy_before_conn(s_current_phy, s_current_phy); + ext_bleprph_advertise(); -#else - bleprph_advertise(); -#endif ``` @@ -291,19 +289,22 @@ ext_bleprph_advertise(void) { struct ble_gap_ext_adv_params params; struct os_mbuf *data = NULL; - uint8_t instance = 1; + uint8_t instance = 0; int rc; /* use defaults for non-set params */ memset (¶ms, 0, sizeof(params)); - /* enable connectable advertising */ - params.connectable = 1; - params.scannable = 1; - params.legacy_pdu = 1; + if (s_current_phy == BLE_HCI_LE_PHY_1M_PREF_MASK) { + params.scannable = 1; + params.legacy_pdu = 1; + } - /* advertise using random addr */ - params.own_addr_type = BLE_OWN_ADDR_PUBLIC; + /*enable connectable advertising for all Phy*/ + params.connectable = 1; + + /* advertise using the inferred address type */ + params.own_addr_type = own_addr_type; /* Set current phy; get mbuf for scan rsp data; fill mbuf with scan rsp data */ switch (s_current_phy) { @@ -311,24 +312,24 @@ ext_bleprph_advertise(void) params.primary_phy = BLE_HCI_LE_PHY_1M; params.secondary_phy = BLE_HCI_LE_PHY_1M; data = ext_get_data(ext_adv_pattern_1M, sizeof(ext_adv_pattern_1M)); + params.sid = 0; break; case BLE_HCI_LE_PHY_2M_PREF_MASK: params.primary_phy = BLE_HCI_LE_PHY_1M; params.secondary_phy = BLE_HCI_LE_PHY_2M; data = ext_get_data(ext_adv_pattern_2M, sizeof(ext_adv_pattern_2M)); + params.sid = 1; break; case BLE_HCI_LE_PHY_CODED_PREF_MASK: params.primary_phy = BLE_HCI_LE_PHY_CODED; params.secondary_phy = BLE_HCI_LE_PHY_CODED; data = ext_get_data(ext_adv_pattern_coded, sizeof(ext_adv_pattern_coded)); + params.sid = 2; break; } - //params.tx_power = 127; - params.sid = 1; - params.itvl_min = BLE_GAP_ADV_FAST_INTERVAL1_MIN; params.itvl_max = BLE_GAP_ADV_FAST_INTERVAL1_MIN; diff --git a/examples/bluetooth/nimble/ble_proximity_sensor/proximity_sensor_cent/main/main.c b/examples/bluetooth/nimble/ble_proximity_sensor/proximity_sensor_cent/main/main.c index d746282fc6..4b4bf0d058 100644 --- a/examples/bluetooth/nimble/ble_proximity_sensor/proximity_sensor_cent/main/main.c +++ b/examples/bluetooth/nimble/ble_proximity_sensor/proximity_sensor_cent/main/main.c @@ -261,10 +261,10 @@ ext_ble_prox_cent_should_connect(const struct ble_gap_ext_disc_desc *disc) /* The device has to advertise support for Proximity sensor (link loss) * service (0x1803). */ - do { + while (offset < disc->length_data) { ad_struct_len = disc->data[offset]; - if (!ad_struct_len) { + if (ad_struct_len == 0 || offset + ad_struct_len + 1 > disc->length_data) { break; } @@ -276,8 +276,7 @@ ext_ble_prox_cent_should_connect(const struct ble_gap_ext_disc_desc *disc) } offset += ad_struct_len + 1; - - } while ( offset < disc->length_data ); + } return 0; } diff --git a/examples/bluetooth/nimble/blecent/main/main.c b/examples/bluetooth/nimble/blecent/main/main.c index 6b5fbcc18b..1f11f9f2bd 100644 --- a/examples/bluetooth/nimble/blecent/main/main.c +++ b/examples/bluetooth/nimble/blecent/main/main.c @@ -539,23 +539,24 @@ ext_blecent_should_connect(const struct ble_gap_ext_disc_desc *disc) /* The device has to advertise support for the Alert Notification * service (0x1811). */ - do { + while (offset < disc->length_data) { ad_struct_len = disc->data[offset]; - if (!ad_struct_len) { + if (ad_struct_len == 0 || offset + ad_struct_len + 1 > disc->length_data) { break; } - /* Search if ANS UUID is advertised */ - if (disc->data[offset] == 0x03 && disc->data[offset + 1] == 0x03) { - if ( disc->data[offset + 2] == 0x18 && disc->data[offset + 3] == 0x11 ) { - return 1; + /* Search if ANS UUID (0x1811) is advertised */ + if (ad_struct_len >= 3 && (disc->data[offset + 1] == 0x02 || disc->data[offset + 1] == 0x03)) { + for (int i = 2; i + 1 <= ad_struct_len; i += 2) { + if (disc->data[offset + i] == 0x18 && disc->data[offset + i + 1] == 0x11) { + return 1; + } } } offset += ad_struct_len + 1; - - } while ( offset < disc->length_data ); + } return 0; } @@ -1110,6 +1111,8 @@ app_main(void) #if NIMBLE_BLE_CONNECT #if MYNEWT_VAL(STATIC_PASSKEY) + /* WARNING: Hardcoded passkey for demonstration only. + * In production, generate a random passkey per pairing. */ ble_sm_configure_static_passkey(456789, true); #endif diff --git a/examples/bluetooth/nimble/bleprph/main/main.c b/examples/bluetooth/nimble/bleprph/main/main.c index af47c35424..8b34454043 100644 --- a/examples/bluetooth/nimble/bleprph/main/main.c +++ b/examples/bluetooth/nimble/bleprph/main/main.c @@ -370,6 +370,8 @@ bleprph_gap_event(struct ble_gap_event *event, void *arg) if (event->passkey.params.action == BLE_SM_IOACT_DISP) { pkey.action = event->passkey.params.action; + /* WARNING: Hardcoded passkey for demonstration only. + * In production, generate a random passkey per pairing. */ pkey.passkey = 123456; // This is the passkey to be entered on peer ESP_LOGI(tag, "Enter passkey %" PRIu32 "on the peer side", pkey.passkey); rc = ble_sm_inject_io(event->passkey.conn_handle, &pkey); @@ -600,6 +602,8 @@ app_main(void) #endif #if MYNEWT_VAL(STATIC_PASSKEY) && NIMBLE_BLE_CONNECT + /* WARNING: Hardcoded passkey for demonstration only. + * In production, generate a random passkey per pairing. */ ble_sm_configure_static_passkey(456789, true); #endif diff --git a/examples/bluetooth/nimble/bleprph_host_only/main/gatt_svr.c b/examples/bluetooth/nimble/bleprph_host_only/main/gatt_svr.c index 8a9d8b0e7c..3b4b4d6a2d 100644 --- a/examples/bluetooth/nimble/bleprph_host_only/main/gatt_svr.c +++ b/examples/bluetooth/nimble/bleprph_host_only/main/gatt_svr.c @@ -1,16 +1,11 @@ /* - * SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2015-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ -#include -#include -#include #include "host/ble_hs.h" -#include "host/ble_uuid.h" #include "services/gap/ble_svc_gap.h" #include "services/gatt/ble_svc_gatt.h" -#include "bleprph.h" #include "services/ans/ble_svc_ans.h" /*** Maximum number of characteristics with the notify flag ***/ diff --git a/examples/bluetooth/nimble/bleprph_host_only/main/main.c b/examples/bluetooth/nimble/bleprph_host_only/main/main.c index 4429b14dfd..2c829415f2 100644 --- a/examples/bluetooth/nimble/bleprph_host_only/main/main.c +++ b/examples/bluetooth/nimble/bleprph_host_only/main/main.c @@ -4,14 +4,12 @@ * SPDX-License-Identifier: Apache-2.0 */ -#include "esp_log.h" #include "nvs_flash.h" /* BLE */ #include "nimble/nimble_port.h" #include "nimble/nimble_port_freertos.h" #include "host/ble_hs.h" #include "host/util/util.h" -#include "console/console.h" #include "services/gap/ble_svc_gap.h" #include "bleprph.h" #include "uart_driver.h" @@ -345,6 +343,8 @@ bleprph_gap_event(struct ble_gap_event *event, void *arg) if (event->passkey.params.action == BLE_SM_IOACT_DISP) { pkey.action = event->passkey.params.action; + /* WARNING: Hardcoded passkey for demonstration only. + * In production, generate a random passkey per pairing. */ pkey.passkey = 123456; // This is the passkey to be entered on peer ESP_LOGI(tag, "Enter passkey %" PRIu32 "on the peer side", pkey.passkey); rc = ble_sm_inject_io(event->passkey.conn_handle, &pkey); diff --git a/examples/bluetooth/nimble/common/nimble_central_utils/misc.c b/examples/bluetooth/nimble/common/nimble_central_utils/misc.c index 08537545be..1c89f7e390 100644 --- a/examples/bluetooth/nimble/common/nimble_central_utils/misc.c +++ b/examples/bluetooth/nimble/common/nimble_central_utils/misc.c @@ -1,15 +1,10 @@ /* - * SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2021-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Unlicense OR CC0-1.0 */ -#include -#include -#include #include "host/ble_hs.h" -#include "host/ble_uuid.h" -#include "esp_central.h" /** * Utility function to log an array of bytes. diff --git a/examples/bluetooth/nimble/common/nimble_central_utils/peer.c b/examples/bluetooth/nimble/common/nimble_central_utils/peer.c index 1034cff33b..3d2626913d 100644 --- a/examples/bluetooth/nimble/common/nimble_central_utils/peer.c +++ b/examples/bluetooth/nimble/common/nimble_central_utils/peer.c @@ -4,8 +4,6 @@ * SPDX-License-Identifier: Unlicense OR CC0-1.0 */ -#include -#include #include "host/ble_hs.h" #include "esp_central.h" diff --git a/examples/bluetooth/nimble/power_save/main/main.c b/examples/bluetooth/nimble/power_save/main/main.c index 20cc5195ec..d8bef88564 100644 --- a/examples/bluetooth/nimble/power_save/main/main.c +++ b/examples/bluetooth/nimble/power_save/main/main.c @@ -427,6 +427,8 @@ bleprph_gap_event(struct ble_gap_event *event, void *arg) if (event->passkey.params.action == BLE_SM_IOACT_DISP) { pkey.action = event->passkey.params.action; + /* WARNING: Hardcoded passkey for demonstration only. + * In production, generate a random passkey per pairing. */ pkey.passkey = 123456; // This is the passkey to be entered on peer ESP_LOGI(tag, "Enter passkey %" PRIu32 "on the peer side", pkey.passkey); rc = ble_sm_inject_io(event->passkey.conn_handle, &pkey); diff --git a/examples/bluetooth/nimble/throughput_app/README.md b/examples/bluetooth/nimble/throughput_app/README.md index 0b26ba21db..f19b438111 100644 --- a/examples/bluetooth/nimble/throughput_app/README.md +++ b/examples/bluetooth/nimble/throughput_app/README.md @@ -1,16 +1,37 @@ -# Throughput demo Examples +# Throughput Demo Examples -There are two different example folders inside this `throughput_app`, `bleprph_throughput` and `blecent_throughput`. As the names suggest, both of them play role of `peripheral` and `central` respectively. These example demonstrate application throughput for NimBLE on ESP32. Two ESP32 boards are needed to run this demo. The `blecent_throughput` example has CLI support to select GATT operation from READ/WRITE/NOTIFY. It can also accept connection parameters at start of program, more details can be found in respective READMEs. +There are two example folders inside this `throughput_app`: `bleprph_throughput` (peripheral) and `blecent_throughput` (central). These examples demonstrate BLE GATT throughput measurement using NimBLE on ESP32. Two ESP32 boards are needed to run this demo. The `blecent_throughput` example has CLI support to select GATT operation from READ/WRITE/NOTIFY and configure connection parameters at runtime. More details can be found in respective READMEs. -## Using the examples +## Using the Examples -First build and flash two ESP32 boards with `bleprph_throughput` and `blecent_throughput` examples. The central automatically scans and connects to peripheral based on peripheral name string (`nimble_prph`). In the next step, user may choose to configure connection parameters (`MTU`, `connection interval`, `latency`, `supervision timeout`, `connection event length`). In the next step, user needs to specify throughput test name (`read`, `write` or `notify`) and test time in seconds. Below are -sample numbers of different throughput test runs for 60 seconds (MTU = 512, conn itvl = 7.5msec, conn event length = 7.5msec) +Build and flash two ESP32 boards with `bleprph_throughput` and `blecent_throughput` examples. The central automatically scans and connects to the peripheral based on device name (`nimble_prph`). After connection, the user may optionally configure connection parameters (`MTU`, `connection interval`, `latency`, `supervision timeout`, `connection event length`). Then the user specifies the throughput test type (`read`, `write` or `notify`) and test duration in seconds. -|GATT method | Measurement time | Application Throughput| +Below are sample throughput numbers for a 60-second test run (MTU = 512, conn itvl = 7.5ms, DLE = 251 bytes, 1M PHY): + +|GATT Method | Measurement Time | Application Throughput| |--- | --- | ---| -|NOTIFY | 60 seconds | ~340Kbps| -|READ | 60 seconds | ~200kbps| -|WRITE | 60 seconds | ~500kbps| +|NOTIFY | 60 seconds | ~340 Kbps| +|READ | 60 seconds | ~200 Kbps| +|WRITE | 60 seconds | ~500 Kbps| -The notify output is seen on `bleprph_throughput` console and read/write throughput are seen on `blecent_throughput` console. +The notify throughput output is displayed on the `bleprph_throughput` console, while read/write throughput results are shown on the `blecent_throughput` console. + +## Throughput Optimization + +The following parameters have the most significant impact on throughput: + +1. **Connection Interval**: The default is 7.5ms (minimum allowed by BLE spec). Shorter intervals mean more connection events per second and higher throughput. + +2. **Connection Event Length**: Controls how long a single connection event can last. The default maximum CE length is 15ms, allowing the controller to extend events and transmit more PDUs per event when possible. + +3. **Notification Pipelining**: The peripheral queues multiple notifications simultaneously (pipeline depth of 15) rather than waiting for each one to complete before sending the next. This allows the controller to pack multiple PDUs into each connection event. + +4. **Payload Size**: Payload sizes are set to the maximum ATT capacity for each operation (509 bytes for notify/write, 510 bytes for read) to maximize data per PDU. + +5. **Data Length Extension (DLE)**: LL packet length is set to 251 bytes to use the maximum Link Layer PDU size. + +6. **MTU**: Set to 512 bytes to allow large ATT payloads and reduce protocol overhead. + +7. **MSYS Buffer Count**: Both peripheral and central are configured with 50 MSYS blocks (`CONFIG_BT_NIMBLE_MSYS_1_BLOCK_COUNT=50`) to provide sufficient buffer space for high-throughput operations. + +8. **PHY**: On BLE 5.0 supported chipsets, 2M PHY can be selected to double the air data rate. Use Extended Advertising mode and specify PHY in the throughput CLI command. diff --git a/examples/bluetooth/nimble/throughput_app/blecent_throughput/README.md b/examples/bluetooth/nimble/throughput_app/blecent_throughput/README.md index 51cd8f3733..6db1595be7 100644 --- a/examples/bluetooth/nimble/throughput_app/blecent_throughput/README.md +++ b/examples/bluetooth/nimble/throughput_app/blecent_throughput/README.md @@ -7,19 +7,20 @@ `blecent_throughput` demonstrates client side implementation required for NimBLE throughput example. It connects to `bleprph_throughput` based on name string `nimble_prph`. It has interactive CLI support to start READ/WRITE/NOTIFY GATT operation for user specified time. -It performs read operation on peripheral's `THRPT_LONG_CHR_READ_WRITE` characteristic, write operation on `THRPT_CHR_READ_WRITE` and subscribes to `THRPT_CHR_NOTIFY` characteristic for notifications. If user does not specify any throughput test method for 30 seconds (`BLE_RX_TIMEOUT`) then the program starts with default READ operations for 60 seconds. +It performs read operations on the peripheral's `THRPT_LONG_CHR_READ_WRITE` characteristic (510 bytes), write operations on `THRPT_CHR_READ_WRITE` (509-byte payload), and subscribes to `THRPT_CHR_NOTIFY` for notifications. If the user does not specify a throughput test method within 30 seconds (`BLE_RX_TIMEOUT`), the program defaults to READ operations for 60 seconds. `blecent_throughput` uses the `NimBLE` as BLE host. -### Procedure to use this demo example +### Procedure to Use This Demo Example -* `idf.py menuconfig` and configure the parameters as needed (connection related parameters in example parameters). +* `idf.py menuconfig` and configure the parameters as needed (connection related parameters in Example Configuration). +* The default connection interval is set to 7.5ms (6 units) for maximum throughput. This can be changed via menuconfig or at runtime. * To test throughput on 1M PHY, make sure to disable the Example Configuration -> Enable Extended Adv flag in menuconfig on both sides. * To test on 2M PHY or Coded PHY (S2/S8), you must enable the Example Configuration -> Enable Extended Adv flag on both sides. * `bleprph_throughput` example needs to be run along with this client side example. -* After connection link is established between these two devices, user is given a window of `YES_NO_PARAM` (5 seconds) to customize connection parameters. If user has configured parameters from menuconfig, this step can be skipped by either waiting it out or entering `Insert no`. -* User needs to enter `Insert yes` to customize connection parameters. Enter `MTU` and other connection parameters as directed on console instructions e.g. `MTU 512` for MTU and `conn 6 120 0 500 0 0` for connection parameters in sequence of `min conn_itvl`, `max conn_itvl`, `latency`, `supervision timeout`, `min conn_evt_len` and `max_conn_evt_len`. -* User will be now presented with throughput test related console prints, this suggests application is now ready to be run throughput test for user defined time. The prints may appear like below +* After the connection link is established between the two devices, the user is given a window of `YES_NO_PARAM` (5 seconds) to customize connection parameters. If the user has configured parameters from menuconfig, this step can be skipped by either waiting it out or entering `Insert No`. +* Enter `Insert Yes` to customize connection parameters. Enter `MTU` and other connection parameters as directed on console instructions, e.g. `MTU 512` for MTU and `conn 6 6 0 500 12 24` for connection parameters in sequence of `min conn_itvl`, `max conn_itvl`, `latency`, `supervision timeout`, `min conn_evt_len` and `max_conn_evt_len`. +* The user will be presented with throughput test related console prints. The prints may appear like below ``` ==================================================================================== @@ -41,8 +42,8 @@ It performs read operation on peripheral's `THRPT_LONG_CHR_READ_WRITE` character ``` -* If user fail to enter any values for next 30 seconds, the app falls to default behavior of READ for 60 seconds mode and 1M phy channel. -* Read and write throughput numbers will be presented in `blecent_throughput` console output. For notification `bleprph_throughput` console shall be referred, as the peripheral is the one who is sending notifications. Below is the sample output of the app: +* If the user fails to enter any values within 30 seconds, the app falls to the default behavior of READ for 60 seconds on 1M PHY. +* Read and write throughput numbers are displayed on the `blecent_throughput` console. For notification throughput, refer to the `bleprph_throughput` console, as the peripheral is the one sending notifications. Below is sample output: ``` Type 'help' to get the list of commands. @@ -136,9 +137,11 @@ idf.py -p PORT flash monitor See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects. -## Example scope +## Example Scope -This demo example tries to demonstrate stable implementation of GATT operations like read/write and notify. The READ and WRITE GATT operations require ACK from peer, so this central app makes sure that next GATT operation is performed after completion of previous one. In case of notification (`bleprph_throughput` sends) operation there is no waiting for ACK, however one needs to mind `os_mbufs` getting full, so one may need to allocate higher number of mbufs through menuconfig. +This demo example demonstrates stable GATT read/write and notify operations at high throughput. The READ and WRITE GATT operations require an ACK from the peer, so this central app ensures that the next GATT operation is performed after completion of the previous one. For write operations, `ble_gattc_write_no_rsp_flat` is used (Write Without Response) which does not require an ATT-level ACK, allowing higher write throughput. If the mbuf pool is exhausted (`BLE_HS_ENOMEM`), the app yields briefly and retries. + +For notifications (`bleprph_throughput` sends), the peripheral uses pipelined notification sending with a configurable pipeline depth, allowing the controller to pack multiple PDUs per connection event. Both sides are configured with `CONFIG_BT_NIMBLE_MSYS_1_BLOCK_COUNT=50` to provide sufficient buffer space for high-throughput operations. ## Example output diff --git a/examples/bluetooth/nimble/throughput_app/blecent_throughput/main/Kconfig.projbuild b/examples/bluetooth/nimble/throughput_app/blecent_throughput/main/Kconfig.projbuild index 6cca927de7..6750fc74f5 100644 --- a/examples/bluetooth/nimble/throughput_app/blecent_throughput/main/Kconfig.projbuild +++ b/examples/bluetooth/nimble/throughput_app/blecent_throughput/main/Kconfig.projbuild @@ -8,15 +8,17 @@ menu "Example Configuration" config EXAMPLE_CONN_ITVL_MIN int "Minimum connection itvl" - default 104 + default 6 help Set the minimum connection interval in 1.25msec units. + Default 6 (7.5ms) for maximum throughput. config EXAMPLE_CONN_ITVL_MAX int "Maximum connection itvl" - default 104 + default 6 help Set the maximum connection interval in 1.25msec units. + Default 6 (7.5ms) for maximum throughput. config EXAMPLE_CONN_LATENCY int "Connection latency" @@ -35,12 +37,14 @@ menu "Example Configuration" default 12 help Set the minimum connection event length in 0.625msec units. + Default 12 (7.5ms) matches the minimum connection interval. config EXAMPLE_CONN_CE_LEN_MAX int "Maximum connection event length" - default 12 + default 24 help Set the maximum connection event length in 0.625msec units. + Default 24 (15ms) allows controller to extend events when possible. config EXAMPLE_EXTENDED_ADV bool diff --git a/examples/bluetooth/nimble/throughput_app/blecent_throughput/main/main.c b/examples/bluetooth/nimble/throughput_app/blecent_throughput/main/main.c index db76f0e859..5e0ef1e2e0 100644 --- a/examples/bluetooth/nimble/throughput_app/blecent_throughput/main/main.c +++ b/examples/bluetooth/nimble/throughput_app/blecent_throughput/main/main.c @@ -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 */ @@ -49,8 +49,8 @@ #define WRITE_THROUGHPUT 2 #define NOTIFY_THROUGHPUT 3 -#define READ_THROUGHPUT_PAYLOAD 500 -#define WRITE_THROUGHPUT_PAYLOAD 495 +#define READ_THROUGHPUT_PAYLOAD 510 /* MTU(512) - ATT read rsp header(1) - 1 (avoid Read Blob) */ +#define WRITE_THROUGHPUT_PAYLOAD 509 /* MTU(512) - ATT write cmd header(3) */ #define LL_PACKET_TIME 2120 #define LL_PACKET_LENGTH 251 static const char *tag = "blecent_throughput"; @@ -59,7 +59,7 @@ static SemaphoreHandle_t xSemaphore; static int mbuf_len_total; static int failure_count; static TaskHandle_t throughput_task_handle = NULL; -static int conn_params_def[] = {40, 40, 0, 500, 80, 80}; +static int conn_params_def[] = {6, 6, 0, 500, 12, 24}; /* test_data accepts test_name and test_time from CLI */ static int test_data[6] = {1, 600, 0, 0, 0, 0}; static int mtu_def = 512; diff --git a/examples/bluetooth/nimble/throughput_app/blecent_throughput/sdkconfig.defaults b/examples/bluetooth/nimble/throughput_app/blecent_throughput/sdkconfig.defaults index a8b0c5145c..215ee57cb5 100644 --- a/examples/bluetooth/nimble/throughput_app/blecent_throughput/sdkconfig.defaults +++ b/examples/bluetooth/nimble/throughput_app/blecent_throughput/sdkconfig.defaults @@ -13,5 +13,6 @@ CONFIG_BT_NIMBLE_ENABLED=y CONFIG_BT_NIMBLE_ATT_PREFERRED_MTU=512 CONFIG_BT_NIMBLE_TRANSPORT_ACL_FROM_LL_COUNT=20 CONFIG_BT_NIMBLE_TRANSPORT_EVT_SIZE=255 +CONFIG_BT_NIMBLE_MSYS_1_BLOCK_COUNT=50 CONFIG_BT_NIMBLE_LOG_LEVEL=4 CONFIG_BT_NIMBLE_LOG_LEVEL_NONE=y diff --git a/examples/bluetooth/nimble/throughput_app/bleprph_throughput/README.md b/examples/bluetooth/nimble/throughput_app/bleprph_throughput/README.md index f22f9effc5..d43009f167 100644 --- a/examples/bluetooth/nimble/throughput_app/bleprph_throughput/README.md +++ b/examples/bluetooth/nimble/throughput_app/bleprph_throughput/README.md @@ -5,7 +5,7 @@ (See the README.md file in the upper level 'examples' directory for more information about examples.) -`bleprph_throughput` demonstrates server side implementation required for NimBLE throughput example. It has characteristics supporting READ, WRITE and NOTIFY (`PTS_LONG_CHR_READ_WRITE`,`PTS_CHR_READ_WRITE`,`PTS_CHR_NOTIFY`). The data of 500 Bytes (`READ_THROUGHPUT_PAYLOAD`) and 400 Bytes (`WRITE_THROUGHPUT_PAYLOAD`) is transferred for throughput GATT read and write operations respectively. +`bleprph_throughput` demonstrates server side implementation required for NimBLE throughput example. It has characteristics supporting READ, WRITE and NOTIFY (`THRPT_LONG_CHR_READ_WRITE`, `THRPT_CHR_READ_WRITE`, `THRPT_CHR_NOTIFY`). The read characteristic holds 510 bytes (`READ_THROUGHPUT_PAYLOAD`) and the write characteristic accepts up to 509 bytes (`WRITE_THROUGHPUT_PAYLOAD`). Notifications are sent with a 509-byte payload (`NOTIFY_THROUGHPUT_PAYLOAD`). These sizes are chosen to maximize ATT PDU utilization with the default MTU of 512. `bleprph_throughput` uses the `nimble` component as BLE host. @@ -61,7 +61,9 @@ I (83943) bleprph_throughput: Notification test completed for stipulated time o > Here, bps is bits per second; count is number of Notifications successfully sent. -## Example scope +## Example Scope -This demo example along with `blecent_throughput` tries to demonstrate stable implementation of GATT operations like read/write and notify. For `bleprph_throughput` app, notifications are sent almost continuously for stipulated period of time. The almost part is because we use counting semaphore (~100) to mimic continuous notifications. Here one needs to understand that notifications are sent in `os_mbufs` packets and there can always be chance of them getting full because of continuous operation, so one may need to allocate higher number of mbufs through menuconfig, whenever there is `os_mbuf` memory exhaustion, app provides delay so NimBLE host stack can breathe and free `mbuf chains`. +This demo example along with `blecent_throughput` demonstrates stable GATT read/write and notify operations at high throughput. For notifications, the peripheral uses a pipelined approach: a counting semaphore (max 100) allows multiple notifications to be queued simultaneously (pipeline depth of 15). This enables the BLE controller to pack multiple PDUs into each connection event for maximum throughput. + +Notifications are sent in `os_mbufs` packets. The example is configured with `CONFIG_BT_NIMBLE_MSYS_1_BLOCK_COUNT=50` to provide sufficient buffer space. If mbuf exhaustion occurs during continuous transfer, the app yields briefly to allow the NimBLE host stack to free completed `mbuf chains` before retrying. diff --git a/examples/bluetooth/nimble/throughput_app/bleprph_throughput/main/gatt_svr.c b/examples/bluetooth/nimble/throughput_app/bleprph_throughput/main/gatt_svr.c index 4af6b559de..40adb0e4d3 100644 --- a/examples/bluetooth/nimble/throughput_app/bleprph_throughput/main/gatt_svr.c +++ b/examples/bluetooth/nimble/throughput_app/bleprph_throughput/main/gatt_svr.c @@ -33,8 +33,8 @@ #define THRPT_CHR_NOTIFY 0x000a #define THRPT_LONG_CHR_READ_WRITE 0x000b -#define READ_THROUGHPUT_PAYLOAD 500 -#define WRITE_THROUGHPUT_PAYLOAD 500 +#define READ_THROUGHPUT_PAYLOAD 510 /* MTU(512) - ATT read rsp header(1) - 1 (avoid Read Blob) */ +#define WRITE_THROUGHPUT_PAYLOAD 509 /* MTU(512) - ATT write cmd header(3) */ static const char *tag = "bleprph_throughput"; diff --git a/examples/bluetooth/nimble/throughput_app/bleprph_throughput/main/main.c b/examples/bluetooth/nimble/throughput_app/bleprph_throughput/main/main.c index da2d922f0d..27d95cc72d 100644 --- a/examples/bluetooth/nimble/throughput_app/bleprph_throughput/main/main.c +++ b/examples/bluetooth/nimble/throughput_app/bleprph_throughput/main/main.c @@ -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 */ @@ -30,8 +30,9 @@ static uint8_t s_current_phy; static const char *device_name = "nimble_prph"; -#define NOTIFY_THROUGHPUT_PAYLOAD 495 -#define MIN_REQUIRED_MBUF 2 /* Assuming payload of 500Bytes and each mbuf can take 292Bytes. */ +#define NOTIFY_THROUGHPUT_PAYLOAD 509 /* MTU(512) - ATT notify header(3) */ +#define MIN_REQUIRED_MBUF 2 /* Assuming payload of 500Bytes and each mbuf can take 292Bytes. */ +#define NOTIFY_PIPELINE_DEPTH 15 /* Number of notifications to keep in flight for throughput */ #define PREFERRED_MTU_VALUE 512 #define LL_PACKET_TIME 2120 #define LL_PACKET_LENGTH 251 @@ -276,8 +277,8 @@ notify_task(void *arg) do { om = ble_hs_mbuf_from_flat(payload, sizeof(payload)); if (om == NULL) { - /* Memory not available for mbuf */ - vTaskDelay(100 / portTICK_PERIOD_MS); + /* Memory not available for mbuf, yield briefly */ + vTaskDelay(1); } } while (om == NULL); @@ -286,18 +287,14 @@ notify_task(void *arg) ESP_LOGE(tag, "Error while sending notification; rc = %d", rc); notify_count -= 1; xSemaphoreGive(notify_sem); - /* Most probably error is because we ran out of mbufs (rc = 6), - * increase the mbuf count/size from menuconfig. Though - * inserting delay is not good solution let us keep it - * simple for time being so that the mbufs get freed up - * (?), of course assumption is we ran out of mbufs */ - vTaskDelay(10 / portTICK_PERIOD_MS); + /* Yield to let mbufs free up */ + vTaskDelay(1); } } else { - ESP_LOGE(tag, "Not enough OS_MBUFs available; reduce notify count "); xSemaphoreGive(notify_sem); notify_count -= 1; - vTaskDelay(10 / portTICK_PERIOD_MS); + /* Yield briefly to let mbufs free up */ + vTaskDelay(1); } end_time = esp_timer_get_time(); @@ -395,7 +392,14 @@ gatts_gap_event(struct ble_gap_event *event, void *arg) ESP_LOGI(tag, "notify test time = %d", *(int *)arg); notify_test_time = *((int *)arg); } - xSemaphoreGive(notify_sem); + if (notify_state) { + /* Prime the notification pipeline to allow multiple in-flight + * notifications. This enables the controller to fill connection + * events with back-to-back PDUs for maximum throughput. */ + for (int i = 0; i < NOTIFY_PIPELINE_DEPTH; i++) { + xSemaphoreGive(notify_sem); + } + } } else if (event->subscribe.attr_handle != notify_handle) { notify_state = event->subscribe.cur_notify; }