# BLE Periodic Advertisement with Responses (PAwR) Sync Example Walkthrough

## Introduction

This tutorial examines the BLE Periodic Advertisement with Responses (PAwR) Sync example code for ESP32 chipsets with BLE 5.0+ support. The code demonstrates how to implement a scanner/synchronizer device that connects to a PAwR advertiser, establishes synchronization, and exchanges data through response slots.

## Includes

This example is located in the examples folder of the ESP-IDF under the [ble_pawr_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"
```
These includes provide:

- ESP32 logging functionality (esp_log.h)

- Non-volatile storage (nvs_flash.h)

- NimBLE stack porting and FreeRTOS integration

- BLE host stack functionality

- Utility functions for BLE operations

## Main Entry Point

The program’s entry point is the app_main() function:
```c
void app_main(void)
{
    esp_err_t ret;

    /* Initialize NVS — it is used to store PHY calibration data */
    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 = on_reset;
    ble_hs_cfg.sync_cb = on_sync;

    nimble_port_freertos_init(pawr_host_task);
}
```

Key steps:

- Initialize NVS flash storage for PHY calibration data

- Initialize the NimBLE stack

- Set up reset and sync callbacks

Start the NimBLE 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;
}
```

## Configuration Parameters

The example defines several key parameters:

```c
#define TAG                     "NimBLE_BLE_PAwR"
#define TARGET_NAME             "Nimble_PAwR"
#define BLE_PAWR_RSP_DATA_LEN   (16)
static uint8_t sub_data_pattern[BLE_PAWR_RSP_DATA_LEN] = {0};
```
These parameters control:

- Target advertiser name to sync with

- Response data length

- Data pattern for responses


## GAP Event Callback
The gap_event_cb function handles PAwR sync events:
```c
static int gap_event_cb(struct ble_gap_event *event, void *arg)
{
    switch (event->type) {
    case BLE_GAP_EVENT_EXT_DISC:
        /* Handle advertiser discovery */
        break;

    case BLE_GAP_EVENT_PERIODIC_REPORT:
        /* Handle periodic reports and send responses */
        break;

    case BLE_GAP_EVENT_PERIODIC_SYNC:
        /* Handle sync establishment */
        break;

    case BLE_GAP_EVENT_PERIODIC_SYNC_LOST:
        /* Handle sync loss */
        break;
    }
    return 0;
}
```

## Scanning and Synchronization
1. Starting the Scan

```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
    };

    ble_gap_ext_disc(BLE_OWN_ADDR_PUBLIC, 0, 0, 1, 0, 0, NULL, 
                    &disc_params, gap_event_cb, NULL);
}
```
Key parameters:

- Scan interval: 600ms

- Scan window: 300ms

- Passive scanning mode

2. Creating Periodic Sync

```c
static int create_periodic_sync(struct ble_gap_ext_disc_desc *disc)
{
    struct ble_gap_periodic_sync_params params = {
        .skip = 0,
        .sync_timeout = 4000,  // 4 seconds timeout
        .reports_disabled = 0
    };

    #if CONFIG_EXAMPLE_PERIODIC_ADV_ENH
        params.filter_duplicates = 1;
    #endif

    return ble_gap_periodic_adv_sync_create(&disc->addr, disc->sid, 
                                          &params, gap_event_cb, NULL);
}
```

## Response Handling
When receiving periodic reports:

```c
case BLE_GAP_EVENT_PERIODIC_REPORT:
    // Prepare response parameters
    struct ble_gap_periodic_adv_response_params param = {
        .request_event = event->periodic_report.event_counter,
        .request_subevent = event->periodic_report.subevent,
        .response_subevent = event->periodic_report.subevent,
        .response_slot = rsp_slot_idx
    };

    // Prepare response data
    sub_data_pattern[0] = event->periodic_report.data[0];
    memset(sub_data_pattern + 1, event->periodic_report.subevent,
          BLE_PAWR_RSP_DATA_LEN - 1);

    // Set response data
    rc = ble_gap_periodic_adv_set_response_data(
        event->periodic_report.sync_handle, &param, data);
    break;
```
## Subevent Configuration

After sync establishment:

```c
// Choose subevents to listen to
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);
```

## Error Handling
When sync is lost:
```c
case BLE_GAP_EVENT_PERIODIC_SYNC_LOST:
    ESP_LOGE(TAG, "[Periodic Sync Lost] handle:%d, Reason = 0x%x",
             event->periodic_sync_lost.sync_handle,
             event->periodic_sync_lost.reason);
    synced = false;
    start_scan();
    return 0;
```

## Conclusion

This implementation demonstrates a complete PAwR synchronization solution, showcasing advertiser discovery via extended scanning, periodic sync establishment with configurable subevents (0-4), and efficient bidirectional communication through managed response slots. The robust architecture handles sync loss recovery while maintaining low-power operation, making it ideal for IoT applications requiring scheduled, bidirectional communication with multiple endpoints. The solution leverages BLE 5.0's PAwR features to optimize power efficiency and reliability in dense RF environments.