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.

Reply
  • 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.

Children
No Data
Related