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

ble_app_uart_c losing characters

Hello again Nordic World.

We are slowly attempting to transition from the nRF51 environment to nRF52.  We are starting, not with development environment for our product, (it will remain at the 51 leve for now) but with the replacement of our nRF51 Dongles, which we use with the ble_app_uart_c code as a terminal interface to our board for testing.  We are attempt in to replace the nRF51 Dongle with the nRF52 DK.

The problem is that the ble_app_uart_c code from the 15.2 SDK, running on the 52 DK is dropping characters.   Please review the following snippets and note the alignment and missing characters which always seem to get lost in the 3rd column:

3689 4100 419 3689 4123 0 0 2 4100 4193 0 3689 4123 0 4100 3400 0 4000 3300 0 1 1 0 0 5190 4734 1
3682 4100 4193 3682 4123 -7 0 2 4100 4193 0 3682 4123 0 4100 3400 0 4000 3300 0 1 1 0 0 5191 4735 1
3682 4100 4193 3682 4123 0 0 2 4100 4193 0 3682 4123 0 4100 3400 0 4000 3300 0 1 1 0 0 5192 4736 1
3682 4100 4193 3682 4123 0 0 2 4100 4193 0 3682 4123 0 4100 3400 0 4000 3300 0 1 1 0 0 5193 4737 1
3682 4100 4193 3682 4123 0 0 2 4100 4193 0 3682 4123 0 4100 3400 0 4000 3300 0 1 1 0 0 5194 4738 1
3689 4100 4193 3682 4123 7 0 2 4100 4193 0 3682 4123 0 4100 3400 0 4000 3300 0 1 1 0 0 5195 4739 1
3682 4100 4193 3682 4123 -7 0 2 4100 4193 0 3682 4123 0 4100 3400 0 4000 3300 0 1 1 0 0 5196 4740 1
3682 4100 4193 3682 4123 0 0 2 4100 4193 0 3682 4123 0 4100 3400 0 4000 3300 0 1 1 0 0 5197 4741 1
3689 4100 4193 3682 4123 7 0 2 4100 4193 0 3682 4123 0 4100 3400 0 4000 3300 0 1 1 0 0 5198 4742 1
3682 4100 4193 3682 4123 -7 0 2 4100 4193 0 3682 4123 0 4100 3400 0 4000 3300 0 1 1 0 0 5199 4743 1
3689 4100 419 3682 4123 7 0 2 4100 4193 0 3682 4123 0 4100 3400 0 4000 3300 0 1 1 0 0 5200 4744 1
3682 4100 4193 3682 4123 -7 0 2 4100 4193 0 3682 4123 0 4100 3400 0 4000 3300 0 1 1 0 0 5201 4745 1
3682 4100 4193 3682 4123 0 0 2 4100 4193 0 3682 4123 0 4100 3400 0 4000 3300 0 1 1 0 0 5202 4746 1
3682 4100 4193 3682 4123 0 0 2 4100 4193 0 3682 4123 0 4100 3400 0 4000 3300 0 1 1 0 0 5203 4747 1

Here is the exact same information as printed by the 51 Dongle under SDK 12.3:  Note the perfect alignment and no missing characters.

3710 4100 4193 3710 4123 0 0 2 4100 4193 0 3710 4123 0 4100 3400 0 4000 3300 0 1 1 0 0 4861 4405 1
3710 4100 4193 3710 4123 0 0 2 4100 4193 0 3710 4123 0 4100 3400 0 4000 3300 0 1 1 0 0 4862 4406 1
3710 4100 4193 3710 4123 0 0 2 4100 4193 0 3710 4123 0 4100 3400 0 4000 3300 0 1 1 0 0 4863 4407 1
3710 4100 4193 3710 4123 0 0 2 4100 4193 0 3710 4123 0 4100 3400 0 4000 3300 0 1 1 0 0 4864 4408 1
3710 4100 4193 3710 4123 0 0 2 4100 4193 0 3710 4123 0 4100 3400 0 4000 3300 0 1 1 0 0 4865 4409 1
3710 4100 4193 3710 4123 0 0 2 4100 4193 0 3710 4123 0 4100 3400 0 4000 3300 0 1 1 0 0 4866 4410 1
3710 4100 4193 3710 4123 0 0 2 4100 4193 0 3710 4123 0 4100 3400 0 4000 3300 0 1 1 0 0 4867 4411 1
3710 4100 4193 3710 4123 0 0 2 4100 4193 0 3710 4123 0 4100 3400 0 4000 3300 0 1 1 0 0 4868 4412 1
3710 4100 4193 3710 4123 0 0 2 4100 4193 0 3710 4123 0 4100 3400 0 4000 3300 0 1 1 0 0 4869 4413 1
3710 4100 4193 3710 4123 0 0 2 4100 4193 0 3710 4123 0 4100 3400 0 4000 3300 0 1 1 0 0 4870 4414 1
3710 4100 4193 3710 4123 0 0 2 4100 4193 0 3710 4123 0 4100 3400 0 4000 3300 0 1 1 0 0 4871 4415 1
3710 4100 4193 3710 4123 0 0 2 4100 4193 0 3710 4123 0 4100 3400 0 4000 3300 0 1 1 0 0 4872 4416 1
3710 4100 4193 3710 4123 0 0 2 4100 4193 0 3710 4123 0 4100 3400 0 4000 3300 0 1 1 0 0 4873 4417 1
3710 4100 4193 3710 4123 0 0 2 4100 4193 0 3710 4123 0 4100 3400 0 4000 3300 0 1 1 0 0 4874 4418 1
3710 4100 4193 3710 4123 0 0 2 4100 4193 0 3710 4123 0 4100 3400 0 4000 3300 0 1 1 0 0 4875 4419 1
3710 4100 4193 3710 4123 0 0 2 4100 4193 0 3710 4123 0 4100 3400 0 4000 3300 0 1 1 0 0 4876 4420 1
3710 4100 4193 3710 4123 0 0 2 4100 4193 0 3710 4123 0 4100 3400 0 4000 3300 0 1 1 0 0 4877 4421 1
3710 4100 4193 3710 4123 0 0 2 4100 4193 0 3710 4123 0 4100 3400 0 4000 3300 0 1 1 0 0 4878 4422 1

Any ideas on where to search for and correct this problem is greatly appreciated.

Robin @ TL

Some additional information requested by stateside FAE:

The dropping is consistent in that the only characters dropped are the 16th, 17th, or possibly 18th (not know as both are spaces) in the first string sent.to each line.  However when it is dropped seems to be somewhat random.

The 52DK is connected form its u-usb connector on board to a USB-C on my computer by a short (~18") cable.

The only change is the hardware and the version of the unaltered example code running on each.

2 each 52DK's and 2 each cables have been tested.

Parents
  • Hello Robin,

    I don't know if I understood the 16th, 17th or 18th part, but it may be the same problem.

    Are you able to detect where in the path this information is lost? You are using the ble_app_uart_c, so I assume that the data is originally coming from another device over UART?

    Some source -> UART -> ble_app_uart -> BLE -> UART -> Computer.

    Is this correct?

    I don't know how large the chunks you are sending are, but if the entire line is sent in one message, the first character that is missing is the '3' in 4193, right? So that is the 14th character, right?

    So if you test the BLE link, that would be in the ble_nus_chars_received_uart_print() function in the ble_nus_c example, which is called from the BLE_NUS_C_EVT_NUS_TX_EVT event.

    Can you try to check whether the 4193 is missing the '3' there?

    static void ble_nus_chars_received_uart_print(uint8_t * p_data, uint16_t data_len)
    {
        ret_code_t ret_val;
    
        NRF_LOG_DEBUG("Receiving data.");
        NRF_LOG_HEXDUMP_DEBUG(p_data, data_len);
        if (p_data[12] != '3')      //p_data[12] contains the 13th element.
        {
            NRF_LOG_INFO("missing character");
        }
    
        for (uint32_t i = 0; i < data_len; i++)
        {
            ...

    Best regards,

    Edvin

  • Hello again Edvin,

    W/r/t the path the source is our custom board based on the ble_app_uart.  Our board does not use UART, so the path, as I understand it is is:

    my_board.ble_app_uart>ble>DK52.ble_app_uart_c>UART>computer.

    W/r/t missing characters, the paste function on this web page truncates whitespace, so you dont see all the problem.  Yes the first character missing is the 3 from the number 1493, in many cases it is one of the following spaces.  There are 2 spaces between each table entry, so the most common dropped characters are as I said, 16th, 17th, or 18th. Not sure about 17/18 since they are both spaces.  I have since found instances at another location where a digit is replaced by whitespace.

    Data chunk size for each transmission is 20 char including the null.  Each line is made up of several xmitted strings.

    With regard to your code to test, the data is not constant, so I cannot look for a fixed digit. I suppose I could change me code to send constant digits, but that a lot of work right now. 

    Also, where does the log message show in your code mod, the UART?

    I also want to make sure you understand that using the 51 Dongle, THERE ARE NOT PROBLEMS! So, it cannot be missing in the ble xmit, it would have to be a rcv issue in the 52DK.

    I kindly ask that you set up this test and debug, as I do not have the resource to do so easily, and I am certain that nothing in my code is causing the problem.  Can you do this please?  Here is some info on our board so you can emulate if you would:

    nRF51822 running under SDK 12.3,

    app is closely based on ble_app_uart, with string size counting, termination char detection, and a ring_buffer to resolve lost strings.  Her is a snippet of the send chars function:

    static void send_chars(const char * str)
    {
      if (connected)
      {
        uint8_t i = 0;
        while (str[i] != 0x00) 
        {
          i+=1;
        }
        tx_buffer_insert(&m_nus, (uint8_t*)str, i);
        tx_buffer_process();
      }
    }

    and the ring buffer:

    void tx_buffer_process(void)
    {
      if (m_tx_index != m_tx_insert_index)
      {
        uint32_t err_code;
        err_code = ble_nus_string_send(m_tx_buffer[m_tx_index].p_nus,
                                       m_tx_buffer[m_tx_index].p_data,
                                       m_tx_buffer[m_tx_index].length);
        if (err_code == NRF_SUCCESS)
        {
          ++m_tx_index;
          m_tx_index &= TX_BUFFER_MASK;
        }
      }
    }

    Any expeditious help you can offer is greatly appreciated. 

    As I mentioned, this code all works perfectly using and nRF51 dongle under SDK12.3. 

    We are in the process of setting up our production test workstations which, the intentions were, would each service up to 6 of our boards, each via a 52DK, for testing before final assembly.  Boards are due in next week, so I am sure you can understand my fright at not being ready at this point.  We would have continued using the 51 Dongle, but they are getting hard to find and the price is escalating because of that shortage.

    Thank you again,

    All expedience in the matter is greatly appreciated.

    Robin @ TL

  • Robin said:
    Also, where does the log message show in your code mod, the UART?

     That depends on your project settings, but in the ble_app_uart(_c) examples, the log is by default printing in the RTT Log. If you use Segger Embedded Studio, you will see these log messages in the IDE while you are debugging. If you are not debugging, or if you are using another IDE, you can use J-Link RTT Viewer.

    Is it possible for me to recreate the issue here in the office if I have an nRF52832 DK and an nRF51 dongle? Do you have the application that sends the strings that I can look for missing characters in, in addition to the application that you use on the DK?

    BR,
    Edvin

  • Hello Edvin,

    I see that somehow my response to this did not get posted, so here goes again:

    W/r/t the the IDE, I am using Keil, and not sure where to find the log with it as I have had no opportunity to use that feature as of yet.  Nor have I yet used the J-link RTT viewer. If you could give me a lead on using either of these options it would be greatly appreciated.

    W/r/t how you might be able to recreate this problem I would suggest you use a 51 based board running ble uart peripheral under SDK 12.3 and a 52 DK board ruing ble uart central under SDK 15.2 or 15.3. I am using Termite on a Windows 10 machine to view the characters sent by the peripheral.  Above are snippets of the send_chars routine and a circular buffer I am using to insure no lost groups.

    As mentioned when using the 51 dongle running the central under SDK 12.3 there are no issues and that is the only thing that changed.  The 52DK ruing the central under SDK 15.2 and 15.2 drop chars.

    Let me know what else may need from me to get moving and finding this "feature:.

    Sorry for the delay, I thought my last attempt at reply went through.

    Robin @ TL

     

  • Robin said:
    Nor have I yet used the J-link RTT viewer. If you could give me a lead on using either of these options it would be greatly appreciated.

     If you have installed J-Link on your computer (which I suspect that you may have done), you can find it under:

    C\Program_files (0x86)\SEGGER\JLink_VXX\JlinkRTTViewer.exe.

    If that is not installed, you can find it here:

    https://www.segger.com/products/debug-probes/j-link/tools/rtt-viewer/

    When you open it, you should be prompted with this window:

    Select the 3 dots to the right in "Specify Target Device" and select "Nordic Semi" from the dropdown menu to find the nRF52832_xxAA.

    These settings should be stored, so you shouldn't have to find "nRF52832_xxAA" from the list the next time you want to check the RTT Log.

    Maybe you can try to send a static string from the nRF51 dongle, so that you know what characters should be coming in? 

    I don't know how fast you are sending the strings from your dongle, so it is hard to reproduce. I did some testing just now, by typing in some strings in one Termite window (51 dongle), and they appear correctly in the other termite window (52 DK).

    So how fast are these messages being sent? Is the baudrate on the UART on the DK fast enough to keep up?

    Best regards,

    Edvin

  • Hello Edvin,

    W/r/t how fast, each line of chars is sent once per minute, but there is no delay between each set of 19 chars that make up each line, which I hope would not be a problem with the ring buffer catching at least a full line 

    When I have time, I will get used  to the segger tool and setup a test to send static chars from our 51 based board, then repeat from the 51 dongle.  But it will be a few days before I get there.  In the meanwhile I humbly ask that you set up to send lines the same size as mine, at once a minute and watch for drops.  If you want the ring buffer code and snippets of the string sending code let me know and I can up post them for you.

    Thank you again

    Robin @ TL

Reply
  • Hello Edvin,

    W/r/t how fast, each line of chars is sent once per minute, but there is no delay between each set of 19 chars that make up each line, which I hope would not be a problem with the ring buffer catching at least a full line 

    When I have time, I will get used  to the segger tool and setup a test to send static chars from our 51 based board, then repeat from the 51 dongle.  But it will be a few days before I get there.  In the meanwhile I humbly ask that you set up to send lines the same size as mine, at once a minute and watch for drops.  If you want the ring buffer code and snippets of the string sending code let me know and I can up post them for you.

    Thank you again

    Robin @ TL

Children
  • I have tried sending 19 chars once every second or so, but I don't see any drops in the log. Have you changed anything in the ble_app_uart_central project? What function do you use to print the UART strings?

  • Hello Edvin,

    Each line is multiple (6, I believe) 19 char sets per second.  Not exactly sur how many, as I am not at the computer right now.  Will check later and get back to you.

     Actually they are not  "strings", I strip off the null for compatibility with terminal software.  

    I have not modified the central code.

  • Hello Edvin,

    Back at the computer.  Seems to be my second (maybe first) home these days.  Here is the straight answer on how many chars are being sent per second.  Below is the code that send the chars that is executed once per minute.  As you can can see, there are 13 transmissions each second, but for when the hourly header is xmited which the doubles the rate.  The first 12 xmits have have 12 chars, and the 13th has 13 to account for the new line and carriage return.  Hope this helps.  We NEED this working soon and I am way to wrapped up in other concerns to get to chasing it down 

    Again, thanks for any and all help.

    Robin @ TL


      if ((loop_count % 60 == 0) & board_life_print_on)
      {
        sprintf(message_buffer, "BATT  CMIN  ");
        m_send_chars(message_buffer);
        sprintf(message_buffer, "CMAX  DMIN  ");
        m_send_chars(message_buffer);
        sprintf(message_buffer, "DMAX  RATE  ");
        m_send_chars(message_buffer);
        sprintf(message_buffer, "CHRG? LIFST ");
        m_send_chars(message_buffer);
        sprintf(message_buffer, "ICMIN ICMAX ");
        m_send_chars(message_buffer);
        sprintf(message_buffer, "ICCNT IDMIN ");
        m_send_chars(message_buffer);
        sprintf(message_buffer, "IDMAX IDCNT ");
        m_send_chars(message_buffer);
        sprintf(message_buffer, "LCMIN LCMAX ");
        m_send_chars(message_buffer);
        sprintf(message_buffer, "LCCNT LDMIN ");
        m_send_chars(message_buffer);
        sprintf(message_buffer, "LDMAX LDCNT ");
        m_send_chars(message_buffer);
        sprintf(message_buffer, "FULL? DISC? ");
        m_send_chars(message_buffer);
        sprintf(message_buffer, "CHRG? EMPT? ");
        m_send_chars(message_buffer);
        sprintf(message_buffer, "LFCNT CHCNT\r\n");
        m_send_chars(message_buffer);
        if (loop_count > 0)
        {
          nvram_write();
        }
        
      }
      if (board_life_print_on)
      {
        sprintf(message_buffer, "%4d  %4d  ", battery_state.charge, battery_state.min_charge);
        m_send_chars(message_buffer);
        sprintf(message_buffer, "%4d  %4d  ", battery_state.max_charge, battery_state.min_discharge);
        m_send_chars(message_buffer);
        sprintf(message_buffer, "%4d  %4d   ", battery_state.max_discharge, battery_state.charge_rate);
        m_send_chars(message_buffer);
        sprintf(message_buffer, "%4d  %4d ", (battery_state.charge_events CHECK_CHARGING) , batt_life_chk_state);
        m_send_chars(message_buffer);
        sprintf(message_buffer, "%5d %5d ", sys_cal_data.board_life.init_charge_min, sys_cal_data.board_life.init_charge_max);
        m_send_chars(message_buffer);
        sprintf(message_buffer, "%5d %5d ", sys_cal_data.board_life.init_charge_count, sys_cal_data.board_life.init_discharge_min);
        m_send_chars(message_buffer);
        sprintf(message_buffer, "%5d %5d ", sys_cal_data.board_life.init_discharge_max, sys_cal_data.board_life.init_discharge_count);
        m_send_chars(message_buffer);
        sprintf(message_buffer, "%5d %5d ", sys_cal_data.board_life.last_charge_min, sys_cal_data.board_life.last_charge_max);
        m_send_chars(message_buffer);
        sprintf(message_buffer, "%5d %5d ", sys_cal_data.board_life.last_charge_count, sys_cal_data.board_life.last_discharge_min);
        m_send_chars(message_buffer);
        sprintf(message_buffer, "%5d %5d ", sys_cal_data.board_life.last_discharge_max, sys_cal_data.board_life.last_discharge_count);
        m_send_chars(message_buffer);
        sprintf(message_buffer, "%5d %5d ", ((battery_state.charge_events CHECK_WAS_FULL) >> 4), 
                                          ((battery_state.charge_events CHECK_WAS_DISCHARGING) >> 3));
        m_send_chars(message_buffer);
        sprintf(message_buffer, "%5d %5d ",  ((battery_state.charge_events CHECK_WAS_CHARGING) >> 2), 
                                          ((battery_state.charge_events CHECK_WAS_EMPTY) >> 1));
        m_send_chars(message_buffer);
        sprintf(message_buffer, "%5d %5d\r\n", sys_cal_data.board_life.life_count, charge_count);
        m_send_chars(message_buffer);
      }
    
     

  • Ok. So this is the code that transmits the strings over BLE from the peripheral to the central, right?

    is m_send_chars() the same function as the function send_chars(), which you sent in an earlier snippet in this ticket? Can you either confirm this or send this function? If it is the send_chars() what does your tx_buffer_insert() and tx_buffer_process() look like? It may be other "middle layer" processing functions, so please include functions all the way down to the softdevice calls. What I am looking for in particular is the sd_ble_gatts_hvx() call. How is the return value for this function handled?

  • Hello Edvin,

    Yes, this is the code that gets characters from our app to ble_nus_string_send, via the tx_buffer.

    Yes, m_send_chars() and send_chars() are the same function.

    Here is the chain that I have touched:

    from our app.c:

    ...
            m_send_chars(reply);
    ...

    to main.c:

    ...
    static void send_chars(const char * str)
    {
      if (connected)
      {
        uint8_t i = 0;
        while (str[i] != 0x00) 
        {
          i+=1;
        }
        tx_buffer_insert(&m_nus, (uint8_t*)str, i);
        tx_buffer_process();
      }
    }
    ...

    then on to tx_buffer.c

    #include "ble_nus.h"
    #include "tx_buffer.h"
    #include "string.h"
     
    #define  TX_BUFFER_MASK 31
    
    static tx_message_t m_tx_buffer[32];                                       /**< Transmit buffer for messages to be transmitted to the Notification Provider. */
    static uint32_t     m_tx_insert_index = 0;                                 /**< Current index in the transmit buffer where the next message should be inserted. */
    static uint32_t     m_tx_index        = 0;                                 /**< Current index in the transmit buffer from where the next message to be transmitted resides. */
    
    void tx_buffer_init(void)
    {
      memset(m_tx_buffer, 0, sizeof(m_tx_buffer));
      m_tx_index = 0;
      m_tx_insert_index = 0;
    }
    
    void tx_buffer_insert(ble_nus_t * nus, uint8_t * p_string, uint16_t length)
    {
      memset(&(m_tx_buffer[m_tx_insert_index]), 0, sizeof(tx_message_t));
      memcpy(m_tx_buffer[m_tx_insert_index].p_data, p_string, length);
      m_tx_buffer[m_tx_insert_index].length = length;
      m_tx_buffer[m_tx_insert_index].p_nus = nus;
      m_tx_insert_index++;
      m_tx_insert_index &= TX_BUFFER_MASK;
    }
    
    void tx_buffer_process(void)
    {
      if (m_tx_index != m_tx_insert_index)
      {
        uint32_t err_code;
        err_code = ble_nus_string_send(m_tx_buffer[m_tx_index].p_nus,
                                       m_tx_buffer[m_tx_index].p_data,
                                       m_tx_buffer[m_tx_index].length);
        if (err_code == NRF_SUCCESS)
        {
          ++m_tx_index;
          m_tx_index &= TX_BUFFER_MASK;
        }
      }
    }
    

    Here is untouched code from SDK 12.3 to complete the chain:

    From ble_nus.c:

    uint32_t ble_nus_string_send(ble_nus_t * p_nus, uint8_t * p_string, uint16_t length)
    {
        ble_gatts_hvx_params_t hvx_params;
    
        VERIFY_PARAM_NOT_NULL(p_nus);
    
        if ((p_nus->conn_handle == BLE_CONN_HANDLE_INVALID) || (!p_nus->is_notification_enabled))
        {
            return NRF_ERROR_INVALID_STATE;
        }
    
        if (length > BLE_NUS_MAX_DATA_LEN)
        {
            return NRF_ERROR_INVALID_PARAM;
        }
    
        memset(&hvx_params, 0, sizeof(hvx_params));
    
        hvx_params.handle = p_nus->rx_handles.value_handle;
        hvx_params.p_data = p_string;
        hvx_params.p_len  = &length;
        hvx_params.type   = BLE_GATT_HVX_NOTIFICATION;
    
        return sd_ble_gatts_hvx(p_nus->conn_handle, &hvx_params);
    }
    

    From ble_gatts.h

    SVCALL(SD_BLE_GATTS_HVX, uint32_t, sd_ble_gatts_hvx(uint16_t conn_handle, ble_gatts_hvx_params_t const *p_hvx_params));
    

    Thanks,

    Robin @ TL

Related