Serial Port receiving partial messages, so can't parse correctly

WPF - updating canvas element via serial port

  • (2 Pages)
  • +
  • 1
  • 2

20 Replies - 2621 Views - Last Post: 05 June 2015 - 09:51 PM Rate Topic: -----

#1 blindchicken11   User is offline

  • D.I.C Head

Reputation: 0
  • View blog
  • Posts: 93
  • Joined: 19-September 12

Serial Port receiving partial messages, so can't parse correctly

Posted 01 June 2015 - 06:11 AM

I've been having issues for a few weeks now reading in data from a serial port and updating a canvas element based on the message being received. The canvas element to be updated is a small image, that is supposed to rotate to a certain angle based on the third section of the message being received. I'm not sure what's going wrong, it seems that a full message isn't always being received. I'm going to give as much detail about the port and data as I can. Structure - 8 data bits, 1 start bit, 1 stop bit, no parity. Message frequency is 15 Hz (the number of lines written every second). The default baud rate is 9,600.

There are five sections: 1. Section1 - two decimal places. 2. Section2 - two decimal places. 3. Angle - multiplied by 10 – i.e. 256.0 degrees is shown as 2560. 4. Section4 multiplied by -100 – i.e. 6.66 degrees is -666. 5. Section5 multiplied by 100 – i.e. 55.5 degrees is 555. A message starts with a colon symbol (: ) and ends with <CR><LF>. A field that contains asterisks, '***', indicates that no value is defined for that field.

An example to illustrate:

Column 
1 15 22 29 36 43 
1.00 *** 0 0 0 
: 1.00 20.20 2460 0 0
: 2.40 20.45 2460 10000 -10000 
: 3.00 20.45 2355 1000 554



I have the latest message received being shown at the top of the window to ensure the user that data is being received, but I noticed the message is only show bits and pieces of what it should be, and thus messing up the rotation of the canvas element. So for instance, the message might be : 20 500 at first, and then get a complete message.

Here's a screenshot of the data being sent:
Posted Image

Here's relative code in my Mainwindow.cs:
    private Port port = new Port();
    private double rovHeading;
    Point rotate_Origin = new Point(0.5, 0.5);
    public string LastCOMMessage
    {
        get { return (string)this.GetValue(LastCoMMessageProperty); }
        set { this.SetValue(LastCoMMessageProperty, value); }

    }
    private void Connect_Port(object sender, RoutedEventArgs e)
    {
        if (port.Status == Port.PortStatus.Disconnected)
        {
            try
            {
                port.Connect("COM1", 9600, false, 250); //When set to false, test data is used on Connect. Last parameter is how often the UI is updated. Closer to 0, the faster it updates.

                //Sets button State and Creates function call on data recieved
                Connect_btn.Content = "Disconnect";

                port.OnMessageReceived += new Port.MessageReceivedHandler(port_OnMessageReceived);
            }

            catch (Exception)
            {
                System.Windows.MessageBox.Show("Port is not available.");
            }
        }

        else
        {
            try // just in case serial port is not open, could also be achieved using if(serial.IsOpen)
            {
                port.Disconnect();
                Connect_btn.Content = "Connect";
            }

            catch
            {
            }
        }
    }

    /// <summary>
    /// Reads in the data streaming from the port, taking out the third value which is the ROV's heading and assigning it to rovHeading.
    /// It then calls orientRovImage.
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void port_OnMessageReceived(object sender, Port.MessageEventArgs e)
    {
        LastCOMMessage = e.Message;

        if (rovImage != null && e.Message.EndsWith("\r\n")) //messages end in <CR><LF>. Was having issues where only half a message would be received and updated the heading to wrong number.
        {
            string[] messageComponents = LastCOMMessage.Split(" ".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);

            if (messageComponents.Length >= 3)
            {
                double.TryParse(messageComponents[3], out rovHeading);

                if (rovHeading > 0)
                {
                    rovHeading /= 10;

                    orientRovImage();
                }
            }
        }
    }

    /// <summary>
    /// Rotates the ROV icon based on the heading being streamed in.
    /// </summary>
    private void orientRovImage()
    {
        RotateTransform rotateRov = new RotateTransform(rovHeading + angle_Offset);
        rovImage.RenderTransformOrigin = rotate_Origin;
        rovImage.RenderTransform = rotateRov;
    }



Here's my Port.cs:
class Port
{
    private SerialPort serial = null;
    private DispatcherTimer testTimer;
    private string[] testData = new string[] { ": 3.00 20.45 2355   1000 554\r\n", ": 5.00 78.09 1725 3200 121\r\n", ": 9.20 10.12 1492 8820 197\r\n" }; //test data to be sent when liveData is set to false.
    private int testDataIndex = -1;
    private DateTime dateOflastMessageHandled;

    public enum PortStatus
    {
        Connected,
        Disconnected
    }
    public PortStatus Status { get; private set; }
    public bool LiveData { get; private set; }
    public int MillisecondsDelayBetweenMessages { get; private set; }

    public class MessageEventArgs : EventArgs
    {
        public string Message { get; private set; }

        public MessageEventArgs(string message)
        {
            Message = message;
        }
    }
    public delegate void MessageReceivedHandler(object sender, MessageEventArgs e);
    public event MessageReceivedHandler OnMessageReceived;


    private void MessageReceived(string message)
    {
        if (OnMessageReceived == null)
        {
            return;
        }

        OnMessageReceived(this, new MessageEventArgs(message));
    }


    public Port()
    {
        Status = PortStatus.Disconnected;
    }

    public void Connect(string portName, int baudRate, bool liveData, int millisecondsDelayBetweenMessages)
    {
        LiveData = liveData;
        MillisecondsDelayBetweenMessages = millisecondsDelayBetweenMessages;

        Disconnect();

        if (liveData)
        {
            serial = new SerialPort();

            serial.PortName = portName;
            serial.BaudRate = baudRate;
            serial.Handshake = Handshake.None;
            serial.Parity = Parity.None;
            serial.DataBits = 8;
            serial.StopBits = StopBits.One;
            serial.ReadTimeout = 200;
            serial.WriteTimeout = 50;

            serial.Open();

            serial.DataReceived += new SerialDataReceivedEventHandler(Receive);
        }

        else
        {
            testTimer = new DispatcherTimer();
            testTimer.Interval = new TimeSpan(0, 0, 0, 0, 3);
            testTimer.Tick += new EventHandler(testTimer_Tick);
            testTimer.Start();
        }

        Status = PortStatus.Connected;
    }


    private void testTimer_Tick(object sender, EventArgs e)
    {
        if (dateOflastMessageHandled == null || DateTime.Now.Subtract(dateOflastMessageHandled).TotalMilliseconds >= MillisecondsDelayBetweenMessages)
        {
            dateOflastMessageHandled = DateTime.Now;
            MessageReceived(testData[testDataIndex]);

            testDataIndex++;
            if (testDataIndex >= testData.Length)
            {
                testDataIndex = 0;
            }
        }
    }


    private void Receive(object sender, SerialDataReceivedEventArgs e)
    {
        if (dateOflastMessageHandled == null || DateTime.Now.Subtract(dateOflastMessageHandled).TotalMilliseconds >= MillisecondsDelayBetweenMessages)
        {
            dateOflastMessageHandled = DateTime.Now;

            Application.Current.Dispatcher.BeginInvoke(new Action(
                delegate()
                {
                    //MessageReceived(serial.ReadLine());
                    MessageReceived(serial.ReadExisting());
                }));
        }
    }


    public void Disconnect()
    {
        if (testTimer != null)
        {
            testTimer.Stop();
        }

        testDataIndex = 0;

        Status = PortStatus.Disconnected;

        if (serial != null)
        {
            serial.Close();
        }
    }
}



And finally in my Mainwindow.xaml, this just shows the last message received:
        <Button Content="Connect" Click="Connect_Port" Name="Connect_btn" />
        <TextBlock Text="{Binding LastCOMMessage, ElementName=this}" />



This is the first time I've programmed anything using a serial port, and I can only test the actual device sending the data once a week for a few hours so I'd like to get it as close to correct as possible before I get back to the device to test it. I did post this question on stackoverflow a few weeks ago. I'm not entirely sure how to use the buffer correctly. Any advice/hints would be great, thank you!

This post has been edited by blindchicken11: 01 June 2015 - 06:17 AM


Is This A Good Question/Topic? 0
  • +

Replies To: Serial Port receiving partial messages, so can't parse correctly

#2 horace   User is offline

  • D.I.C Lover
  • member icon

Reputation: 768
  • View blog
  • Posts: 3,832
  • Joined: 25-October 06

Re: Serial Port receiving partial messages, so can't parse correctly

Posted 02 June 2015 - 02:39 AM

difficult to give advice as some of your code appears to be missing - can you post the complete code?

have you tried running as a stand alone C# console program

it may be easier to debug than using the Windows Presentation Foundation Host
can you connect another device e.g. a PC to the serial port so you can test the program without the actual target device
Was This Post Helpful? 0
  • +
  • -

#3 tlhIn`toq   User is offline

  • Xamarin Cert. Dev.
  • member icon

Reputation: 6534
  • View blog
  • Posts: 14,450
  • Joined: 02-June 10

Re: Serial Port receiving partial messages, so can't parse correctly

Posted 02 June 2015 - 04:22 AM

I always write simulators for devices I don't have regular access to.

Write a 2nd program to *be* the device. You have the spec data for it, right? So have it write out of COM port 3 the exact data you should be receiving.
Use a serial cross over cable to send outgoing port 3 to incoming port 1.
If you have 2 PC's, great. If you don't, it works just as well on one.

If things work great you might be seeing a case where the actual device doesn't live up to the data sheet. Welcome to real world programming. You'll have to compensate for its shortcomings.

If you see the same issues - even better. Now you can fix up your code and make it more robust.
Was This Post Helpful? 1
  • +
  • -

#4 horace   User is offline

  • D.I.C Lover
  • member icon

Reputation: 768
  • View blog
  • Posts: 3,832
  • Joined: 25-October 06

Re: Serial Port receiving partial messages, so can't parse correctly

Posted 02 June 2015 - 04:53 AM

View PosttlhIn`toq, on 02 June 2015 - 12:22 PM, said:

I always write simulators for devices I don't have regular access to.

good idea! I tend to use a microcontroller board such as Microchips Microstick or Explorer 16 boards as a device simulator connected to a host PC via RS232 or USB
http://www.microchip...d4-6fb5ee8681d3
http://www.microchip...velopment+Board

if you don't have access to such boards or a second PC you could use a Virtual Serial Port program such as
http://www.virtual-serial-port.com/
http://freevirtualserialports.com/

which enable one to create a pair of virtual COM ports so one can run the device simulator on the same PC as the program being tested
Was This Post Helpful? 2
  • +
  • -

#5 tlhIn`toq   User is offline

  • Xamarin Cert. Dev.
  • member icon

Reputation: 6534
  • View blog
  • Posts: 14,450
  • Joined: 02-June 10

Re: Serial Port receiving partial messages, so can't parse correctly

Posted 02 June 2015 - 06:12 AM

I think I'm more old school. Virtual ports are great and all... But I like to feel something in my hands. I like real hardware. You can get USB-Serial adapters for less than ten bucks these days. This way you can have real hardware devices. For $30 you can add 3 more ports to your PC (and to a laptop that doesn't normally have serial ports), and cross connect.

Not to mention you now have something to replace a flaky COM port. I've also seen (odd as this is) where the USB adapter cleaned up bad signals better than a true serial port. We did an installation one time where the sensor cable ran past a huge electric motor that was inducing voltage through our cable - thus making false signals. For some crazy reason the adapter cleaned up this noise.

Also, with these in my laptop bag I've proven to clients that the problem is in their serial port not my software. Just plug in the adapter and suddenly things work as expected - therefore - there is a problem with their serial port board.

The boards look interesting. I've never used them. I made a simulator program that I can pick different devices for. Once you have the skeleton made you can add more modes as needed. So I can pick "timer" or "Roller Coaster PLC" or "Dookickey", and have a screen with all the parameters for that device. If I want to test my target program with signals that come in faster or slower I change a parameter on the simulator and it sends faster or slower. Tick the [x]Random Noise checkbox or [x]Randomly drop ACK to see if my target program is robust enough. And so on. Sometimes you have to build your toolbox before you can build your program. But once you do you'll be so happy you did. 40 hours on that simulator has saved me hundreds of hours on the various target programs. AND helped me make completely bullet-proof programs that can handle all sorts of error conditions.

This post has been edited by tlhIn`toq: 02 June 2015 - 06:13 AM

Was This Post Helpful? 0
  • +
  • -

#6 horace   User is offline

  • D.I.C Lover
  • member icon

Reputation: 768
  • View blog
  • Posts: 3,832
  • Joined: 25-October 06

Re: Serial Port receiving partial messages, so can't parse correctly

Posted 02 June 2015 - 06:51 AM

I agree! I don't use Virtual Serial Port Kits myself prefering a real serial device for testing - in the end one has to be used for final commissioning
I tend to use the FTDI USB serial adapters in particular the USB to TTL serial cables
http://www.ftdichip....SBTTLSerial.htm

no need to have a MAX232 chip on the PCB to convert the TTL signals to/from the micro to RS232 - saves costs, layout time and space!
Was This Post Helpful? 0
  • +
  • -

#7 blindchicken11   User is offline

  • D.I.C Head

Reputation: 0
  • View blog
  • Posts: 93
  • Joined: 19-September 12

Re: Serial Port receiving partial messages, so can't parse correctly

Posted 03 June 2015 - 03:30 PM

Thanks for the replies guys. I'll try using a virtual serial port and another program in conjunction w/ this to do some testing, but does my overall code for the message received handling look okay? Is there anything noticable that's missing? From my code, I was under the impression that it would only update the UI based on full messages and discard the others, but that doesn't seem to be the case. Even though from the sending side of things it looks like a full message is sent every time, on the receiving end sometimes only three or so of the data fields are received and thus the UI element is updated incorrectly.
Was This Post Helpful? 0
  • +
  • -

#8 click_here   User is offline

  • D.I.C Regular

Reputation: 46
  • View blog
  • Posts: 300
  • Joined: 25-November 13

Re: Serial Port receiving partial messages, so can't parse correctly

Posted 03 June 2015 - 03:58 PM

At my work we only use the USB serial adapters.

When testing I have a USB to RS232 connected to a Female/Female D connector lead (I made one for a few dollars) and then back into another USB to RS232 converter. I then run PuTTY on the other port which allows me to see what is going out, and manually type the responses.

There are ways that you can help the connection such as making a NULL modem - http://airborn.com.a...rial/rs232.html (I don't recommend that you buy these from the website - You can make your own for very cheap)

Do you have control of the responding frame? If so, I recommend that you put a charactor at the end of the string to signify that the end of text has been reached

: 2.40 20.45 2460 10000 -10000;


And then use something like this..
        void port_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            buffer.Append(port.ReadExisting());

            if (buffer.ToString().Contains(';'))
            {
                // (Some event when end of frame is received)
                onReceivedLine(buffer.ToString().Trim());

                
                buffer.Clear();
            }   
        }


Was This Post Helpful? 0
  • +
  • -

#9 blindchicken11   User is offline

  • D.I.C Head

Reputation: 0
  • View blog
  • Posts: 93
  • Joined: 19-September 12

Re: Serial Port receiving partial messages, so can't parse correctly

Posted 05 June 2015 - 06:15 AM

Unfortunately I don't have control of the sending side of things so I can't add anything to the end of a string, but I can say w/ 100% certainty the <CR><LF> is at the end of each line.
Was This Post Helpful? 0
  • +
  • -

#10 horace   User is offline

  • D.I.C Lover
  • member icon

Reputation: 768
  • View blog
  • Posts: 3,832
  • Joined: 25-October 06

Re: Serial Port receiving partial messages, so can't parse correctly

Posted 05 June 2015 - 06:49 AM

I think the simplest approach is to get the serial receive working correctly using a second serial device to send test data, e.g. use a stand alone C# console program to get the serial operational and checking for valid data etc (use your Port class as a starting point?)
when that is working you can then include it in the complete system
Was This Post Helpful? 0
  • +
  • -

#11 blindchicken11   User is offline

  • D.I.C Head

Reputation: 0
  • View blog
  • Posts: 93
  • Joined: 19-September 12

Re: Serial Port receiving partial messages, so can't parse correctly

Posted 05 June 2015 - 07:59 AM

Okay turns out I have some simulation software that simulates the output of the actual device. So in my mainwindow.xaml (posted at the end of my first post), the incoming message is properly updated each time. However, the image is not being updated/rotated at all from the orientRovImage() call. So something regarding the bit of code below is not working properly:

            if (rovImage != null && e.Message.EndsWith("\r\n")) //messages end in <CR><LF>. Was having issues where only half a message would be received and updated the heading to wrong number. //rovImage is just a .jpg that is on the screen that is to be rotated via orientRovImage()
            {
                string[] messageComponents = LastCOMMessage.Split(" ".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);

                if (messageComponents.Length >= 3)
                {
                    double.TryParse(messageComponents[3], out rovHeading);

                    if (rovHeading > 0)
                    {
                        rovHeading /= 10;

                        orientRovImage();
                    }
                }
            }


Was This Post Helpful? 0
  • +
  • -

#12 horace   User is offline

  • D.I.C Lover
  • member icon

Reputation: 768
  • View blog
  • Posts: 3,832
  • Joined: 25-October 06

Re: Serial Port receiving partial messages, so can't parse correctly

Posted 05 June 2015 - 08:04 AM

can you print the elements of string[] messageComponents to the Console too check the split is working OK
if that is OK the result of the TryParse()?

alternativly add
using System.Diagnostics;

and write to the Debug window (run in debug mode)

This post has been edited by horace: 05 June 2015 - 08:16 AM

Was This Post Helpful? 0
  • +
  • -

#13 blindchicken11   User is offline

  • D.I.C Head

Reputation: 0
  • View blog
  • Posts: 93
  • Joined: 19-September 12

Re: Serial Port receiving partial messages, so can't parse correctly

Posted 05 June 2015 - 08:20 AM

Hmm, assuming I'm debugging correctly (using debug.writeline and message.show), messageComponents isn't getting anything, which means something with the if statement above it is wrong. I'm guessing the (e.message.EndsWith("\r\n")) is the culprit.

Update: If I remove that if statement and do "Debug.WriteLine(messageComponents)", it just shows System.String[] repeatedly.

Update 2: If I'm using my test data, messageComponents[3] shows the right number via debug.writeline. However, once I start using the real sim data, I get an error that points to the Debug.WriteLine(messagecomponents[3] that says "Index was outside the bounds of the array."

This post has been edited by blindchicken11: 05 June 2015 - 08:39 AM

Was This Post Helpful? 0
  • +
  • -

#14 horace   User is offline

  • D.I.C Lover
  • member icon

Reputation: 768
  • View blog
  • Posts: 3,832
  • Joined: 25-October 06

Re: Serial Port receiving partial messages, so can't parse correctly

Posted 05 June 2015 - 09:18 AM

you can write the elements of messageComponents so
            string[] messageComponents = {"hello","OK","end"};
            for (int i = 0; i < messageComponents.Length; i++)
                Debug.WriteLine("element " + i + " is " +  messageComponents[i]);


when I run the debug window shows
element 0 is hello
element 1 is OK
element 2 is end

try printing LastCOMMessage to see what you have before the split?
Was This Post Helpful? 0
  • +
  • -

#15 blindchicken11   User is offline

  • D.I.C Head

Reputation: 0
  • View blog
  • Posts: 93
  • Joined: 19-September 12

Re: Serial Port receiving partial messages, so can't parse correctly

Posted 05 June 2015 - 09:44 AM

Woops, I was getting the error because I had "9 < messageComponents" instead of "i < messageComponents". This is what prints for messageComponents repeatedly. It goes up to element 15 then resets back to 0.

element0is***1001.32
element1is2000
element2is0
element3is0
:
element4is***1001.32
element5is2000
element6is0
element7is0
:
element8is***1001.32
element9is2000
element10is0
element11is0
:
element12is***1001.32
element13is2000
element14is0
element15is0
:

So the split doesn't seem to be working correctly, as element 1 should be :, 2 should be ***, 3 should be 1001.32, 4 should be 2000, 5 is 0, 6 is 0, then the <CR><LF>.

That's only when I comment out the if statement, if I leave the if statement in, it doesn't even get called.

Update: Okay, so I seemed to have it working. I got rid of the first if statement in the port_OnMessageReceived method. Since the number I want is at element 1, 5, 9, and 13, I just do "double.TryParse(messageComponents[1], out rovHeading)". It seems to be updating properly. This is probably a sloppy method of doing it so if there's any cleaner suggestions please let me know! I figure since the data is always going to be output this way it's okay for now. Oh, and I also update messageComponents.Length >=4 instead of 3.

Updated code below:
        private void port_OnMessageReceived(object sender, Port.MessageEventArgs e)
        {
            LastCOMMessage = e.Message;
            
                string[] messageComponents = LastCOMMessage.Split(" ".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
                for (int i = 0; i < messageComponents.Length; i++)
                {
                    System.Diagnostics.Debug.WriteLine("element" + i + "is" + messageComponents[i]);
                }
                if (messageComponents.Length >= 4)
                {
                    double.TryParse(messageComponents[1], out rovHeading);

                    if (rovHeading > 0)
                    {
                        rovHeading /= 10;

                        orientRovImage();
                    }
                }
        }


This post has been edited by blindchicken11: 05 June 2015 - 09:53 AM

Was This Post Helpful? 0
  • +
  • -

  • (2 Pages)
  • +
  • 1
  • 2