mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
(doc) nimble: Added the tutorial for ble_periodic_adv and ble_periodic_syncexamples.
This commit is contained in:
parent
f0437b945f
commit
2264828ae5
@ -0,0 +1,320 @@
|
||||
# BLE Periodic Advertisement Example Walkthrough
|
||||
|
||||
## 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.
|
||||
|
||||
## 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 "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"
|
||||
```
|
||||
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.
|
||||
|
||||
* `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.
|
||||
|
||||
## Main Entry Point
|
||||
|
||||
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) {
|
||||
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;
|
||||
}
|
||||
|
||||
/* Initialize the NimBLE host configuration. */
|
||||
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;
|
||||
/* Set the default device name. */
|
||||
rc = ble_svc_gap_device_name_set("nimble_periodic_adv");
|
||||
assert(rc == 0);
|
||||
|
||||
/* XXX Need to have a 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.
|
||||
```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);
|
||||
|
||||
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
|
||||
|
||||
```c
|
||||
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;
|
||||
```
|
||||
|
||||
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.
|
||||
```c
|
||||
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();
|
||||
```
|
||||
|
||||
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
|
||||
|
||||
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`.
|
||||
|
||||
```c
|
||||
#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
|
||||
```
|
||||
|
||||
## 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.
|
||||
|
||||
## 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.
|
||||
|
||||
```c
|
||||
static void
|
||||
start_periodic_adv(void)
|
||||
{
|
||||
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;
|
||||
ble_addr_t addr;
|
||||
|
||||
/* 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.primary_phy = BLE_HCI_LE_PHY_1M;
|
||||
params.secondary_phy = BLE_HCI_LE_PHY_2M;
|
||||
params.sid = 2;
|
||||
|
||||
/* configure instance 1 */
|
||||
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);
|
||||
|
||||
memset(&adv_fields, 0, sizeof(adv_fields));
|
||||
adv_fields.name = (const uint8_t *)"Periodic ADV";
|
||||
adv_fields.name_len = strlen((char *)adv_fields.name);
|
||||
|
||||
/* Default to legacy PDUs size, mbuf chain will be increased if needed
|
||||
*/
|
||||
data = os_msys_get_pkthdr(BLE_HCI_MAX_ADV_DATA_LEN, 0);
|
||||
assert(data);
|
||||
|
||||
rc = ble_hs_adv_set_fields_mbuf(&adv_fields, data);
|
||||
assert(rc == 0);
|
||||
|
||||
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);
|
||||
|
||||
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);
|
||||
rc = ble_gap_periodic_adv_set_data(instance, data);
|
||||
assert (rc == 0);
|
||||
|
||||
/* start periodic advertising */
|
||||
assert (rc == 0 rc = ble_gap_periodic_adv_start(instance);
|
||||
);
|
||||
|
||||
/* 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.
|
||||
|
||||
## Conclusion
|
||||
|
||||
This Walkthrough covers the code explanation of the BLE_PERIODIC_ADV. The following points are concluded through this walkthrough.
|
||||
|
||||
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.
|
@ -0,0 +1,367 @@
|
||||
# BLE Periodic Sync Example Walkthrough
|
||||
|
||||
## 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.
|
||||
|
||||
## 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 "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"
|
||||
```
|
||||
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.
|
||||
|
||||
* `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.
|
||||
|
||||
## Main Entry Point
|
||||
|
||||
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) {
|
||||
ESP_ERROR_CHECK(nvs_flash_erase());
|
||||
ret = nvs_flash_init();
|
||||
}
|
||||
ESP_ERROR_CHECK(ret);
|
||||
|
||||
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 = periodic_sync_on_reset;
|
||||
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);
|
||||
|
||||
/* Set the default device name. */
|
||||
rc = ble_svc_gap_device_name_set("nimble_periodic_sync");
|
||||
assert(rc == 0);
|
||||
|
||||
/* 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 );
|
||||
```
|
||||
|
||||
## 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);
|
||||
|
||||
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
|
||||
|
||||
```c
|
||||
ble_hs_cfg.reset_cb = periodic_sync_on_reset;
|
||||
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.
|
||||
```c
|
||||
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();
|
||||
```
|
||||
|
||||
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_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
|
||||
|
||||
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
|
||||
|
||||
BLE_OWN_ADDR_RANDOM
|
||||
|
||||
BLE_OWN_ADDR_RPA_PUBLIC_DEFAULT
|
||||
|
||||
BLE_OWN_ADDR_RPA_RANDOM_DEFAULT
|
||||
```
|
||||
|
||||
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.
|
||||
|
||||
```c
|
||||
static void
|
||||
periodic_sync_scan(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);
|
||||
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);
|
||||
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.
|
||||
|
||||
## 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:
|
||||
|
||||
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)
|
||||
{
|
||||
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));
|
||||
if (disc->sid == 2 && synced == 0) {
|
||||
synced++;
|
||||
const ble_addr_t addr;
|
||||
uint8_t adv_sid;
|
||||
struct ble_gap_periodic_sync_params params;
|
||||
int rc;
|
||||
memcpy((void *)&addr, (void *)&disc->addr, sizeof(disc->addr));
|
||||
memcpy(&adv_sid, &disc->sid, sizeof(disc->sid));
|
||||
params.skip = 10;
|
||||
params.sync_timeout = 1000;
|
||||
rc = ble_gap_periodic_adv_sync_create(&addr, adv_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);
|
||||
return 0;
|
||||
case BLE_GAP_EVENT_PERIODIC_SYNC_LOST:
|
||||
MODLOG_DFLT(INFO, "Periodic sync lost\n");
|
||||
print_periodic_sync_lost_data(event);
|
||||
synced = 0;
|
||||
return 0;
|
||||
case BLE_GAP_EVENT_PERIODIC_SYNC:
|
||||
MODLOG_DFLT(INFO, "Periodic sync event : \n");
|
||||
print_periodic_sync_data(event);
|
||||
return 0;
|
||||
#endif
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 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.
|
||||
|
||||
### 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.
|
||||
|
||||
### 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
|
||||
|
||||
### 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.
|
||||
|
||||
## 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.
|
Loading…
Reference in New Issue
Block a user