BLE_HCI_CONNECTION_TIMEOUT(0x08) followed by the BLE_GAP_EVT_CONN_PARAM_UPDATE_REQUEST event on central application

I am developing a multilink central application on 52840 which would connect up to 6 peripherals.

The connected peripherals will send a connection params update to the central, requesting to change to a longer connection interval (min 380ms, max 1000ms) about one minutes after the connection established.

On the central application, I can see the BLE_GAP_EVT_CONN_PARAM_UPDATE_REQUEST event comes in. In the event handler, sd_ble_gap_conn_param_update() is called and NRF_SUCCESS is returned.

case BLE_GAP_EVT_CONN_PARAM_UPDATE_REQUEST:
{
    NRF_LOG_DEBUG("BLE_GAP_EVT_CONN_PARAM_UPDATE_REQUEST.");
    // Accept parameters requested by peer.
    err_code = sd_ble_gap_conn_param_update(p_gap_evt->conn_handle,
            &p_gap_evt->params.conn_param_update_request.conn_params);
    APP_ERROR_CHECK(err_code);
} break;

But after the while, the central would receive BLE_GAP_EVT_DISCONNECTED event with reason 0x08 (BLE_HCI_CONNECTION_TIMEOUT)

I have try to connect the peripheral to Android Phone, no disconnection issue would occur. So I conclude the problem would most likely on the central side.

Where should I start to check to resolve this connection timeout issue?
Thanks!

Ray



  • Hi,

    Do the problem have any dependency on the number of peers connected to the central? Does it happen allways? When you establish the link as a central device, what are the connection parameters you specify (e.g. interval, timeout, and slave latency).

    Kenneth

  • Hi Kenneth,

    1. No dependency on how many peers connected. It would disconnect even only connect to a single peer.
    2. It happens always.
    3. The conn params set on the central side: (my code is copied from ble_app_hrs_rscs_relay example)

    /**@brief Function for initializing the GAP.
     *
     * @details This function sets up all the necessary GAP (Generic Access Profile) parameters of the
     *          device, including the device name, appearance, and the preferred connection parameters.
     */
    static void gap_params_init(void)
    {
        ret_code_t              err_code;
        ble_gap_conn_params_t   gap_conn_params;
        ble_gap_conn_sec_mode_t sec_mode;
    
        BLE_GAP_CONN_SEC_MODE_SET_OPEN(&sec_mode);
    
        err_code = sd_ble_gap_device_name_set(&sec_mode,
                                              (const uint8_t *)DEVICE_NAME,
                                              strlen(DEVICE_NAME));
        APP_ERROR_CHECK(err_code);
    
        memset(&gap_conn_params, 0, sizeof(gap_conn_params));
    
        gap_conn_params.min_conn_interval = m_scan.conn_params.min_conn_interval;
        gap_conn_params.max_conn_interval = m_scan.conn_params.max_conn_interval;
        gap_conn_params.slave_latency     = m_scan.conn_params.slave_latency;
        gap_conn_params.conn_sup_timeout  = m_scan.conn_params.conn_sup_timeout;
    
        err_code = sd_ble_gap_ppcp_set(&gap_conn_params);
        APP_ERROR_CHECK(err_code);
    }
    


    where I use the following param definitions

    #define NRF_BLE_SCAN_MIN_CONNECTION_INTERVAL 7.5
    
    #define NRF_BLE_SCAN_MAX_CONNECTION_INTERVAL 300
    
    #define NRF_BLE_SCAN_SLAVE_LATENCY 0
    
    #define NRF_BLE_SCAN_SUPERVISION_TIMEOUT 4000


    On the peripherals side, the conn params are initially set as
    min conn interval: 7.5 (ms)
    max conn interval: 580 (ms)
    slave latency: 0
    supervision timeout: 4000 (ms)

    after 60 seconds, the peripheral request to change the conn params to
    min conn interval: 380 (ms)
    max conn interval: 1000 (ms)
    slave latency: 0
    supervision timeout: 5000 (ms)

    From the log, I can see
    A. while GAP connected
    peripheral: min: 300ms, max: 300ms
    central: min: 300ms, max: 300ms
    (read from the (ble_gap_evt_t*)->params.connected.conn_params)

    B. Peripheral request to update conn params to 7.5-580ms
    peripheral: min: 580ms, max: 580ms
    central: min: 580ms, max: 580ms
    (read from BLE_GAP_EVT_CONN_PARAM_UPDATE event, (ble_gap_evt_t*)->params.conn_param_update.conn_params)

    C. after about 60s, the peripheral request to update conn params to 380 - 1000ms
    peripheral: min: 1000ms, max: 1000ms
    central: min: 1000ms, max: 581000ms
    (read from BLE_GAP_EVT_CONN_PARAM_UPDATE event, (ble_gap_evt_t*)->params.conn_param_update.conn_params)

    Then the connection time out occurs and the peripheral disconnected. 
    Both sides received the BLE_GAP_EVT_DISCONNECTED event with reason code 0x08




  • Hi,

    Without going too much into details here, when a central device is scanning or trying to establish a connection it will use the parameters in green bold:

    sd_ble_gap_scan_start(ble_gap_scan_params_t const *p_scan_params, ble_data_t const * p_adv_report_buffer));

    sd_ble_gap_connect(ble_gap_addr_t const *p_peer_addr, ble_gap_scan_params_t const *p_scan_params, ble_gap_conn_params_t const *p_conn_params, uint8_t conn_cfg_tag));

    Ideally to avoid scanning and connection establishment affect the active link(s) in the least amount of way, you should set the window to a short time (such as 5ms) and the interval can also be set to the same value (if you have power constrains on the central you can increase the interval value to fit your current consumption requirement). Also, when using sd_ble_gap_connect() make sure that the .timeout (in 10ms periods) is not set to 0 in p_scan_params, because this may cause the central to try to connect for a peripheral infinitely (for instance if the peripheral device was powered off, out of range or connected to a different central), instead use for instance a reasonable timeout of 10seconds (and if no device is connected, then you will receive a BLE_GAP_EVT_TIMEOUT event, in which case you can call sd_ble_gap_scan_start() again. 

    When establishing a link through the sd_ble_gap_connect() api, the actual connection parameters is controlled by the red bold above. If you have multiple links you should not use 7.5ms, this will for sure cause conflicts between the links (it will not have time to schedule all of them), also you should avoid using slave latency of 0. Instead I suggest using a (min) connection interval of 30ms and slave latency of 4. This should be a good trade off between discovery time and also maintainig other links in parallell. This is slightly higher than what is suggested here (because I don't know what you have configured your event length to):
    https://infocenter.nordicsemi.com/topic/sds_s140/SDS/s1xx/multilink_scheduling/suggested_intervals_windows_s132.html 

    All that said, I suspect you should also look into the configured tolerance you have set for your LFCLK. This is typically controlled in sdk_config through the NRF_SDH_CLOCK_LF_ACCURACY define. I don't know what source you are using, but for test I at least suggest using NRF_SDH_CLOCK_LF_ACCURACY 1 (500ppm) to see if that affect the problem you have.

    Hope the above will make this stable.

    Kenneth

  • Hi Kenneth,

    Thanks for the detail explanation of the softdevice APIs.

    After trying the suggested modification on the scan and conn params, the NRF_SDH_CLOCK_LF_ACCURACY is the root cause of the disconnection. (So nothing to do with the conn interval updates in my case)

    One thing not clear to me is, on the peripheral side, the board use a crystal with 20ppm as the LFCLK source. So the NRF_SDH_CLOCK_LF_ACCURACY is set to 20ppm on the peripheral side.
    On the central side, the board also use the same 20ppm crystal, but setting it to 20ppm apparently not works and lead to peripheral connection drops frequently. Setting it to 500ppm solves the issue. So, is there any guideline on choosing the proper LF accuracy setting on central application?

    Thanks for the responsive support!

    Ray


  • Hi Ray,

    Sounds good that it now works.

    If the peer side is using a clock source that is outside of it's configured tolerance, then a workaround can be to configure the central to set it's tolerance to a higher number than what it actually is. Whether you need to go as high as 500ppm, or for instance 250ppm (or less) is enough I think it's difficult to know. Have in mind though that these tolerances can vary a bit depending for instance temperature, aging and variation between crystals, so setting the value higher than what it initially might be is reasonable. You are however trading a bit of current consumption for this change, since the peripheral device need to wakeup slightly earlier each connection interval.

    Kenneth

Related