This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

Multi-peripheral, bonding and whitelisting

Hi,

I need a way to have a concurrent connection (multi-peripheral), with whitelisting and bonding. Is there any example of this?

I found an example for multi-peripherals, but this is not for concurrent connections. I also found the HID-Mouse example which uses whitelisting and bonding, but it's only supporting single links (because it uses the advertising module).

I some sort of managed to include those two together, but then sometimes the softdevice asserts. So I was wondering if there are any other examples of doing this?

  • Thank you for your answer. I didn't know to look on those error codes like that.

    But how do I disable advertising with the advertising module? I only found the method to directly use the softdevice

    sd_ble_gap_adv_stop(m_advertising.adv_handle);

    But then the device is not starting at all (I don't even get a log). But probably this is not the way to go because I use the advertising module.

    And another thing. Why can I connect more centrals while I use the advertising module, but the advertising module is only made for one link? 
  • I don't know what your application looks like, and where the pm_device_identities_list_set() was called from. Is it triggered from an event in main.c?

    If so, you can try to use some functions like this:

    static void custom_adv_stop(void)
    {
        NRF_LOG_INFO("stop advertising");
        ret_code_t err_code;
        
        uint8_t my_adv_handle = m_advertising.adv_handle;
        err_code = sd_ble_gap_adv_stop(my_adv_handle);
        APP_ERROR_CHECK(err_code);
        
        err_code = bsp_indication_set(BSP_INDICATE_IDLE);
        APP_ERROR_CHECK(err_code);
    }
    
    static void custom_adv_start(void)
    {
        NRF_LOG_INFO("start advertising");
        uint32_t err_code = ble_advertising_start(&m_advertising, BLE_ADV_MODE_FAST);
        APP_ERROR_CHECK(err_code);
    }

    custom_adv_start() is actually exactly the same as advertising_start() found in most BLE examples, so just use the default advertising_start() function.

    In case this doesn't work, it may be because the peer manager's event handler is called before the main file's event handler. 

    E.g, make sure that the custom_advertising_stop() is called before pm_handler_secure_on_connection(p_ble_evt);

    Best regards,

    Edvin

  • I put custom_advertising_stop() before pm_device_identities_list_set() and then start advertising.

    If I'm not bonded, I can disconnect and the peripheral doesn't crash. But as soon as I bond it crashes, as in that it's not advertising anymore and I get "00> <error> app: End of error report" as log info. So that's not that helpfull.

    Below the relevant code. On line 268 and 281 I respectively stop and start advertising.

    /**@brief Function for setting filtered whitelist.
     *
     * @param[in] skip  Filter passed to @ref pm_peer_id_list.
     */
    static void whitelist_set(pm_peer_id_list_skip_t skip) {
        pm_peer_id_t peer_ids[BLE_GAP_WHITELIST_ADDR_MAX_COUNT];
        uint32_t peer_id_count = BLE_GAP_WHITELIST_ADDR_MAX_COUNT;
    
        ret_code_t err_code = pm_peer_id_list(peer_ids, &peer_id_count, PM_PEER_ID_INVALID, skip);
        APP_ERROR_CHECK(err_code);
    
        NRF_LOG_INFO("\tm_whitelist_peer_cnt %d, MAX_PEERS_WLIST %d",
                     peer_id_count + 1,
                     BLE_GAP_WHITELIST_ADDR_MAX_COUNT);
    
        err_code = pm_whitelist_set(peer_ids, peer_id_count);
        APP_ERROR_CHECK(err_code);
    }
    
    /**@brief Clear bond information from persistent storage.
     */
    static void delete_bonds(void) {
        ret_code_t err_code;
    
        NRF_LOG_INFO("Erase bonds!");
    
        err_code = pm_peers_delete();
        APP_ERROR_CHECK(err_code);
    }
    
    static void pm_evt_handler(pm_evt_t const *p_evt) {
        ret_code_t err_code;
    
        switch (p_evt->evt_id) {
            case PM_EVT_BONDED_PEER_CONNECTED:
                NRF_LOG_DEBUG("PM_EVT_BONDED_PEER_CONNECTED");
                // Update the rank of the peer.
                err_code = pm_peer_rank_highest(p_evt->peer_id);
                break;
            case PM_EVT_CONN_SEC_START:
                NRF_LOG_DEBUG("PM_EVT_CONN_SEC_START");
                break;
            case PM_EVT_CONN_SEC_SUCCEEDED:
                NRF_LOG_DEBUG("PM_EVT_CONN_SEC_SUCCEEDED");
                // Update the rank of the peer.
                err_code = pm_peer_rank_highest(p_evt->peer_id);
                break;
            case PM_EVT_CONN_SEC_FAILED:
                NRF_LOG_DEBUG("PM_EVT_CONN_SEC_FAILED");
                // In some cases, when securing fails, it can be restarted directly. Sometimes it can be
                // restarted, but only after changing some Security Parameters. Sometimes, it cannot be
                // restarted until the link is disconnected and reconnected. Sometimes it is impossible
                // to secure the link, or the peer device does not support it. How to handle this error
                // is highly application-dependent.
                //            NRF_LOG_INFO("Failed to secure connection. Disconnecting.");
                //            err_code = sd_ble_gap_disconnect(m_conn_handle,
                //                                             BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION);
                //            if (err_code != NRF_ERROR_INVALID_STATE)
                //            {
                //                APP_ERROR_CHECK(err_code);
                //            }
                //            m_conn_handle = BLE_CONN_HANDLE_INVALID;
    
                break;
            case PM_EVT_CONN_SEC_CONFIG_REQ: {
                NRF_LOG_DEBUG("PM_EVT_CONN_SEC_CONFIG_REQ");
                // A connected peer (central) is trying to pair, but the Peer Manager already has a bond
                // for that peer. Setting allow_repairing to false rejects the pairing request.
                // If this event is ignored (pm_conn_sec_config_reply is not called in the event
                // handler), the Peer Manager assumes allow_repairing to be false.
                pm_conn_sec_config_t conn_sec_config = {.allow_repairing = false};
                pm_conn_sec_config_reply(p_evt->conn_handle, &conn_sec_config);
            } break;
            case PM_EVT_CONN_SEC_PARAMS_REQ: {
                NRF_LOG_DEBUG("PM_EVT_CONN_SEC_PARAMS_REQ");
                // If you want to set different security parameters for particular peers, you can do
                // so here. Pass the desired security parameters to pm_conn_sec_params_reply().
                //            err_code = pm_conn_sec_params_reply(p_evt->conn_handle,
                //                                               &sec_param,
                //                                                p_evt->params.conn_sec_params_req.p_context);
                //            APP_ERROR_CHECK(err_code);
            } break;
            case PM_EVT_STORAGE_FULL:
                NRF_LOG_DEBUG("PM_EVT_STORAGE_FULL");
                // Run garbage collection on the flash.
                err_code = fds_gc();
                if (err_code == FDS_ERR_BUSY || err_code == FDS_ERR_NO_SPACE_IN_QUEUES) {
                    // Retry.
                } else {
                    APP_ERROR_CHECK(err_code);
                }
                break;
            case PM_EVT_ERROR_UNEXPECTED:
                NRF_LOG_DEBUG("PM_EVT_ERROR_UNEXPECTED");
                // Assert.
                APP_ERROR_CHECK(p_evt->params.error_unexpected.error);
                break;
            case PM_EVT_PEER_DATA_UPDATE_SUCCEEDED:
                NRF_LOG_DEBUG("PM_EVT_PEER_DATA_UPDATE_SUCCEEDED");
                if (p_evt->params.peer_data_update_succeeded.flash_changed && (p_evt->params.peer_data_update_succeeded.data_id == PM_PEER_DATA_ID_BONDING)) {
                    NRF_LOG_INFO("New Bond, add the peer to the whitelist if possible");
                    // Note: You should check on what kind of white list policy your application should use.
    
                    whitelist_set(PM_PEER_ID_LIST_SKIP_NO_ID_ADDR);
                }
                break;
            case PM_EVT_PEER_DATA_UPDATE_FAILED:
                NRF_LOG_DEBUG("PM_EVT_PEER_DATA_UPDATE_SUCCEEDED");
                // Assert.
                APP_ERROR_CHECK_BOOL(false);
                break;
            case PM_EVT_PEER_DELETE_SUCCEEDED:
                NRF_LOG_DEBUG("PM_EVT_PEER_DELETE_SUCCEEDED");
                break;
            case PM_EVT_PEER_DELETE_FAILED:
                NRF_LOG_DEBUG("PM_EVT_PEER_DELETE_FAILED");
                // Assert.
                APP_ERROR_CHECK(p_evt->params.peer_delete_failed.error);
                break;
            case PM_EVT_PEERS_DELETE_SUCCEEDED:
                NRF_LOG_DEBUG("PM_EVT_PEERS_DELETE_SUCCEEDED");
                // At this point it is safe to start advertising or scanning.
                break;
            case PM_EVT_PEERS_DELETE_FAILED:
                NRF_LOG_DEBUG("PM_EVT_PEERS_DELETE_FAILED");
                // Assert.
                APP_ERROR_CHECK(p_evt->params.peers_delete_failed_evt.error);
                break;
            case PM_EVT_LOCAL_DB_CACHE_APPLIED:
                NRF_LOG_DEBUG("PM_EVT_LOCAL_DB_CACHE_APPLIED");
                break;
            case PM_EVT_LOCAL_DB_CACHE_APPLY_FAILED:
                NRF_LOG_DEBUG("PM_EVT_LOCAL_DB_CACHE_APPLY_FAILED");
                // The local database has likely changed, send service changed indications.
                pm_local_database_has_changed();
                break;
            case PM_EVT_SERVICE_CHANGED_IND_SENT:
                NRF_LOG_DEBUG("PM_EVT_SERVICE_CHANGED_IND_SENT");
                break;
            case PM_EVT_SERVICE_CHANGED_IND_CONFIRMED:
                NRF_LOG_DEBUG("PM_EVT_SERVICE_CHANGED_IND_CONFIRMED");
                break;
        }
    }
    
    /**@brief Function for starting advertising.
     */
    static void advertising_start(bool erase_bonds) {
        NRF_LOG_INFO("start advertising");
        if (erase_bonds == true) {
            delete_bonds();
            // Advertising is started by PM_EVT_PEERS_DELETE_SUCCEEDED event.
        } else {
            whitelist_set(PM_PEER_ID_LIST_SKIP_NO_ID_ADDR);
            bsp_board_led_on(ADVERTISING_LED);
            ret_code_t ret = ble_advertising_start(&m_advertising, BLE_ADV_MODE_FAST);
            APP_ERROR_CHECK(ret);
        }
    }
    
    static void custom_adv_stop(void) {
        NRF_LOG_INFO("stop advertising");
        ret_code_t err_code;
    
        uint8_t my_adv_handle = m_advertising.adv_handle;
        err_code = sd_ble_gap_adv_stop(my_adv_handle);
        APP_ERROR_CHECK(err_code);
    
        err_code = bsp_indication_set(BSP_INDICATE_IDLE);
        APP_ERROR_CHECK(err_code);
    }
    
    /**@brief Function for setting filtered device identities.
     *
     * @param[in] skip  Filter passed to @ref pm_peer_id_list.
     */
    static void identities_set(pm_peer_id_list_skip_t skip) {
        pm_peer_id_t peer_ids[BLE_GAP_DEVICE_IDENTITIES_MAX_COUNT];
        uint32_t peer_id_count = BLE_GAP_DEVICE_IDENTITIES_MAX_COUNT;
    
        ret_code_t err_code = pm_peer_id_list(peer_ids, &peer_id_count, PM_PEER_ID_INVALID, skip);
        APP_ERROR_CHECK(err_code);
    
        err_code = pm_device_identities_list_set(peer_ids, peer_id_count);
        APP_ERROR_CHECK(err_code);
    
        // Advertising is not running when all connections are taken, and must therefore be started.
        //   advertising_start(false);
    }
    
    /**@brief Function for handling advertising events.
     *
     * @details This function will be called for advertising events which are passed to the application.
     *
     * @param[in] ble_adv_evt  Advertising event.
     */
    static void on_adv_evt(ble_adv_evt_t ble_adv_evt) {
        ret_code_t err_code;
    
        switch (ble_adv_evt) {
            case BLE_ADV_EVT_DIRECTED_HIGH_DUTY:
                NRF_LOG_INFO("High Duty Directed advertising.");
                err_code = bsp_indication_set(BSP_INDICATE_ADVERTISING_DIRECTED);
                APP_ERROR_CHECK(err_code);
                break;
    
            case BLE_ADV_EVT_DIRECTED:
                NRF_LOG_INFO("Directed advertising.");
                err_code = bsp_indication_set(BSP_INDICATE_ADVERTISING_DIRECTED);
                APP_ERROR_CHECK(err_code);
                break;
    
            case BLE_ADV_EVT_FAST:
                NRF_LOG_INFO("Fast advertising.");
                err_code = bsp_indication_set(BSP_INDICATE_ADVERTISING);
                APP_ERROR_CHECK(err_code);
                break;
    
            case BLE_ADV_EVT_SLOW:
                NRF_LOG_INFO("Slow advertising.");
                err_code = bsp_indication_set(BSP_INDICATE_ADVERTISING_SLOW);
                APP_ERROR_CHECK(err_code);
                break;
    
            case BLE_ADV_EVT_FAST_WHITELIST:
                NRF_LOG_INFO("Fast advertising with whitelist.");
                err_code = bsp_indication_set(BSP_INDICATE_ADVERTISING_WHITELIST);
                APP_ERROR_CHECK(err_code);
                break;
    
            case BLE_ADV_EVT_SLOW_WHITELIST:
                NRF_LOG_INFO("Slow advertising with whitelist.");
                err_code = bsp_indication_set(BSP_INDICATE_ADVERTISING_WHITELIST);
                APP_ERROR_CHECK(err_code);
                break;
    
            case BLE_ADV_EVT_IDLE:
                sleep_mode_enter();
                break;
    
            case BLE_ADV_EVT_WHITELIST_REQUEST: {
                ble_gap_addr_t whitelist_addrs[BLE_GAP_WHITELIST_ADDR_MAX_COUNT];
                ble_gap_irk_t whitelist_irks[BLE_GAP_WHITELIST_ADDR_MAX_COUNT];
                uint32_t addr_cnt = BLE_GAP_WHITELIST_ADDR_MAX_COUNT;
                uint32_t irk_cnt = BLE_GAP_WHITELIST_ADDR_MAX_COUNT;
    
                err_code = pm_whitelist_get(whitelist_addrs, &addr_cnt,
                                            whitelist_irks, &irk_cnt);
                APP_ERROR_CHECK(err_code);
                NRF_LOG_INFO("pm_whitelist_get returns %d addr in whitelist and %d irk whitelist",
                             addr_cnt, irk_cnt);
    
                // Set the correct identities list (no excluding peers with no Central Address Resolution).
    
                identities_set(PM_PEER_ID_LIST_SKIP_NO_IRK);
    
                // Apply the whitelist.
                err_code = ble_advertising_whitelist_reply(&m_advertising,
                                                           whitelist_addrs,
                                                           addr_cnt,
                                                           whitelist_irks,
                                                           irk_cnt);
                APP_ERROR_CHECK(err_code);
            } break;  //BLE_ADV_EVT_WHITELIST_REQUEST
    
            case BLE_ADV_EVT_PEER_ADDR_REQUEST: {
                pm_peer_data_bonding_t peer_bonding_data;
                custom_adv_stop();
                // Only Give peer address if we have a handle to the bonded peer.
                if (m_peer_id != PM_PEER_ID_INVALID) {
                    err_code = pm_peer_data_bonding_load(m_peer_id, &peer_bonding_data);
                    if (err_code != NRF_ERROR_NOT_FOUND) {
                        APP_ERROR_CHECK(err_code);
    
                        // Manipulate identities to exclude peers with no Central Address Resolution.
                        identities_set(PM_PEER_ID_LIST_SKIP_ALL);
    
                        ble_gap_addr_t *p_peer_addr = &(peer_bonding_data.peer_ble_id.id_addr_info);
                        err_code = ble_advertising_peer_addr_reply(&m_advertising, p_peer_addr);
                        APP_ERROR_CHECK(err_code);
                        advertising_start(false);
                    }
                }
    
            }
    
            break;  //BLE_ADV_EVT_PEER_ADDR_REQUEST
    
            default:
                break;
        }
    }
    
    /**@brief Function for initializing the Advertising functionality.
     */
    static void advertising_init(void) {
        uint32_t err_code;
        uint8_t adv_flags;
        ble_advertising_init_t init;
    
        memset(&init, 0, sizeof(init));
    
        adv_flags = BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE;
        init.advdata.name_type = BLE_ADVDATA_FULL_NAME;
        init.advdata.include_appearance = true;
        init.advdata.flags = adv_flags;
        init.advdata.uuids_complete.uuid_cnt = sizeof(m_adv_uuids) / sizeof(m_adv_uuids[0]);
        init.advdata.uuids_complete.p_uuids = m_adv_uuids;
    
        init.config.ble_adv_whitelist_enabled = true;
        init.config.ble_adv_directed_high_duty_enabled = true;
        init.config.ble_adv_directed_enabled = false;
        init.config.ble_adv_directed_interval = 0;
        init.config.ble_adv_directed_timeout = 0;
        init.config.ble_adv_fast_enabled = true;
        init.config.ble_adv_fast_interval = APP_ADV_FAST_INTERVAL;
        init.config.ble_adv_fast_timeout = APP_ADV_FAST_DURATION;
        init.config.ble_adv_slow_enabled = true;
        init.config.ble_adv_slow_interval = APP_ADV_SLOW_INTERVAL;
        init.config.ble_adv_slow_timeout = APP_ADV_SLOW_DURATION;
    
        init.evt_handler = on_adv_evt;
        init.error_handler = ble_advertising_error_handler;
    
        err_code = ble_advertising_init(&m_advertising, &init);
        APP_ERROR_CHECK(err_code);
    
        ble_advertising_conn_cfg_tag_set(&m_advertising, APP_BLE_CONN_CFG_TAG);
    }

  • Does the log say anything befor "End of error report"?

    What function does the log point to?

    I see some possible reasons, depending on where the error handler is pointing to:

    sd_ble_gap_adv_stop() returns an error, because it is not yet advertising, or it takes some time to stop the advertisements, and you call pm_peer_data_bonding_load() too quick after custom_adv_stop(). 

    Check what the error handler points to. 

    You should also be aware that the advertising module may start the advertisement at some point in time. Please see the on_disconnected() function in ble_advertising.c. What is your p_advertising->adv_modes_config.ble_adv_on_disconnect_disabled?

    BR,
    Edvin

  • Does the log say anything befor "End of error report"?

    On this:

    00> <info> app:   m_whitelist_peer_cnt 2, MAX_PEERS_WLIST 8
    00> <info> app: pm_whitelist_get returns 1 addr in whitelist and 1 irk whitelist

    Sometimes it doesn't say anything at all after this message, but the device just crashes with no error report. Sometimes it gives only the 

    00> <error> app: End of error report
    as a message.

    Debug mode is on in the sdk_config.h and also in the compiler flag. So it's very strange to me that it doesn't point to where the error is appearing?

    Check what the error handler points to

    The error handler doesn't point to anything, just the error log as above

    You should also be aware that the advertising module may start the advertisement at some point in time. Please see the on_disconnected() function in ble_advertising.c. What is your p_advertising->adv_modes_config.ble_adv_on_disconnect_disabled?

    I put this in on_connected in ble_advertising.c, which looks like below, is that correct?

    static void on_disconnected(ble_advertising_t * const p_advertising, ble_evt_t const * p_ble_evt)
    {
        uint32_t ret;
    
        p_advertising->whitelist_temporarily_disabled = false;
        NRF_LOG_INFO("ble_adv_on_disconnect_disabled is: %d", p_advertising->adv_modes_config.ble_adv_on_disconnect_disabled);
    
        if (p_ble_evt->evt.gap_evt.conn_handle == p_advertising->current_slave_link_conn_handle &&
            p_advertising->adv_modes_config.ble_adv_on_disconnect_disabled == false)
        {
           ret = ble_advertising_start(p_advertising, BLE_ADV_MODE_DIRECTED_HIGH_DUTY);
           if ((ret != NRF_SUCCESS) && (p_advertising->error_handler != NULL))
           {
               p_advertising->error_handler(ret);
           }
        }
    }

    Then I get the following log message: 

    00> <info> app: ble_adv_on_disconnect_disabled is: 0

    But again, this only happens sometimes. Mostly the app just crashes, only after a few resets it's giving this message. So it's still very unstable behaviour.

Related