tags:

views:

895

answers:

6

Hi guys-

I'm working on a SerialPort app and one very simple part of it is giving me issues. I simply want to read a constant stream of data from the port and write it out to a binary file as it comes in. The problem seems to be speed: my code has worked fine on my 9600 baud test device, but when carried over to the 115200bps live device, I seem to be losing data. What happens is after a variable period of time, I miss 1 byte which throws off the rest of the data. I've tried a few things:

private void serialPort1_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
    bwLogger.Write((byte)serialPort1.ReadByte());
}

or

private void serialPort1_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
    byte[] inc = new byte[serialPort1.BytesToRead];
    serialPort1.Read(inc, 0, inc.Length);

    bwLogger.Write(inc);
}

and a few variations. I can't use ReadLine() as I am working with a constant stream of data (right?). I've tried fiddling with the buffer size (both serialPort1.ReadBufferSize and the hardware FIFO buffer). Ideally, for usability purposes, I'd handle this on the software side and not make the user have to change Windows driver settings.

Any ideas?

A: 

The machines I've been working with recently all send a stop code (in my case ASCII code 3 or 4). If you also have this feature, you can make use of ReadTo(string) off your SerialPort object.

Austin Salonen
Unfortunately I don't have that luxury :\ Just a constant stream of binary data. The ReadTo method will definitely be applicable elsewhere in the utility. Thanks for the heads up.
Slim
+2  A: 

I'd try the following:

  • Set the Buffer-Size to at least 230K Bytes
  • Set the Incoming Threshold to 16K, 32K or 65K
  • Write this fixed blocks of data to the file

I'm not sure if this might help, but it should at least take the pressure of the framework to fire the event that often.

Bobby
It didn't seem to help the issue, though logically I thought it would. Regardless, it is good advice that seems have to reduced my memory usage due to fewer raised events. Thanks.
Slim
A: 
  1. I would check the number of bytes read which is returned by the Read(Byte>[], Int32, Int32) method and make sure it matches what you expect.

  2. Make sure you are listening for SerialErrorReceivedEventHandler ErrorReceived events on the port object. An RXOver error would indicate your buffer is full.

  3. Check the thread safety on your output buffer. If the write to the output buffer is not thread safe, a second write may corrupt the first write.

Liam
+3  A: 

You might try enabling handshaking, using the Handshake property of the SerialPort object.

You'll have to set it on both the sender or receiver. however: if you're overflowing the receiver's UART's buffer (very small, 16 bytes IIRC), there's probably no other way. If you can't enable handshaking on the sender, you'll probably have to stay at 9600 or below.

XXXXX
Seems to be the right idea. I messed around handshaking settings but always left it software controlled. Changing the driver settings to a hardware flow control *seems* to have solved the issue, at least on short tests.I'll have to run a 12+ hour test to see if it holds up, in which case I'll let you know and give you one of them checkmarks. Good call and something I should have looked into.
Slim
Hope it works out. :-)
XXXXX
A: 

Is your bwLogger a BinaryWriter class? You might try using it with a BufferedStream to make the disk I/O nonblocking.

Also, if your packets have a known ending character, you can set the SerialPort.NewLine property to enable you to use ReadLine/WriteLine, although I don't think that would make much of a performance difference.

mtrw
+3  A: 

If the problem seems to be that you can't process the data fast enough, what you could try would be to double-buffer your data.

1) Allow one thread to read the serial port into one buffer. This may involve copying data off the port into the buffer (i'm not intimately familiar with .NET).

2) When you are ready to handle the incoming data, (on a different thread) make your program read into the 2nd buffer, and while this is happening you should write the first buffer to disk.

3) When the first buffer is written to disk, swap it back to the serial port buffer, and write the 2nd buffer to disk. Repeat process, continually swapping the buffers.

San Jacinto
After working around various issues that never actually solved my problem - it turns out I needed to implement a double buffer. I'm glad I revisited this question and even more so that you took the time to repost another great suggestion. Thanks! Thankfully, .NET took care of the everything by simply wrapping a BufferedStream around a standard FileStream (which has its own internal buffer). I guess I have a few more tests before I can *absolutely* confirm this, but this buffering logic seems to have flattened out a few little kinks.
Slim