unable to see extended advertising on IOS (15/17 with IOD 26.x) with nrfConnect/SI Connect or Nano Connect

I am using the older SDk 17_1_0 used in the Qorvo UWB sdk. 

nrfConnect ->connected to -> controller ->connected to ->beacon, neither appear in scanner as expected once conected

while controller  connected to the beacon device,  I need to send some info (from controller device) to other controller(s) on other devices, but they cannot connect or scan for the controller or beacon,  because I am (functionaly) connected to both
so I am trying to use extended advertising to send the data .  Because the older SDK doesn't support more than one advertising set (can't move to new SDK), we are stopping the advertising that was used to find and connect to the controller device and repurposing for advert with extended advertising..

the initial advertising setup works a expected

   QLOGD("ble_stack_manager_init: 8-advertising_init");
    // WORKAROUND: Skip Nordic advertising module - we'll use direct SoftDevice calls
    // Just initialize the advertising handle to NOT_SET so we can use handle 0
    m_advertising.adv_handle = BLE_GAP_ADV_SET_HANDLE_NOT_SET;
    m_advertising.initialized = false;
    
    QLOGD("Skipped Nordic advertising module - will use direct SoftDevice calls");

    QLOGD("ble_stack_manager_init: 9-scan_init");

#ifdef BLE_CENTRAL_ROLE_ENABLE
    // Initialize scanning module (only for central role support)
    QLOGD("Initializing BLE scanning module");
    
    // Static scan parameters (must persist beyond initialization)
    static ble_gap_scan_params_t scan_params = {
        .active = 1,
        .interval = SCAN_INTERVAL,
        .window = SCAN_WINDOW,
        .timeout = SCAN_TIMEOUT,
        .scan_phys = BLE_GAP_PHY_1MBPS,
        .filter_policy = BLE_GAP_SCAN_FP_ACCEPT_ALL,
    };
    
    nrf_ble_scan_init_t scan_init_params;
    memset(&scan_init_params, 0, sizeof(scan_init_params));
    scan_init_params.p_scan_param = &scan_params;

    // Provide scan event handler
    err_code = nrf_ble_scan_init(&m_scan, &scan_init_params, scan_evt_handler);


here is the setup for the extended advertising

 */
void ble_stack_manager_update_ext_adv_ranging_data(const char *beacon_name,
                                                    const uint8_t *ntf_data,
                                                    uint16_t ntf_len)
{
    ret_code_t err_code;

    if (beacon_name == NULL || ntf_data == NULL || ntf_len == 0)
    {
        QLOGE("ext_adv: invalid arguments");
        return;
    }

    /* ---------- Build AD structure ---------- */
    /* Max extended ADV payload = 255 bytes.
     * AD[0] Flags:                3             bytes  (len + type + 0x06)
     * AD[1] Name provided: 2 + name_len  bytes
     * AD[2] Service-data field:   4 + ntf_len   bytes  (len + type + UUID16 + payload)
     * AD[3] 128-bit UUID record:  18             bytes  (len + type + 16-byte UUID)
     * Keep ntf_len within the remaining budget. */
    static uint8_t ext_adv_buf[255];
    uint16_t idx = 0;

    /* AD[0]: Flags (0x01) — explicit ranging-broadcast marker.
     * 0x06 = LE General Discoverable Mode + BR/EDR Not Supported.
     * Combined with the non-connectable/non-scannable PDU type, the name
     * from beacon and the building UUID, an observer can unambiguously
     * identify these packets as controller ranging broadcasts. */
    ext_adv_buf[idx++] = 2;     /* AD length: 1 (type byte) + 1 (value byte) */
    ext_adv_buf[idx++] = 0x01;  /* AD type: Flags */
    ext_adv_buf[idx++] = 0x06;  /* 0x06 = General Discoverable + BR/EDR Not Supported */

    /* AD[1]: Complete Local Name (0x09) — .
     * Using the controller name (not the individual beacon name) lets observers
     * immediately identify these as controller ranging broadcasts rather than
     * beacon connectable advertisements. */
    const char *ctrl_name = beacon_name;
    uint8_t name_len = (uint8_t)strlen(ctrl_name);
    if (name_len > 15) name_len = 15;              /* safety cap */
    ext_adv_buf[idx++] = (uint8_t)(1 + name_len);  /* AD length field */
    ext_adv_buf[idx++] = 0x09;                     /* AD type: Complete Local Name */
    memcpy(&ext_adv_buf[idx], ctrl_name, name_len);
    idx += name_len;

    /* AD[2]: Service Data (0x16) — UCI service UUID 0x0001 (LE) + NTF bytes.
     * Reserve 18 bytes for the AD[3] 128-bit UUID record that follows. */
    uint16_t max_svc_payload = (uint16_t)(sizeof(ext_adv_buf) - idx - 4 - 18);
    if (ntf_len > max_svc_payload) ntf_len = max_svc_payload;

    ext_adv_buf[idx++] = (uint8_t)(1 + 2 + ntf_len);  /* AD length */
    ext_adv_buf[idx++] = 0x16;                         /* AD type: Service Data - 16-bit UUID */
    ext_adv_buf[idx++] = (uint8_t)(UCI_SERVICE_UUID_SHORT & 0xFF);   /* UUID LSB */
    ext_adv_buf[idx++] = (uint8_t)(UCI_SERVICE_UUID_SHORT >> 8);     /* UUID MSB */
    memcpy(&ext_adv_buf[idx], ntf_data, ntf_len);
    idx += ntf_len;

    /* AD[3]: Complete List of 128-bit Service UUIDs (0x07) — controller ext_adv UUID.
     * A distinct UUID suffix (0xFB) lets observers differentiate three packet types:
     *   0xF9 = room-mode     beacon   connectable advertising
     *   0xFA = building-mode beacon   connectable advertising
     *   0xFB = building-mode controller ext_adv ranging-data broadcast  ← this packet
     * UUID: xxxx stored little-endian. */
    static const uint8_t building_uuid_le[16] = {xxxxx
    };
    ext_adv_buf[idx++] = 17;    /* AD length: 1 (type byte) + 16 (UUID bytes) */
    ext_adv_buf[idx++] = 0x07;  /* AD type: Complete List of 128-bit Service UUIDs */
    memcpy(&ext_adv_buf[idx], building_uuid_le, 16);
    idx += 16;

    /* ---------- Configure advertising set ---------- */
    ble_gap_adv_data_t adv_data;
    memset(&adv_data, 0, sizeof(adv_data));
    adv_data.adv_data.p_data = ext_adv_buf;
    adv_data.adv_data.len    = idx;
    /* No scan response for non-scannable advertising */
    adv_data.scan_rsp_data.p_data = NULL;
    adv_data.scan_rsp_data.len    = 0;

    ble_gap_adv_params_t adv_params;
    memset(&adv_params, 0, sizeof(adv_params));
    /* BLE 5 extended, non-connectable, non-scannable undirected.
     * iOS CoreBluetooth requirement: primary PHY must be 1M (ADV_EXT_IND pointer
     * packet), but the secondary PHY carrying the actual data (AUX_ADV_IND) must
     * be 2M.  iOS ignores extended advertising packets whose secondary PHY is 1M,
     * so keeping both at 1M makes the packets invisible to iPhones. */
    adv_params.properties.type = BLE_GAP_ADV_TYPE_EXTENDED_NONCONNECTABLE_NONSCANNABLE_UNDIRECTED;
    adv_params.p_peer_addr     = NULL;
    adv_params.filter_policy   = BLE_GAP_ADV_FP_ANY;
    adv_params.interval        = 512;   /* 512 × 0.625 ms = 320 ms  */
    adv_params.duration        = 0;     /* advertise indefinitely    */
    adv_params.primary_phy     = BLE_GAP_PHY_1MBPS;   /* ADV_EXT_IND pointer — 1M required */
    adv_params.secondary_phy   = BLE_GAP_PHY_2MBPS;   /* AUX_ADV_IND data    — 2M required by iOS */

    if (!m_ext_adv_active)
    {
        /* First call — reuse m_adv_handle for extended non-connectable advertising.
         * S140 v7.3 allows only ONE advertising set handle; m_adv_handle is shared.
         * Connectable advertising is stopped when the host app is connected (which is
         * when ranging/NTFs are active), so stop it defensively before reconfiguring. */
        sd_ble_gap_adv_stop(m_adv_handle);   /* no-op / ignore error if already stopped */

        err_code = sd_ble_gap_adv_set_configure(&m_adv_handle, &adv_data, &adv_params);
        if (err_code != NRF_SUCCESS)
        {
            QLOGE("ext_adv: sd_ble_gap_adv_set_configure (create) err=%ld", err_code);
            return;
        }
        err_code = sd_ble_gap_adv_start(m_adv_handle, APP_BLE_CONN_CFG_TAG);
        if (err_code != NRF_SUCCESS)
        {
            QLOGE("ext_adv: sd_ble_gap_adv_start err=%ld (handle=0x%02X)", err_code, m_adv_handle);
            return;
        }
        m_ext_adv_active = true;
        QLOGD("ext_adv: started on shared handle (0x%02X, len=%d)", m_adv_handle, idx);
    }
    else
    {
        /* Subsequent calls — extended advertising requires stop → update → restart.
         * Unlike legacy advertising, S140 does NOT allow data update while an
         * extended advertising set is active (returns NRF_ERROR_INVALID_STATE=8). */
        sd_ble_gap_adv_stop(m_adv_handle);   /* must stop before updating data */

        err_code = sd_ble_gap_adv_set_configure(&m_adv_handle, &adv_data, NULL);
        if (err_code != NRF_SUCCESS)
        {
            QLOGE("ext_adv: sd_ble_gap_adv_set_configure (update) err=%ld", err_code);
            return;
        }
        err_code = sd_ble_gap_adv_start(m_adv_handle, APP_BLE_CONN_CFG_TAG);
        if (err_code != NRF_SUCCESS)
        {
            QLOGE("ext_adv: sd_ble_gap_adv_start (restart) err=%ld (handle=0x%02X)", err_code, m_adv_handle);
            return;
        }
        QLOGD("ext_adv: data updated and restarted (handle=0x%02X, len=%d)", m_adv_handle, idx);
    }
}

get good return codes, and no errors, but nothing on any scanner
using the NRF s140_7.3.0 softdevice. ( we thought it might support multiple advert sets, but not,  sdk _7_1_0 comes with 7.2)

Parents Reply Children
Related