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

  • Hello Edvin,

    Thank you for your reply.

    Actually the Central device is a USB dongle.
    What my project do is:
    Peripheral will send data through BLE NUS, and the Central device will receive the data and transfer it through UART to PC.

    For Problem #1:
    Thank you for your suggestion.
    I have moved the uart_init() from main to the event handler of event BLE_GAP_EVT_CONNECTED, so the UART will only init after BLE is connected. And also I have created a flag to make sure it run only once.
    And the problem seems to be fixed. I will do further test on this later on.

    For Problem #2:
    Would the following description be one of the possibility of this problem?
    At first devices bonding are done, so the key is establish and shared.
    But either one of them does not save permanently in flash storage, so after power off, one of them has lost the key?
    If so, is there anyway I can check if the device has stored the key?

    Actually in pm_evt_handler, I have added allow_repairing for both Peripheral and Central.

    case PM_EVT_CONN_SEC_CONFIG_REQ:
    NRF_LOG_INFO("PM_EVT_CONN_SEC_CONFIG_REQ");
    pm_conn_sec_config_t conn_sec_config = {.allow_repairing = true};
    pm_conn_sec_config_reply(p_evt->conn_handle, &conn_sec_config);
    break;

    But from the log, I did not see any log message "PM_EVT_CONN_SEC_CONFIG_REQ".
    So maybe this part of code is not being called?

    Best Regards,
    Joe

  • sdtjoe said:
    Thank you for your suggestion.
    I have moved the uart_init() from main to the event handler of event BLE_GAP_EVT_CONNECTED, so the UART will only init after BLE is connected. And also I have created a flag to make sure it run only once.
    And the problem seems to be fixed. I will do further test on this later on.

     Good! An alternative is to just ignore the COMMUNICATION_ERROR event unless the device is connected. You can experiment with different workarounds.

    Make sure the allow_repairing is set to true in the device that did not lose the bonding information. Or set it in both devices, if there is a chance that they both lose it. 

    Does the PM_EVT_CONN_SEC_CONFIG_REQ event occur in any of the devices?

    BR,

    Edvin

  • Hello Edvin,

    Thank you for your reply.

    For Problem #1 (Solved):
    I have followed your suggestion and ignore the COMMUNICATION_ERROR unless the device is connected.
    It works correctly and I applied this solution as I believe this is a better workaround.

    For Problem #2:
    I have added a log to print when PM_EVT_CONN_SEC_CONFIG_REQ is being called in pm_evt_handler.
    However neither Central or Peripheral the event PM_EVT_CONN_SEC_CONFIG_REQ is not being fired.
    So I think the allow_pairing did not set at all.

    case PM_EVT_CONN_SEC_CONFIG_REQ:
    NRF_LOG_INFO("PM_EVT_CONN_SEC_CONFIG_REQ");
    pm_conn_sec_config_t conn_sec_config = {.allow_repairing = true};
    pm_conn_sec_config_reply(p_evt->conn_handle, &conn_sec_config);
    break;

    Here is additional information that may help.
    I think I am using a Just Work Bonding. My sec_param is set as follow:

    sec_param.bond = 1;
    sec_param.mitm = 0;
    sec_param.lesc = 0;
    sec_param.keypress = 0;
    sec_param.io_caps = BLE_GAP_IO_CAPS_NONE;
    sec_param.oob = 0;
    sec_param.min_key_size = 7;
    sec_param.max_key_size = 16;
    sec_param.kdist_own.enc = 1;
    sec_param.kdist_own.id = 1;
    sec_param.kdist_peer.enc = 1;
    sec_param.kdist_peer.id = 1;

    In addition, I call delete_bonds() function for both Central and Peripheral.
    It will call pm_peer_delete() same as sample code.

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

    Here is my steps and observation:
    Scenario 1 (No problem at all):
    1. Turn on both devices which are never connected.
    2. They will bond.
    3. Turn off either Central / Peripheral and they will connect again.
    4. Press delete button on Peripheral side, PM_EVT_PEERS_DELETE_SUCCEEDED event is fired.
    5. Try to connect again, PM_CONN_SEC_ERROR_PIN_OR_KEY_MISSING, which is my expected behavior.

    Scenario 2 (Unexpected behavior):
    1. Turn on both devices which are never connected.
    2. They will bond.
    3. Turn off either Central / Peripheral and they will connect again.
    4. Press delete button on Central side, PM_EVT_PEERS_DELETE_SUCCEEDED event is fired.
    5. Try to connect again, the devices connected successfully. But I expect it should be rejected as I have deleted the Central bonding in step 4.

    Maybe I have missed some setting on either side?

    Best Regards,
    Joe

  • Hello Joe,

    I am a bit confused:

     

    sdtjoe said:
    However neither Central or Peripheral the event PM_EVT_CONN_SEC_CONFIG_REQ is not being fired.

     But from the latest screenshot of the log, the line before "Unexpected Behavior" says:

    "PM_EVT_CONN_SEC_PARAMS_REQ" (not the same, but similar). From peer_manager_types.h:

    /**< @brief Security parameters (@ref ble_gap_sec_params_t) are needed for an ongoing security procedure. Reply with @ref pm_conn_sec_params_reply before the event handler returns. If no reply is sent, the parameters given in @ref pm_sec_params_set are used. If a peripheral connection, the central's sec_params will be available in the event. */

    Do you reply with pm_conn_sec_params_reply? Do you get any events on the peripheral at this point in time? 

    Where did you set .allow_repairing = true in the central?

    BR,

    Edvin

  • Hello Edvin,

    I don't have pm_conn_sec_params_reply at all in my code.
    I the only similar one is pm_conn_sec_config_reply.
    But I believe this event has never been fired, as it didn't shown in the log.

    Here is my pm_evt_handler function of the Central device.

    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:
                // Bonds are deleted. Start scanning.
                NRF_LOG_INFO("PM_EVT_PEERS_DELETE_SUCCEEDED");
                scan_start();
                break;
    
            case PM_EVT_CONN_SEC_CONFIG_REQ:
                NRF_LOG_INFO("PM_EVT_CONN_SEC_CONFIG_REQ");
                pm_conn_sec_config_t conn_sec_config = {.allow_repairing = true};
                pm_conn_sec_config_reply(p_evt->conn_handle, &conn_sec_config);
                break;
    
            case PM_EVT_BONDED_PEER_CONNECTED: // 0
                NRF_LOG_INFO("PM_EVT_BONDED_PEER_CONNECTED");
                break;
    
            case PM_EVT_CONN_SEC_START: // 1
                NRF_LOG_INFO("PM_EVT_CONN_SEC_START");
                break;
    
            case PM_EVT_CONN_SEC_SUCCEEDED: // 2
                NRF_LOG_INFO("PM_EVT_CONN_SEC_SUCCEEDED");
                break;
    
            case PM_EVT_CONN_SEC_FAILED: // 3
                NRF_LOG_INFO("PM_EVT_CONN_SEC_FAILED");
                break;
    
            case PM_EVT_CONN_SEC_PARAMS_REQ: // 5
                NRF_LOG_INFO("PM_EVT_CONN_SEC_PARAMS_REQ");
                break;
    
            case PM_EVT_PEER_DATA_UPDATE_SUCCEEDED: // 8
                NRF_LOG_INFO("PM_EVT_PEER_DATA_UPDATE_SUCCEEDED");
                break;
    
            case PM_EVT_LOCAL_DB_CACHE_APPLIED: // 14
                NRF_LOG_INFO("PM_EVT_LOCAL_DB_CACHE_APPLIED");
                break;
    
            case PM_EVT_SLAVE_SECURITY_REQ: // 18
                NRF_LOG_INFO("PM_EVT_SLAVE_SECURITY_REQ");
                break;
    
            default:
                NRF_LOG_INFO("p_evt->evt_id = %d", p_evt->evt_id);
                break;
        }
    }

    If response to the pm_conn_sec_params_reply is needed, shall I just response the same setting as param_sec?

    case PM_EVT_CONN_SEC_PARAMS_REQ: // 5
    	NRF_LOG_INFO("PM_EVT_CONN_SEC_PARAMS_REQ");
    	ble_gap_sec_params_t sec_param;
    
    	memset(&sec_param, 0, sizeof(ble_gap_sec_params_t));
    
    	// Security parameters to be used for all security procedures.
    	sec_param.bond           = 1;
    	sec_param.mitm           = 0;
    	sec_param.lesc           = 0;
    	sec_param.keypress       = 0;
    	sec_param.io_caps        = BLE_GAP_IO_CAPS_NONE;
    	sec_param.oob            = 0;
    	sec_param.min_key_size   = 7;
    	sec_param.max_key_size   = 16;
    	sec_param.kdist_own.enc  = 1;
    	sec_param.kdist_own.id   = 1;
    	sec_param.kdist_peer.enc = 1;
    	sec_param.kdist_peer.id  = 1;
    	pm_conn_sec_params_reply(p_evt->conn_handle, &sec_param, NULL);
    	break;


    Best Regards,
    Joe

Related