views:

10469

answers:

3

I have tried using Readline() and data gets dropped, I tried using Read() but I am not sure how to have an error proof method of doing it, since I may get several packets one after another and I have no way of knowing that there is going to be another packet coming in. In between packets BytesToRead is 0, so I can't use it. When reading data to the buffer to you have a timer or put the thread to sleep to allow for all the packets to arrive?

I am lost. Don't know what to try next.

I should mention that I get no guarantee that the string coming off the serial port will be ended with \n or \r or \r\n. I simply need a fool proof way to read ALL the packets that will come from the scale when the user presses PRINT on it.

Someone answered here with the idea I liked - waiting for a certain amount of time for all the packets, but they erased their answer. ANy chance you could re-post it?

+8  A: 

Have you tried listening to the DataRecieved event of the SerialPort class?

public class MySerialReader : IDisposable
{
 private SerialPort serialPort;
 private Queue<byte> recievedData = new Queue<byte>();

 public MySerialReader()
 {
  serialPort = new SerialPort();
  serialPort.Open();

  serialPort.DataReceived += serialPort_DataReceived;
 }

 void serialPort_DataReceived(object s, SerialDataReceivedEventArgs e)
 {
  byte[] data = new byte[serialPort.BytesToRead];
  serialPort.Read(data, 0, data.Length);

  data.ToList().ForEach(b => recievedData.Enqueue(b));

  processData();
 }

 void processData()
 {
  // Determine if we have a "packet" in the queue
  if (recievedData.Count > 50)
  {
   var packet = Enumerable.Range(0, 50).Select(i => recievedData.Dequeue());
  }
 }

 public void Dispose()
 {
  if (serialPort != null)
   serialPort.Dispose();
 }
Samuel
+2  A: 

We went through the same process a while ago.

The only way to read 'packets' is to have some concept of where the start and end of them are in a stream.

From msdn:

Because the SerialPort class buffers data, and the stream contained in the BaseStream property does not, the two might conflict about how many bytes are available to read. The BytesToRead property can indicate that there are bytes to read, but these bytes might not be accessible to the stream contained in the BaseStream property because they have been buffered to the SerialPort class.

We used a backround thread (you could use a BackgroundWorker) to read the data into a buffer. If you can't reliably set the terminating character using the SerialPort.Newline property (because, say, it varies!) then you will need to implement your own packet detection system because you wont be able to use the blocking SerialPort.Readline() method.

You could just read into a byte buffer using SerialPort.Read() (or string using the SerialPort.ReadExisting() method) and generate an event when you detect a valid packet in the data. Note that Read() (and I assume ReadExisting() ) empty the SerialPort's buffer, so you'll need to keep the data somewhere else.

If you set the SerialPort.ReadTimeout, you can handle the TimeoutException and have an easy way to handle conditions where your device is not transmitting. This is a good way to reset your packet detection if you are using a fixed number of bytes, or some other non-terminated scheme. (use SerialPort.DiscardInBuffer() on a timeout if you don't need partial packets).

Good luck

Byron Ross
from what I know there is no detection scheme. I will try timing it - waiting for a few millisecs before processing to allow the packets to arrive. I don't see any other alternative. I was using ReadLine() for 6 months until someone noticed a bug where data gets dropped.
gnomixa
We use a lot of industrial scales and you can normally set the termination character in the device. Do you have control over the input devices so you could look at this? ReadLine is *much* easier :)
Byron Ross
we do: \n (ascii 13). What I am going to try to do is read the data byte by byte with SerialPort.ReadByte() and look for 13. I am also going to set SerialPort.NewLine to \n ( i have it set to \r\n now....hmmm, maybe that's the issue. Thanks!
gnomixa
sorry for the mumbo jumbo:) initially I had SerialPort.NewLine set to \n but that didn't work which is why I switched to \r\n. I used ReadLine up till now.
gnomixa
+1  A: 

Hello,

Since bytes may come in at any time, buffering incoming data is critical. So you should

  1. buffer the incoming data
  2. scan your buffer to find complete data
  3. remove the used data from the buffer

I am just wondering if you are still having problems with the serial port. If so, I developed a serial port programming language in C# and I believe it solves nearly all of the problems that everybody encounters with.

Would you please take a look and try it ? For example; you can buffer incoming data from the serial port like the following and do string operations easily.

state Init
  // define a global variable
  our $BUFFER = "";
  jump(Receive);
end state

state Receive
  recv();
  $len = length($DATA_PACKET);
  if("$len > 0") {
    $BUFFER += $DATA_PACKET;
    call(Parser);
  }
end state

state Parser
  // check if buffer matchs regular expression pattern
  if(match($BUFFER, "(?<WILLDELETE>.*?<STX>(?<DATA>.*?)<ETX>(?<CHECKSUM>[0-9A-F]{2}))")) {
    // Received complete data
    $lenData = length($WILLDELETE);
    $BUFFER = remove($BUFFER, 0, $lenData);

    // Do operations with the other parsed fields. $DATA and $CHECKSUM in this example.
  }
end state

Project is freely available on sourceforge and if you have any questions, please feel free to ask.

Project Homepage

Download Link

Thank you,

Orhan

albay