Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C#: reading serial port - BytesToRead

Tags:

c#

serial-port

I am modifying a C# based UI that interfaces to a small PIC microcontroller tester device.

The UI consists of a couple buttons that initiates a test by sending a "command" to the microcontroller via a serial port connection. Every 250 milliseconds, the UI polls the serial interface looking for a brief message comprised of test results from the PIC. The message is displayed in a text box.

The code I inherited is as follows:

try
{
    btr = serialPort1.BytesToRead;
    if (btr > 0)
        Thread.Sleep(300);
    btr = serialPort1.BytesToRead;
    if (btr > 0)
    {
        Thread.Sleep(300);
        btr = serialPort1.BytesToRead;

        numbytes = serialPort1.Read(stuffchar, 0, btr);
        for (x = 0; x < (numbytes); x++)
        {
            cc = (stuffchar[x]);
            stuff += Convert.ToString(Convert.ToChar((stuffchar[x])));
        }

What would be the rationale for the first several lines consisting of three calls to BytesToRead and two 300 millisecond sleep calls before finally reading the serial port? Unless I am interpreting the code incorrectly, any successful read from the serial port will take more than 600 milliseconds, which seems peculiar to me.

like image 453
TomWr Avatar asked Sep 27 '22 05:09

TomWr


1 Answers

It is a dreadful hack around the behavior of SerialPort.Read(). Which returns only the number of bytes actually received. Usually just 1 or 2, serial ports are slow and modern PCs are very fast. So by calling Thread.Sleep(), the code is delaying the UI thread long enough to get the Read() call to return more bytes. Hopefully all of them, whatever the protocol looks like. Usually works, not always. And in the posted code it didn't work and the programmer just arbitrarily delayed twice as long. Ugh.

The great misery of course is that the UI thread is pretty catatonic when it is forced to sleep. Pretty noticeable, it gets very slow to paint and to respond to user input.

This needs to be repaired by first paying attention to the protocol. The PIC needs to either send a fixed number of bytes in its response, so you can simply count them off, or give the PC a way to detect that the full response is received. Usually done by sending a unique byte as the last byte of a response (SerialPort.NewLine) or by including the length of the response as a byte value at the start of the message. Specific advice is hard to give, you didn't describe the protocol at all.

You can keep the hacky code and move it into a worker thread so it won't affect the UI so badly. You get one for free from the SerialPort.DataReceived event. But that tend to produce two problems instead of solving the core issue.

like image 190
Hans Passant Avatar answered Sep 30 '22 08:09

Hans Passant