Beware that this post is related to an SDK in maintenance mode
More Info: Consider nRF Connect SDK for new designs
This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

Libuarte and GPS

Hello everyone,

I ran into an issue where I just can't get my head around.

I followed the Libuarte example, and I thought I modified it "correctly" to fit my application where I am connected to a GPS device emitting a number of messages every second.

I was able to correctly receive the data from the GPS and write it to a variable, but I run into either an NRF_BUSY, NRF_SUCCESS or NRF_NO_MEM error or sometimes into a hard fault error when I try and send a command to the unit. (it has the form of uint8_t RMC[] = "$PCAS03,0,0,0,0,1,0,0,0,0,0,,,0,0*03\r\n" - so there must be something I am missing and doing wrong.

Could someone please help me with an example of the uart_event_handler to correctly receive the output from the device( and possible how to look for a specific message in the output) and how to send commands to the device from somewhere else in the code.

Currently, my uart_event handler looks like this:

NRF_LIBUARTE_ASYNC_DEFINE(libuarte, 0, 0, 0, NRF_LIBUARTE_PERIPHERAL_NOT_USED, 255, 3);


typedef struct {
    uint8_t * p_data;
    uint32_t length;
} buffer_t;

NRF_QUEUE_DEF(buffer_t, m_buf_queue, 10, NRF_QUEUE_MODE_NO_OVERFLOW);


uint8_t uart0_array[500]; //buffer to copy uart data to

void uart_event_handler(void * context, nrf_libuarte_async_evt_t * p_evt)
{
    nrf_libuarte_async_t * p_libuarte = (nrf_libuarte_async_t *)context;
    static uint8_t index = 0;
    switch(p_evt->type)
    { 
      case NRF_LIBUARTE_ASYNC_EVT_RX_DATA:
      {
          sprintf(uart0_array,"%s",p_evt->data.rxtx.p_data);
            
          nrf_libuarte_async_rx_free(p_libuarte, p_evt->data.rxtx.p_data, p_evt->data.rxtx.length);
      }break;
      case NRF_LIBUARTE_ASYNC_EVT_ERROR:
      {
          //printf("\nUART ERROR: %d", p_evt->data.errorsrc);
      }break;
      case NRF_LIBUARTE_ASYNC_EVT_OVERRUN_ERROR:
      {
          //printf("\nUART OVERRUN ERROR");
      }break;
      
    }
}

And I am able to see the data in uart0_array but it obviously overflows since I don't do any processing on it now.

When I used this code with a device that does not emit every second that caused the errors, I was able to send over UART with the following function I used:

//Function to send via UART
void send_UART(char *sdata, int len)
{
  for (int i = 0; i < len; i++)
  {
    ret_code_t ret;
    ret = nrf_libuarte_async_tx(&libuarte, &sdata[i], 1);
    
    APP_ERROR_CHECK(ret);
    nrf_delay_ms(1);
  }
}

But this does not work with the GPS as it causes anyone of that errors. I tried to increase the timeout of the Uart as well but to no avail.



So to recap:

I require help/examples of how to send a typical command like "$PCAS03,0,0,0,0,1,0,0,0,0,0,,,0,0*03\r\n" to the GPS device over UART, and receive a typical message like follows every second

$GNRMC,164352.97,V,,,,,,,060421,,,N,V*11
$GNGGA,164352.97,,,,,0,04,8.0,,M,,M,,*7D
$GNGLL,,,,,164352.97,V,N*5D
$GPGSV,16,,,31,26,,,20,27,,,32,1*6D

where I would for instance only want to save the "$GNGLL,,,,,164352.97,V,N*5D" message so if you can help me to only extract a message that starts with for instance $GNGLL and ends with /r/n that would be a bonus and great, and I would forever be thankful for your help.


Any help would be appreciated.

Thank you for your time.

Kind regards,

Darius

Parents
  • Hi,

    Why do you send the data one and one byte at a time?

    You should be able to write the full TX buffer like this:

    //Function to send via UART
    void send_UART(char *sdata, int len)
    {
        ret_code_t ret;
        ret = nrf_libuarte_async_tx(&libuarte, sdata, len);
        
        APP_ERROR_CHECK(ret);
    }

    Note that you can only start a single transfer at a time, if there is already an ongoing transfer, you will get the NRF_ERROR_BUSY return code. 

    You can use a do-while to do a blocking wait for the previous transfer to finish:

    //Function to send via UART
    void send_UART(char *sdata, int len)
    {
        ret_code_t ret;
        do {
            ret = nrf_libuarte_async_tx(&libuarte, sdata, len);
        while (ret == NRF_ERROR_BUSY);
        
        APP_ERROR_CHECK(ret);
    }

    Not sure where you get the NRF_ERROR_NO_MEM return code from, this does not seem to be a valid return code from the nrf_libuarte_drv_tx() function. 

    To look for a substring in a string, you can use strstr() function. Note that the string function may take long time to process, you should consider doing this outside of the interrupt/event handler, to allow other interrupts to be handled while processing strings.

    Best regards,
    Jørgen

Reply
  • Hi,

    Why do you send the data one and one byte at a time?

    You should be able to write the full TX buffer like this:

    //Function to send via UART
    void send_UART(char *sdata, int len)
    {
        ret_code_t ret;
        ret = nrf_libuarte_async_tx(&libuarte, sdata, len);
        
        APP_ERROR_CHECK(ret);
    }

    Note that you can only start a single transfer at a time, if there is already an ongoing transfer, you will get the NRF_ERROR_BUSY return code. 

    You can use a do-while to do a blocking wait for the previous transfer to finish:

    //Function to send via UART
    void send_UART(char *sdata, int len)
    {
        ret_code_t ret;
        do {
            ret = nrf_libuarte_async_tx(&libuarte, sdata, len);
        while (ret == NRF_ERROR_BUSY);
        
        APP_ERROR_CHECK(ret);
    }

    Not sure where you get the NRF_ERROR_NO_MEM return code from, this does not seem to be a valid return code from the nrf_libuarte_drv_tx() function. 

    To look for a substring in a string, you can use strstr() function. Note that the string function may take long time to process, you should consider doing this outside of the interrupt/event handler, to allow other interrupts to be handled while processing strings.

    Best regards,
    Jørgen

Children
  • Thank you, Jorgen, that worked:)

    Is there a way to handle the data up to the "\n" character? so that it keeps filling the uart0_array[] with the data from the GPS until the "/n" character is reached and then break or copy the whole string to another buffer so I can process it?

    Because I tried the following (sprintf() and memcpy()) and get the string up to "/n" but after3-4 seconds I get a HardFault_Handler: error.


    NRF_LIBUARTE_ASYNC_DEFINE(libuarte, 0, 0, 0, NRF_LIBUARTE_PERIPHERAL_NOT_USED, 255, 5);
    
    
    typedef struct {
        uint8_t * p_data;
        uint32_t length;
    } buffer_t;
    //NRF_QUEUE_DEF(buffer_t, m_buf_queue, 10, NRF_QUEUE_MODE_NO_OVERFLOW);
    NRF_QUEUE_DEF(buffer_t, m_buf_queue, 10, NRF_QUEUE_MODE_NO_OVERFLOW);
    
    
    uint8_t uart0_array[500]; //buffer to copy uart data to
    
    
    void uart_event_handler(void * context, nrf_libuarte_async_evt_t * p_evt)
    {
        nrf_libuarte_async_t * p_libuarte = (nrf_libuarte_async_t *)context;
        
        static uint8_t data_array[100];
        static uint16_t index = 0;
        
        switch(p_evt->type)
        { 
          case NRF_LIBUARTE_ASYNC_EVT_RX_DATA:
          {
            strcat(data_array,p_evt->data.rxtx.p_data);
            index++;
    
          if ((data_array[index] == '\n') ||
              (data_array[index - 1] == '\r'))
          {
    
              //memcpy(uart0_array,data_array,sizeof(data_array));
              sprintf(uart0_array, "%s", data_array);
              nrf_libuarte_async_rx_free(p_libuarte, p_evt->data.rxtx.p_data, p_evt->data.rxtx.length);
                
    
    
              index = 0;
          }
    
    
              //sprintf(byte,"%s",p_evt->data.rxtx.p_data);
              //strcat(uart0_array,p_evt->data.rxtx.p_data);
              
              //nrf_libuarte_async_rx_free(p_libuarte, p_evt->data.rxtx.p_data, p_evt->data.rxtx.length);
          }break;
          case NRF_LIBUARTE_ASYNC_EVT_ERROR:
          {
              //printf("\nUART ERROR: %d", p_evt->data.errorsrc);
          }break;
          case NRF_LIBUARTE_ASYNC_EVT_OVERRUN_ERROR:
          {
              //printf("\nUART OVERRUN ERROR");
          }break;
          
        }
    }


    Thanks for your help so far.

  • The only thing I can think of is to use strchr() to look for the first instance of '\n' character, and copy the string from start og buffer until the pointer where the '\n' character was found using memcpy().

Related