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
  • Hi

    We also have a throughput example project for the nRF5 SDK that might be useful to test if you still see the packet loss there as well.

    We suggest using a BLE_GATTS_EVT_HVN_TX_COMPLETE event and count the number of packets you queue to make sure it doesn't fill up and the transmission is completed successfully. You can check out this case where my colleague Einar explains it in detail, as I think you're missing something like this in your application.

    Best regards,

    Simon

  • Hi  Sorry for the late reply. I did try what you suggested, but it didn't work. I went through the throughput example code and tried to set up some parameters similar in my code. It also didn't work neither. 

    Regarding the case you referred above, I got the same issue when a counter variable( BLE_NUS_TX_BUFFER) set on BLE_GATTS_EVT_HVN_TX_COMPLETE event is always 1. 

    BLE_NUS_TX_BUFFER -= p_ble_evt->evt.gatts_evt.params.hvn_tx_complete.count;

    The thing is that I've never seen any posts regarding BLE multirole - multilink with up to 250 Hz sampling rate. It seems like implementing for low sampling rate is only way to make it work. However, it'll ruin my specification for this project Frowning2

Reply
  • Hi  Sorry for the late reply. I did try what you suggested, but it didn't work. I went through the throughput example code and tried to set up some parameters similar in my code. It also didn't work neither. 

    Regarding the case you referred above, I got the same issue when a counter variable( BLE_NUS_TX_BUFFER) set on BLE_GATTS_EVT_HVN_TX_COMPLETE event is always 1. 

    BLE_NUS_TX_BUFFER -= p_ble_evt->evt.gatts_evt.params.hvn_tx_complete.count;

    The thing is that I've never seen any posts regarding BLE multirole - multilink with up to 250 Hz sampling rate. It seems like implementing for low sampling rate is only way to make it work. However, it'll ruin my specification for this project Frowning2

Children
No Data
Related