Bob Dole
Bob Dole

Reputation: 53

strstr issue in C

I'm using strstr to search for the beginning and end of brackets so I can filter out incorrect entries sent on the UART from information I actually want/need.

I have my UART interrupt storing characters into an array and incrementing the counter. Below is the code in my main function:

  volatile char     UART_RX_Buffer[128]; // updated in UART
  volatile uint16_t UART_Byte_Counter = 0; // updated in UART
  
  while (1)
  {
        if(strstr(&UART_RX_Buffer[0], "{") != NULL)
        {
            if(strstr(&UART_RX_Buffer[0], "}") != NULL)
            {
                char *ret;
                int startindex;

                ret = strstr(&UART_RX_Buffer[0], "{");
                startindex = (ret - UART_RX_Buffer);

                // Clear out UART array and counter
                char Empty_Buffer[128] = {0};
                memcpy (UART_RX_Buffer, Empty_Buffer, sizeof(UART_RX_Buffer));
                UART_Byte_Counter = 0;
            }
        }
  }

So if I send it, say 012{345,678}, it will set the startindex as 3 (which is what I want). It's successfully entering the loop, setting startindex, and clearing the UART_RX_Buffer and counter correctly.

What's odd is it only works the first time through. If I send it a second time, I can see the buffer is holding the same data as the first time around . . . but it's not entering the first loop, recognizing the "{" character. I can see an issue if I'm not doing things correctly with the UART interrupt and the buffer isn't being filled correctly . . . but that doesn't appear to be the case. I don't really understand why I would get different results with what appears to be the same data (especially if I can confirm the data is in the UART buffer when trying to run that line).

Clearly I'm missing something. Any ideas what might be causing this? If it helps, it's an STM32L4R5ZI chip (though I would assume this is a C issue and not device specific). I'll include screenshots from the debugger all the same in case it helps. Thanks in advanceenter image description here!

Upvotes: 1

Views: 237

Answers (2)

Lundin
Lundin

Reputation: 215305

The root of all your problems is nothing mentioned so far. But merely the fact that strings send over UART, from example through a PC terminal, are unlikely to be null terminated. They are not C strings! Far more likely, they are terminated by a single line feed '\n' or in case of Windows perhaps carriage return + line feed, \r\n.

This means that standard library string handling functions cannot be used. Forget about strstr, strchr and similar. You can only use those on null terminated strings (aka "C strings").

If you know the length of the data received - you should know this by counting the bytes received - you could use memchr. Or alternatively just parse the data manually, which might be the most efficient if you are looking for a particular format anyway.

But before doing anything else, figure out how you know where a string ends. Is there line feed, null termination, perhaps a size counter in the UART packet etc. Don't just assume one particular format - programming cannot be done through guessing.


Furthermore, this code here is not "thread safe", or interrupt safe in this case. You cannot read directly from a buffer that an ISR is filling up at any moment - not without some means of synchronization. A busy flag, disabling the ISR temporarily etc. Interrupts are difficult to work with and sometimes it seems that barely any programmer implements them correctly.

There are three sensible ways to implement UART Rx functionality:

  • DMA (highly recommended). If the MCU supports this, it solves all problems with buffering and repeated interrupts, and makes "thread safety" much easier. You can generally work with one part of the DMA buffer while the UART works with another part.
  • Ring buffers + ISR. The old school way when you've got an UART peripheral with limited hardware buffers. You implement a ring buffer ADT, optionally with thread-safety support built in, and all the ISR does is to add to it.
  • Buffer swaps + ISR. The simplest way is to allocate two buffers (double buffering) and have a pointer point at the active one. The ISR will fill one buffer while the background program works with the other one. When the ISR has filled the buffer, you swap them by changing where the pointer points at. You'll never do slow memcpy that way.

Upvotes: 2

gulpr
gulpr

Reputation: 4618

strstr issue in c

No, it is always your code issue. strstr, the compiler, linker and the silicon are OK.

(especially if I can confirm the data is in the UART buffer when trying to run that line).

But it is not the data you think you have :)

enter image description here The C sting is empty as it starts with the null terminating character. strstr sees the empty string. Your buffer-filling algorithm is definitely broken.

I can even tell you that you have not updated the index or pointer of the current buffer position as '0' goes to the place where the previous transition ended: enter image description here

Upvotes: 4

Related Questions