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

NRF_ERROR_INVALID_STATE on disconnect

Hi - I have an application where I sometimes get an NRF_ERROR_INVALID_STATE error on disconnect when the distance between the client and server is beyond certain limits.

The flow is as follows:

Server receives an internal event and initiates advertising.

Client discovers server and connects to it.

Server sends notification enable on a client characteristic.

Client sends notification.

Server responds to notification.

Client sends disconnect.

Thereafter the client will sometimes time out on the disconnect request and a NRF_ERROR_INVALID_STATE error occurs.

I would like to preserve my state, so I do not power off but rather call idle_state_handle.

My question is - Is there a way to recover from this error so that I do not have to reset and lose state information for the next attempt.

Under normal conditions this always works, but may fail if the distance between the two is past a certain level.

Thank You

Parents
  • Hi Francois, 

    sd_ble_gatts_hvx() will return NRF_ERROR_INVALID_STATE  when one or more of the following is true:
    * - Invalid Connection State
    * - Notifications and/or indications not enabled in the CCCD
    * - An ATT_MTU exchange is ongoing

    In your case it is most likely the first, Invalid Connection State, i.e. the connection handle is invalid. So always make sure that you check if the connection handle is valid or not before calling, like the ble_nus_data_send() function in the ble_app_uart example in the SDK

    uint32_t ble_nus_data_send(ble_nus_t * p_nus,
                               uint8_t   * p_data,
                               uint16_t  * p_length,
                               uint16_t    conn_handle)
    {
        ret_code_t                 err_code;
        ble_gatts_hvx_params_t     hvx_params;
        ble_nus_client_context_t * p_client;
    
        VERIFY_PARAM_NOT_NULL(p_nus);
    
        err_code = blcm_link_ctx_get(p_nus->p_link_ctx_storage, conn_handle, (void *) &p_client);
        VERIFY_SUCCESS(err_code);
    
        if ((conn_handle == BLE_CONN_HANDLE_INVALID) || (p_client == NULL))
        {
            return NRF_ERROR_NOT_FOUND;
        }
    
        if (!p_client->is_notification_enabled)
        {
            return NRF_ERROR_INVALID_STATE;
        }
    
        if (*p_length > BLE_NUS_MAX_DATA_LEN)
        {
            return NRF_ERROR_INVALID_PARAM;
        }
    
        memset(&hvx_params, 0, sizeof(hvx_params));
    
        hvx_params.handle = p_nus->tx_handles.value_handle;
        hvx_params.p_data = p_data;
        hvx_params.p_len  = p_length;
        hvx_params.type   = BLE_GATT_HVX_NOTIFICATION;
    
        return sd_ble_gatts_hvx(conn_handle, &hvx_params);
    }

    Best regards
    Bjørn

  • Hi - I do check if the connection state is correct before trying to disconnect. I have traced event to the update_timeout_handler in ble_conn_params.c. It seems that it failed due to max_conn_params_update_count exceeded.

    I just would like some advice on how to recover from this error.

    Regards

  • Which SDK version are  you using? In SDK v15.2.0 this appears to be handled, i.e. it does not generate an error event if sd_ble_gap_disconnect() returns NRF_ERROR_INVALID_STATE, see snippet below:

    /**@brief Function called after conn_params_update_delay has happened. This is triggered by app_timer.
     *
     * @param[in]  p_context  Context identifying which connection this is for.
     */
    static void update_timeout_handler(void * p_context)
    {
        uint32_t                     conn_handle = (uint32_t)p_context;
        ble_conn_params_instance_t * p_instance  = instance_get(conn_handle);
    
        if (p_instance != NULL)
        {
            // Check if we have reached the maximum number of attempts
            if (p_instance->update_count < m_conn_params_config.max_conn_params_update_count)
            {
                bool update_sent = send_update_request(conn_handle, &p_instance->preferred_conn_params);
                if (update_sent)
                {
                    p_instance->update_count++;
                }
            }
            else
            {
                p_instance->update_count = 0;
    
                // Negotiation failed, disconnect automatically if this has been configured
                if (m_conn_params_config.disconnect_on_fail)
                {
                    ret_code_t err_code;
    
                    err_code = sd_ble_gap_disconnect(conn_handle, BLE_HCI_CONN_INTERVAL_UNACCEPTABLE);
                    if ((err_code != NRF_SUCCESS) && (err_code != NRF_ERROR_INVALID_STATE)) // NRF_ERROR_INVALID_STATE means disconnect is already in progress.
                    {
                        send_error_evt(err_code);
                    }
                }
    
                // Notify the application that the procedure has failed
                if (m_conn_params_config.evt_handler != NULL)
                {
                    ble_conn_params_evt_t evt;
    
                    evt.evt_type = BLE_CONN_PARAMS_EVT_FAILED;
                    evt.conn_handle = conn_handle;
                    m_conn_params_config.evt_handler(&evt);
                }
            }
        }
    }

Reply
  • Which SDK version are  you using? In SDK v15.2.0 this appears to be handled, i.e. it does not generate an error event if sd_ble_gap_disconnect() returns NRF_ERROR_INVALID_STATE, see snippet below:

    /**@brief Function called after conn_params_update_delay has happened. This is triggered by app_timer.
     *
     * @param[in]  p_context  Context identifying which connection this is for.
     */
    static void update_timeout_handler(void * p_context)
    {
        uint32_t                     conn_handle = (uint32_t)p_context;
        ble_conn_params_instance_t * p_instance  = instance_get(conn_handle);
    
        if (p_instance != NULL)
        {
            // Check if we have reached the maximum number of attempts
            if (p_instance->update_count < m_conn_params_config.max_conn_params_update_count)
            {
                bool update_sent = send_update_request(conn_handle, &p_instance->preferred_conn_params);
                if (update_sent)
                {
                    p_instance->update_count++;
                }
            }
            else
            {
                p_instance->update_count = 0;
    
                // Negotiation failed, disconnect automatically if this has been configured
                if (m_conn_params_config.disconnect_on_fail)
                {
                    ret_code_t err_code;
    
                    err_code = sd_ble_gap_disconnect(conn_handle, BLE_HCI_CONN_INTERVAL_UNACCEPTABLE);
                    if ((err_code != NRF_SUCCESS) && (err_code != NRF_ERROR_INVALID_STATE)) // NRF_ERROR_INVALID_STATE means disconnect is already in progress.
                    {
                        send_error_evt(err_code);
                    }
                }
    
                // Notify the application that the procedure has failed
                if (m_conn_params_config.evt_handler != NULL)
                {
                    ble_conn_params_evt_t evt;
    
                    evt.evt_type = BLE_CONN_PARAMS_EVT_FAILED;
                    evt.conn_handle = conn_handle;
                    m_conn_params_config.evt_handler(&evt);
                }
            }
        }
    }

Children
Related