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

Parents Reply Children
  • - 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