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

Questions about implementing BLE bonding with NUS

Hi

This is Joe.
I would like to ask questions about implementing BLE bonding.

SDK:
16.0.0 (nRF5SDK160098a08e2)

Projects:
The base project I start with is NUS example (both Peripheral and Central)
example/ble_peripheral/ble_app_uart
example/ble_central/ble_app_uart_c

What I have done / tried:
1. First of all I just build both projects and run on 2 devices. Make sure the NUS example is working correctly.

2. Then, by referencing the code from another example (ble_app_hrs and ble_app_hrs_c), I added peer manager and bonding related code, such as pm_evt_handler, peer_manager_init, delete_bonds, etc to both projects (ble_app_uart & ble_app_uart_c).

3. And also according to this forum post (https://devzone.nordicsemi.com/f/nordic-q-a/43400/peer_manager_sm-could-not-perform-security-procedure-smd_params_reply-or-smd_link_secure-returned-nrf_error_invalid_addr), cc310 is also added to the project.

The project structure looks like this:


What is the current status:
1. The Peripheral (ble_app_uart) is able to connect to the iOS app "nRF Connect".
When connected to iOS app "nRF Connect", I can see the device advertising, connect to the device, notify from the device, disconnect from the device. Everything working correctly.

2. The Peripheral (ble_app_uart) is not able to connect to Central (ble_app_uart_c).
When try to connect to the Central device (ble_app_uart_c), it shows connected (green rectangle in below image) at first but then disconnected (red rectangle in below image) after 1 second.
(P.S. To minimize unknown problem, I execute delete_bonds everytime when the Peripheral or Central is power on)

This is the RTT output of Central device.


The error message shows that connection failed due to connection security failed (error 4352).
In this forum post (https://devzone.nordicsemi.com/f/nordic-q-a/49894/peermanager-error-error-4352), 4352 is PM_CONN_SEC_ERROR_DISCONNECT.

What my question is:
1. I cannot figure out what caused the problem and now I have no idea how to fix this. Could anyone give me some help or some hints to guide me to the correct way?
2. Actually what I would like to archive is that I want the previously paired devices remember each other. Just like our earphone always connect to our smartphone when both power on. Therefore even if there are multiple peripherals and centrals are power on, the devices will not paired to a device that is not paired before.
As far as my understanding, the original example of NUS, Central will search for any Peripheral device with NUS UUID. So I cannot guarantee which device connect to which if I have multiple Peripheral advertising and Central scanning at the same time. So I googled and found bonding seems to be the solution. Is my understanding correct? Please correct me if I am wrong.

Thank you very much.

Best Regards
Joe

Parents
  • Hello,

    You can see where this error is printed:

    It is from the PM_EVT_CONN_SEC_FAILED event in pm_handler_pm_evt_log() from peer_manager_handler.c:

                NRF_LOG_INFO("Connection security failed: role: %s, conn_handle: 0x%x, procedure: %s, error: %d",
                             m_roles_str[ble_conn_state_role(p_pm_evt->conn_handle)],
                             p_pm_evt->conn_handle,
                             m_sec_procedure_str[p_pm_evt->params.conn_sec_start.procedure],
                             p_pm_evt->params.conn_sec_failed.error);

    You can see that the error number is printed as a decimal value (%d), so this means that the decimal value is 0x1100. So what does that mean?

    Looking at the type of the p_pm_evt->params.conn_sec_failed.error, which is an pm_sec_error_code_t, you can see from the description that it is:

    /**@brief Errors from security procedures in Peer Manager.
     *
     * @details Possible values are defined in @ref PM_SEC_ERRORS and @ref BLE_GAP_SEC_STATUS.
     */
    typedef uint16_t pm_sec_error_code_t;

    (from peer_manager_types.h, line 74-78)

    So from @defgroup PM_SEC_ERRORS:

    #define PM_CONN_SEC_ERROR_DISCONNECT         (PM_CONN_SEC_ERROR_BASE + 0x100) /**< @brief Pairing or encryption did not finish before the link disconnected for an unrelated reason. */

    (PM_CONN_SEC_ERROR_BASE is 0x1000)

    So the error you receive is because of the disconnect.

    Are you prompted with any pairing questions when connecting with nRF Connect for Desktop? Did you try to click the settings icon of the connection and click pair (and check of "bond")?

     

    2. Actually what I would like to archive is that I want the previously paired devices remember each other. Just like our earphone always connect to our smartphone when both power on. Therefore even if there are multiple peripherals and centrals are power on, the devices will not paired to a device that is not paired before.
    As far as my understanding, the original example of NUS, Central will search for any Peripheral device with NUS UUID. So I cannot guarantee which device connect to which if I have multiple Peripheral advertising and Central scanning at the same time. So I googled and found bonding seems to be the solution. Is my understanding correct? Please correct me if I am wrong.

     That is almost correct. Bonding means that you store the encryption keys of the connection. After you have bonded, either your central or peripheral (or both) may use something called a whitelist when it is scanning/advertising. If you do this, it will only accept connections to that device.

    That being said. Even if you bond with a phone, it doesn't mean that this phone will try to connect to the peripheral whenever it is advertising. I don't know the exact details about earphones, because that is not BLE, but Classic Bluetooth. However, I suspect that it is similar to HID devices. You will see that if you try to flash the ble_app_hids_keyboard example, the phone will automatically reconnect to that device. The reason for this is that it is a HID [device] (Human Interface Device), and the phone's OS finds it useful. However, if you use most other examples, the phone will not automatically connect even though they are bonded. If you want something custom to automatically connect to your phone, you must write an application on the phone that will scan for and connect to your peripheral.

    Back to whitelists: The main purpose of whitelists is to look for specific devices, and deny connection from others. One example that uses a whitelist is the ble_app_gls, and the ble_app_hrs_c.

    Best regards,

    Edvin

  • Hello

    Sorry I was busy on other project so I was unable to update the progress.

    After referencing the examples, looks like I am able to bond two nRF52 devices.
    According to the log, the peer data was updated in flash.


    If I just turn the Peripheral device off and on again, the Central remain on, the devices reconnected successfully and no change is made in flash.


    Problem #1:
    However, if I turn the Central off, and turn it on again, it shows the following error:
    ERROR 12 [NRF_ERROR_DATA_SIZE]
    (The error is occurred on Central side, not Peripheral side)
    (The Central program is based on the example ble_uart_c)

    case APP_UART_COMMUNICATION_ERROR:
    NRF_LOG_ERROR("Communication error occurred while handling UART.");
    APP_ERROR_HANDLER(p_event->data.error_communication); // Line 1006
    break;


    And I can only erase the Central and re-download the program again.
    I have read several posts talking about this.
    Most are on Peripheral side, but mine is on Central side.


    Problem #2:
    After problem #1 occured, the pairing no long success.
    When trying to connect, the error of 4102 is returned.
    4102 => 0x1006 => PM_CONN_SEC_ERROR_PIN_OR_KEY_MISSING


    Is there any hint on the above two problems?


    Best Regards
    Joe

  • Hello Edvin,

    Just like some additional observation.

    Instead of deleting bond by pressing button to call delete_bond from the problem, I tried Target > Erase All to remove the whole device memory by J-Link.

    (The observed result is same as previous.)
    Scenario 1:
    If I perform remove all to the Peripheral, the Central can no longer connect due to PM_CONN_SEC_ERROR_PIN_OR_KEY_MISSING.
    Scenario 2:
    On the otherhand, if I perform remove all to the Central, the Peripheral still can connect again.

    According to the observation, I understand the situation as follow:
    Scenario 1 proved that the Central is actually remembering the Peripheral, because when the Peripheral device is cleared the key is also lost, therefore, the "old" Central rejects the connection of the "new" Peripheral?
    Scenario 2 proved that the Peripheral is not remembering the Central, because the Peripheral can connect to a "new" Central, even it has been bonded with the "old" Central.

    So I guess the problem is due to some security or bonding setting?
    I think should look for the problem from the code on Peripheral side rather than on Central side.

    Best Regards,
    Joe

  • I don't think I follow what you are doing anymore. If one of the devices still has bonding information and the other one doesn't, I believe you should get an event to which you should call a reply with .allow_repairing = true or false. I believe this event is the PM_EVT_CONN_SEC_CONFIG_REQ event.

    If you are not sure how whether your bonds are deleted, I suggest you work with manually erasing the chip for now, until you figure out how to handle the different setups (one device having the bonding information, while the other doesn't).

    Best regards,

    Edvin

  • Hi Edvin,

    Thank you for your reply.
    May be I have do something wrong when integrating my project code from the sample code.
    Let me try to figure it out why PM_EVT_CONN_SEC_CONFIG_REQ does not appear first.
    And I will use manually erasing as you suggested for testing.

    Best Regards,

    Joe

  • Hi Edvin,

    I have solved the problem finally. It is because I missed the whitelist in the Peripheral side.
    After integrating the whitelist by referencing example ble_app_hids_keyboard, the Problem #2 is fixed.
    After some simple tests, bonded device will only connect to each other correctly.

    And I found a new problem about advertising on Peripheral.

    Background:
    There is a button on Peripheral, the button will erase the the saved bonding information on Peripheral. And the same for Central.
    After erasing, I want the device to restart scan for Central and advertising for Peripheral.

    Scenario:
    (Code sample are below)
    1. Peripheral and Central are bonded and connected.
    2. On Peripheral, press on the button to call advertising_start(true), which will call delete_bond().
    3. pm_peers_delete() will trigger event PM_EVT_PEERS_DELETE_SUCCEEDED in pm_evt_handler.
    4. PM_EVT_PEERS_DELETE_SUCCEEDED event will call advertising_start(false)
    5. When ble_advertising_start is called, the error NRF_ERROR_CONN_COUNT is returned.

    static void advertising_start(bool erase_bonds)
    {
        NRF_LOG_INFO("advertising_start(%d)", erase_bonds);
        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);
    
            ret_code_t err_code = ble_advertising_start(&m_advertising, BLE_ADV_MODE_FAST);
            APP_ERROR_CHECK(err_code);	// Line 1180
    
            m_is_advertising = true;
            NRF_LOG_INFO("Advertising start");
        }
    }

    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)
    {
        pm_handler_on_pm_evt(p_evt);
        pm_handler_flash_clean(p_evt);
    
        switch (p_evt->evt_id)
        {
            case PM_EVT_PEERS_DELETE_SUCCEEDED:
                NRF_LOG_INFO("PM_EVT_PEERS_DELETE_SUCCEEDED");
                advertising_start(false);
                break;
    		//...
    }

    I found that the error is from sd_ble_gap_adv_start(...), line 653 of ble_advertising.c

    if (p_advertising->adv_mode_current != BLE_ADV_MODE_IDLE)
    {
    
    	ret = sd_ble_gap_adv_set_configure(&p_advertising->adv_handle, p_advertising->p_adv_data, &p_advertising->adv_params);
    	if (ret != NRF_SUCCESS)
    	{
    		return ret;
    	}
    	ret = sd_ble_gap_adv_start(p_advertising->adv_handle, p_advertising->conn_cfg_tag);		// Line 653
    
    	if (ret != NRF_SUCCESS)
    	{
    		return ret;
    	}
    }

    According to nrf_error.h:

    #define NRF_ERROR_CONN_COUNT (NRF_ERROR_BASE_NUM + 18) ///< Maximum connection count exceeded.

    Does it mean that I cannot advertise when there is an active connection?
    If so, does it mean that I have to disconnect from Central before start to advertise?

    Best Regards,
    Joe

  • Hello,

    Thanks for sharing your progress. It is very useful for other users who encounter similar problems.

    The softdevice can only have a certain number of connections (declared in the application). If you are in a connection, and you have set the maximum number of connections to 1, then sd_ble_gap_adv_start() will return NRF_ERROR_CONN_COUNT. So in your case, you are probably still connected, and you start advertising again. 

    If you only intend to support one connection at the time, you need to disconnect before you start advertising. Deleting the bonds will not disconnect your current connections, so you need to do this "manually". 

    Perhaps your button handler can disconnect from the central before it deletes the bonds. Remember that disconnecting can take some time, especially if you have a long connection interval. If so, perhaps you should set a flag (a local variable), and disconnect. When you get the disconnected event, you can check this flag, and call advertising_start(true) if the flag is set, and advertising_start(false) if the flag is not set (just a normal disconnect). 

    If you intend to support more than one connection, you need to increase the connection count. Please refer to the multilink examples. ble_app_multilink_central for multilink central, and ble_app_multiperipheral for multiperipheral.

    Best regards,

    Edvin

Reply
  • Hello,

    Thanks for sharing your progress. It is very useful for other users who encounter similar problems.

    The softdevice can only have a certain number of connections (declared in the application). If you are in a connection, and you have set the maximum number of connections to 1, then sd_ble_gap_adv_start() will return NRF_ERROR_CONN_COUNT. So in your case, you are probably still connected, and you start advertising again. 

    If you only intend to support one connection at the time, you need to disconnect before you start advertising. Deleting the bonds will not disconnect your current connections, so you need to do this "manually". 

    Perhaps your button handler can disconnect from the central before it deletes the bonds. Remember that disconnecting can take some time, especially if you have a long connection interval. If so, perhaps you should set a flag (a local variable), and disconnect. When you get the disconnected event, you can check this flag, and call advertising_start(true) if the flag is set, and advertising_start(false) if the flag is not set (just a normal disconnect). 

    If you intend to support more than one connection, you need to increase the connection count. Please refer to the multilink examples. ble_app_multilink_central for multilink central, and ble_app_multiperipheral for multiperipheral.

    Best regards,

    Edvin

Children
  • Hi Edvin,

    Thank you very much for your help so far.
    Finally I have get my device working correctly.

    My purpose is 1-to-1 connection, so I just followed your suggestion.
    What I do is add a disconnect process and wait for the disconnect event before deleting the bonding.

    Here is my code for reference, in case someone need it in the future.

    static void button_handler(uint8_t pin_no, uint8_t button_action)
    {
        uint32_t err_code;
        //NRF_LOG_INFO("button_handler");
        if(button_action == APP_BUTTON_PUSH)
        {
            NRF_LOG_INFO("APP_BUTTON_PUSH (pin_no = %d)", pin_no);
            switch(pin_no)
            {
                case DELETE_BUTTON_PIN:
                      if (m_is_connected) {
                          m_delete_bond_after_disconnect = true;
                          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);
                          }
                      } else {
                          advertising_start(true);
                      }
                    break;
                default:
                    break;
            }
        }
    }

    static void ble_evt_handler(ble_evt_t const * p_ble_evt, void * p_context)
    {
        uint32_t err_code;
    
        switch (p_ble_evt->header.evt_id)
        {
            // ...
            case BLE_GAP_EVT_DISCONNECTED:
                NRF_LOG_INFO("Disconnected");
                // ...
                advertising_start(m_delete_bond_after_disconnect);
                m_delete_bond_after_disconnect = false;
            break;
        // ...
    }

    Best Regards,
    Joe

  • Hello Joe,

    Thank you for sharing, and I am glad to hear that it worked out.

    Have a nice weekend.

    Best regards,

    Edvin

Related