Beware that this post is related to an SDK in maintenance mode
More Info: Consider nRF Connect SDK for new designs

nRF52840 - Device Reboots when BLE connection is terminated

I'm using a nRF52840 configured as Central and Peripheral, with nRF5_SDK 17.1.0 (I'm Not using Zephyr) for reading the Device Name from the Generic Service of a beacon. 
I'm already able to connect to the beacon GAP, discover the characteristic I need and read the device name. Up to this point everything is fine.
But when my beacon terminates the connection or when I try to finish it by calling sd_ble_gap_disconnect(...), my nRF52840 reboots.

Since I'm not using a development kit (I'm using a final product that has the nRF52840 as microcontroller) the only way I can debug is via a serial port the product has. But although I've put a lot of "printf like" calls to try to track where the reset is comming from, I wasn't able to find it. My serial debug goes all fine until I call the sd_ble_gap_disconnect or until the beacon shuts off the connection.

Does anyone have an idea of what can be happening and how can I solve it?

#define BEACON_GAP_UUID         0xFEAA
#define APP_BLE_CONN_CFG_TAG    1                        
#define APP_BLE_OBSERVER_PRIO   3                                       

uint8_t BufferDataRead[30];
uint16_t myConnHandle;

BLE_DB_DISCOVERY_DEF(m_db_disc);           
NRF_BLE_GQ_DEF(m_ble_gatt_queue,                           
               NRF_SDH_BLE_CENTRAL_LINK_COUNT,
               NRF_BLE_GQ_QUEUE_SIZE);
NRF_BLE_SCAN_DEF(m_scan);  

//========== MAIN ==========
int main(void)
{
    err_code = nrf_sdh_enable_request();
    APP_ERROR_CHECK(err_code);

    ble_stack_init();
    gatt_init();
    beacon_db_discovery_init();
    beacon_scan_init();

    beacon_scan_start(); //starts scanning
    //... rest of the program's logic
    //...
}
//==========================
void gatt_init(void)
{
    ret_code_t err_code;

    err_code = nrf_ble_gatt_init(&m_gatt, gatt_evt_handler);
    APP_ERROR_CHECK(err_code);

    err_code = nrf_ble_gatt_att_mtu_periph_set(&m_gatt, NRF_SDH_BLE_GATT_MAX_MTU_SIZE);
    APP_ERROR_CHECK(err_code);

    err_code = nrf_ble_gatt_att_mtu_central_set(&m_gatt, NRF_SDH_BLE_GATT_MAX_MTU_SIZE);
    APP_ERROR_CHECK(err_code);
}
static void ble_stack_init(void)
{
    ret_code_t err_code;

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

    err_code = nrf_sdh_ble_enable(&ram_start);
    APP_ERROR_CHECK(err_code);

    NRF_SDH_BLE_OBSERVER(m_ble_observer, APP_BLE_OBSERVER_PRIO, ble_evt_handler, NULL);
}
static void db_disc_handler(ble_db_discovery_evt_t * p_evt)
{
    uint16_t att_handle;
    ble_gatt_db_char_t * p_chars = p_evt->params.discovered_db.charateristics;

    if (p_evt->evt_type == BLE_DB_DISCOVERY_COMPLETE)
    {
        if (p_evt->params.discovered_db.srv_uuid.uuid == BLE_UUID_GAP &&
            p_evt->params.discovered_db.srv_uuid.type == BLE_UUID_TYPE_BLE)
        {
            for (uint32_t i = 0; i < p_evt->params.discovered_db.char_count; i++)
            {
                if (p_chars[i].characteristic.uuid.uuid == BLE_UUID_GAP_CHARACTERISTIC_DEVICE_NAME)
                {
                    att_handle = p_chars[i].characteristic.handle_value;
                    sd_ble_gattc_read(p_evt->conn_handle, att_handle, 0);
                }
            }
        }
    }
}
void beacon_db_discovery_init(void)
{
    ble_db_discovery_init_t db_init;

    memset(&db_init, 0, sizeof(ble_db_discovery_init_t));

    db_init.evt_handler  = db_disc_handler;
    db_init.p_gatt_queue = &m_ble_gatt_queue;

    ret_code_t err_code = ble_db_discovery_init(&db_init);
    APP_ERROR_CHECK(err_code);
}
void beacon_scan_start(void)
{
    ret_code_t err_code;

    err_code = nrf_ble_scan_start(&m_scan);
    APP_ERROR_CHECK(err_code);
}
static void scan_evt_handler(scan_evt_t const * p_scan_evt)
{
    ret_code_t err_code;
    
    switch(p_scan_evt->scan_evt_id)
    {
        case NRF_BLE_SCAN_EVT_CONNECTED:
            sd_ble_gap_scan_stop();
            break;
    }
}
void beacon_scan_init(void)
{
    ret_code_t          err_code;
    nrf_ble_scan_init_t init_scan;
    static ble_uuid_t beacon_uuid_generic;

    beacon_uuid_generic.type = BLE_UUID_TYPE_BLE;
    beacon_uuid_generic.uuid = BLE_UUID_GAP;

    ble_uuid_t beacon_uuid =
    {
        .type = BLE_UUID_TYPE_BLE,
        .uuid = BEACON_GAP_UUID,
    };

    memset(&init_scan, 0, sizeof(init_scan));

    init_scan.connect_if_match = true;
    init_scan.conn_cfg_tag     = APP_BLE_CONN_CFG_TAG;

    ble_db_discovery_evt_register(&beacon_uuid_generic);

    err_code = nrf_ble_scan_init(&m_scan, &init_scan, scan_evt_handler);
    APP_ERROR_CHECK(err_code);

    err_code = nrf_ble_scan_filter_set(&m_scan, SCAN_UUID_FILTER, &beacon_uuid);
    APP_ERROR_CHECK(err_code);

    err_code = nrf_ble_scan_filters_enable(&m_scan, NRF_BLE_SCAN_UUID_FILTER, false);
    APP_ERROR_CHECK(err_code);
}
static void ble_evt_handler(ble_evt_t const * p_ble_evt, void * p_context)
{
    ret_code_t            err_code;
    ble_gap_evt_t const * p_gap_evt = &p_ble_evt->evt.gap_evt;

    switch(p_ble_evt->header.evt_id)
    {
        case BLE_GAP_EVT_CONNECTED:
        {
            myConnHandle = p_ble_evt->evt.gattc_evt.conn_handle;
            err_code = ble_db_discovery_start(&m_db_disc, p_ble_evt->evt.gap_evt.conn_handle);
            APP_ERROR_CHECK(err_code);

            break;
        }
        case BLE_GATTC_EVT_READ_RSP:
        {
            uint8_t lengthDataRead = p_ble_evt->evt.gattc_evt.params.read_rsp.len;
            memcpy(BufferDataRead, p_ble_evt->evt.gattc_evt.params.read_rsp.data, lengthDataRead);
            //EVERYTHING GOES FINE UNTIL HERE
            //BUT WHEN I TRY DO DISCONNECT IT, uC REBOOTS
            sd_ble_gap_disconnect(myConnHandle, BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION);
            break;
        }
        case BLE_GAP_EVT_SEC_PARAMS_REQUEST:
        {
            err_code = sd_ble_gap_sec_params_reply(p_ble_evt->evt.gap_evt.conn_handle, BLE_GAP_SEC_STATUS_PAIRING_NOT_SUPP, NULL, NULL);
            APP_ERROR_CHECK(err_code);
            break;
        }
        case BLE_GAP_EVT_CONN_PARAM_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:
        {
            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;
        }
        case BLE_GATTC_EVT_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:
        {
            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;
        }
    }
}

Parents
  • SWD is usually required for production programming, I don't see why you can't use that here...?

    APP_ERROR_CHECK reboots the device by default when there is no debugger connected. I believe you can reconfigure the SDK to skip the checks. There was also a method to print out errors using your own custom function, but that is not 100% reliable in case of stack overflows.

    Probably simpler to port your code onto a DK board for testing the central stuff specifically.

    You also want to learn Zephyr ASAP, there is very little support for the old SDK versions from this point forward.

  • Also I noticed in an exemple of NUS that they use NRF_BLE_GQ_DEF (BLE GATT Queue instance). Should I use it? If so, how can I do that?

  • Hi there.

    Could you give an example of how I use the GATT queue? In the SDK examples code, they start the instance like this:

    NRF_BLE_GQ_DEF(m_ble_gatt_queue,                                        /**< BLE GATT Queue instance. */
                   NRF_SDH_BLE_CENTRAL_LINK_COUNT,
                   NRF_BLE_GQ_QUEUE_SIZE);

    But I don't know how to use that in the code, for instance, how can I queue the sd_ble_gattc_read() and sd_ble_gap_disconnect() so they don't overlap?

  • Hi Ricardo,

    I am discussing this internally and will get back to you. Please expect a slight delay as it is the summer holidays here in Norway and we are a bit short staffed at the moment.

    Regards,

    Priyanka

  • Hi Ricardo,

    May be removing of APP_ERROR_CHECK is not a good idea to find out what is wrong? It might be helpful to do the opposite: add more error checks to find out why exactly it reboots. Can also replace all NVIC_SystemReset(); in error handling with for(;;){}; so it hangs instead of rebooting and then examine with debugger what exactly happens.

    Not sure what that "GATT queue" is, it's not part of SoftDevice, probably some SDK module? In general, sd_ble_gap_disconnect() can be called any time and should just work.

    We went through your app, can you make sure you don't have buffer overflow here? 

           case BLE_GATTC_EVT_READ_RSP:

           {

               uint8_t lengthDataRead = p_ble_evt->evt.gattc_evt.params.read_rsp.len;

              memcpy(BufferDataRead, p_ble_evt->evt.gattc_evt.params.read_rsp.data, lengthDataRead);

              //EVERYTHING GOES FINE UNTIL HERE

               //BUT WHEN I TRY DO DISCONNECT IT, uC REBOOTS

              sd_ble_gap_disconnect(myConnHandle, BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION);

               break;

           }

    Some examples set NRF_SDH_BLE_GATT_MAX_MTU_SIZE to high value, that may overflow 30 bytes they reserve in BufferDataRead.

    -Priyanka

  • Hi.

    Thanks for taking time to analyze the code.

    So far what I did was redefine the __WEAK function that reboots the mcu, removing the NVIC_SystemReset().
    it works now, and it doesn't hang or block the normal work of the mcu.

    I also used it to print the error code and it has shown me 2 codes: Error 18 and Error 8.

    Error 18 seems to be associated with advertising problems. I do use this code with a bigger app as central and peripheral, so it is in fact sending advertisement packets, but I didn't get what is the conflict it may be happening between disconnecting the Gap and advertisement packets.

    About error 8 I honestly couldn't understand what may be going on.

  • Hi Ricardo,

    Ignoring critical errors does not seem like a good way to go. If you just remove NVIC_SystemReset() and continue after critical error, we cannot make any guarantees that SoftDevice will work as expected.

    -Priyanka

Reply Children
No Data
Related