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

Long time to disconnect

Hi, 

Our NRF52840 (running 14.2) often takes a very long time to disconnect, from when a disconnection is initiated by the phone. It can take 30s, sometimes even more, for the disconnect to happen. Sometimes the disconnect happens immediately like it should, but I would say that it is more common for the disconnect to take a long time than for the disconnect to be normal. 

I installed the NRF Sniffer and have done a capture, but I am not really sure what to make of the result. 

The below was a bad disconnect. Here, the highlighted line was about when the disconnect was initiated on the phone (image 1). Then I got "Empty PDU" for a long time. The empty PDU continues for a long time, with only one "encrypted packet decrypted incorrectly" in between, then in the second image, the highlighted line shows when the disconnect actually happens. There is ~13 sec in between the initiation and the actual disconnect.

By contrast, this was one of the times that I got a normal disconnection. 

The above was done with our custom app. The issue also happens when using the NRF Connect App for iOS. For reference, below is the capture when the disconnect was initiated by the NRF app:

I pressed the disconnect button around the time of this highlighted line: 

There is no command of any kind here though; the closest command is this one (but I don't know if they're even related, this is just the first command I found when scrolling up):

The disconnect comes what looks like 23 seconds later: 

My questions:

(MAIN QUESTION) How can I find out why it is taking a long time to disconnect? Note that it happens with different phones, but we have only been able to test with iPhones so far.

AUXILLIARY QUESTIONS: 

1. What are control opcodes and why do they only show up in the test using the NRF app, but not our custom app? Should we do something to enable these opcodes? 

2. What is empty PDU? 

3. What is "bad MIC"?

4. Where do I learn about how to interpret the captured packets? I read this but it doesn't really explain what I should be looking for.

Thanks!

Parents
  • Thanks 

    here is some code: 

    define APP_ADV_INTERVAL                 244                                        /**< The advertising interval (in units of 0.625 ms. Value of 300 corresponds to 187.5 ms). */
    #define APP_ADV_TIMEOUT_IN_SECONDS       0                                          /**< The advertising timeout in units of seconds. */
    
    #define MIN_CONN_INTERVAL                MSEC_TO_UNITS(25, UNIT_1_25_MS)            /**< Minimum acceptable connection interval (0.1 seconds). */
    #define MAX_CONN_INTERVAL                MSEC_TO_UNITS(1500, UNIT_1_25_MS)          /**< Maximum acceptable connection interval (0.2 second). */
    #define SLAVE_LATENCY                    0                                          /**< Slave latency. */
    #define CONN_SUP_TIMEOUT                 MSEC_TO_UNITS(5000, UNIT_10_MS)            /**< Connection supervisory timeout (4 seconds). */
    
    #define FIRST_CONN_PARAMS_UPDATE_DELAY   APP_TIMER_TICKS(5000)                      /**< Time from initiating event (connect or start of notification) to first time sd_ble_gap_conn_param_update is called (5 seconds). */
    #define NEXT_CONN_PARAMS_UPDATE_DELAY    APP_TIMER_TICKS(30000)                     /**< Time between each call to sd_ble_gap_conn_param_update after the first call (30 seconds). */
    #define MAX_CONN_PARAMS_UPDATE_COUNT     2                                          /**< Number of attempts before giving up the connection parameter negotiation. */
    
    #define SEC_PARAM_BOND                   1                                          /**< Perform bonding. */
    #define SEC_PARAM_MITM                   0                                          /**< Man In The Middle protection required for Passkey */
    #define SEC_PARAM_LESC                   0                                          /**< LE Secure Connections not enabled. */
    #define SEC_PARAM_KEYPRESS               0                                          /**< Keypress notifications not enabled. */
    #define SEC_PARAM_IO_CAPABILITIES        BLE_GAP_IO_CAPS_DISPLAY_ONLY               /**< Changed for Passkey */
    #define SEC_PARAM_OOB                    0                                          /**< Out Of Band data not available. */
    #define SEC_PARAM_MIN_KEY_SIZE           7                                          /**< Minimum encryption key size. */
    #define SEC_PARAM_MAX_KEY_SIZE           16                                         /**< Maximum encryption key size. */
    
    #define SECURITY_REQUEST_DELAY          APP_TIMER_TICKS(400) // Passkey
    #define APP_FEATURE_NOT_SUPPORTED       BLE_GATT_STATUS_ATTERR_APP_BEGIN + 2 
    
    /**@brief Function for handling BLE events.
     *
     * @param[in]   p_ble_evt   Bluetooth stack event.
     * @param[in]   p_context   Unused.
     */
    static void ble_evt_handler(ble_evt_t const * p_ble_evt, void * p_context)
    {
        ret_code_t err_code;
    
        switch (p_ble_evt->header.evt_id)
        {
            case BLE_GAP_EVT_CONNECTED:
                NRF_LOG_INFO("Connected.");
                m_peer_to_be_deleted = PM_PEER_ID_INVALID;
                m_conn_handle = p_ble_evt->evt.gap_evt.conn_handle;
    
                nrf_gpio_pin_set(LED_BLUE);
                
                // Start Security Request timer.
                err_code = app_timer_start(m_sec_req_timer_id, SECURITY_REQUEST_DELAY, NULL);
                APP_ERROR_CHECK(err_code);
                break;
    
            case BLE_GAP_EVT_DISCONNECTED:
                NRF_LOG_INFO("Disconnected.");
                NRF_LOG_RAW_INFO("disconnect reason:0x%X \n",p_ble_evt->evt.gap_evt.params.disconnected.reason);
                m_conn_handle = BLE_CONN_HANDLE_INVALID;
                memset(&m_ble_db_discovery, 0 , sizeof (m_ble_db_discovery));
                
                // Turn LED back green
                nrf_gpio_pin_clear(LED_BLUE);
                nrf_gpio_pin_set(LED_GREEN);
    
                // Check if the last connected peer had not used MITM, if so, delete its bond information.
                if (m_peer_to_be_deleted != PM_PEER_ID_INVALID)
                {
                    err_code = pm_peer_delete(m_peer_to_be_deleted);
                    APP_ERROR_CHECK(err_code);
                    NRF_LOG_DEBUG("Collector's bond deleted");
                    m_peer_to_be_deleted = PM_PEER_ID_INVALID;
                }
                break;
    
    #ifndef S140
            case BLE_GAP_EVT_PHY_UPDATE:
            {
                NRF_LOG_DEBUG("PHY update request.");
                ble_gap_phys_t const phys =
                {
                    .rx_phys = BLE_GAP_PHY_AUTO,
                    .tx_phys = BLE_GAP_PHY_AUTO,
                };
                err_code = sd_ble_gap_phy_update(p_ble_evt->evt.gap_evt.conn_handle, &phys);
                APP_ERROR_CHECK(err_code);
            } break;
    #endif
    
            case BLE_GATTC_EVT_TIMEOUT:
                // Disconnect on GATT Client timeout event.
                NRF_LOG_DEBUG("GATT Client Timeout.");
                err_code = sd_ble_gap_disconnect(p_ble_evt->evt.gattc_evt.conn_handle,
                                                 BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION);
                APP_ERROR_CHECK(err_code);
                break;
    
            case BLE_GATTS_EVT_TIMEOUT:
                // Disconnect on GATT Server timeout event.
                NRF_LOG_DEBUG("GATT Server Timeout.");
                err_code = sd_ble_gap_disconnect(p_ble_evt->evt.gatts_evt.conn_handle,
                                                 BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION);
                APP_ERROR_CHECK(err_code);
                break;
    
            // Added for Passkey
            case BLE_GAP_EVT_SEC_PARAMS_REQUEST:
                NRF_LOG_DEBUG("BLE_GAP_EVT_SEC_PARAMS_REQUEST");
                break;
    
            case BLE_GAP_EVT_PASSKEY_DISPLAY:
                // Don't send delayed Security Request if security procedure is already in progress.
                err_code = app_timer_stop(m_sec_req_timer_id);
                APP_ERROR_CHECK(err_code);
                NRF_LOG_DEBUG("BLE_GAP_EVT_PASSKEY_DISPLAY");
                break;
    
            case BLE_EVT_USER_MEM_REQUEST:
                err_code = sd_ble_user_mem_reply(m_conn_handle, NULL);
                APP_ERROR_CHECK(err_code);
                break;
    
            case BLE_GATTS_EVT_RW_AUTHORIZE_REQUEST:
            {
                ble_gatts_evt_rw_authorize_request_t  req;
                ble_gatts_rw_authorize_reply_params_t auth_reply;
    
                req = p_ble_evt->evt.gatts_evt.params.authorize_request;
    
                if (req.type != BLE_GATTS_AUTHORIZE_TYPE_INVALID)
                {
                    if ((req.request.write.op == BLE_GATTS_OP_PREP_WRITE_REQ)     ||
                        (req.request.write.op == BLE_GATTS_OP_EXEC_WRITE_REQ_NOW) ||
                        (req.request.write.op == BLE_GATTS_OP_EXEC_WRITE_REQ_CANCEL))
                    {
                        if (req.type == BLE_GATTS_AUTHORIZE_TYPE_WRITE)
                        {
                            auth_reply.type = BLE_GATTS_AUTHORIZE_TYPE_WRITE;
                        }
                        else
                        {
                            auth_reply.type = BLE_GATTS_AUTHORIZE_TYPE_READ;
                        }
                        auth_reply.params.write.gatt_status = APP_FEATURE_NOT_SUPPORTED;
                        err_code = sd_ble_gatts_rw_authorize_reply(p_ble_evt->evt.gatts_evt.conn_handle,
                                                                   &auth_reply);
                        APP_ERROR_CHECK(err_code);
                    }
                }
            } break; // BLE_GATTS_EVT_RW_AUTHORIZE_REQUEST
    
            default:
                // No implementation needed.
                break;
        }
    
        bluetooth_on_ble_evt(p_ble_evt);
    }

    I'm not sure what else to include, because I'm not sure where I should look for the processing of the disconnection event. Can you point me to where a disconnection event is supposed to be received in the GATT event handling, or wherever else relevant? 

    It doesn't seem that your device receives the disconnection event, or handles it correctly

    1. where in the code can I find where the disconnection event should be received/ handled?

    2. what exactly happens / what is sent when the disconnect button is pressed in the app?

    ---

    UPDATE: note that this long time to disconnect issue happens both when running our firmware on our custom device and also on a NRF52840 dev kit. So it doesn't seem to be a hardware issue. 

    Please let me know if you need another relevant code sample

Reply
  • Thanks 

    here is some code: 

    define APP_ADV_INTERVAL                 244                                        /**< The advertising interval (in units of 0.625 ms. Value of 300 corresponds to 187.5 ms). */
    #define APP_ADV_TIMEOUT_IN_SECONDS       0                                          /**< The advertising timeout in units of seconds. */
    
    #define MIN_CONN_INTERVAL                MSEC_TO_UNITS(25, UNIT_1_25_MS)            /**< Minimum acceptable connection interval (0.1 seconds). */
    #define MAX_CONN_INTERVAL                MSEC_TO_UNITS(1500, UNIT_1_25_MS)          /**< Maximum acceptable connection interval (0.2 second). */
    #define SLAVE_LATENCY                    0                                          /**< Slave latency. */
    #define CONN_SUP_TIMEOUT                 MSEC_TO_UNITS(5000, UNIT_10_MS)            /**< Connection supervisory timeout (4 seconds). */
    
    #define FIRST_CONN_PARAMS_UPDATE_DELAY   APP_TIMER_TICKS(5000)                      /**< Time from initiating event (connect or start of notification) to first time sd_ble_gap_conn_param_update is called (5 seconds). */
    #define NEXT_CONN_PARAMS_UPDATE_DELAY    APP_TIMER_TICKS(30000)                     /**< Time between each call to sd_ble_gap_conn_param_update after the first call (30 seconds). */
    #define MAX_CONN_PARAMS_UPDATE_COUNT     2                                          /**< Number of attempts before giving up the connection parameter negotiation. */
    
    #define SEC_PARAM_BOND                   1                                          /**< Perform bonding. */
    #define SEC_PARAM_MITM                   0                                          /**< Man In The Middle protection required for Passkey */
    #define SEC_PARAM_LESC                   0                                          /**< LE Secure Connections not enabled. */
    #define SEC_PARAM_KEYPRESS               0                                          /**< Keypress notifications not enabled. */
    #define SEC_PARAM_IO_CAPABILITIES        BLE_GAP_IO_CAPS_DISPLAY_ONLY               /**< Changed for Passkey */
    #define SEC_PARAM_OOB                    0                                          /**< Out Of Band data not available. */
    #define SEC_PARAM_MIN_KEY_SIZE           7                                          /**< Minimum encryption key size. */
    #define SEC_PARAM_MAX_KEY_SIZE           16                                         /**< Maximum encryption key size. */
    
    #define SECURITY_REQUEST_DELAY          APP_TIMER_TICKS(400) // Passkey
    #define APP_FEATURE_NOT_SUPPORTED       BLE_GATT_STATUS_ATTERR_APP_BEGIN + 2 
    
    /**@brief Function for handling BLE events.
     *
     * @param[in]   p_ble_evt   Bluetooth stack event.
     * @param[in]   p_context   Unused.
     */
    static void ble_evt_handler(ble_evt_t const * p_ble_evt, void * p_context)
    {
        ret_code_t err_code;
    
        switch (p_ble_evt->header.evt_id)
        {
            case BLE_GAP_EVT_CONNECTED:
                NRF_LOG_INFO("Connected.");
                m_peer_to_be_deleted = PM_PEER_ID_INVALID;
                m_conn_handle = p_ble_evt->evt.gap_evt.conn_handle;
    
                nrf_gpio_pin_set(LED_BLUE);
                
                // Start Security Request timer.
                err_code = app_timer_start(m_sec_req_timer_id, SECURITY_REQUEST_DELAY, NULL);
                APP_ERROR_CHECK(err_code);
                break;
    
            case BLE_GAP_EVT_DISCONNECTED:
                NRF_LOG_INFO("Disconnected.");
                NRF_LOG_RAW_INFO("disconnect reason:0x%X \n",p_ble_evt->evt.gap_evt.params.disconnected.reason);
                m_conn_handle = BLE_CONN_HANDLE_INVALID;
                memset(&m_ble_db_discovery, 0 , sizeof (m_ble_db_discovery));
                
                // Turn LED back green
                nrf_gpio_pin_clear(LED_BLUE);
                nrf_gpio_pin_set(LED_GREEN);
    
                // Check if the last connected peer had not used MITM, if so, delete its bond information.
                if (m_peer_to_be_deleted != PM_PEER_ID_INVALID)
                {
                    err_code = pm_peer_delete(m_peer_to_be_deleted);
                    APP_ERROR_CHECK(err_code);
                    NRF_LOG_DEBUG("Collector's bond deleted");
                    m_peer_to_be_deleted = PM_PEER_ID_INVALID;
                }
                break;
    
    #ifndef S140
            case BLE_GAP_EVT_PHY_UPDATE:
            {
                NRF_LOG_DEBUG("PHY update request.");
                ble_gap_phys_t const phys =
                {
                    .rx_phys = BLE_GAP_PHY_AUTO,
                    .tx_phys = BLE_GAP_PHY_AUTO,
                };
                err_code = sd_ble_gap_phy_update(p_ble_evt->evt.gap_evt.conn_handle, &phys);
                APP_ERROR_CHECK(err_code);
            } break;
    #endif
    
            case BLE_GATTC_EVT_TIMEOUT:
                // Disconnect on GATT Client timeout event.
                NRF_LOG_DEBUG("GATT Client Timeout.");
                err_code = sd_ble_gap_disconnect(p_ble_evt->evt.gattc_evt.conn_handle,
                                                 BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION);
                APP_ERROR_CHECK(err_code);
                break;
    
            case BLE_GATTS_EVT_TIMEOUT:
                // Disconnect on GATT Server timeout event.
                NRF_LOG_DEBUG("GATT Server Timeout.");
                err_code = sd_ble_gap_disconnect(p_ble_evt->evt.gatts_evt.conn_handle,
                                                 BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION);
                APP_ERROR_CHECK(err_code);
                break;
    
            // Added for Passkey
            case BLE_GAP_EVT_SEC_PARAMS_REQUEST:
                NRF_LOG_DEBUG("BLE_GAP_EVT_SEC_PARAMS_REQUEST");
                break;
    
            case BLE_GAP_EVT_PASSKEY_DISPLAY:
                // Don't send delayed Security Request if security procedure is already in progress.
                err_code = app_timer_stop(m_sec_req_timer_id);
                APP_ERROR_CHECK(err_code);
                NRF_LOG_DEBUG("BLE_GAP_EVT_PASSKEY_DISPLAY");
                break;
    
            case BLE_EVT_USER_MEM_REQUEST:
                err_code = sd_ble_user_mem_reply(m_conn_handle, NULL);
                APP_ERROR_CHECK(err_code);
                break;
    
            case BLE_GATTS_EVT_RW_AUTHORIZE_REQUEST:
            {
                ble_gatts_evt_rw_authorize_request_t  req;
                ble_gatts_rw_authorize_reply_params_t auth_reply;
    
                req = p_ble_evt->evt.gatts_evt.params.authorize_request;
    
                if (req.type != BLE_GATTS_AUTHORIZE_TYPE_INVALID)
                {
                    if ((req.request.write.op == BLE_GATTS_OP_PREP_WRITE_REQ)     ||
                        (req.request.write.op == BLE_GATTS_OP_EXEC_WRITE_REQ_NOW) ||
                        (req.request.write.op == BLE_GATTS_OP_EXEC_WRITE_REQ_CANCEL))
                    {
                        if (req.type == BLE_GATTS_AUTHORIZE_TYPE_WRITE)
                        {
                            auth_reply.type = BLE_GATTS_AUTHORIZE_TYPE_WRITE;
                        }
                        else
                        {
                            auth_reply.type = BLE_GATTS_AUTHORIZE_TYPE_READ;
                        }
                        auth_reply.params.write.gatt_status = APP_FEATURE_NOT_SUPPORTED;
                        err_code = sd_ble_gatts_rw_authorize_reply(p_ble_evt->evt.gatts_evt.conn_handle,
                                                                   &auth_reply);
                        APP_ERROR_CHECK(err_code);
                    }
                }
            } break; // BLE_GATTS_EVT_RW_AUTHORIZE_REQUEST
    
            default:
                // No implementation needed.
                break;
        }
    
        bluetooth_on_ble_evt(p_ble_evt);
    }

    I'm not sure what else to include, because I'm not sure where I should look for the processing of the disconnection event. Can you point me to where a disconnection event is supposed to be received in the GATT event handling, or wherever else relevant? 

    It doesn't seem that your device receives the disconnection event, or handles it correctly

    1. where in the code can I find where the disconnection event should be received/ handled?

    2. what exactly happens / what is sent when the disconnect button is pressed in the app?

    ---

    UPDATE: note that this long time to disconnect issue happens both when running our firmware on our custom device and also on a NRF52840 dev kit. So it doesn't seem to be a hardware issue. 

    Please let me know if you need another relevant code sample

Children
No Data
Related