Trouble with non-concurrent multiple peers

Using an nRF52832 with SDK 14.0.0, S132 SoftDevice

We are able to connect to the device with an iOS/Android phone just fine.

Upon disconnecting, a second phone is not able to connect until the nRF52 device is reset.

We would like only one device to be able to connect to one phone at a time (non-concurrently) but multiple phones should be able to connect in series.

The following is set in sdk_config.h

#ifndef NRF_SDH_BLE_PERIPHERAL_LINK_COUNT
#define NRF_SDH_BLE_PERIPHERAL_LINK_COUNT 1
#endif

// <o> NRF_SDH_BLE_CENTRAL_LINK_COUNT - Maximum number of central links. 
#ifndef NRF_SDH_BLE_CENTRAL_LINK_COUNT
#define NRF_SDH_BLE_CENTRAL_LINK_COUNT 0
#endif

// <o> NRF_SDH_BLE_TOTAL_LINK_COUNT - Maximum number of total concurrent connections using the default configuration. 
#ifndef NRF_SDH_BLE_TOTAL_LINK_COUNT
#define NRF_SDH_BLE_TOTAL_LINK_COUNT 1
#endif

Here is our peer_manager_init():

static void peer_manager_init()
{
    NRF_LOG_INFO("Peer Manager Init\n");
    ble_gap_sec_params_t sec_param;
    ret_code_t           err_code;

    err_code = pm_init();
    APP_ERROR_CHECK(err_code);

    memset(&sec_param, 0, sizeof(ble_gap_sec_params_t));

    // Security parameters to be used for all security procedures.
    sec_param.bond           = SEC_PARAM_BOND;
    sec_param.mitm           = SEC_PARAM_MITM;
    sec_param.lesc           = SEC_PARAM_LESC;
    sec_param.keypress       = SEC_PARAM_KEYPRESS;
    sec_param.io_caps        = SEC_PARAM_IO_CAPABILITIES;
    sec_param.oob            = SEC_PARAM_OOB;
    sec_param.min_key_size   = SEC_PARAM_MIN_KEY_SIZE;
    sec_param.max_key_size   = SEC_PARAM_MAX_KEY_SIZE;
    sec_param.kdist_own.enc  = 1;
    sec_param.kdist_own.id   = 1;
    sec_param.kdist_peer.enc = 1;
    sec_param.kdist_peer.id  = 1;

    err_code = pm_sec_params_set(&sec_param);
    APP_ERROR_CHECK(err_code);

    err_code = pm_register(pm_evt_handler);
    APP_ERROR_CHECK(err_code);
}

delete_bonds():

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);
}

pm_event_handler():

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_INFO("Connected to a previously bonded device.");
        } break;

        case PM_EVT_CONN_SEC_SUCCEEDED:
        {
            NRF_LOG_INFO("Connection secured: role: %d, conn_handle: 0x%x, procedure: %d.",
                         ble_conn_state_role(p_evt->conn_handle),
                         p_evt->conn_handle,
                         p_evt->params.conn_sec_succeeded.procedure);
        } break;

        case PM_EVT_CONN_SEC_FAILED:
        {
            /* Often, when securing fails, it shouldn't be restarted, for security reasons.
             * Other times, 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. */
        } break;

        case PM_EVT_CONN_SEC_CONFIG_REQ:
        {
            // Reject pairing request from an already bonded peer.
            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_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_PEERS_DELETE_SUCCEEDED:
        {
            advertising_start(false);
        } break;

        case 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_PEER_DATA_UPDATE_FAILED:
        {
            // Assert.
            APP_ERROR_CHECK(p_evt->params.peer_data_update_failed.error);
        } break;

        case PM_EVT_PEER_DELETE_FAILED:
        {
            // Assert.
            APP_ERROR_CHECK(p_evt->params.peer_delete_failed.error);
        } break;

        case PM_EVT_PEERS_DELETE_FAILED:
        {
            // Assert.
            APP_ERROR_CHECK(p_evt->params.peers_delete_failed_evt.error);
        } break;

        case PM_EVT_ERROR_UNEXPECTED:
        {
            // Assert.
            APP_ERROR_CHECK(p_evt->params.error_unexpected.error);
        } break;

        case PM_EVT_CONN_SEC_START:
        case PM_EVT_PEER_DATA_UPDATE_SUCCEEDED:
        case PM_EVT_PEER_DELETE_SUCCEEDED:
        case PM_EVT_LOCAL_DB_CACHE_APPLIED:
        case PM_EVT_SERVICE_CHANGED_IND_SENT:
        case PM_EVT_SERVICE_CHANGED_IND_CONFIRMED:
        default:
            break;
    }
}
  • As long as you are not advertising with a whitelist any device should be able to connect. Are you advertising with a whitelist? Is the second phone able to see the advertisements after you have disconnected?

  • Hi Petter and thanks for your response.

    It appears that this issue may be related to our custom app as nRF Connect is able to connect on one phone, read and write Characteristics, then disconnect, then another phone is able to connect with nRF Connect and do the same, etc., etc., back and forth, no problem.

    Is there anything on the iOS/Android side of things when communicating with nRF52 devices that might account for this inability to connect sequentially?

  • Make sure you don't have nRF Connect running in ht background (there is no nRF Connect notification) when you connect in your app. nRF Connect, when you have GATT Server enabled in it, may overtake the connection so when you disconnect in your app the physical connection is still open as nRF Connect is still connected. To avoid this close nRF Connect with BACK button, not just switch to another app. You may also disable the GATT server there (drop down menu and DISABLE icon). If you have it open, and you disconnect from your app, switch back to nRF Connect and disconnect there, then you should be able to connect on another phone as the physical connection will be closed (unless some other app also is connected like nRF Connect, not likely). This issue will no longer bother you on Android 8.1+, as then the background connection is done as opportunistic (like a weak reference).

  • Hello Aleksander. This is good to know but the issue is not with nRF Connect, which works just fine.

    The issue is what might cause this behavior in a custom iOS/Android application?

  • My point was, that if you have nRF Connect running in background while you test your app, and disconnect from the device in your app, you'll get onConnectionStateChanged with State disconnected, but nRF Connect may keep the physical connection if it wasn't excited with back button and had gatt server enabled. Then your device wouldn't advertise again, as it's still connected to this Android device. If you are sure that nRF Connect is properly closed (there is no notification saying that nRF Connect's service is running), and you will have this problem, check if you disconnect from all BluetoothGatt objects (in case you connected multiple times to the same device). I can't find any other reason why wouldn't it works with your app and would with nRF Connect.