BLE data packet drop during transmission

.Hi Nordic Support team,

I'm using NRF52832, SoftDevice S132, SDK version is nRF5_SDK_17.1.0_ddde560.

I want to transmit 9 bytes of data collected from an I2C sensor every 2.5ms via BLE. The sensor has an internal buffer so I can queue up to 288 bytes (80ms from empty to full sensor buffer). However, I2C can only receive 255 bytes per queue, so I only collect data from the sensor every 60ms, because for 60ms, the sensor produces 216 bytes of data and it is within 1 MTU packet ( configure it to be 247 bytes per MTU). That means I will send 1 MTU every 60ms. That means I only need a connection interval of 60ms. Am I correct?

In my BLE structure, I also have another different characteristic that reports the device temperature (takes only 2 bytes) every 10 seconds (yes, second not milliseconds). However, from my experiment, 60ms cannot keep up with this data rate from both characteristics. Do I misunderstand anything? Should I use the connection event extension length? 

Here are my BLE parameters: NRF_SDH_BLE_GAP_EVENT_LENGTH is 6. TX queue length is 1. Min connection interval = Max connection interval = 60ms. Save lantecy is 4.

I also have tried to increase the TX queue length to 18. Set Min connection interval = 30ms, Max connection interval = 120ms, enable Connection Event Length Extension by the following code right after ble_advertising_init. However, I still occasionally get NRF_ERROR_RESOURCES from sd_ble_gatts_value_set(p_cus->conn_handle, p_cus->data_char_handles.value_handle, &gatts_value);

What I am trying to do is optimize power consumption while also ensuring absolutely NO data loss via BLE transfer.

// Connection Event Length Extension
ble_opt_t  opt;

memset(&opt, 0x00, sizeof(opt));
opt.common_opt.conn_evt_ext.enable = true;

err_code = sd_ble_opt_set(BLE_COMMON_OPT_CONN_EVT_EXT, &opt);
APP_ERROR_CHECK(err_code);

Please let me know if I do anything wrong. 

Best regards,

Xander

  • Hi Xander,

    I want to transmit 9 bytes of data collected from an I2C sensor every 2.5ms via BLE. The sensor has an internal buffer so I can queue up to 288 bytes (80ms from empty to full sensor buffer). However, I2C can only receive 255 bytes per queue, so I only collect data from the sensor every 60ms, because for 60ms, the sensor produces 216 bytes of data and it is within 1 MTU packet ( configure it to be 247 bytes per MTU). That means I will send 1 MTU every 60ms. That means I only need a connection interval of 60ms. Am I correct?

    It is weird that the sensor can queue 288 bytes, but only allow 255 bytes read at a time.

    Otherwise, this seems correct. 

    In my BLE structure, I also have another different characteristic that reports the device temperature (takes only 2 bytes) every 10 seconds (yes, second not milliseconds). However, from my experiment, 60ms cannot keep up with this data rate from both characteristics. Do I misunderstand anything? Should I use the connection event extension length? 

    You don't have to worry about connection event length, but you should use Data Length Extension (DLE), because the amount of overhead at that data size is too much.

    However, 60ms should be more than enough to transmit 216 bytes, with or without DLE. So, something is not right here.

    To change GAP Data Length, you can change NRF_SDH_BLE_GAP_DATA_LENGTH in sdk_config.h.

    Here are my BLE parameters: NRF_SDH_BLE_GAP_EVENT_LENGTH is 6. TX queue length is 1. Min connection interval = Max connection interval = 60ms. Save lantecy is 4.

    I also have tried to increase the TX queue length to 18. Set Min connection interval = 30ms, Max connection interval = 120ms

    For this, I would like to reuse my answer in another case:

    Furthermore, please know that this is just what the peripheral "proposes" to the central device. It is still the central device that decides the actual connection interval. To debug what the actual connection interval is, please check the GAP events.

    At the beginning of a connection, BLE_GAP_EVT_CONNECTED is received, and the connection parameters can be found in ble_evt_t.ble_gap_evt_t.connected.

    After that, the connection parameters may be updated, BLE_GAP_EVT_CONN_PARAM_UPDATE is received, and the connection parameters can be found in ble_evt_t.ble_gap_evt_t.conn_param_update.

    Furthermore, in BLE, packets cannot be dropped. If a packet is drop, the connection itself is also dropped, so if you don't see a disconnection, but data is lost, then it might indicate a problem with preprocessing the data to be sent, or postprocessing the data received. 

    Hieu

  • Hi Hieu,

    It is weird that the sensor can queue 288 bytes, but only allow 255 bytes read at a time.

    I mean the NRF52832 has the I2C queue limits to 255 bytes. The sensor has no such limitation.

    However, 60ms should be more than enough to transmit 216 bytes, with or without DLE. So, something is not right here.

    I just simplified the real scenario. In my app, I poll the sensor every 60ms, returning 216 bytes. However, I hold them in a buffer and wait until the buffer size exceeds 243 bytes since this is the nearest multiple of 9 smaller than MTU size 247 bytes (I verified this number from the connection event). Therefore, the first time the 60ms timer is timeout, there won't be any MTU packet being sent (216 bytes < 243 bytes). But the second time timer timeout, 243 bytes of data from the buffer will be sent (with some remaining data left which will be sent from the third time timer timeout,... ). However, I don't think this won't be an issue as there cannot be any timeout 2 MTU packets will be sent.

    You don't have to worry about connection event length, but you should use Data Length Extension (DLE), because the amount of overhead at that data size is too much.

    Where can I find an example of how to use DLE?

    To debug what the actual connection interval is, please check the GAP events.

    GAP events look fine to me. By the way, 1 interesting I found on this screenshot is on the line "Peer on connection 0x0 requested an ATT MTU of 527 bytes.". Does this mean the other device (my PC) have requested 523 bytes? I believe NRF52832 won't support this high MTU size so both ends agree at 247 bytes MTU size? 

    Connection intervals look correct to me as well from debug message as well.

    Furthermore, in BLE, packets cannot be dropped. If a packet is drop, the connection itself is also dropped, so if you don't see a disconnection, but data is lost, then it might indicate a problem with preprocessing the data to be sent, or postprocessing the data received. 

    We currently don't do APP_ERROR_CHECK() for any data written to the BLE queue buffer because we believe this data rate must be able to handle these BLE parameters. I think data loss will happen because we ignore the NRF_ERROR_RESOURCES, which indicates data cannot be queued in the BLE TX queue.

    By the way, it's nice to meet a Viet guy on this forum Handshake

  • Hi Xander,

    Xander To said:
    I mean the NRF52832 has the I2C queue limits to 255 bytes. The sensor has no such limitation.

    Ah OK. I would think that you could simply get all of the sensor's buffer in two transactions instead, but your approach is just fine. I think I prefer that too.

    Xander To said:
    Where can I find an example of how to use DLE?

    Your log suggests the GATT Module is being used, and the configurations are already done so that Data Length is set to the maximum of 251. Not the max_rx_octets and max_tx_octets in the log.

    Xander To said:
    GAP events look fine to me. By the way, 1 interesting I found on this screenshot is on the line "Peer on connection 0x0 requested an ATT MTU of 527 bytes.". Does this mean the other device (my PC) have requested 523 bytes? I believe NRF52832 won't support this high MTU size so both ends agree at 247 bytes MTU size? 

    Your guess is completely correct.

    Xander To said:
    Connection intervals look correct to me as well from debug message as well.

    I agree.

    Xander To said:
    We currently don't do APP_ERROR_CHECK() for any data written to the BLE queue buffer because we believe this data rate must be able to handle these BLE parameters. I think data loss will happen because we ignore the NRF_ERROR_RESOURCES, which indicates data cannot be queued in the BLE TX queue.

    APP_ERROR_CHECK() is actually just a sample handling of error. It is useful in various corner cases, but for more common failures, it is better that a graceful handling is added in. If you find that NRF_ERROR_RESOURCES is being thrown constantly, you should review your usage of the function that is returning that error, and consider changing configurations to provide the resource necessary, or handle the error differently.

    Xander To said:
    By the way, it's nice to meet a Viet guy on this forum Handshake

    The feeling is mutual Slight smile 

  • Hi Hieu,

    If you find that NRF_ERROR_RESOURCES is being thrown constantly, you should review your usage of the function that is returning that error, and consider changing configurations to provide the resource necessary, or handle the error differently.

    But I still don't understand why the NRF_ERROR_RESOURCES appears if we only send 1 MTU per 60ms (1 connection event). Every 10 seconds, there will be another MTU of 2 bytes being sent from the temperature characteristic.

    As mentioned above, I have tried to increase the TX size to 18, so queue size shouldn't be a problem but only the throughput is the problem. For every 1 connection interval (60ms), as I set NRF_SDH_BLE_GAP_EVENT_LENGTH = 6, which means it will spend 7.5ms to send data for every 1 connection interval, right? And I think 1-2 MTU packets + a few more bytes for the packet header will still take less than 7.5ms to be sent over BLE, right? Can you please let me know if I misunderstood anything? I want to know what factor caused the NRF_ERROR_RESOURCES error in this case. Is it the connection interval, or is it the NRF_SDH_BLE_GAP_EVENT_LENGTH , or another parameter?

    Best  regards,

    Xander

  • Hi Xander,

    What function is returning NRF_ERROR_RESOURCES? I know in your opening post, you said sd_ble_gatts_value_set(), but according to documentation, that function doesn't return NRF_ERROR_RESOURCES.

    Best regards,

    Hieu

Related