Possibility of using Nordic nRF52840 as PAwR advertiser and the other device as PAwR scanner

Hi,

I am currently investigating the possibility of using Nordic nRF52840 as the PAwR advertiser and the other device (from a different vendor) as the PAwR scanner. 

I don't want to include in the PAST procedure in my first demo, and found this ticket related: PaWR subevent data - Nordic Q&A - Nordic DevZone - Nordic DevZone

I have went through the recommended `periodic_adv_conn` sample application. But I am not really sure whether it is possible to get this working. Moreover, I am actually not using the STACK on the other device, but writing the application based on the radio driver. 

Would appreciate it if you can give some suggestions Slight smile

  • I modified the example as follows:


    	int err;
    	struct bt_le_ext_adv *pawr_adv;
    	init_bufs();
    
    	/* Initialize the Bluetooth Subsystem */
    	err = bt_enable(NULL);
    	if (err) {
    		printk("Bluetooth init failed (err %d)\n", err);
    		return 0;
    	}
    
    	/* Create a non-connectable advertising set */
    	err = bt_le_ext_adv_create(BT_LE_EXT_ADV_NCONN, &adv_cb, &pawr_adv);
    	if (err) {
    		printk("Failed to create advertising set (err %d)\n", err);
    		return 0;
    	}
    
    	/* Set periodic advertising parameters */
    	err = bt_le_per_adv_set_param(pawr_adv, &per_adv_params);
    	if (err) {
    		printk("Failed to set periodic advertising parameters (err %d)\n", err);
    		return 0;
    	}
    
    	/* Enable Periodic Advertising */
    	printk("Start Periodic Advertising\n");
    	err = bt_le_per_adv_start(pawr_adv);
    	if (err) {
    		printk("Failed to enable periodic advertising (err %d)\n", err);
    		return 0;
    	}
    
    	printk("Start Extended Advertising\n");
    	err = bt_le_ext_adv_start(pawr_adv, BT_LE_EXT_ADV_START_DEFAULT);
    	if (err) {
    		printk("Failed to start extended advertising (err %d)\n", err);
    		return 0;
    	}
    
    	/* Send the HCI subevent data request */
    	struct net_buf *buf;
    	struct bt_hci_evt_le_per_adv_subevent_data_request *evt;
    	buf = bt_hci_evt_create(BT_HCI_EVT_LE_PER_ADV_SUBEVENT_DATA_REQUEST, sizeof(struct bt_hci_evt_le_per_adv_subevent_data_request));
    	if (!buf)
    	{
    		printk("Unable to allocate event buffer\n");
    		return -1;
    	}
    
    	/* Update the parameters */
    	evt = net_buf_push(buf, sizeof(struct bt_hci_evt_le_per_adv_subevent_data_request));
    	if (!evt)
    	{
    		printk("Unable to allocate memory for the request event\n");
    		return -1;
    	}
    
    	uint8_t adv_handle;
    	err = bt_hci_get_adv_handle(pawr_adv, &adv_handle);
    	if (err)
    	{
    		printk("Failed to get the advertising handle (err %d)\n", err);
    		return -1;
    	}
    
    	evt->adv_handle = adv_handle;
    	evt->subevent_start = 1;
    	evt->subevent_data_count = NUM_SUBEVENTS;
    
    	/* Request... */
    	extern void bt_hci_le_per_adv_subevent_data_request(struct net_buf *buf);
    	bt_hci_le_per_adv_subevent_data_request(buf);
    
    	while (true) {
    		k_sleep(K_SECONDS(1));
    	}


    When running this PAwR advertiser application, I got the following errors



  • I did check the meaning of this -5 error. According to Zephyr documentation, this is an I/O error. But I don't really understand what does this I/O error mean in this context. Could u please shed some light on understanding and resolving this issue?

  • Hi,

    I am having some problems getting the full picture here. Which example did you modify, and with which SDK version? Can you share the modified project or a diff?

  • - My local repository is based on the main branch of Zephyr (zephyrproject-rtos/zephyr: Primary Git Repository for the Zephyr Project. Zephyr is a new generation, scalable, optimized, secure RTOS for multiple hardware architectures.)
    - I modified the example of `periodic_adv_rsp`
    - The updated main.c is shared in the following code snippet

    /*
     * Copyright (c) 2023 Nordic Semiconductor ASA
     *
     * SPDX-License-Identifier: Apache-2.0
     */
    
    #include "zephyr/drivers/bluetooth.h"
    #include <zephyr/bluetooth/att.h>
    #include <zephyr/bluetooth/bluetooth.h>
    #include <zephyr/bluetooth/conn.h>
    #include <zephyr/bluetooth/gatt.h>
    #include <zephyr/bluetooth/hci.h>
    
    #define NUM_RSP_SLOTS 5
    #define NUM_SUBEVENTS 5
    #define PACKET_SIZE   5
    #define NAME_LEN      30
    
    static K_SEM_DEFINE(sem_connected, 0, 1);
    static K_SEM_DEFINE(sem_discovered, 0, 1);
    static K_SEM_DEFINE(sem_written, 0, 1);
    static K_SEM_DEFINE(sem_disconnected, 0, 1);
    
    struct k_poll_event events[] = {
    	K_POLL_EVENT_STATIC_INITIALIZER(K_POLL_TYPE_SEM_AVAILABLE, K_POLL_MODE_NOTIFY_ONLY,
    					&sem_connected, 0),
    	K_POLL_EVENT_STATIC_INITIALIZER(K_POLL_TYPE_SEM_AVAILABLE, K_POLL_MODE_NOTIFY_ONLY,
    					&sem_disconnected, 0),
    };
    
    static const struct bt_le_per_adv_param per_adv_params = {
    	.interval_min = 0xFF,
    	.interval_max = 0xFF,
    	.options = 0,
    	.num_subevents = NUM_SUBEVENTS,
    	.subevent_interval = 0x30,
    	.response_slot_delay = 0x5,
    	.response_slot_spacing = 0x50,
    	.num_response_slots = NUM_RSP_SLOTS,
    };
    
    static struct bt_le_per_adv_subevent_data_params subevent_data_params[NUM_SUBEVENTS];
    static struct net_buf_simple bufs[NUM_SUBEVENTS];
    static uint8_t backing_store[NUM_SUBEVENTS][PACKET_SIZE];
    
    BUILD_ASSERT(ARRAY_SIZE(bufs) == ARRAY_SIZE(subevent_data_params));
    BUILD_ASSERT(ARRAY_SIZE(backing_store) == ARRAY_SIZE(subevent_data_params));
    
    static uint8_t counter;
    
    static void request_cb(struct bt_le_ext_adv *adv, const struct bt_le_per_adv_data_request *request)
    {
    	int err;
    	uint8_t to_send;
    	struct net_buf_simple *buf;
    
    	to_send = MIN(request->count, ARRAY_SIZE(subevent_data_params));
    
    	buf = &bufs[0];
    	buf->data[buf->len - 1] = 0x12;
    	subevent_data_params[0].subevent = 0;
    	subevent_data_params[0].response_slot_start = 0;
    	subevent_data_params[0].response_slot_count = NUM_RSP_SLOTS;
    	subevent_data_params[0].data = buf;
    
    	err = bt_le_per_adv_set_subevent_data(adv, 1, &subevent_data_params[0]);
    	if (err) {
    		printk("Failed to set subevent data (err %d)\n", err);
    	} else {
    		printk("Subevent data set %d\n", counter);
    	}
    }
    
    static bool print_ad_field(struct bt_data *data, void *user_data)
    {
    	ARG_UNUSED(user_data);
    
    	printk("    0x%02X: ", data->type);
    	for (size_t i = 0; i < data->data_len; i++) {
    		printk("%02X", data->data[i]);
    	}
    
    	printk("\n");
    
    	return true;
    }
    
    static struct bt_conn *default_conn;
    
    static void response_cb(struct bt_le_ext_adv *adv, struct bt_le_per_adv_response_info *info,
    		     struct net_buf_simple *buf)
    {
    	if (buf) {
    		printk("Response: subevent %d, slot %d\n", info->subevent, info->response_slot);
    		bt_data_parse(buf, print_ad_field, NULL);
    	}
    }
    
    static const struct bt_le_ext_adv_cb adv_cb = {
    	.pawr_data_request = request_cb,
    	.pawr_response = response_cb,
    };
    
    void connected_cb(struct bt_conn *conn, uint8_t err)
    {
    	printk("Connected (err 0x%02X)\n", err);
    
    	__ASSERT(conn == default_conn, "Unexpected connected callback");
    
    	if (err) {
    		bt_conn_unref(default_conn);
    		default_conn = NULL;
    	}
    }
    
    void disconnected_cb(struct bt_conn *conn, uint8_t reason)
    {
    	printk("Disconnected, reason 0x%02X %s\n", reason, bt_hci_err_to_str(reason));
    
    	k_sem_give(&sem_disconnected);
    }
    
    void remote_info_available_cb(struct bt_conn *conn, struct bt_conn_remote_info *remote_info)
    {
    	/* Need to wait for remote info before initiating PAST */
    	k_sem_give(&sem_connected);
    }
    
    BT_CONN_CB_DEFINE(conn_cb) = {
    	.connected = connected_cb,
    	.disconnected = disconnected_cb,
    };
    
    void init_bufs(void)
    {
    	for (size_t i = 0; i < ARRAY_SIZE(backing_store); i++) {
    		backing_store[i][0] = ARRAY_SIZE(backing_store[i]) - 1;
    		backing_store[i][1] = BT_DATA_MANUFACTURER_DATA;
    		backing_store[i][2] = 0x59; /* Nordic */
    		backing_store[i][3] = 0x00;
    
    		net_buf_simple_init_with_data(&bufs[i], &backing_store[i],
    					      ARRAY_SIZE(backing_store[i]));
    	}
    }
    
    #define MAX_SYNCS (NUM_SUBEVENTS * NUM_RSP_SLOTS)
    struct pawr_timing {
    	uint8_t subevent;
    	uint8_t response_slot;
    } __packed;
    
    
    int main(void)
    {
    	int err;
    	struct bt_le_ext_adv *pawr_adv;
    
    	init_bufs();
    
    	printk("Starting Periodic Advertising Demo\n");
    
    	/* Initialize the Bluetooth Subsystem */
    	err = bt_enable(NULL);
    	if (err) {
    		printk("Bluetooth init failed (err %d)\n", err);
    		return 0;
    	}
    
    	/* Create a non-connectable advertising set */
    	err = bt_le_ext_adv_create(BT_LE_EXT_ADV_NCONN, &adv_cb, &pawr_adv);
    	if (err) {
    		printk("Failed to create advertising set (err %d)\n", err);
    		return 0;
    	}
    
    	/* Set periodic advertising parameters */
    	err = bt_le_per_adv_set_param(pawr_adv, &per_adv_params);
    	if (err) {
    		printk("Failed to set periodic advertising parameters (err %d)\n", err);
    		return 0;
    	}
    
    	/* Enable Periodic Advertising */
    	printk("Start Periodic Advertising\n");
    	err = bt_le_per_adv_start(pawr_adv);
    	if (err) {
    		printk("Failed to enable periodic advertising (err %d)\n", err);
    		return 0;
    	}
    
    	printk("Start Extended Advertising\n");
    	err = bt_le_ext_adv_start(pawr_adv, BT_LE_EXT_ADV_START_DEFAULT);
    	if (err) {
    		printk("Failed to start extended advertising (err %d)\n", err);
    		return 0;
    	}
    
    	struct net_buf *buf;
    	struct bt_hci_evt_le_per_adv_subevent_data_request *evt;
    	buf = bt_hci_evt_create(BT_HCI_EVT_LE_PER_ADV_SUBEVENT_DATA_REQUEST, sizeof(struct bt_hci_evt_le_per_adv_subevent_data_request));
    	if (!buf)
    	{
    		printk("Unable to allocate event buffer\n");
    		return -1;
    	}
    
    	/* Update the parameters */
    	evt = net_buf_push(buf, sizeof(struct bt_hci_evt_le_per_adv_subevent_data_request));
    	if (!evt)
    	{
    		printk("Unable to allocate memory for the request event\n");
    		return -1;
    	}
    
    	uint8_t adv_handle;
    	err = bt_hci_get_adv_handle(pawr_adv, &adv_handle);
    	if (err)
    	{
    		printk("Failed to get the advertising handle (err %d)\n", err);
    		return -1;
    	}
    
    	evt->adv_handle = adv_handle;
    	evt->subevent_start = 1;
    	evt->subevent_data_count = NUM_SUBEVENTS;
    
    	/* Request... */
    	extern void bt_hci_le_per_adv_subevent_data_request(struct net_buf *buf);
    	bt_hci_le_per_adv_subevent_data_request(buf);
    
    	while (true) {
    		k_sleep(K_SECONDS(1));
    	}
    
    	return 0;
    }
    

    My biggest confusion is how the PAwR advertiser starts sending the subevent, i.e. AUX_SYNC_SUBEVENT_IND. I explored the code base. According to my understanding, the subevent data are set in the callback `pawr_data_request`, which is triggered when in the function `bt_hci_le_per_adv_subevent_data_request`, and this function is actually the event handler of `BT_HCI_EVT_LE_PER_ADV_SUBEVENT_DATA_REQUEST`.


    So does it mean that to kick-off the PAwR (well, let's say, sending out the AUX_SYNC_SUBEVENT_IND), there must be a HCI event. Then I don't understand how this sample application works. 

    I also tried using the BLE sniffer to capture the traffic over the air of the nRF52840DK running the PAwR advertiser, but didn't really see the AUX_SYNC_SUBEVENT_IND.

    Can u please give me a high-level overview of how the PAwR advertiser is implemented in Zephyr? I mean I want to understand how to get the PAwR advertiser working at least. 

Related