How to correctly read from SerialPort in .NET.

I'm embarrassed to ask a question like this, but I'm having a very hard time figuring out how to reliably read data over a serial port with a .NET class SerialPort

.

My first approach:

static void Main(string[] args)
{
    _port = new SerialPort
    {
        PortName = portName,
        BaudRate = 57600,
        DataBits = 8,
        Parity = Parity.None,
        StopBits = StopBits.One,
        RtsEnable = true,
        DtrEnable = false,
        WriteBufferSize = 2048,
        ReadBufferSize = 2048,
        ReceivedBytesThreshold = 1,
        ReadTimeout = 5000,
    };    

    _port.DataReceived += _port_DataReceived;
    _port.Open();

    // whatever
}

private void _port_DataReceived(object sender, SerialDataReceivedEventArgs e)
{           
    var buf = new byte[_port.BytesToRead];
    var bytesRead = _port.Read(buf, 0, buf.Length);

    _port.DiscardInBuffer();
    for (int i = 0; i < bytesRead; ++i)
    {
        // read each byte, look for start/end values, 
        // signal complete packet event if/when end is found
    }
}

      

So this has an obvious problem; I am calling DiscardInBuffer

, so any data that was sent after the event was fired is discarded i.e. I am deleting data.

Now the documentation for SerialPort.Read () doesn't even indicate if it advances the current stream position (really?), But I've found other sources that claim it does (which makes sense). However, if I don't call DiscardInBuffer

, I eventually get an error RXOver

, that is, I take too long to process each message and the buffer overflows.

So ... I'm really not a fan of this interface. If I have to process each buffer in a separate thread, I will, but I have my own set of problems and I hope I'm missing something since I don't have much experience with this interface.

+3


source to share


3 answers


Jason makes a few points about reducing access to the UI from the worker thread, but better yet, don't get the data into the worker thread in the first place.



Use port.BaseStream.ReadAsync

to stream your event driven data wherever you want. I wrote more about this approach at http://www.sparxeng.com/blog/software/must-use-net-system-io-ports-serialport

+8


source


To properly process data from a serial port, you need to do a few things.

First, don't process the data in a receive event. Copy the data to a different location and do any processing on a different thread. (This is true for most events - it's a bad idea to do some kind of tedious processing in an event handler as it delays the caller and can create problems. Also you need to be careful as your event is hoisted on a different thread to your main application)

Second, you cannot guarantee that you will receive exactly one packet or a complete packet when you receive data - it may come to you in small fragments.

So you have to create your own buffer (big enough to hold multiple packets) and when you get the data, add it to your buffer. Then, on another thread, you can process the buffer, looking to see if the packet can be decoded from it and then use that data. You may have to skip the end of the partial packet before you find the beginning of the valid one. If you don't have enough data to create a complete packet, you may need to wait for a bit until more data arrives.



You don't have to call "Cancel" on the port - just read the data and use it. Each time you are called, a different chunk of data will be processed. It doesn't remember the data of previous calls - every time you call your event, it is presented with a small packet of data that has appeared since the last call. Just use the data you provided and return.

: , , . , , / , , Rts/Dtr, , , , , , , - , .

Specifically, setting ReceivedBytesThreshold to 1 is probably the reason you mentioned the error as you are requesting the serial port to invoke the event handler with only one byte at a time, 57,600 times per second - giving your event handler just 0.017 milliseconds for processing each byte before you start getting repeated calls.

+4


source


DiscardInBuffer is usually used immediately after opening the serial port. It is not required to exchange a standard serial port, so you should not have it in your dataReceived handler.

0


source







All Articles