Dealing with data packet loss between a peripheral device (nRF52832) and a central device (nRF52840)

Hi everyone,

I'm setting up 02 peripheral devices (nRF53832 Dev KIT) that connect to a central device (nRF52840 Dev KIT). In the past few days, I've tested its throughput using only 1 peripheral device to send data to the central one at 250 samples/sec with 16 bytes data packet (15-byte data packet + 1 byte index to check if any data packet loss) as shown in the following figure. I'm using SDK17.02 and NUS service for both peripheral device and central device. I'm using 2M PHY, DLE enabled and ATT MTU 247 bytes.

The following code is the ble_nus_data_send function being used to send data to the central device. 

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;
    //ble_evt_t const * p_ble_evt;
    ble_nus_evt_t * p_evt;

    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;
    
    err_code = sd_ble_gatts_hvx(conn_handle, &hvx_params);

    return err_code;
}

I put this function in while (1) loop along with my sensor data saved in array as one of parameters (uint8_t* p_data) in the main.c to keep sending data. The error I got from this function: sd_ble_gatts_hvx() is NRF_ERROR_RESOURCES. Following some suggestions on other posts like this [1, 2], there are two approaches I used:

Approach #1: Once I receive NRF_ERROR_RESOURCES, I resend the data packet till the error goes away. I keep printing the err_code and it showed NRF_SUCCESS; however, I checked on the terminal debug of the central device, I'm still seeing some data packet loss. I noticed some data packets were resent several times. The following illustrates what I'm saying. P/s: I checked on the index number (16th byte) of each data packet and printed out on the central side.

Approach #2: It's slightly similar to the first one, but once I received the NRF_ERROR_RESOURCES, I wait till the BLE_GATTS_EVT_HVN_TX_COMPLETE event happened. This event was triggered at the ble_nus_on_ble_evt function:

void ble_nus_on_ble_evt(ble_evt_t const * p_ble_evt, void * p_context)
{
    if ((p_context == NULL) || (p_ble_evt == NULL))
    {
        return;
    }

    ble_nus_t * p_nus = (ble_nus_t *)p_context;

    switch (p_ble_evt->header.evt_id)
    {
        case BLE_GAP_EVT_CONNECTED:
            on_connect(p_nus, p_ble_evt);
            break;

        case BLE_GATTS_EVT_WRITE:
            on_write(p_nus, p_ble_evt);
            break;

        case BLE_GATTS_EVT_HVN_TX_COMPLETE:
            on_hvx_tx_complete(p_nus, p_ble_evt);
            break;

        default:
            // No implementation needed.
            break;
    }
}

So whenever BLE_GATTS_EVT_HVN_TX_COMPLETE event happened, it called the following function, and I used BLE_NUS_EVT_TX_RDY as an event to see if Service is ready to accept new data to be transmitted. I create a bool variable (as a flag) and whenever BLE_NUS_EVT_TX_RDY is called, the flag is TRUE. Thus when sending data to over BLE, if NRF_ERROR_RESOURCES happened, I wait for the flag is TRUE before calling the BLE sending function. However, the data packet loss still happened. 

static void on_hvx_tx_complete(ble_nus_t * p_nus, ble_evt_t const * p_ble_evt)
{
    ret_code_t                 err_code;
    ble_nus_evt_t              evt;
    ble_nus_client_context_t * p_client;

    err_code = blcm_link_ctx_get(p_nus->p_link_ctx_storage,
                                 p_ble_evt->evt.gatts_evt.conn_handle,
                                 (void *) &p_client);
    if (err_code != NRF_SUCCESS)
    {
        NRF_LOG_ERROR("Link context for 0x%02X connection handle could not be fetched.",
                      p_ble_evt->evt.gatts_evt.conn_handle);
        return;
    }

    if ((p_client->is_notification_enabled) && (p_nus->data_handler != NULL))
    {
        memset(&evt, 0, sizeof(ble_nus_evt_t));
        evt.type        = BLE_NUS_EVT_TX_RDY;
        evt.p_nus       = p_nus;
        evt.conn_handle = p_ble_evt->evt.gatts_evt.conn_handle;
        evt.p_link_ctx  = p_client;

        p_nus->data_handler(&evt);
    }
}

Please advise on if I need to update/modify the above approaches to solve data loss problem. Thanks!

Parents
  • I forgot to mention that if I use an Android phone as a central. Data packet loss never happens. 

    Regarding the event BLE_NUS_EVT_TX_RDY, I put a counter in the following code to see if it is triggered linearly or not and I was able to receive a counter with increment by 1. It means, whenever 16-byte data packet was successfully transmitted, the counter in that event increased by 1. I'm wondering if any setup I need to to notice from the central side?

    /**@brief Function for handling the data from the Nordic UART Service.
     *
     * @details This function will process the data received from the Nordic UART BLE Service and send
     *          it to the UART module.
     *
     * @param[in] p_evt       Nordic UART Service event.
     */
    /**@snippet [Handling the data received over BLE] */
    static void nus_data_handler(ble_nus_evt_t * p_evt)
    {
    
        uint32_t err_code;
    
        switch(p_evt->type)
        {
    	case BLE_NUS_EVT_RX_DATA:
    
              NRF_LOG_DEBUG("Received data from BLE NUS. Writing data on UART.");
              NRF_LOG_HEXDUMP_DEBUG(p_evt->params.rx_data.p_data, p_evt->params.rx_data.length);
    
              for (uint32_t i = 0; i < p_evt->params.rx_data.length; i++)
              {
                  do
                  {
                      err_code = app_uart_put(p_evt->params.rx_data.p_data[i]);
                      if ((err_code != NRF_SUCCESS) && (err_code != NRF_ERROR_BUSY))
                      {
                          NRF_LOG_ERROR("Failed receiving NUS message. Error 0x%x. ", err_code);
                          APP_ERROR_CHECK(err_code);
                      }
                  } while (err_code == NRF_ERROR_BUSY);
              }
              if (p_evt->params.rx_data.p_data[p_evt->params.rx_data.length - 1] == '\r')
              {
                  while (app_uart_put('\n') == NRF_ERROR_BUSY);
              }
    	  break;
    	case BLE_NUS_EVT_TX_RDY: //  check if Service is ready to accept new data to be transmitted.
    	  
    	  is_transmitted = true;
    	  index ++;
    	  NRF_LOG_INFO("Is data transmmitted: %d\r\n", index);
    	  break;
    
    	default:
    	  
    	  break;
        }
    
    }
    /**@snippet [Handling the data receive

  • It is likely there is a disconnect-connect event using the nRF52840 (which has a poor antenna compared to the Android phone) which will cause loss of a packet unless you add a handler to work around the issues caused by such an event. To prove this add a counter which increments on each BLE disconnect and compare with the packet loss counter; force a few disconnects on the Android by walking away. For a more in-depth discussion see dropped-packets-with-bt_nus_send

    static void ble_evt_handler(ble_evt_t const * p_ble_evt, void * p_context)
    {
        switch (p_ble_evt->header.evt_id)
        {
            case BLE_GAP_EVT_CONNECTED:
                ...
                // Indicate number of connections for checking against missing packets
                mBleConnectionCounter++;

Reply
  • It is likely there is a disconnect-connect event using the nRF52840 (which has a poor antenna compared to the Android phone) which will cause loss of a packet unless you add a handler to work around the issues caused by such an event. To prove this add a counter which increments on each BLE disconnect and compare with the packet loss counter; force a few disconnects on the Android by walking away. For a more in-depth discussion see dropped-packets-with-bt_nus_send

    static void ble_evt_handler(ble_evt_t const * p_ble_evt, void * p_context)
    {
        switch (p_ble_evt->header.evt_id)
        {
            case BLE_GAP_EVT_CONNECTED:
                ...
                // Indicate number of connections for checking against missing packets
                mBleConnectionCounter++;

Children
Related