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

BLE Central: freeze on disconnect with FreeRTOS

Hello support team,

I try to get the NRF52840 DK to work as a central with FreeRTOS. Connecting and getting notifications works well, but the device freezes on a disconnect event.

After trying to debug it with gdb, it seems that it is indefinetly hanging in the softdevice_task in nrf_sdh_freertos.c. I migrated to SDK 15.3.0 today as there were hints in this forum  about an accidental deadlock in previous versions of this file, but the problem unfortunately persists.

Here is the code I am using for the central:

#include "app_config.h"

#include "nrf_sdh.h"
#include "nrf_sdh_ble.h"
#include "nrf_drv_clock.h"

#include "nrf_sdh_freertos.h"
#include "FreeRTOS.h"
#include "task.h"

#include "ble_advdata.h"
#include "nrf_ble_gatt.h"

#include "app_timer.h"
#include "bsp.h"

#include "nrf_log.h"
#include "nrf_log_ctrl.h"
#include "nrf_log_default_backends.h"

#define TASK_DELAY                      200                                 /**< Task delay. Delays the LED task for 200 ms */

#define SCAN_INTERVAL                   0x00A0                              /**< Determines scan interval in units of 0.625 millisecond. */
#define SCAN_WINDOW                     0x0050                              /**< Determines scan window in units of 0.625 millisecond. */
#define SCAN_DURATION                   0x0000                              /**< Timout when scanning. 0x0000 disables timeout. */

#define MIN_CONNECTION_INTERVAL         MSEC_TO_UNITS(7.5, UNIT_1_25_MS)    /**< Determines minimum connection interval in milliseconds. */
#define MAX_CONNECTION_INTERVAL         MSEC_TO_UNITS(15, UNIT_1_25_MS)     /**< Determines maximum connection interval in milliseconds. */
#define SLAVE_LATENCY                   0                                   /**< Determines slave latency in terms of connection events. */
#define SUPERVISION_TIMEOUT             MSEC_TO_UNITS(4000, UNIT_10_MS)     /**< Determines supervision time-out in units of 10 milliseconds. */

#define APP_BLE_CONN_CFG_TAG            1                                   /**< A tag identifying the SoftDevice BLE configuration. */
#define APP_BLE_OBSERVER_PRIO           3                                   /**< Application's BLE observer priority. You shouldn't need to modify this value. */

NRF_BLE_GATT_DEF(m_gatt);                                                   /**< GATT module instance. */

static TaskHandle_t m_led_thread;                                           /**< Definition of LED bliking thread. */

/**@brief Parameters used when scanning. */
static ble_gap_scan_params_t const m_scan_params =
{
    .active   = 1,
    .interval = SCAN_INTERVAL,
    .window   = SCAN_WINDOW,

    .timeout           = SCAN_DURATION,
    .scan_phys         = BLE_GAP_PHY_1MBPS,
    .filter_policy     = BLE_GAP_SCAN_FP_ACCEPT_ALL,
};

/**@brief Connection parameters requested for connection. */
static ble_gap_conn_params_t const m_connection_param =
{
    (uint16_t)MIN_CONNECTION_INTERVAL,
    (uint16_t)MAX_CONNECTION_INTERVAL,
    (uint16_t)SLAVE_LATENCY,
    (uint16_t)SUPERVISION_TIMEOUT
};

static char const m_target_periph_name[] = DEVICE_NAME_PERIPHERAL;          /**< Name of the device we try to connect to. This name is searched in the scan report data*/

#define INVALID_CONN_HANDLE 0xFFFF
uint16_t m_conn_handle = INVALID_CONN_HANDLE;

ble_gap_addr_t m_peer_addr;

static uint8_t m_scan_buffer_data[BLE_GAP_SCAN_BUFFER_MIN]; /**< buffer where advertising reports will be stored by the SoftDevice. */

/**@brief Pointer to the buffer where advertising reports will be stored by the SoftDevice. */
static ble_data_t m_scan_buffer =
{
    m_scan_buffer_data,
    BLE_GAP_SCAN_BUFFER_MIN
};

/**@brief Function to start scanning ble devices.
 */
static void app_ble_scan_start(void *p_context)
{
    UNUSED_PARAMETER(p_context);
    ret_code_t err_code;

    (void) sd_ble_gap_scan_stop();

    err_code = sd_ble_gap_scan_start(&m_scan_params, &m_scan_buffer);
    APP_ERROR_CHECK(err_code);

    // Signal scanning.
    NRF_LOG_INFO("Started scanning.");
}

/**@brief Function for handling the advertising report BLE event.
 *
 * @param[in] p_adv_report  Advertising report from the SoftDevice.
 */
static void on_adv_report(ble_gap_evt_adv_report_t const * p_adv_report)
{
    ret_code_t err_code;

    if (ble_advdata_name_find(p_adv_report->data.p_data,
                              p_adv_report->data.len,
                              m_target_periph_name))
    {
        // copy address of found device
        memcpy(&m_peer_addr, &p_adv_report->peer_addr, sizeof(ble_gap_addr_t));
      
        NRF_LOG_DEBUG("Compatible device found, connecting.");
        err_code = sd_ble_gap_scan_start(NULL, &m_scan_buffer);

        // Name is a match, initiate connection.
        err_code = sd_ble_gap_connect(&m_peer_addr,
                                      &m_scan_params,
                                      &m_connection_param,
                                      APP_BLE_CONN_CFG_TAG);
        APP_ERROR_CHECK(err_code);
    }
    else
    {
      //NRF_LOG_DEBUG("Compatible device not found, continue scanning.");
        err_code = sd_ble_gap_scan_start(NULL, &m_scan_buffer);
        APP_ERROR_CHECK(err_code);
    }
}


/**@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;

    // For readability.
    ble_gap_evt_t const * p_gap_evt = &p_ble_evt->evt.gap_evt;

    switch (p_ble_evt->header.evt_id)
    {
        // Update connection handle.
        case BLE_GAP_EVT_CONNECTED:
        {
            m_conn_handle = p_gap_evt->conn_handle;
            NRF_LOG_INFO("BLE peripheral connected.");
            bsp_board_led_on(BSP_BOARD_LED_1);
        } break;

        // Upon disconnection, reset the connection handle of the peer which disconnected
        case BLE_GAP_EVT_DISCONNECTED:
        {
            m_conn_handle = INVALID_CONN_HANDLE;
            NRF_LOG_INFO("BLE peripheral disconnected.");
            bsp_board_led_off(BSP_BOARD_LED_1);
            
            // start scanning again
            app_ble_scan_start(NULL);
        } break;

        // Call advertising report handler and connect if applicable
        case BLE_GAP_EVT_ADV_REPORT:
        {
            on_adv_report(&p_gap_evt->params.adv_report);
        } break;

        // We have not specified a timeout for scanning, so only connection attemps can timeout.
        case BLE_GAP_EVT_TIMEOUT:
        {
            NRF_LOG_DEBUG("GAP Connection Timeout.");
            if (p_gap_evt->params.timeout.src == BLE_GAP_TIMEOUT_SRC_CONN)
            {
          NRF_LOG_DEBUG("Connection request timed out.");
            }
        } break;

        // Accept parameters requested by peer.
        case BLE_GAP_EVT_CONN_PARAM_UPDATE_REQUEST:
        {
            NRF_LOG_DEBUG("Connection Parameter Update Request.");
            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;

        case BLE_GAP_EVT_PHY_UPDATE_REQUEST:
        {
            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;

        // Disconnect on GATT Client timeout event.
        case BLE_GATTC_EVT_TIMEOUT:
        {
            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;

        // Disconnect on GATT Server timeout event.
        case BLE_GATTS_EVT_TIMEOUT:
        {
            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;

        // No implementation needed.
        default:
        {
            NRF_LOG_DEBUG("ble_evt_handler evt id %i", p_ble_evt->header.evt_id);
        } break;
    }
}



/**@brief Function for initializing the BLE stack.
 *
 * @details Initializes the SoftDevice and the BLE event interrupts.
 */
static void app_ble_stack_init(void)
{
    ret_code_t err_code;

    err_code = nrf_sdh_enable_request();
    APP_ERROR_CHECK(err_code);

    // Configure the BLE stack using the default settings.
    // Fetch the start address of the application RAM.
    uint32_t ram_start = 0;
    err_code = nrf_sdh_ble_default_cfg_set(APP_BLE_CONN_CFG_TAG, &ram_start);
    APP_ERROR_CHECK(err_code);

    // Enable BLE stack.
    err_code = nrf_sdh_ble_enable(&ram_start);
    APP_ERROR_CHECK(err_code);

    // Register a handler for BLE events.
    NRF_SDH_BLE_OBSERVER(m_ble_observer, APP_BLE_OBSERVER_PRIO, ble_evt_handler, NULL);

    NRF_LOG_INFO("BLE stack initialized.");
}

/**@brief Function for initializing the log.
 */
static void log_init(void)
{
    ret_code_t err_code = NRF_LOG_INIT(NULL);
    APP_ERROR_CHECK(err_code);

    NRF_LOG_DEFAULT_BACKENDS_INIT();
    NRF_LOG_INFO("Logging initialized.");
}


/**@brief Function for initializing the clock.
 */
static void clock_init(void)
{
    ret_code_t err_code = nrf_drv_clock_init();
    APP_ERROR_CHECK(err_code);
}


/**@brief A function which is hooked to idle task.
 * @note Idle hook must be enabled in FreeRTOS configuration (configUSE_IDLE_HOOK).
 */
void vApplicationIdleHook( void ) { /*void*/ }


/**@brief Function for initializing the GATT module.
 */
static void app_ble_gatt_init(void)
{
    ret_code_t err_code = nrf_ble_gatt_init(&m_gatt, NULL);
    APP_ERROR_CHECK(err_code);
    NRF_LOG_INFO("GATT initialized.");
}

/**@brief LED task entry function.
 *
 * @param[in] pvParameter   Pointer that will be used as the parameter for the task.
 */
static void led_toggle_task_function (void * pvParameter)
{
    NRF_LOG_INFO("LED toggle task initialized.");

    UNUSED_PARAMETER(pvParameter);
    while (true)
    {
        bsp_board_led_invert(BSP_BOARD_LED_2);
        vTaskDelay(TASK_DELAY);
    }
}

/**@brief Function for initializing the BSP module.
 */
static void led_init(void)
{
    bsp_board_init(BSP_INIT_LEDS);
    bsp_board_led_on(BSP_BOARD_LED_0);
    NRF_LOG_INFO("LEDs initialized.");

    // Start blinking LED3.
    if (pdPASS != xTaskCreate(led_toggle_task_function,
                              "LED", 128, NULL, 3,
                              &m_led_thread))
    {
        APP_ERROR_HANDLER(NRF_ERROR_NO_MEM);
    }
}

int main(void)
{
    // Initialize modules.
    log_init();
    clock_init();

    // Configure and initialize BLE stack and modules.
    app_ble_stack_init();
    app_ble_gatt_init();

    led_init();

    // Create a FreeRTOS task for the BLE stack.
    // The task will run app_ble_scan_start(NULL) before entering its loop.
    nrf_sdh_freertos_init(app_ble_scan_start, NULL);

    // Start FreeRTOS scheduler.
    vTaskStartScheduler();

    // application should never leave FreeRTOS scheduler and reach this point
    for (;;)
    {
        APP_ERROR_HANDLER(NRF_ERROR_FORBIDDEN);
    }
}

  • The FreeRTOS expert is currently out of office, but we'll try to look into this next week.

  • Hi Daniel,

    You need to give us more details on what you mean by "freezes" on a disconnect event.

    How did you come to the conclusion that the execution is hanging inside softdevice_task inside nrf_sdh_freertos.c task? Was there some event that your application expected that never arrived?

  • Hi Susheel,

    in the minimal example I have created a blinking LED task which stops blinking after a disconnect. I made it to make an easy reproducible example, I hope you can try it out and reproduce the behaviour. It connects to any peripheral you want which has the same device name as specified in DEVICE_NAME_PERIPHERAL.

    I'm not that experienced with gdb, but by setting a breakpoint on the line 151 and stepping through the following instructions it won't get into any fault handler, but it finally stays in ulTaskNotifyTake inside the softdevice_task without allowing any possiblity to make another step in the debugger.

  • DanielK,

    I found the bug you are trying to trace.

    It seems that the ble_conn_params.c could have a bug that it might not be suitable for central. I am not 100% sure on that part but the problem seems to be a hardfault when trying to remove a timer from kernel queue where the timer does not exist.

    The app_timer is never started in ble_conn_params.c since the negotiations were never triggered.

    So in ble_conn_params.c->on_disconnect()-> comment out the app_timer_stop function as below

            // Stop timer if running
            //err_code = app_timer_stop(p_instance->timer_id);

    I will discuss this internally how to handle it. But for you this workaround should fix your problem.

  • Hello everyone!

    I've run into the same issue a few days ago, then luckily found this thread, I had spent lots of time to find this bug. Based on Susheel Nuguru's post I made a fix in "app_timer_freertos.c"

    The timer not just never started, it is even never created, therefore a null pointer is given to the "app_timer_stop" function. If you comment out those lines, the same SDK would make problems when it is used in an application with BLE device. Hence a better fix:

    uint32_t app_timer_stop(app_timer_id_t timer_id)
    {
        //invalid timer
        if (timer_id == NULL)
        {
            return NRF_ERROR_INVALID_STATE;
        }
    
        app_timer_info_t * pinfo = (app_timer_info_t*)(timer_id);
        ...

    Same applies to "app_timer_start" just to avoid future problems.

Related