Issue with maximum size of ext adv data, CONFIG_BT_CTLR_ADV_DATA_LEN_MAX

Hi all,

I have an issue with achieving bigger sizes of ext adv data as 31 bytes.

My current BLE code is pretty much the same as the nrf_dm sample code:

https://github.com/nrfconnect/sdk-nrf/blob/main/samples/bluetooth/nrf_dm/src/main.c

I just want to add more data into the "sd[]" array / extended advertising data. But it seems the maximum size is 31 bytes. Although I set:

CONFIG_BT_EXT_ADV=y
CONFIG_BT_CTLR_ADV_DATA_LEN_MAX=192 

in my configuration. Following things I figured out:

The bitfield flag "BT_ADV_EXT_ADV" seems to not be set. Thus in adv.c, hci_set_adv_ext_complete() the total_data_len gets truncated to 31 bytes. The code will internally return that the advertising payload is too large. However when I force setting the flag, then the program will generate an error later on in bt_hci_cmd_send_sync with "W: opcode 0x2038 status 0x12" 

What is the correct way of increasing my advertising size?

  • Hi Mark, 
    Could you try to reproduce the code in one of our sample or provide a minimum sample that we can test here ? 

    You can take a look at the peripheral_hr_coded (you can modify so that it uses 1Mbps instead of CODED) to see how extended advertising is used. 

  • Hi Hung Bui,

    as mentioned It is reproducible on the nrf_dm example. I get this output from the device: (which is an nrf52833dk).

    *** Booting nRF Connect SDK v3.0.1-9eb5615da66b ***
    *** Using Zephyr OS v4.0.99-77f865b8f8d0 ***
    Starting Distance Measurement sample
    I: SoftDevice Controller build revision: 
    I: 89 9a 50 8a 95 01 9c 58 |..P....X
    I: fc 39 d2 c1 10 04 ee 02 |.9......
    I: 64 ce 25 be             |d.%.    
    I: HW Platform: Nordic Semiconductor (0x0002)
    I: HW Variant: nRF52x (0x0002)
    I: Firmware: Standard Bluetooth controller (0x00) Version 137.20634 Build 2617349514
    I: Identity: C4:74:F6:86:20:61 (random)
    I: HCI: version 6.0 (0x0e) revision 0x10f3, manufacturer 0x0059
    I: LMP: version 6.0 (0x0e) subver 0x10f3
    DM Bluetooth LE Synchronization initialization
    E: Too big advertising data
    Failed setting adv data (err -22)
    Failed to start advertising (err -22)
    Synchronisation init failed (err -22)
    
    

    This is my diff to the nrf_dm sample on ncs 3.0.1.

    git diff
    diff --git a/samples/bluetooth/nrf_dm/prj.conf b/samples/bluetooth/nrf_dm/prj.conf
    index 8b994dab81..24d3d51ede 100644
    --- a/samples/bluetooth/nrf_dm/prj.conf
    +++ b/samples/bluetooth/nrf_dm/prj.conf
    @@ -36,3 +36,6 @@ CONFIG_NCS_SAMPLES_DEFAULTS=y
     CONFIG_DK_LIBRARY=y
     
     CONFIG_DM_HIGH_PRECISION_CALC=y
    +
    +CONFIG_BT_EXT_ADV=y
    +CONFIG_BT_CTLR_ADV_DATA_LEN_MAX=192
    diff --git a/samples/bluetooth/nrf_dm/src/main.c b/samples/bluetooth/nrf_dm/src/main.c
    index ccb650a8e9..c8ae88a3ec 100644
    --- a/samples/bluetooth/nrf_dm/src/main.c
    +++ b/samples/bluetooth/nrf_dm/src/main.c
    @@ -62,9 +62,12 @@ static const struct bt_data ad[] = {
            BT_DATA(BT_DATA_NAME_COMPLETE, DEVICE_NAME, DEVICE_NAME_LEN),
     };
     
    +double posData[3] = {0, 1, 2};
     static const struct bt_data sd[] = {
            BT_DATA(BT_DATA_MANUFACTURER_DATA, (unsigned char *)&mfg_data, sizeof(mfg_data)),
            BT_DATA_BYTES(BT_DATA_UUID128_ALL, BT_UUID_DDFS_VAL),
    +       BT_DATA(BT_DATA_INDOOR_POS, posData, sizeof(posData))
    +
     };
     
     static struct bt_le_scan_param scan_param = {
    

  • Hi Mark, 

    If you want to do extended advertising (longer payload) you would need to include BT_LE_ADV_OPT_EXT_ADV into the adv_param_conn. 


    When you do extended advertising you will have to choose to have your payload in either advertising data or scan response data. It doesn't work the same as when you do legacy advertising. Please take a look at the description of the bt_le_ext_adv_set_data() function: 

     * When both @ref BT_LE_ADV_OPT_EXT_ADV and @ref BT_LE_ADV_OPT_SCANNABLE are
     * enabled then advertising data is ignored.
     * When @ref BT_LE_ADV_OPT_SCANNABLE is not enabled then scan response data is
     * ignored.
    I would strongly suggest to take a look at peripheral_hr_coded  to see how extended advertising is done. 
  • Hi Hung Bui,

    ok that is interesting. I am not sure if in this way I can realize my use case. I would appreciate your guidance.

    As mentioned I build on top of the nrf_dm example which relies on advertising and the additional scan responses. So in This case. is it possible at all to increase payload size for either of the advertising or the scan response?

    This is the current implementation:

    #define DEVICE_NAME CONFIG_BT_DEVICE_NAME
    #define DEVICE_NAME_LEN (sizeof(DEVICE_NAME) - 1)
    
    #define SUPPORT_DM_CODE 0xFF55AA5A
    
    struct adv_mfg_data
    {
        uint16_t company_code;    /* Company Identifier Code. */
        uint32_t support_dm_code; /* To identify the device that supports distance measurement. */
        uint32_t rng_seed;        /* Random seed used for generating hopping patterns. */
    } __packed;
    
    static adv_mfg_data mfg_data;
    bt_le_adv_param adv_param_conn = BT_LE_ADV_PARAM_INIT(BT_LE_ADV_OPT_CONN | BT_LE_ADV_OPT_NOTIFY_SCAN_REQ,
                                                          BT_GAP_ADV_FAST_INT_MIN_2, BT_GAP_ADV_FAST_INT_MAX_2, NULL);
    
    bt_le_adv_param* adv_param = &adv_param_conn;
    
    // double posData[3] = {0, 1, 2};
    float posData[1] = {0};
    static const bt_data ad[] = {
        BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)),
        BT_DATA(BT_DATA_NAME_COMPLETE, DEVICE_NAME, DEVICE_NAME_LEN),
        //BT_DATA(BT_DATA_INDOOR_POS, posData, sizeof(posData)),
    };
    
    static const bt_data sd[] = {
        BT_DATA(BT_DATA_MANUFACTURER_DATA, reinterpret_cast<unsigned char*>(&mfg_data), sizeof(mfg_data)),
        BT_DATA_BYTES(BT_DATA_UUID128_ALL, BT_UUID_DDFS_VAL)};
    
    static bt_le_scan_param scan_param = {
        .type = BT_LE_SCAN_TYPE_ACTIVE,
        .options = BT_LE_SCAN_OPT_NONE,
        .interval = BT_GAP_SCAN_FAST_INTERVAL,
        .window = BT_GAP_SCAN_FAST_WINDOW,
        .timeout = 0,
    };
    
    static bt_scan_init_param scan_init = {.scan_param = &scan_param, .connect_if_match = false, .conn_param = nullptr};
    
    static uint32_t scanner_random_share;
    
    static bt_le_ext_adv* adv;
    static void adv_work_handle(k_work* item);
    static K_WORK_DEFINE(adv_work, adv_work_handle);
    static void adv_update_data();
    static uint32_t scanner_addr_to_random_share(const bt_addr_t* p_scanner_addr);
    static uint32_t get_id_addr_random_share();
    
    static bt_scan_manufacturer_data scan_mfg_data = {
        .data = reinterpret_cast<unsigned char*>(&mfg_data),
        .data_len = sizeof(mfg_data.company_code) + sizeof(mfg_data.support_dm_code),
    };
    
    static bool data_cb(bt_data* data, void* user_data)
    {
        dm_request req{};
    
        switch (data->type)
        {
        case BT_DATA_MANUFACTURER_DATA:
            if (sizeof(adv_mfg_data) == data->data_len)
            {
                auto recv_mfg_data = (adv_mfg_data*)data->data;
    
                bt_addr_le_copy(&req.bt_addr, static_cast<const bt_addr_le_t*>(user_data));
                req.role = DM_ROLE_INITIATOR;
                req.ranging_mode = peer_ranging_mode_get();
    
                /* We need to make sure that we only initiate a ranging to a single peer.
                 * A scan response that is received by this device can be received by
                 * multiple other devices which can all start a ranging at the same time
                 * as a consequence. To prevent this, we need to make sure that we set a
                 * per-peer random as the random seed. This helps the ranging library to
                 * avoid interference from other devices trying to range at the same time.
                 *
                 * This means that the initiator and the reflector need to set the same
                 * value for the random seed.
                 */
                req.rng_seed = sys_le32_to_cpu(recv_mfg_data->rng_seed) + scanner_random_share;
                req.start_delay_us = 0;
                req.extra_window_time_us = 0;
    
                dm_request_add(&req);
            }
            return true;
        case BT_DATA_INDOOR_POS:
            // parse position data
            Debug::log("Received pos data\n");
            return true;
        default:
            return true;
        }
    }
    
    static uint32_t get_id_addr_random_share()
    {
        bt_addr_le_t addrs[CONFIG_BT_ID_MAX];
        size_t count = CONFIG_BT_ID_MAX;
    
        bt_id_get(addrs, &count);
    
        __ASSERT(count == 1, "The sample assumes a single ID addr");
    
        return scanner_addr_to_random_share(&addrs[0].a);
    }
    
    static uint32_t scanner_addr_to_random_share(const bt_addr_t* p_scanner_addr)
    {
        return (p_scanner_addr->val[0] | p_scanner_addr->val[1] << 8 | p_scanner_addr->val[2] << 16 |
                p_scanner_addr->val[3] << 24) +
               (p_scanner_addr->val[4] | p_scanner_addr->val[5] << 8);
    }
    
    static void scan_filter_match(bt_scan_device_info* device_info, bt_scan_filter_match* filter_match, bool connectable)
    {
        bt_addr_le_t addr;
    
        bt_addr_le_copy(&addr, device_info->recv_info->addr);
        peer_supported_add(device_info->recv_info->addr);
        bt_data_parse(device_info->adv_data, data_cb, &addr);
    }
    
    BT_SCAN_CB_INIT(scan_cb, scan_filter_match, NULL, NULL, NULL);
    
    static void adv_scanned_cb(bt_le_ext_adv* adv, bt_le_ext_adv_scanned_info* info)
    {
        dm_request req{};
        if (peer_supported_test(info->addr))
        {
            bt_addr_le_copy(&req.bt_addr, info->addr);
            req.role = DM_ROLE_REFLECTOR;
            req.ranging_mode = peer_ranging_mode_get();
    
            /* We need to make sure that we only initiate a ranging to a single peer.
             * A scan response from this device can be received by multiple peers which can
             * all start a ranging at the same time as a consequence. To prevent this,
             * we need to make sure that we set a per-peer random as the random seed.
             * This helps the ranging library to avoid interference from other devices
             * trying to range at the same time.
             *
             * This means that the initiator and the reflector need to set the same value
             * for the random seed.
             */
            req.rng_seed = peer_rng_seed_get() + scanner_addr_to_random_share(&info->addr->a);
            req.start_delay_us = 0;
            req.extra_window_time_us = 0;
    
            dm_request_add(&req);
            adv_update_data();
        }
    }
    
    const static bt_le_ext_adv_cb adv_cb = {
        .scanned = adv_scanned_cb,
    };
    
    static void adv_update_data()
    {
        if (!adv)
        {
            return;
        }
        mfg_data.rng_seed = peer_rng_seed_prepare();
        int err = bt_le_ext_adv_set_data(adv, ad, ARRAY_SIZE(ad), sd, ARRAY_SIZE(sd));
        if (err)
        {
            printk("Failed setting adv data (err %d)\n", err);
        }
    }
    
    static int adv_start()
    {
        int err;
        bt_le_ext_adv_start_param ext_adv_start_param = {0};
    
        if (adv)
        {
            err = bt_le_ext_adv_stop(adv);
            if (err)
            {
                printk("Failed to stop extended advertising  (err %d)\n", err);
                return err;
            }
            err = bt_le_ext_adv_delete(adv);
            if (err)
            {
                printk("Failed to delete advertising set  (err %d)\n", err);
                return err;
            }
        }
    
        err = bt_le_ext_adv_create(adv_param, &adv_cb, &adv);
        if (err)
        {
            printk("Failed to create advertising set (err %d)\n", err);
            return err;
        }
    
        err = bt_le_ext_adv_set_data(adv, ad, ARRAY_SIZE(ad), sd, ARRAY_SIZE(sd));
        if (err)
        {
            printk("Failed setting adv data (err %d)\n", err);
            return err;
        }
    
        err = bt_le_ext_adv_start(adv, &ext_adv_start_param);
        if (err)
        {
            printk("Failed to start extended advertising  (err %d)\n", err);
            return err;
        }
    
        return err;
    }
    
    static int scan_start()
    {
        bt_scan_init(&scan_init);
        bt_scan_cb_register(&scan_cb);
    
        int err = bt_scan_filter_add(BT_SCAN_FILTER_TYPE_MANUFACTURER_DATA, &scan_mfg_data);
        if (err)
        {
            printk("Scanning filters cannot be set (err %d)\n", err);
            return err;
        }
    
        err = bt_scan_filter_enable(BT_SCAN_MANUFACTURER_DATA_FILTER, false);
        if (err)
        {
            printk("Filters cannot be turned on (err %d)\n", err);
            return err;
        }
    
        scanner_random_share = get_id_addr_random_share();
    
        err = bt_scan_start(BT_SCAN_TYPE_SCAN_ACTIVE);
        if (err)
        {
            printk("Scanning failed to start (err %d)\n", err);
            return err;
        }
    
        return err;
    }
    
    static void adv_work_handle(k_work* item)
    {
        adv_start();
    }
    
    static int bt_sync_init()
    {
        /* Synchronisation is based on advertising and scanning modes.
         * It occurs when SCAN_REQ and SCAN_RESP packets are exchanged.
         */
        printk("DM Bluetooth LE Synchronization initialization\n");
    
        mfg_data.company_code = sys_cpu_to_le16(CONFIG_BT_COMPANY_ID_NORDIC);
        mfg_data.support_dm_code = sys_cpu_to_le32(SUPPORT_DM_CODE);
        mfg_data.rng_seed = sys_cpu_to_le32(peer_rng_seed_prepare());
    
        int err = adv_start();
        if (err)
        {
            printk("Failed to start advertising (err %d)\n", err);
            return err;
        }
    
        err = scan_start();
        if (err)
        {
            printk("Failed to start scanning (err %d)\n", err);
        }
    
        return err;
    }
    
    static void data_ready(dm_result* result)
    {
        if (result->status)
        {
            peer_update(result);
        }
    }
    
    static dm_cb dm_cb = {
        .data_ready = data_ready,
    };
    
    void Bluetooth::init()
    {
        dm_init_param init_param{};
        printk("Starting Distance Measurement sample\n");
    
        int err = peer_init();
        if (err)
        {
            printk("Peer init failed (err %d)\n", err);
            return;
        }
    
        init_param.cb = &dm_cb;
    
        err = dm_init(&init_param);
        if (err)
        {
            printk("Distance measurement init failed (err %d)\n", err);
            return;
        }
    
        err = service_ddfs_init();
        if (err)
        {
            printk("DDF Service init failed (err %d)\n", err);
            return;
        }
    
        err = bt_enable(NULL);
        if (err)
        {
            printk("Bluetooth init failed (err %d)\n", err);
            return;
        }
    
        err = bt_sync_init();
        if (err)
        {
            printk("Synchronisation init failed (err %d)\n", err);
        }
    }

  • Hi Mark, 
    I don't think the dm library has any requirement on the advertising packets. This mean you can choose to do what you want with the advertising. 

    What's the issue doing extended advertising with nrf_dm  ? 

    If you proprietary data can not fit into the 31 bytes payload of either advertising packet or scan request packet then I don't think there is any other option. 

Related