UART receive timeout

Ello all,
I have a simple UART system, where I sent a poll message to a device and wait for a response. Like so:

  uart1.Write(pollPacket);//Send the per-frame poll msg
            
            var rxBuffer = new byte[1024];
            while (uart1.BytesToRead == 0) { };//Wait for device to respond
            
            if (uart1.BytesToRead > 0)
            {
                int tempToRead = uart1.BytesToRead;
                var bytesReceived = uart1.Read(rxBuffer, uartOffset, uart1.BytesToRead);

                // Debug.WriteLine("recd: " + bytesReceived);
                    for (int i = uartOffset; i < tempToRead; i++)
                    {
                        if (rxBuffer[i] == '\n')
                        {
                            uartOffset = 0;
                            
                            byte[] truncArray = new byte[uartOffset];
                            Array.Copy(rxBuffer, truncArray, truncArray.Length);

                            parsePacket(truncArray);//Parse the data
                        }
                        else
                        {
                            uartOffset = i + 1;
                        }
                }
            }

This works well if the client device is actually connected. If the device is not plugged in, however, this will block forever.

What would be a reasonable way to listen, and if the device doesn’t respond within say 10ms, break out and continue?

EDIT: I’m adding that this may happen at several hundred Hz, so it’d prefer not to use the event listener.

Do you know where it is hanging? On which line?

In general, the many checks of BytesToRead isn’t ‘safe’. The value can change for each call. Better to capture it once in a variable and use the variable instead of calling the getter repeatedly. That’s probably not why it’s hanging, but it isn’t a good pattern.

This would be the line in question:

while (uart1.BytesToRead == 0) { };//Wait for device to respond

Of course, it’s easy to see why it would block with no incoming data. I’m just trying to ensure I capture all the data.

In this case, at least the way I’ve written it, I don’t necessarily care that BytesToRead is different each call. I’m just reading everything, iterating through the bytes received, and looking for that \n marker.

Perhaps that is unnecessary due to how TinyCLR has implemented UART, but I’m approaching it as though it was a standard UART buffer.

Ah! I misunderstood what you were asking. To break out after X mS, Just capture the time before entering the loop, and again inside the loop. Subtract the time you got in the loop from what you captured before entering the loop, and ‘break’ when that exceeds your timeout.

this microsecond could be problematic if your data rate is high enough. Your Read() call should only read tempToRead bytes not what is in the UART at that point, as if another byte appeared between the two checks you can have the \n in the rxBuffer but not be able to reach it in the FOR loop. Also to be more robust (in case a full message hadn’t arrived when you exited the waiting loop) you probably need a WHILE loop not an IF loop ? Without knowing your data I have no idea if that’s a real concern or not.

Thanks! Got it, so I have this then:

                var end = DateTime.Now;
            while (uart1.BytesToRead == 0) {
                TimeSpan diff = DateTime.Now - end;
                if(diff.TotalMilliseconds > 10){
                    break;//10ms timeout on the serial read
                }
            };

Seems like it should work.

@Brett
The data will be 115200 baud, anywhere from 4 to 500 bytes transferred at a time. Not all the data is ASCII, but it’s always terminated with a newline regardless. This is over RS232.

I now agree with changing the if to a while.

1 Like

You will still block other threads if you don’t yield with a Thread.Sleep()

What I do is call Thread.Sleep(0); when the BytesToRead returns 0 and at the same time, check if the data timeout has expired and then exit the loop.

3 Likes

Highly recommended adding Thread.sleep(), at least reduce power :)).