# BLE Periodic Advertisement with Response (PAwR) Advertiser Connection Example Walkthrough

## Introduction

This tutorial examines the BLE Periodic Advertisement with Responses (PAwR) example code for ESP32 chipsets with BLE 5.0+ support. The code demonstrates how to implement PAwR connection functionality using NimBLE APIs, which enables bidirectional communication between advertiser and scanner devices in a power-efficient manner. This version additionally supports connections initiated from responses.

## Includes

This example is located in the examples folder of the ESP-IDF under the [ble_pawr_adv_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"
#include "nvs_flash.h"
/* BLE */
#include "nimble/nimble_port.h"
#include "nimble/nimble_port_freertos.h"
#include "host/ble_hs.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

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

## PAwR Configuration
```c
#define BLE_PAWR_EVENT_INTERVAL               (520)
#define BLE_PAWR_NUM_SUBEVTS                  (10)
#define BLE_PAWR_SUB_INTERVAL                 (52)
#define BLE_PAWR_RSP_SLOT_DELAY               (5)
#define BLE_PAWR_RSP_SLOT_SPACING             (10)
#define BLE_PAWR_NUM_RSP_SLOTS                (25)
#define BLE_PAWR_SUB_DATA_LEN                 (20)
```
These parameters configure PAwR interval, subevents, response slot timing, and payload length.

## Periodic Advertising Configuration

```c
memset(&pparams, 0, sizeof(pparams));
pparams.include_tx_power = 0;
pparams.itvl_min = BLE_GAP_PERIODIC_ITVL_MS(3000);
pparams.itvl_max = BLE_GAP_PERIODIC_ITVL_MS(3000);
pparams.num_subevents = BLE_PAWR_NUM_SUBEVTS;
pparams.subevent_interval = BLE_PAWR_SUB_INTERVAL;
pparams.response_slot_delay = BLE_PAWR_RSP_SLOT_DELAY;
pparams.response_slot_spacing = BLE_PAWR_RSP_SLOT_SPACING;
pparams.num_response_slots = BLE_PAWR_NUM_RSP_SLOTS;
```

These values are passed to ble_gap_periodic_adv_configure() to start PAwR.

## PAwR Advertisement
The start_periodic_adv() function:
- Configures extended advertising parameters
- Sets up periodic advertising using subevent and slot parameters
- Starts extended + periodic advertising

## Need of Extended Advertisement in Periodic Advertisement

Extended advertisements contain synchronization info that lets scanners align with periodic advertising. This enables precise subevent-based communicati

## GAP Event Callback

The function gap_event_cb() handles multiple events:
```c

case BLE_GAP_EVENT_PER_SUBEV_DATA_REQ:
    // Send subevent data
case BLE_GAP_EVENT_PER_SUBEV_RESP:
    // Receive response and initiate connection
case BLE_GAP_EVENT_CONNECT:
    // Connection complete
case BLE_GAP_EVENT_DISCONNECT:
    // Disconnection complete
```

## Using ble_gap_connect_with_synced()

The API ble_gap_connect_with_synced() is a NimBLE API used by a PAwR Advertiser to initiate a BLE connection with a synced scanner. This allows the advertiser to transition from scheduled subevent-based communication to a higher-throughput, lower-latency connection with a specific scanner.

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,
    adv_handle,
    subevent,
    &peer_addr,
    30000,
    BLE_GAP_LE_PHY_1M_MASK,
    NULL, NULL, NULL,
    gap_event_cb, NULL);
```

Highlights:

- Uses subevent + handle to target synced scanner
- Avoids scanning (direct connection)
- Enables faster, deterministic connection

📌 Tip: Choose connection interval as a multiple of subevent interval for optimal scheduling.

## Host Task
```c
void pawr_host_task(void *param)
{
    ESP_LOGI(TAG, "BLE Host Task Started");
    nimble_port_run();
    nimble_port_freertos_deinit();
}
```
## 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 Advertisement

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

Conclusion

This PAwR with connection example demonstrates:

- Periodic advertising with subevents and response slots
- Dynamic connection initiation based on scanner responses
- Use of extended advertisement for synchronization
- Efficient, scalable, low-power bidirectional communication

