# BLE Periodic Advertisement with Response Scanner Example Walkthrough (PAwR Sync + Conn)

## Introduction

This tutorial reviews the PAwR Synchronizer with Connection example for ESP32 chipsets using NimBLE. It shows how a scanner:

- Discovers periodic advertisers.

- Synchronizes to scheduled subevents.

- Sends response data in specific slots.

- Accepts connections initiated by the advertiser

## Includes

This example is located in the examples folder of the ESP-IDF under the [ble_pawr_sync_conn/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"          // ESP32 logging
#include "nvs_flash.h"        // Non-volatile storage for PHY calibration
#include "nimble/nimble_port.h"       // NimBLE core initialization
#include "nimble/nimble_port_freertos.h" // NimBLE FreeRTOS support
#include "host/ble_hs.h"      // BLE host stack APIs
#include "host/util/util.h"   // BLE utilities (e.g., address helpers)
```


## Main Entry Point

The program’s entry point is the app_main() function:
```c
void app_main(void) {
    nvs_flash_init();
            .        // Prepare flash for storing calibration data
            .
            .
    nimble_port_init();       // Initialize the BLE controller and host
            .
            .
    ble_hs_cfg.reset_cb = on_reset;  // Register reset callback
    ble_hs_cfg.sync_cb  = on_sync;   // Register sync callback
            .
            .
    nimble_port_freertos_init(pawr_host_task); // Launch BLE host task
}
```
- nvs_flash_init: Ensures persistent storage is ready for BLE keys and PHY data.

- nimble_port_init: Brings up the BT controller (radio + LL) and NimBLE host.

- Callbacks: on_reset handles controller resets; on_sync starts scanning when BLE is ready.

- pawr_host_task: Runs the BLE event loop on a FreeRTOS task.

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

## PAwR Synchronization

### Start Scanning

Configures a passive extended scan to detect periodic advertisers:
```c
static void start_scan(void) {
    struct ble_gap_ext_disc_params d = {
        .itvl   = BLE_GAP_SCAN_ITVL_MS(600), // Scan every 600ms
        .window = BLE_GAP_SCAN_ITVL_MS(300), // Listen for 300ms
        .passive= 1                          // Do not send scan requests
    };
    // Start discovery; gap_event_cb handles each advertisement
    ble_gap_ext_disc(BLE_OWN_ADDR_PUBLIC, 0, 0, 1, 0, 0,
                     NULL, &d, gap_event_cb, NULL);
}
```

- BLE_OWN_ADDR_PUBLIC: Use the device’s public address.

- gap_event_cb: Processes discovery events (EXT_DISC) to find our target.`

## Create Periodic Sync

When a periodic advertiser is found, request synchronization:
```c
static int create_periodic_sync(struct ble_gap_ext_disc_desc *disc) {
    struct ble_gap_periodic_sync_params p = {
        .skip            = 0,    // Do not skip any events
        .sync_timeout    = 4000, // Give 4000ms to establish sync
        .reports_disabled= 0,    // Keep reports enabled
#if CONFIG_EXAMPLE_PERIODIC_ADV_ENH
        .filter_duplicates = 1,  // Only receive when data-id changes
#endif
    };
    // Initiate sync; callback will receive PERIODIC_SYNC
    return ble_gap_periodic_adv_sync_create(
        &disc->addr, disc->sid, &p,
        gap_event_cb, NULL);
}
```
- disc->addr / sid: Address and Sync ID identify the PAwR train.

- ble_gap_periodic_adv_sync_create: Starts low-power sync to periodic events.

## Sending Response Data

Once synchronized, respond during periodic reports:

```c

case BLE_GAPCreate Periodic Sync

When a periodic advertiser is found, request synchronization:

static int create_periodic_sync(struct ble_gap_ext_disc_desc *disc) {
    struct ble_gap_periodic_sync_params p = {
        .skip            = 0,    // Do not skip any events
        .sync_timeout    = 4000, // Give 4000ms to establish sync
        .reports_disabled= 0,    // Keep reports enabled
#if CONFIG_EXAMPLE_PERIODIC_ADV_ENH
        .filter_duplicates = 1,  // Only receive when data-id changes
#endif
    };
    // Initiate sync; callback will receive PERIODIC_SYNC
    return ble_gap_periodic_adv_sync_create(
        &disc->addr, disc->sid, &p,
        gap_event_cb, NULL);
}

disc->addr / sid: Address and Sync ID identify the PAwR train.

ble_gap_periodic_adv_sync_create: Starts low-power sync to periodic events.

_EVENT_PERIODIC_REPORT: {
    struct ble_gap_periodic_adv_response_params r = {
        .request_event    = event->periodic_report.event_counter,
        .request_subevent = event->periodic_report.subevent,
        .response_subevent= event->periodic_report.subevent,
        .response_slot    = 2, // Always use slot 2
    };
    // Allocate buffer for response payload
    struct os_mbuf *m = os_msys_get_pkthdr(BLE_PAWR_RSP_DATA_LEN, 0);
    // First byte: subevent index
    sub_data_pattern[0] = event->periodic_report.subevent;
    // Next 6 bytes: our public address
    ble_hs_id_copy_addr(BLE_ADDR_PUBLIC, device_addr, NULL);
    memcpy(&sub_data_pattern[1], device_addr, BLE_DEV_ADDR_LEN);
    // Fill remaining bytes with slot index
    sub_data_pattern[7] = r.response_slot;
    os_mbuf_append(m, sub_data_pattern, BLE_PAWR_RSP_DATA_LEN);
    // Send response data back to advertiser
    ble_gap_periodic_adv_set_response_data(
        event->periodic_report.sync_handle,
        &r, m);
    break;
}
```
- os_msys_get_pkthdr: Allocates memory for the response.

- Payload layout: [subevent, 6-byte address, slot index].

- ble_gap_periodic_adv_set_response_data: Transmits response in the next slot.


## Accepting Connections

The advertiser may connect after receiving our response:

```c
case BLE_GAP_EVENT_CONNECT:
    if (event->connect.status == 0) {
        ble_gap_conn_find(event->connect.conn_handle, &desc);
        print_conn_desc(&desc); // Log connection parameters and addresses
    }
    break;

case BLE_GAP_EVENT_DISCONNECT:
    // Handle cleanup when connection ends
    break;
```
- BLE_GAP_EVENT_CONNECT: Triggered when connection completes.

- print_conn_desc: Displays connection handle, addresses, intervals.

## GAP Event Callback Summary

gap_event_cb() covers:

- EXT_DISC → Identify periodic advertiser and call sync.

- PERIODIC_SYNC → Confirm sync and choose subevents.

- PERIODIC_REPORT → Send response data.

- PERIODIC_SYNC_LOST → Restart scanning.

- CONNECT/DISCONNECT → Handle connection lifecycle.


## Host Task
```c
void pawr_host_task(void *param) {
    nimble_port_run();          // Process BLE events indefinitely
    nimble_port_freertos_deinit();
}
```
This function runs the NimBLE host event loop until the stack is stopped.

## Conclusion

This PAwR Sync + Conn example demonstrates:

- Passive discovery of periodic advertisers.

- Low-power synchronization to scheduled subevents.

- Slot-based responses with custom payload.

- Connection handling initiated by the advertiser.

