Click here to Skip to main content
15,890,438 members
Please Sign up or sign in to vote.
3.00/5 (1 vote)
See more:
Hi there all, greetings from Oz :-)

I have an application I wrote sometime ago in VB6 (Enterprise). It involves talking to a RS232 device and receiving packets of data (of varying size) that are encapsulated by the traditional STX/ETX.
Now that I need to add a few new features to the application I have decided to rewrite it using VB.Net (VS2010).
My problem is that the data is appearing at the DataReceived Event is malformed or disjointed.
For example I have created a small test program to demonstrate this behavior, code listed below.
If I use a terminal program (RealTerm) to send the standard 'Hello World' what I receive in the debug window is ....

H
ello
World

I have tried setting a Thread.Sleep(100) command as the first line in the DataReceived event, this alleviates the problem in this example but in the long term it would not suffice as the data length varies up to 10 lines of 14 chars each. Or it could be just one 14 char line. Setting the sleep even higher may cause issues with the next packet of data.

Looking or waiting for the ETX is not an option either as it may not arrive and in this case the packet is to be discarded and its guaranteed that the next packet will be the same packet with the ETX included. Essentially there is some checking and if the data is not completely received it is sent again.

Hoping someone can assist me with this.


My test programming listing below ...
A simple form with a textbox, multiline on, scrollbars vert.

VB
Public Class frmMain

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load

        With rs232
            .BaudRate = 4800
            .PortName = "COM6"
            .StopBits = IO.Ports.StopBits.One
            .Parity = IO.Ports.Parity.None
            .DataBits = 8
            rs232.Open()
        End With


    End Sub

    Private Sub rs232_DataReceived(ByVal sender As Object, ByVal e As System.IO.Ports.SerialDataReceivedEventArgs) Handles rs232.DataReceived

        Dim strBuffer As String
        CheckForIllegalCrossThreadCalls = False

        strBuffer = rs232.ReadExisting
        Debug.Print(strBuffer)
        If InStr(strBuffer, Chr(2)) Then rs232.Write("#")

        txtData.Text = txtData.Text & strBuffer & vbCrLf



    End Sub

End Class


Cheers

Mike S
Posted

Basically, you can't just look at the data you receive that time - but it is very, very unlikely to be complete: you would normally get a DataReceived event for each character or so as it comes in, especially at a low speed like that.

Instead, build up a buffer with all the data, and then parse that, removing the detected packets as they come in.
So when you get a receive event, add the received data on the end of the current data, then check the data for "starts with STX" and discard until it does. If you still have data, look for an ETX. If it isn't there, do nothing.
If it is, take off the whole STX...ETX packet, and process it.

(You'll probably want to add some timer or similar to throw away junk if the ETX is corrupted, as well)
 
Share this answer
 
Comments
mikes42 9-Jul-13 3:01am    
Hey thanks for the prompt response!
I understand what your saying, makes logical sense. Question though, do I stick with the 'ReadExisting' or go to 'Read' or 'ReadChar'. I know readline is of no use as the terminating char (ETX) may not exist so it would flow into the next packet.
Thanks again, Mike S
OriginalGriff 9-Jul-13 3:08am    
I'd stick with ReadExisting - the data is useless to you on a character-by-character basis so read everything that is there and add it to the buffer. If that means you get three characters, fine - the last one may be the ETX you need. If you get none, just look at the buffer (because you got it last time and the system didn't notice in time)

If you read one character per event, you risk missing data if anything gets out of sync.
mikes42 9-Jul-13 3:12am    
Yep gotcha ..... Awesome help. I will rewrite my test app and send "Hello World" to it once again.
Thanks once again.
Mike S
OriginalGriff 9-Jul-13 3:18am    
You're welcome!
OriginalGriff 9-Jul-13 3:46am    
Just as an idea...the way I would do this would be to have the DataReceived event just pump the data into a buffer, and to have a separate thread to de-packet it. When it found a packet, it removes it and signals an event which the main thread then handled to process the packet content.
That way, the task complexity is reduced (the de-packet thread handles any acknowledges required) and the main thread doesn't care what a packet looks like - just that it's complete and ready.
Thanks OriginalGriff.

By no means finished but thanks to your advice I have stepped over the biggest hurdle. I have the serial comms sorted. I still have to do heaps more with the data but that should be a non-issue now. Listed below is my proof of concept code.

VB
Public Class frmMain

    'Dim strRawBuffer As String
    Dim strBuffer As String
    'Dim strRealData As String = ""
    'Dim strRawData As String

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load

        With rs232
            .BaudRate = 4800
            .PortName = "COM6"
            .StopBits = IO.Ports.StopBits.One
            .Parity = IO.Ports.Parity.None
            .DataBits = 8
            rs232.Open()
        End With

    End Sub

    Private Sub rs232_DataReceived(ByVal sender As Object, ByVal e As System.IO.Ports.SerialDataReceivedEventArgs) Handles rs232.DataReceived

        '// Pass received data to processing function
        ProcessData(rs232.ReadExisting)

    End Sub

    Private Sub ProcessData(ByVal data)

        CheckForIllegalCrossThreadCalls = False

        For i = 1 To data.Length

            If Mid(data, i, 1) = Chr(3) Then
                ' // we've reached the end of the data packet, deal with it.
                Debug.Print(strBuffer)
                txtData.Text = strBuffer & txtData.Text
                strBuffer = ""
            ElseIf Mid(data, i, 1) = Chr(2) Then
                '// The previous packet maybe missing an ending (ETX) so discard it and start again
                '// Send the # char to confirm connection and tell the unit to keep sending
                rs232.Write("#")
                strBuffer = "" '// clear
            Else
                ' // Keep building the string
                strBuffer &= Mid(data, i, 1)
                '// look out for connection string device
                If InStr(strBuffer, "%T") Then
                    Debug.Print("Connected....")
                    strBuffer = ""
                    Exit Sub
                End If
            End If
        Next

    End Sub
    Private Sub btnClear_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnClear.Click

        '// Clear the textbox
        txtData.Clear()

    End Sub

    Private Sub btnConnect_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnConnect.Click

        '// Request connection, the device will answer back with 
        rs232.Write("%T")

    End Sub
End Class
 
Share this answer
 

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900