Data Loss With Nordic Uart Service

We are developing our code based on the peripheral UART (NUS) example and are currently testing the baseline before applying our own changes. I have 2x nrf52840-DK boards, one for peripheral and another for central. We are using nrfConnect SDK 2.5.0. My application needs to transfer between 10 and 250 bytes approximately 4 times second. In addition to throughput, we do also care about latency.

The changes so far compared to the example are:

  1. increase in stack size which was needed for it to run with debugging;
  2. reduced the UART Rx timeout from 50ms to 1ms
  3. added some debug prints in the background thread.

I'm having issues (and these are present even if I undo the changes 2 and 3 listed above). I'm sending a data packet of 7 characters (terminated with \r \n) into both Peripheral and Central at roughly 100ms intervals and checking what was received at the peer. I wait for the data to be received before I send the next packet. Specifically the issues seen:

  • Sometime there are missing bytes Peripheral to Central. Logging shows the ble_write_thread did not receive all bytes ... they must have been missed by the UART driver. So far the missing byte(s) are at the end of each 7 byte packet.
  • If I increase the data packet size to 250 bytes I get much more data loss.
  • Central to UART direction works for a while and then lose much more data.

Questions:

  1. Are there known data loss issues with the peripheral UART driver?
  2. Do I need to increase buffers somewhere else in order to send 250 byte data; presumably I should to increase the MTU, please provide a link on how to do that.
  3. Should I switch to indications? If I use indications I understand I cannot send more data until I get the peer acknowledgement. But what happens if that acknowledgment never comes? Should I resend after a timeout?
Parents
  • Hello,

    The BLE link is considered reliable. That is, if a packet fails to reach the receiver after a specified number of retransmissions, the link will be considered lost, and a disconnect event will be triggered. Therefore, it is much more likely that data loss is occurring somewhere in the UART data handling before the data is passed to your sender thread.

    Are there known data loss issues with the peripheral UART driver?

    Please try to enable UART byte counting in HW for both projects by adding CONFIG_UART_0_NRF_HW_ASYNC=y and CONFIG_UART_0_NRF_HW_ASYNC_TIMER=1 to your prj.conf files.(https://github.com/nrfconnect/sdk-zephyr/blob/26fd55e0a750305fbd5d9db750225d7e707458ae/drivers/serial/Kconfig.nrfx_uart_instance#L45

    Do I need to increase buffers somewhere else in order to send 250 byte data; presumably I should to increase the MTU, please provide a link on how to do that.

    To support longer att MTUs, you can add these kconfig symbols to your prj.conf file (copied from throughput sample):

    CONFIG_BT_BUF_ACL_TX_SIZE=502

    CONFIG_BT_BUF_ACL_RX_SIZE=502

    CONFIG_BT_L2CAP_TX_MTU=498

    Effective data payload is att MTU - 3. You can add the following code to your project to get a callback after the MTU has been negotiated:

    void att_mtu_updated(struct bt_conn *conn, uint16_t tx, uint16_t rx)
    {
    	LOG_INF("MTU Updated: tx %d, rx %d", tx, rx);
    }
    
    static struct bt_gatt_cb gatt_cb = {
    	.att_mtu_updated = att_mtu_updated,
    };
    
    int main(void)
    {
        ...
        err = bt_enable(NULL);
    	if (err) {
    		LOG_INF("Bluetooth init failed (err %d)", err);
    		return 0;
    	}
    	
    	/* Register att MTU updated callback. Must be done after bt_enable() */
    	bt_gatt_cb_register(&gatt_cb);
        ...

    Alternatively, you can check the att MTU size during the connection using bt_gatt_get_mtu():

    uint16_t max_pkt_len = bt_gatt_get_mtu(current_conn) - 3;

    In addition, you may add CONFIG_BT_CTLR_DATA_LENGTH_MAX=251 if you want to support longer radio packet lengths (is 27 by default).

    Should I switch to indications? If I use indications I understand I cannot send more data until I get the peer acknowledgement. But what happens if that acknowledgment never comes? Should I resend after a timeout?

    Notifications are still acknowledged by the receiver, but by the link layer and not by the application. The Bluetooth stack will not send the next notification packet until a previous notification has been received. 

    Best regards,

    Vidar

    EDIT: The application should also request the HFXO oscillator when the UART is enabled to ensure a stable clock source for the baud rate generator. Otherwise, the baud rate generator will run off the less accurate internal RC oscillator (HFINT)

    The HFXO can be requested with this code snippet:

    https://github.com/nrfconnect/sdk-nrf/blob/5e0199b154f9e457d5ec091734c517916f0be9b5/samples/peripheral/radio_test/src/main.c#L11 

Reply
  • Hello,

    The BLE link is considered reliable. That is, if a packet fails to reach the receiver after a specified number of retransmissions, the link will be considered lost, and a disconnect event will be triggered. Therefore, it is much more likely that data loss is occurring somewhere in the UART data handling before the data is passed to your sender thread.

    Are there known data loss issues with the peripheral UART driver?

    Please try to enable UART byte counting in HW for both projects by adding CONFIG_UART_0_NRF_HW_ASYNC=y and CONFIG_UART_0_NRF_HW_ASYNC_TIMER=1 to your prj.conf files.(https://github.com/nrfconnect/sdk-zephyr/blob/26fd55e0a750305fbd5d9db750225d7e707458ae/drivers/serial/Kconfig.nrfx_uart_instance#L45

    Do I need to increase buffers somewhere else in order to send 250 byte data; presumably I should to increase the MTU, please provide a link on how to do that.

    To support longer att MTUs, you can add these kconfig symbols to your prj.conf file (copied from throughput sample):

    CONFIG_BT_BUF_ACL_TX_SIZE=502

    CONFIG_BT_BUF_ACL_RX_SIZE=502

    CONFIG_BT_L2CAP_TX_MTU=498

    Effective data payload is att MTU - 3. You can add the following code to your project to get a callback after the MTU has been negotiated:

    void att_mtu_updated(struct bt_conn *conn, uint16_t tx, uint16_t rx)
    {
    	LOG_INF("MTU Updated: tx %d, rx %d", tx, rx);
    }
    
    static struct bt_gatt_cb gatt_cb = {
    	.att_mtu_updated = att_mtu_updated,
    };
    
    int main(void)
    {
        ...
        err = bt_enable(NULL);
    	if (err) {
    		LOG_INF("Bluetooth init failed (err %d)", err);
    		return 0;
    	}
    	
    	/* Register att MTU updated callback. Must be done after bt_enable() */
    	bt_gatt_cb_register(&gatt_cb);
        ...

    Alternatively, you can check the att MTU size during the connection using bt_gatt_get_mtu():

    uint16_t max_pkt_len = bt_gatt_get_mtu(current_conn) - 3;

    In addition, you may add CONFIG_BT_CTLR_DATA_LENGTH_MAX=251 if you want to support longer radio packet lengths (is 27 by default).

    Should I switch to indications? If I use indications I understand I cannot send more data until I get the peer acknowledgement. But what happens if that acknowledgment never comes? Should I resend after a timeout?

    Notifications are still acknowledged by the receiver, but by the link layer and not by the application. The Bluetooth stack will not send the next notification packet until a previous notification has been received. 

    Best regards,

    Vidar

    EDIT: The application should also request the HFXO oscillator when the UART is enabled to ensure a stable clock source for the baud rate generator. Otherwise, the baud rate generator will run off the less accurate internal RC oscillator (HFINT)

    The HFXO can be requested with this code snippet:

    https://github.com/nrfconnect/sdk-nrf/blob/5e0199b154f9e457d5ec091734c517916f0be9b5/samples/peripheral/radio_test/src/main.c#L11 

Children
No Data
Related