Extended Advertising - reception and data size

Hi,

I recently started with nRF52840-DK, Zephyr and nRF Connect SDK (v. 1.9.1). I want to test extended advertising. Since there is no example of "pure" extended advertising (i.e. no periodic), I modified the periodic_adv example. Modification is quite easy: In prj.conf setting CONFIG_BT_PER_ADV=n, in main.c skipping bt_le_per_adv_set_param() and bt_le_per_adv_start(), usage of bt_le_ext_adv_set_data() instead of bt_le_per_adv_set_data(). Now extended advertising runs well. With a Sodera sniffer, I can see three ADV_EXT_IND frames in channels 37/38/39, followed by an AUX_ADV_IND frame in one of the data channels. The AUX_ADV_IND frame contains two AD elements: One with the data I want to transmit and one with the complete local name as set in prj.conf.

For reception, I use the periodic_sync example, which should support extended advertising without the periodic functionality too.

Now the questions:

My receiver calls the scan_recv function, but always I see only one of the AD elements. The buffer contains only the AD element with the local name, which is the last one in the frame (according to the sniffer raw data). There is no indication of the reception of my data in the second AD element. What have I to do to receive both (or even more) AD elements? What is the intention here - to have several calls of scan_recv for every AD element or to have a list of buffers or something else?

Parents
  • Hi

    Can you show me your main.c file, since I'm not able to reproduce this on my end. What parameters have you set in the bt_le_ext_adv_set_data() for example?

    Best regards,

    Simon

  • Hi Simonr,

    I include the main.c of the advertiser and the receiver below:

    /*
     * Copyright (c) 2020 Nordic Semiconductor ASA
     *
     * SPDX-License-Identifier: Apache-2.0
     */
    
    // modified periodic_adv example of ncs to test "pure" extended advertising
    
    #include <bluetooth/bluetooth.h>
    
    /* prj.conf:
     * CONFIG_BT=y
     * CONFIG_BT_EXT_ADV=y
     * CONFIG_BT_PER_ADV=n
     * CONFIG_BT_DEBUG_LOG=y
     * CONFIG_BT_DEVICE_NAME="ABC"
     * CONFIG_BT_CTLR_ADV_DATA_LEN_MAX=255 
     */
    
    // up to 0xde (length 227) possible to receive, up to 0xf4 (length 249) possible to transmit
    static uint8_t mfg_data[] = { 0xff, 0xff, 0x00, 0xbb, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,\
                                                          0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,\
                                                          0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,\
                                                          0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,\
                                                          0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,\
                                                          0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,\
                                                          0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,\
                                                          0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,\
                                                          0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,\
                                                          0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,\
                                                          0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,\
                                                          0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,\
                                                          0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,\
                                                          0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde}; //, 0xdf,\
                                                          0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,\
                                                          0xf0, 0xf1, 0xf2, 0xf3, 0xf4};
    
    static const struct bt_data ad[] = {
    	BT_DATA(BT_DATA_MANUFACTURER_DATA, mfg_data, 227),
    };
    
    void main(void)
    {
    	struct bt_le_ext_adv *adv;
    	struct bt_le_adv_param my_adv_param;
    	int err;
    
    	printk("Starting Advertising Timing Demo\n");
    	printk("\n");
    	printk("CONFIG_BT_CTLR_ADV_DATA_LEN_MAX: %d\n", CONFIG_BT_CTLR_ADV_DATA_LEN_MAX);
    	printk("\n");
    
    	/* Initialize the Bluetooth Subsystem */
    	err = bt_enable(NULL);
    	if (err) {
    		printk("Bluetooth init failed (err %d)\n", err);
    		return;
    	}
    
    	my_adv_param.options = BT_LE_ADV_OPT_EXT_ADV;  // i.e. without a name AD element
    	my_adv_param.interval_min = 0x0020;            // 20 ms
    	my_adv_param.interval_max = 0x0028;            // 25 ms
    	my_adv_param.peer = NULL;
    	my_adv_param.id = BT_ID_DEFAULT;
    	my_adv_param.sid = 0;
    	my_adv_param.secondary_max_skip = 0;
    	err = bt_le_ext_adv_create(&my_adv_param, NULL, &adv);
    	if (err) {
    		printk("Failed to create advertising set (err %d)\n", err);
    		return;
    	}
    
    	printk("Set Advertising Data, length mfg_data[]: %d ...", ARRAY_SIZE(mfg_data));
    	err = bt_le_ext_adv_set_data(adv, ad, ARRAY_SIZE(ad), NULL, 0);
    	if (err) {
    		printk("Failed (err %d)\n", err);
    		return;
    	}
    	printk("done.\n");
    
    	while (true) {
    		printk("Start Extended Advertising...");
    		err = bt_le_ext_adv_start(adv, BT_LE_EXT_ADV_START_DEFAULT);
    		if (err) {
    			printk("Failed to start extended advertising (err %d)\n", err);
    			return;
    		}
    		printk("done.\n");
    
    		k_sleep(K_MSEC(22));  // try to send every data set just once -- mostly ok, in a few cases sending a frame twice, which is ok
    
    		printk("Stop Extended Advertising...");
    		err = bt_le_ext_adv_stop(adv);
    		if (err) {
    			printk("Failed to stop extended advertising (err %d)\n", err);
    			return;
    		}
    		printk("done.\n");
    
    		mfg_data[2]++;
    
    		printk("Set Advertising Data...");
    		err = bt_le_ext_adv_set_data(adv, ad, ARRAY_SIZE(ad), NULL, 0);
    		if (err) {
    			printk("Failed (err %d)\n", err);
    			return;
    		}
    		printk("done.\n");
    
    		k_sleep(K_SECONDS(1));
    	}
    }
    
    /*
     * Copyright (c) 2020 Nordic Semiconductor ASA
     *
     * SPDX-License-Identifier: Apache-2.0
     */
    
    // modified periodic_sync example of ncs to test "pure" extended advertising
    
    #include <device.h>
    #include <devicetree.h>
    #include <drivers/gpio.h>
    #include <bluetooth/bluetooth.h>
    
    /* prj.conf:
     * CONFIG_BT=y
     * CONFIG_BT_OBSERVER=y
     * CONFIG_BT_EXT_ADV=y
     * CONFIG_BT_PER_ADV_SYNC=y
     * CONFIG_BT_DEBUG_LOG=y
     * CONFIG_BT_DEVICE_NAME="Test Reception of Extended Adv"
     * CONFIG_BT_CTLR_ADV_DATA_LEN_MAX=255
     * CONFIG_BT_CTLR_SCAN_DATA_LEN_MAX=255 
     */
    
    #define TIMEOUT_SYNC_CREATE K_SECONDS(10)
    #define NAME_LEN            30
    
    static bool         per_adv_found;
    static bt_addr_le_t per_addr;
    static uint8_t      per_sid;
    
    uint8_t      call_cnt = 0;
    
    static K_SEM_DEFINE(sem_per_adv, 0, 1);
    static K_SEM_DEFINE(sem_per_sync, 0, 1);
    static K_SEM_DEFINE(sem_per_sync_lost, 0, 1);
    
    /* The devicetree node identifier for the "led0" alias. */
    #define LED0_NODE DT_ALIAS(led0)
    
    #if DT_NODE_HAS_STATUS(LED0_NODE, okay)
    #define HAS_LED     1
    static const struct gpio_dt_spec led = GPIO_DT_SPEC_GET(LED0_NODE, gpios);
    #define BLINK_ONOFF K_MSEC(500)
    
    static struct k_work_delayable blink_work;
    static bool                  led_is_on;
    
    static void blink_timeout(struct k_work *work)
    {
    	led_is_on = !led_is_on;
    	gpio_pin_set(led.port, led.pin, (int)led_is_on);
    
    	k_work_schedule(&blink_work, BLINK_ONOFF);
    }
    #endif
    
    static bool data_cb(struct bt_data *data, void *user_data)
    {
    	char *name = user_data;
    	uint8_t len;
    
    	switch (data->type) {
    	case BT_DATA_NAME_SHORTENED:
    	case BT_DATA_NAME_COMPLETE:
    		len = MIN(data->data_len, NAME_LEN - 1);
    		memcpy(name, data->data, len);
    		name[len] = '\0';
    		return false;
    	default:
    		return true;
    	}
    }
    
    static const char *phy2str(uint8_t phy)
    {
    	switch (phy) {
    	case 0: return "No packets";
    	case BT_GAP_LE_PHY_1M: return "LE 1M";
    	case BT_GAP_LE_PHY_2M: return "LE 2M";
    	case BT_GAP_LE_PHY_CODED: return "LE Coded";
    	default: return "Unknown";
    	}
    }
    
    static void scan_recv(const struct bt_le_scan_recv_info *info,
    		      struct net_buf_simple *buf)
    {
    	char le_addr[BT_ADDR_LE_STR_LEN];
    	char name[NAME_LEN];
    	uint16_t i;
    
    	call_cnt++; // for counting the number of calls at all
    
    	printk("call %3d   RSSI %d\n", call_cnt, info->rssi);
    	if ((buf->data[3] == 0xff) && (buf->data[5] == 0xbb)){   // i.e. my frames
    		printk("net_buf_simple_max_length: %u\n", net_buf_simple_max_len(buf));
    		printk("    %.2X %.2X %.2X\n", buf->data[3], buf->data[4], buf->data[5]);
    	}
    }
    
    static struct bt_le_scan_cb scan_callbacks = {
    	.recv = scan_recv,
    };
    
    static void sync_cb(struct bt_le_per_adv_sync *sync,
    		    struct bt_le_per_adv_sync_synced_info *info)
    {
    	char le_addr[BT_ADDR_LE_STR_LEN];
    
    	bt_addr_le_to_str(info->addr, le_addr, sizeof(le_addr));
    
    	printk("PER_ADV_SYNC[%u]: [DEVICE]: %s synced, "
    	       "Interval 0x%04x (%u ms), PHY %s\n",
    	       bt_le_per_adv_sync_get_index(sync), le_addr,
    	       info->interval, info->interval * 5 / 4, phy2str(info->phy));
    
    	k_sem_give(&sem_per_sync);
    }
    
    static void term_cb(struct bt_le_per_adv_sync *sync,
    		    const struct bt_le_per_adv_sync_term_info *info)
    {
    	char le_addr[BT_ADDR_LE_STR_LEN];
    
    	bt_addr_le_to_str(info->addr, le_addr, sizeof(le_addr));
    
    	printk("PER_ADV_SYNC[%u]: [DEVICE]: %s sync terminated\n",
    	       bt_le_per_adv_sync_get_index(sync), le_addr);
    
    	k_sem_give(&sem_per_sync_lost);
    }
    
    static void recv_cb(struct bt_le_per_adv_sync *sync,
    		    const struct bt_le_per_adv_sync_recv_info *info,
    		    struct net_buf_simple *buf)
    {
    	char le_addr[BT_ADDR_LE_STR_LEN];
    	char data_str[129];
    
    	bt_addr_le_to_str(info->addr, le_addr, sizeof(le_addr));
    	bin2hex(buf->data, buf->len, data_str, sizeof(data_str));
    
    	printk("PER_ADV_SYNC[%u]: [DEVICE]: %s, tx_power %i, "
    	       "RSSI %i, CTE %u, data length %u, data: %s\n",
    	       bt_le_per_adv_sync_get_index(sync), le_addr, info->tx_power,
    	       info->rssi, info->cte_type, buf->len, data_str);
    }
    
    static struct bt_le_per_adv_sync_cb sync_callbacks = {
    	.synced = sync_cb,
    	.term = term_cb,
    	.recv = recv_cb
    };
    
    void main(void)
    {
    	struct bt_le_per_adv_sync_param sync_create_param;
    	struct bt_le_per_adv_sync *sync;
    	int err;
    
    	printk("Starting Axel's Extended Advertising Reception Demo\n");
    	printk("\n");
    	printk("CONFIG_BT_CTLR_ADV_DATA_LEN_MAX : %d\n", CONFIG_BT_CTLR_ADV_DATA_LEN_MAX);
    	printk("CONFIG_BT_CTLR_SCAN_DATA_LEN_MAX: %d\n", CONFIG_BT_CTLR_SCAN_DATA_LEN_MAX);
    	printk("\n");
    
    #if defined(HAS_LED)
    	printk("Checking LED device...");
    	if (!device_is_ready(led.port)) {
    		printk("failed.\n");
    		return;
    	}
    	printk("done.\n");
    
    	printk("Configuring GPIO pin...");
    	err = gpio_pin_configure_dt(&led, GPIO_OUTPUT_ACTIVE);
    	if (err) {
    		printk("failed.\n");
    		return;
    	}
    	printk("done.\n");
    
    	k_work_init_delayable(&blink_work, blink_timeout);
    #endif /* HAS_LED */
    
    	/* Initialize the Bluetooth Subsystem */
    	err = bt_enable(NULL);
    	if (err) {
    		printk("Bluetooth init failed (err %d)\n", err);
    		return;
    	}
    
    	printk("Scan callbacks register...");
    	bt_le_scan_cb_register(&scan_callbacks);
    	printk("success.\n");
    
    	printk("Periodic Advertising callbacks register...");
    	bt_le_per_adv_sync_cb_register(&sync_callbacks);
    	printk("Success.\n");
    
    	printk("Start scanning...");
    	err = bt_le_scan_start(BT_LE_SCAN_ACTIVE, NULL);
    	//err = bt_le_scan_start(BT_LE_SCAN_PASSIVE, NULL);
    	if (err) {
    		printk("failed (err %d)\n", err);
    		return;
    	}
    	printk("success.\n");
    
    	do {
    		k_sleep(K_SECONDS(1));
            // no periodic advertising in the test case, pure extended advertising
            // skip the LED stuff
    	} while (true);
    }
    

    Both files are modified versions of the examples coming with ncs. Inside each file, I mentioned also the settings in the appropriate prj.conf files.

    My intension here is to have a data block advertised just once as an extended advertising, followed by a silent period. With the settings in the file, I nearly have this - with the sniffer I can verify that every second the advertiser sends a block of three advertising packets in channels 37/38/39, followed by a data packet in one of the data channels. Sometimes (maybe every tenth frame of less) the advertising is done twice, which would be ok for me, and there is no data set missing. In the advertiser's main.c, currently the data size is limited to 227 bytes, which is the maximum I can receive with the configuration I have so far. Advertising itself runs well also for 249 data bytes.

    In the receiver's main.c, I skipped all the stuff within do { } while(true). There is no periodic advertising and I don't need LEDs here. As already mentioned, reception of the data is ok up to max. 227 data bytes. This limit comes from net_buf_simple_max_len(buf) of 229 (data length + type + size), and the question is, how to extend that length. Tricky is the fact that in case of data size > 227 bytes, there is no call-back at all, there is simply nothing.

    Additional question: Is there any rough number available, how many advertising frames might be processable, i.e. how many calls of the scan_recv() call-back function per second might be ok? I use the nRF52840-DK for advertiser as well as for receiver. In my environment here there are a lot of BLE devices, I have 50..80 calls of scan_recv() per second. This leads to the fact that I catch roughly only every second transmitted data set (which I can clearly identify by the counter). That doesn't mean "one ok, one not ok, ...", it also happens that a few consecutive data sets are ok, followed by a few of them missing.  As mentioned above, advertising itself i.e. Tx) is ok, verified by the sniffer.

    Best regards

    Axel

Reply
  • Hi Simonr,

    I include the main.c of the advertiser and the receiver below:

    /*
     * Copyright (c) 2020 Nordic Semiconductor ASA
     *
     * SPDX-License-Identifier: Apache-2.0
     */
    
    // modified periodic_adv example of ncs to test "pure" extended advertising
    
    #include <bluetooth/bluetooth.h>
    
    /* prj.conf:
     * CONFIG_BT=y
     * CONFIG_BT_EXT_ADV=y
     * CONFIG_BT_PER_ADV=n
     * CONFIG_BT_DEBUG_LOG=y
     * CONFIG_BT_DEVICE_NAME="ABC"
     * CONFIG_BT_CTLR_ADV_DATA_LEN_MAX=255 
     */
    
    // up to 0xde (length 227) possible to receive, up to 0xf4 (length 249) possible to transmit
    static uint8_t mfg_data[] = { 0xff, 0xff, 0x00, 0xbb, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,\
                                                          0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,\
                                                          0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,\
                                                          0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,\
                                                          0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,\
                                                          0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,\
                                                          0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,\
                                                          0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,\
                                                          0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,\
                                                          0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,\
                                                          0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,\
                                                          0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,\
                                                          0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,\
                                                          0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde}; //, 0xdf,\
                                                          0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,\
                                                          0xf0, 0xf1, 0xf2, 0xf3, 0xf4};
    
    static const struct bt_data ad[] = {
    	BT_DATA(BT_DATA_MANUFACTURER_DATA, mfg_data, 227),
    };
    
    void main(void)
    {
    	struct bt_le_ext_adv *adv;
    	struct bt_le_adv_param my_adv_param;
    	int err;
    
    	printk("Starting Advertising Timing Demo\n");
    	printk("\n");
    	printk("CONFIG_BT_CTLR_ADV_DATA_LEN_MAX: %d\n", CONFIG_BT_CTLR_ADV_DATA_LEN_MAX);
    	printk("\n");
    
    	/* Initialize the Bluetooth Subsystem */
    	err = bt_enable(NULL);
    	if (err) {
    		printk("Bluetooth init failed (err %d)\n", err);
    		return;
    	}
    
    	my_adv_param.options = BT_LE_ADV_OPT_EXT_ADV;  // i.e. without a name AD element
    	my_adv_param.interval_min = 0x0020;            // 20 ms
    	my_adv_param.interval_max = 0x0028;            // 25 ms
    	my_adv_param.peer = NULL;
    	my_adv_param.id = BT_ID_DEFAULT;
    	my_adv_param.sid = 0;
    	my_adv_param.secondary_max_skip = 0;
    	err = bt_le_ext_adv_create(&my_adv_param, NULL, &adv);
    	if (err) {
    		printk("Failed to create advertising set (err %d)\n", err);
    		return;
    	}
    
    	printk("Set Advertising Data, length mfg_data[]: %d ...", ARRAY_SIZE(mfg_data));
    	err = bt_le_ext_adv_set_data(adv, ad, ARRAY_SIZE(ad), NULL, 0);
    	if (err) {
    		printk("Failed (err %d)\n", err);
    		return;
    	}
    	printk("done.\n");
    
    	while (true) {
    		printk("Start Extended Advertising...");
    		err = bt_le_ext_adv_start(adv, BT_LE_EXT_ADV_START_DEFAULT);
    		if (err) {
    			printk("Failed to start extended advertising (err %d)\n", err);
    			return;
    		}
    		printk("done.\n");
    
    		k_sleep(K_MSEC(22));  // try to send every data set just once -- mostly ok, in a few cases sending a frame twice, which is ok
    
    		printk("Stop Extended Advertising...");
    		err = bt_le_ext_adv_stop(adv);
    		if (err) {
    			printk("Failed to stop extended advertising (err %d)\n", err);
    			return;
    		}
    		printk("done.\n");
    
    		mfg_data[2]++;
    
    		printk("Set Advertising Data...");
    		err = bt_le_ext_adv_set_data(adv, ad, ARRAY_SIZE(ad), NULL, 0);
    		if (err) {
    			printk("Failed (err %d)\n", err);
    			return;
    		}
    		printk("done.\n");
    
    		k_sleep(K_SECONDS(1));
    	}
    }
    
    /*
     * Copyright (c) 2020 Nordic Semiconductor ASA
     *
     * SPDX-License-Identifier: Apache-2.0
     */
    
    // modified periodic_sync example of ncs to test "pure" extended advertising
    
    #include <device.h>
    #include <devicetree.h>
    #include <drivers/gpio.h>
    #include <bluetooth/bluetooth.h>
    
    /* prj.conf:
     * CONFIG_BT=y
     * CONFIG_BT_OBSERVER=y
     * CONFIG_BT_EXT_ADV=y
     * CONFIG_BT_PER_ADV_SYNC=y
     * CONFIG_BT_DEBUG_LOG=y
     * CONFIG_BT_DEVICE_NAME="Test Reception of Extended Adv"
     * CONFIG_BT_CTLR_ADV_DATA_LEN_MAX=255
     * CONFIG_BT_CTLR_SCAN_DATA_LEN_MAX=255 
     */
    
    #define TIMEOUT_SYNC_CREATE K_SECONDS(10)
    #define NAME_LEN            30
    
    static bool         per_adv_found;
    static bt_addr_le_t per_addr;
    static uint8_t      per_sid;
    
    uint8_t      call_cnt = 0;
    
    static K_SEM_DEFINE(sem_per_adv, 0, 1);
    static K_SEM_DEFINE(sem_per_sync, 0, 1);
    static K_SEM_DEFINE(sem_per_sync_lost, 0, 1);
    
    /* The devicetree node identifier for the "led0" alias. */
    #define LED0_NODE DT_ALIAS(led0)
    
    #if DT_NODE_HAS_STATUS(LED0_NODE, okay)
    #define HAS_LED     1
    static const struct gpio_dt_spec led = GPIO_DT_SPEC_GET(LED0_NODE, gpios);
    #define BLINK_ONOFF K_MSEC(500)
    
    static struct k_work_delayable blink_work;
    static bool                  led_is_on;
    
    static void blink_timeout(struct k_work *work)
    {
    	led_is_on = !led_is_on;
    	gpio_pin_set(led.port, led.pin, (int)led_is_on);
    
    	k_work_schedule(&blink_work, BLINK_ONOFF);
    }
    #endif
    
    static bool data_cb(struct bt_data *data, void *user_data)
    {
    	char *name = user_data;
    	uint8_t len;
    
    	switch (data->type) {
    	case BT_DATA_NAME_SHORTENED:
    	case BT_DATA_NAME_COMPLETE:
    		len = MIN(data->data_len, NAME_LEN - 1);
    		memcpy(name, data->data, len);
    		name[len] = '\0';
    		return false;
    	default:
    		return true;
    	}
    }
    
    static const char *phy2str(uint8_t phy)
    {
    	switch (phy) {
    	case 0: return "No packets";
    	case BT_GAP_LE_PHY_1M: return "LE 1M";
    	case BT_GAP_LE_PHY_2M: return "LE 2M";
    	case BT_GAP_LE_PHY_CODED: return "LE Coded";
    	default: return "Unknown";
    	}
    }
    
    static void scan_recv(const struct bt_le_scan_recv_info *info,
    		      struct net_buf_simple *buf)
    {
    	char le_addr[BT_ADDR_LE_STR_LEN];
    	char name[NAME_LEN];
    	uint16_t i;
    
    	call_cnt++; // for counting the number of calls at all
    
    	printk("call %3d   RSSI %d\n", call_cnt, info->rssi);
    	if ((buf->data[3] == 0xff) && (buf->data[5] == 0xbb)){   // i.e. my frames
    		printk("net_buf_simple_max_length: %u\n", net_buf_simple_max_len(buf));
    		printk("    %.2X %.2X %.2X\n", buf->data[3], buf->data[4], buf->data[5]);
    	}
    }
    
    static struct bt_le_scan_cb scan_callbacks = {
    	.recv = scan_recv,
    };
    
    static void sync_cb(struct bt_le_per_adv_sync *sync,
    		    struct bt_le_per_adv_sync_synced_info *info)
    {
    	char le_addr[BT_ADDR_LE_STR_LEN];
    
    	bt_addr_le_to_str(info->addr, le_addr, sizeof(le_addr));
    
    	printk("PER_ADV_SYNC[%u]: [DEVICE]: %s synced, "
    	       "Interval 0x%04x (%u ms), PHY %s\n",
    	       bt_le_per_adv_sync_get_index(sync), le_addr,
    	       info->interval, info->interval * 5 / 4, phy2str(info->phy));
    
    	k_sem_give(&sem_per_sync);
    }
    
    static void term_cb(struct bt_le_per_adv_sync *sync,
    		    const struct bt_le_per_adv_sync_term_info *info)
    {
    	char le_addr[BT_ADDR_LE_STR_LEN];
    
    	bt_addr_le_to_str(info->addr, le_addr, sizeof(le_addr));
    
    	printk("PER_ADV_SYNC[%u]: [DEVICE]: %s sync terminated\n",
    	       bt_le_per_adv_sync_get_index(sync), le_addr);
    
    	k_sem_give(&sem_per_sync_lost);
    }
    
    static void recv_cb(struct bt_le_per_adv_sync *sync,
    		    const struct bt_le_per_adv_sync_recv_info *info,
    		    struct net_buf_simple *buf)
    {
    	char le_addr[BT_ADDR_LE_STR_LEN];
    	char data_str[129];
    
    	bt_addr_le_to_str(info->addr, le_addr, sizeof(le_addr));
    	bin2hex(buf->data, buf->len, data_str, sizeof(data_str));
    
    	printk("PER_ADV_SYNC[%u]: [DEVICE]: %s, tx_power %i, "
    	       "RSSI %i, CTE %u, data length %u, data: %s\n",
    	       bt_le_per_adv_sync_get_index(sync), le_addr, info->tx_power,
    	       info->rssi, info->cte_type, buf->len, data_str);
    }
    
    static struct bt_le_per_adv_sync_cb sync_callbacks = {
    	.synced = sync_cb,
    	.term = term_cb,
    	.recv = recv_cb
    };
    
    void main(void)
    {
    	struct bt_le_per_adv_sync_param sync_create_param;
    	struct bt_le_per_adv_sync *sync;
    	int err;
    
    	printk("Starting Axel's Extended Advertising Reception Demo\n");
    	printk("\n");
    	printk("CONFIG_BT_CTLR_ADV_DATA_LEN_MAX : %d\n", CONFIG_BT_CTLR_ADV_DATA_LEN_MAX);
    	printk("CONFIG_BT_CTLR_SCAN_DATA_LEN_MAX: %d\n", CONFIG_BT_CTLR_SCAN_DATA_LEN_MAX);
    	printk("\n");
    
    #if defined(HAS_LED)
    	printk("Checking LED device...");
    	if (!device_is_ready(led.port)) {
    		printk("failed.\n");
    		return;
    	}
    	printk("done.\n");
    
    	printk("Configuring GPIO pin...");
    	err = gpio_pin_configure_dt(&led, GPIO_OUTPUT_ACTIVE);
    	if (err) {
    		printk("failed.\n");
    		return;
    	}
    	printk("done.\n");
    
    	k_work_init_delayable(&blink_work, blink_timeout);
    #endif /* HAS_LED */
    
    	/* Initialize the Bluetooth Subsystem */
    	err = bt_enable(NULL);
    	if (err) {
    		printk("Bluetooth init failed (err %d)\n", err);
    		return;
    	}
    
    	printk("Scan callbacks register...");
    	bt_le_scan_cb_register(&scan_callbacks);
    	printk("success.\n");
    
    	printk("Periodic Advertising callbacks register...");
    	bt_le_per_adv_sync_cb_register(&sync_callbacks);
    	printk("Success.\n");
    
    	printk("Start scanning...");
    	err = bt_le_scan_start(BT_LE_SCAN_ACTIVE, NULL);
    	//err = bt_le_scan_start(BT_LE_SCAN_PASSIVE, NULL);
    	if (err) {
    		printk("failed (err %d)\n", err);
    		return;
    	}
    	printk("success.\n");
    
    	do {
    		k_sleep(K_SECONDS(1));
            // no periodic advertising in the test case, pure extended advertising
            // skip the LED stuff
    	} while (true);
    }
    

    Both files are modified versions of the examples coming with ncs. Inside each file, I mentioned also the settings in the appropriate prj.conf files.

    My intension here is to have a data block advertised just once as an extended advertising, followed by a silent period. With the settings in the file, I nearly have this - with the sniffer I can verify that every second the advertiser sends a block of three advertising packets in channels 37/38/39, followed by a data packet in one of the data channels. Sometimes (maybe every tenth frame of less) the advertising is done twice, which would be ok for me, and there is no data set missing. In the advertiser's main.c, currently the data size is limited to 227 bytes, which is the maximum I can receive with the configuration I have so far. Advertising itself runs well also for 249 data bytes.

    In the receiver's main.c, I skipped all the stuff within do { } while(true). There is no periodic advertising and I don't need LEDs here. As already mentioned, reception of the data is ok up to max. 227 data bytes. This limit comes from net_buf_simple_max_len(buf) of 229 (data length + type + size), and the question is, how to extend that length. Tricky is the fact that in case of data size > 227 bytes, there is no call-back at all, there is simply nothing.

    Additional question: Is there any rough number available, how many advertising frames might be processable, i.e. how many calls of the scan_recv() call-back function per second might be ok? I use the nRF52840-DK for advertiser as well as for receiver. In my environment here there are a lot of BLE devices, I have 50..80 calls of scan_recv() per second. This leads to the fact that I catch roughly only every second transmitted data set (which I can clearly identify by the counter). That doesn't mean "one ok, one not ok, ...", it also happens that a few consecutive data sets are ok, followed by a few of them missing.  As mentioned above, advertising itself i.e. Tx) is ok, verified by the sniffer.

    Best regards

    Axel

Children
No Data
Related