0

I am trying to get data from a serial port continuously in a very fast speed. The baud rate is 230400. When I print out the data, time stamp and also BytesToRead in to a file, I noticed a 200ms delay happens whenever BytesToRead drops to a single digit and readLine() is not reading anything in that 200ms. After the delay, BytesToRead goes back to around 3000 and this process happens again and again. Essentially I am not getting data continuously.

I thought maybe I am reading faster than the speed data accumulate in the buffer so I tried changing readBuffer size and put this thread to sleep for 1ms in order to let buffer keep up the speed I am reading. None of them worked. There are still some delays.

Any thoughts is welcomed.

 private void dostuff()//The thread I created after the port is opened
    {
        var startTime = DateTime.Now;
        var stopwatch = Stopwatch.StartNew();
        while (serialPortEncoder.IsOpen)
        {
            if (serialPortEncoder.BytesToRead > 210)
            {
                try
                {
                    var line = serialPortEncoder.ReadLine();
                    var timestamp = (startTime + stopwatch.Elapsed);
                    var lineString = string.Format("{0}  ----{1}",
                                    line,
                                    timestamp.ToString("HH:mm:ss:fff") + " "+serialPortEncoder.BytesToRead+"\r\n");

                    richTextBoxEncoderData.BeginInvoke(new MethodInvoker(delegate()
                    {
                        richTextBoxEncoderData.Text = line;//update UI                           
                    }));                                     
                }
                catch (Exception ex) { MessageBox.Show(ex.ToString()); }


            }}
Soner Gönül
  • 97,193
  • 102
  • 206
  • 364
Timtianyang
  • 273
  • 2
  • 8
  • 19

3 Answers3

3

Unless there's a line feed every 210 bytes, your ReadLine() function is probably timing out and returning nothing. ReadLine() will read the input buffer till it encounters a newline value, then return whatever data was before it. http://msdn.microsoft.com/en-us/library/system.io.ports.serialport.readline.aspx

What kind of information is coming across the port? If you want to read a specific size buffer, just use the Read method. If you need to read till there's a line feed, use ReadLine() and check every so often to see if it returns a string.

B L
  • 1,572
  • 1
  • 14
  • 26
  • So the answer would be to user `Read()`, not `ReadLine()` – JDB Jan 14 '13 at 17:41
  • If I use readExisting, the returned data will be multiple lines of information in the same format. For example: s 0E 0000019F ! one line. – Timtianyang Jan 14 '13 at 17:46
  • @Timtianyang Same format or same data? If just the format but running continious at the right speed the biggest problem seems to be solved. Reformating can't be the hardest part... – lboshuizen Jan 14 '13 at 17:52
  • @Cyborgx37 I appreciate your reply. I want time stamp for each line so I have to use readLine(). Do you know why refilling buffer will cause a delay? Or maybe there are ways to work around it? – Timtianyang Jan 14 '13 at 17:55
  • @Timtianyang I don't understand what you mean. What does calculating a time span have to do with ReadLine()? Can't you just calculate the span after you find the correct amount of bytes to read per line of data? – B L Jan 14 '13 at 18:02
  • @glace I need a time stamp on each line of data when I receive them so I can calculate the difference in time and value between two lines of data. If I use Read() to read certain amount of data (every line of course), I don't know if it can avoid "BytesToRead not enough to be read" problem but I will give it a shot – Timtianyang Jan 14 '13 at 18:10
  • right, what I meant was that, you could store the time that you received a line of data using the DateTime.Now.Ticks value. When the next line comes in, use DateTime.Now.Ticks again, then use the TimeSpan class to find the difference between them. TimeSpan elapsedSpan = new TimeSpan(line2 - line1). Now you can convert elapsedSpan to seconds, minutes, hours, etc. If you simply just want whatever time it was when the line came through, just call DateTime.Now whenever you find a full line of data... – B L Jan 14 '13 at 19:00
2

Your code is pretty fundamentally flawed, it suffers from the "hot wait loop" bug. Your loop is burning 100% core when the serial port doesn't have enough data. That will make Windows put your thread in a dog house for a while after your burned the quantum, giving other threads a chance to run. Being in that dog house for 200 msec is a bit long but certainly not unusual.

You should do this differently, you should give Windows a chance to wake you up when there's actually data available from the serial port. It favors threads that had an I/O complete when it looks for the next thread to schedule. That is very easy to do, simply remove the BytesToRead test. The ReadLine() call is a blocking call that doesn't return until a NewLine is received. Your thread will now consume close to 0% cpu cycles.

You will still lose arbitrary amounts of time when the machine is heavily loaded or the garbage collector runs. And no, that's not good enough to reliably read an encoder and close a feedback loop. Doing that reliably requires a microcontroller with predictable real-time behavior. Readily available from industrial electronics suppliers.

Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
  • I'd really love to see an example of this, if you have a reference to an example. – JDB Jan 14 '13 at 19:24
0

After some endless researching on the web, I found the cause of the delay. Device Manager--->Port---->advance----> change latency to 1ms will solve the problem. Now, every time when the buffer is dropping to zero, it only needs 2ms max to get back to normal. I am now polling data using a separate thread. It works very well.

But like Hans Passant said, I am trying to figure out a better way to design it.

Timtianyang
  • 273
  • 2
  • 8
  • 19