This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

UART data loss on nRF52840DK

Hi all,

Project description and what works:

in my project I have one nRF52840DK as central and three nRF52DK as peripheral. The code on the central is based on the "nrf52-ble-app-uart-c-multilink-master" example and the code on the peripherals is based on the "ble_app_uart" example. Each peripheral sends data with a data-rate of 5,125 kByte/s (41 kBit/s) to the central. To be more precise, each peripheral sends 205 Bytes of data every 40ms. This works like a charm, without errors. (:

Problem:

At the moment I am having trouble with the UART on the central side. The data of all three data links has to be written via UART to a PC (virtual COM port). So the UART has to handle 3*41kBit/s = 123kBit/s. The order or the exact time, when each NUS paket gets written into the virtual COM port does not matter. But no data should be lost in this process. The problem is that the UART dropps data at this data rate. The loss of data is very inconsistent and random.

Test Setup:

To verify the function of the UART each peripheral sends consistently a 205 byte long array (as a NUS-paket) to the central. The NUS paket of each peripheral contains different characters:

Peripheral 1:  !!!!!!!!!!!!!!!!!!!!!!! .... (205 elements)

Peripheral 2: """""""""""""""""" .... (205 elements)

Peripheral 3: ############ .... (205 elements)

I use Putty to view the data which is written in the virtual COM port.

Test Results:

As we can see, UART drops data. Normally I should see 205 elements of each character.

Questions

1. First of all, is the usage of putty in this case reliable? Or did just windows cant keep up with printing this chars in this console?

2. Is there a other/better way to evaluate this?

3. What are the correct parameters in this case for ...

3.1 ... UART_TX_BUF_SIZE and UART_RX_BUF_SIZE? (Actual: 32768)

3.2 ... priority in APP_UART_FIFO_INIT? (Actual: APP_IRQ_PRIORITY_MID)

3.3 ... baudrate in uart_init()? (Actual: UART_BAUDRATE_BAUDRATE_Baud460800)

4. Do I have to change the chosen baudrate in the windows device manager (COM) as well?

5. Can you please provide me any suggestions or help on how to solve this issue?

Any kind of feedback is appreciated. Thank you so much in advance.

Kind regards,

Maria

Parents
  • Hello Maria,

    Have you tried to debug this issue? Can you see whether all the data is correctly received over BLE?

    There are several places the data can "get lost". UART -> peripheral -> BLE -> Central -> UART.

    Try to set a breakpoint, or to print only the length of the received data on the central. See if the length is what you expect. What you might see is that the data is lost either when the central tries to print the data on the UART, or on the peripheral when the UART tries to read all the data. If the cap is the UART speed,  you can try to use an even higher baudrate. Have you tried 1000000?

    BR,

    Edvin

  • Hello Edvin,

    thank you very much for your quick reply.

    I already tried to debug this issue. But until your answer I did not really know what I should debug.

    As you suggested, I checked the length of the received data on central. I added the following to the code to the central:

    static void ble_nus_chars_received_uart_print(uint8_t * p_data, uint16_t data_len)
    {
        ...
        //DEBUG
        if (data_len != 205)
        {
            NRF_LOG_INFO("Too short");
        }
        ...
    }

    The "Too short" message gets never printed. So the length of the received data is correct.

    I figured also out that the printed data via Putty only gets messed up, if I do another heavy task on my pc. So maybe the data in the serial port is correct but putty cant print the data out properly, because it does not always get the required resources from windows. So maybe I have to use another data sink to receive my data from the virtual COM port. I think I have to use a software where I can set up a really large input buffer for the serial port (e.g. LabVIEW). What do you think about this?

    At the moment I am also struggeling with choosing the right baudrate. Which would be the correct baudrate when I want to transmit data with 123 kBit/s over it?

    And finally, does it even make sense to change the UART_TX_BUF_SIZE and UART_RX_BUF_SIZE on the central to their maximum value?

    Thank you very much in advance. Any kind of feedback is always appreciated.

    Kind regards,

    Maria

    #Edit: Is there a proper way to debug, if the central prints all the data correct on the UART?

    Thank you again very much.

Reply
  • Hello Edvin,

    thank you very much for your quick reply.

    I already tried to debug this issue. But until your answer I did not really know what I should debug.

    As you suggested, I checked the length of the received data on central. I added the following to the code to the central:

    static void ble_nus_chars_received_uart_print(uint8_t * p_data, uint16_t data_len)
    {
        ...
        //DEBUG
        if (data_len != 205)
        {
            NRF_LOG_INFO("Too short");
        }
        ...
    }

    The "Too short" message gets never printed. So the length of the received data is correct.

    I figured also out that the printed data via Putty only gets messed up, if I do another heavy task on my pc. So maybe the data in the serial port is correct but putty cant print the data out properly, because it does not always get the required resources from windows. So maybe I have to use another data sink to receive my data from the virtual COM port. I think I have to use a software where I can set up a really large input buffer for the serial port (e.g. LabVIEW). What do you think about this?

    At the moment I am also struggeling with choosing the right baudrate. Which would be the correct baudrate when I want to transmit data with 123 kBit/s over it?

    And finally, does it even make sense to change the UART_TX_BUF_SIZE and UART_RX_BUF_SIZE on the central to their maximum value?

    Thank you very much in advance. Any kind of feedback is always appreciated.

    Kind regards,

    Maria

    #Edit: Is there a proper way to debug, if the central prints all the data correct on the UART?

    Thank you again very much.

Children
  • Hello Maria,

    Maria_19 said:
    The "Too short" message gets never printed. So the length of the received data is correct.

     

    Ok, so I guess we can eliminate the data disappearing before reaching the central then.

     I guess it would make sense since the central is the one who has the must UART throughput. 

     

    Maria_19 said:
    Which would be the correct baudrate when I want to transmit data with 123 kBit/s

     115200 is 115kbit/s, so that is too slow. 460800 should be enough for a steady 123000bit\s, but please be aware that you are not necessarily seeing a "steady" 123kbps. You get your data in chunks with size whatever your message size is. 

    What function do you use to print your data? Are you using the unmodified ble_app_uart_c implementation? Perhaps you can zip it and upload it here, because you have at least made that sample support multiple connections, so you probably did some changes. Perhaps I can have a look.

    Your computer shouldn't struggle to keep up with a UART, but keep in mind that UART is a fairly slow protocol. Also, since your data is coming in bulks, I would try to increase the baudrate to 1000000, and increase the UART_TX_BUF_SIZE, but whether or not that helps depends on the rest of the implementation. What will happen if you get two interrupts fairly close (before the first is done printing)?

    BR,

    Edvin

  • Hello Edvin,

    thank you very much for your answer.

    115200 is 115kbit/s

    So it seems that my calculations were correct. However, I increased the baudrate to 1000000 to get the most performance out of it. 

    What function do you use to print your data?

    I am using the default function for printing characters in the UART from the ble_app_uart_c example: 

    static void ble_nus_chars_received_uart_print(uint8_t * p_data, uint16_t data_len)
    {
        ret_code_t ret_val;
    
        //DEBUG
        if (data_len != 205)
        {
            NRF_LOG_INFO("Too short");
        }
        
        NRF_LOG_DEBUG("Receiving data.");
        NRF_LOG_HEXDUMP_DEBUG(p_data, data_len);
    
        for (uint32_t i = 0; i < data_len; i++)
        {
            do
            {
                ret_val = app_uart_put(p_data[i]);
                if ((ret_val != NRF_SUCCESS) && (ret_val != NRF_ERROR_BUSY))
                {
                    NRF_LOG_ERROR("app_uart_put failed for index 0x%04x.", i);
                    APP_ERROR_CHECK(ret_val);
                }
            } while (ret_val == NRF_ERROR_BUSY);
        }
    }

    Is there an alternative?

    Are you using the unmodified ble_app_uart_c implementation?

    No. The code is based on the nrf-52-ble-app-uart-c-multilink example (from here: github.com/.../nrf52-ble-app-uart-c-multilink). I also added the wireless timer synchronization functionality to it. 

    But the way how the data gets printed on the UART is the same as in the original "ble_app_uart_c" example.

    Perhaps you can zip it and upload it here

    nrf52-ble-app-uart-c-multilink-master_with_timesync_2.zip

    I am using pca10056

    increase the UART_TX_BUF_SIZE

    I increased this buffer to the maximum possible. (32768)

    What will happen if you get two interrupts fairly close (before the first is done printing)?

    To be honest, I do not really know that at the moment. But I will check that out.

    Current Status:

    I wrote a small application in LabVIEW which generates a message, when the received data over the COM-Port does not contain 205 same elements in a row.

    After 1-3 minutes I receive that messages and some data was lost. So it is definitely the UART on the Central which drops some bytes.

    Can you recommend me any settings of the UART which could fix that?

    Do you have suggestions?

    Thank you soo much in advance :) 

    Kind regards,

    Maria

  • Maria_19 said:
    After 1-3 minutes I receive that messages and some data was lost. So it is definitely the UART on the Central which drops some bytes.

     Was this while using baudrate = 1 000 000?

    Is your only option to use UART? Is the product going to be communicating with a computer or another MCU?  If you are using another MCU, I would recommend SPI, as it is a lot faster. 

     

    Maria_19 said:

    I wrote a small application in LabVIEW which generates a message, when the received data over the COM-Port does not contain 205 same elements in a row.

    After 1-3 minutes I receive that messages and some data was lost. So it is definitely the UART on the Central which drops some bytes.

     How is that data sent? from BLE interrupts, or are you generating this UART data from your central application?

    Can you try to use your RX events to just copy the data into separate buffers, and use some flag to print these buffers over UART and see if that helps on the situation?

    If that doesn't work, it may be that you need to look into flow control. Are you using the DKs programmer to transfer the UART data, or another external UART device?

  • Was this while using baudrate = 1 000 000?

    Yes.

    Is your only option to use UART?

    This product is going to be communicating with a computer. So "UART over USB" of the DK would be the only option?

    How is that data sent?

    From BLE interrupts. The data is sent whenever there is a BLE_NUS_C_EVT_NUS_TX_EVT. This is the snippet of the ble_nus_c_evt_handler:

            case BLE_NUS_C_EVT_NUS_TX_EVT:
                ble_nus_chars_received_uart_print(p_ble_nus_evt->p_data, p_ble_nus_evt->data_len);

    Can you try to use your RX events to just copy the data into separate buffers, and use some flag to print these buffers over UART and see if that helps on the situation?

    To be honest, that sounds a bit complicated, but I will see if I can do that.

    Are you using the DKs programmer to transfer the UART data, or another external UART device?

    I am using the DKs programmer. 

    look into flow control

    I have a few questions regarding the flow control:

    1. General question: The nRF52840DK has a UART<-> USB bridge. Can I use flow control (RTS/CTS) in this case? Is there flow control available? (RTS and CTS would be extra pins on a RS-232 interface)

    2. Can I achieve flow control with the following steps?:

    # .flow_control = APP_UART_FLOW_CONTROL_ENABLED

    # Enable flow control on my pc´s application with RTS/CTS

    3. Do I need more to proper use flow control?

    Thank you very much in advance.

    Maria

  • Without being able to pinpoint exactly what is happening, I believe it is related to all your data coming in chunks, and not in a streamlined flow. If two (or possibly more) chunks are too close to one another in time, then it will start to struggle. What will happen if you receive data too fast? If you are processing the data in the BLE interrupts, you may loose some interrupts, and if you process them from the main context, then you will risk the buffers being overflown. 

    Basically, you can't rely on remaining in an interrupt for too long if you expect a lot of interrupts in your application, so you need some way to offload the UART handling to your main context, e.g. by copying the data to some other buffers in the events, and then process these buffers (send them over UART) in your main loop. 

    I am not sure how you do all your tests, and what your test scripts are doing. You say that they are looking for 200 something of the same character. Perhaps you are just trying to print 200 of the same character from the interrupt? What about doing something like this

     (pseudo code):

    #define MAX_CONNECTIONS 5
    #define MAX_BUFFER_LEN 247
    uint8_t all_my_data[MAX_CONNECTIONS][MAX_BUFFER_LEN];
    uint8_t buffer_free[MAX_CONNECTIONS];
    
    static void nus_event_handler(p_ble_evt p_evt)
    {
        // Assuming that the length of an incoming buffer is always 247. If not, keep a separate table where you specify the length.
        uint8_t conn_handle = p_ble_evt->evt...conn_handle;
        if (buffer_free[conn_handle] = true)
        {
            buffer_free[conn_handle] = false;
            for (uint8_t i=0; i<247 /*length*/; i++)
            {
                all_my_data[conn_handle][i] = p_evt->evt...p_data[i];   //copying the buffer.
            }
        }
        else
        {
            NRF_LOG_INFO("Not able to process data on conn_handle %d", conn_handle);
            //figure out what to do in this scenario.
        }
    }
    
    static void process_uart(void)
    {
        ret_code_t err_code;
        for(uint8_t i=0; i<MAX_CONNECTIONS; i++)
        {
            if (buffer_free[i] == false)
            {
                for(uint8_t j=0; j<247 /*length*/; j++)
                {
                    do
                    {
                        err_code = app_uart_put(all_my_data[i][j]);
                        if (err_code != NRF_ERROR_BUSY)
                        {
                            APP_ERROR_CHECK(err_code);
                        }
                    }while (err_code == NRF_ERROR_BUSY);
                }
                //done with one buffer.
                buffer_free[i] = true;
            }
        }
    }
    
    int main(void)
    {
        init_everything();
        ...
        
        while(true)
        {
            process_uart();
            sd_app_evt_wait();
        }
    }

    By default, the ble_app_uart_c example doesn't really handle this that thoroughly, because it is not intended for this. It is merely a simple example showing how to pass data over BLE.

    BR,
    Edvin

Related