Solved

Reading asynchronous serial port data in VB 2005

Posted on 2008-06-12
7
1,424 Views
Last Modified: 2013-11-26
Hello everyone,

I'm developing a driver that sends out text commands via serial port to an instrument. When that instrument is finished executing the command, or an error results, it will send back a text result. I expect this message will be less than 15 characters long.

1) I'm able to send out the commands and have the instrument respond, but am having difficulty capturing the Event and the message, as I'm new to Serial Communication and Event Handling. I've attached the code I've written.

2) What is the best way to wait for a response, with a given timeout value? Each command should be followed by a response, successful or not.

3) Is it better to open the COM port once or open and close it before/after each command?

Thanks!
Haj


Module Module1
 

    Sub Main()

        Dim strCommand As String

        Dim CtrlJ As New Char

        Dim strReset As String ' Reset command

        Dim strPlateRequest As String ' Plate request command

        Dim strPlateReturn As String ' Plate return command

        Dim strReturnValue As String ' Post-command status returned from Q-Stack
 

        ' Commands have a <LF> (Ctrl-J or ASCII (10)) before and after.

        CtrlJ = Chr(10) ' Ctrl-J = Linefeed (ASCII)

        strReset = CtrlJ & "AK5=1" & CtrlJ

        strPlateRequest = CtrlJ & "AK3=1" & CtrlJ

        strPlateReturn = CtrlJ & "AK4=1" & CtrlJ
 

        strCommand = strReset ' Subsitute any of the commands for testing purposes

        OutToCOM4(strCommand, False)
 

        strReturnValue = ""

        InFromCOM4(strReturnValue) ' Wait for status from instrument before proceeding

        MsgBox(strReturnValue)
 

    End Sub
 

    Public Sub OutToCOM4(ByVal serialData As String, _

        ByVal useLineTermination As Boolean)

        Dim com4Port As IO.Ports.SerialPort = Nothing
 

        Try

            ' ---- Access the port.

            com4Port = My.Computer.Ports.OpenSerialPort("COM4", 9600, IO.Ports.Parity.None, 8, IO.Ports.StopBits.One)

            ' COM Port, baud rate, parity, data bits, stop bits
 

            ' ---- Write the data.

            If (useLineTermination = True) Then

                com4Port.WriteLine(serialData)

            Else

                com4Port.Write(serialData)

            End If
 

            ' ---- Finished with the port.

            com4Port.Close()
 

        Catch ex As Exception

            MsgBox("Error writing data to serial port: " & _

                ex.Message)
 

        Finally

            If (com4Port IsNot Nothing) Then com4Port.Dispose()

            com4Port = Nothing
 

        End Try

    End Sub
 

    Public Sub InFromCOM4(ByVal returnData As String)

        Dim com4Port As IO.Ports.SerialPort = Nothing
 
 

        Try

            ' ---- Access the port.

            com4Port = My.Computer.Ports.OpenSerialPort("COM4", 9600, IO.Ports.Parity.None, 8, IO.Ports.StopBits.One)

            ' COM Port, baud rate, parity, data bits, stop bits
 

            ' ---- Wait for incoming data.

            '--->>>   What is the best way to wait for incoming data? <<<---
 

            ' ---- Read the data.

            '--->>>   returnData = com4Port.??? ' How do I capture the incoming data? <<<---
 

            ' ---- Finished with the port.

            com4Port.Close()
 

        Catch ex As Exception

            MsgBox("Error reading data from serial port: " & _

                ex.Message)
 

        Finally

            If (com4Port IsNot Nothing) Then com4Port.Dispose()

            com4Port = Nothing
 

        End Try

    End Sub
 

End Module

Open in new window

0
Comment
Question by:hsano6294
  • 3
  • 2
7 Comments
 
LVL 11

Expert Comment

by:AkisC
Comment Utility
If you are using a SerialPort control (Tool box->Components) it is easy
Let's say you have named your SerialPort control mySerialPort

You capture the data within the DataReceived sub
    Private Sub DataReceived(ByVal sender As Object, ByVal e As System.IO.Ports.SerialDataReceivedEventArgs) Handles mySerialPort.DataReceived

        'fires when data is received in the input buffer

        Dim buffer As String = "", bufASCII As Integer = 0, aCHR As String = ""

        Do

            If mySerialPort.BytesToRead > 0 Then

                bufASCII = mySerialPort.ReadByte

                aCHR = ChrW(bufASCII)

                buffer &= aCHR

            Else

                Exit Do

            End If

        Loop

        If buffer.Length > 0 Then

            '/////com response///////

        End If

    End Sub

Open in new window

0
 

Author Comment

by:hsano6294
Comment Utility
AkisC,

Sorry for the delay. The production system just became available to me.

1) Can your DataReceived subroutine handle ReadLine as well as ReadByte?
2) What do I put in my main routine that sent out my WriteLine string and is waiting for the response? The response may take as long as 45 seconds before the mechanical components finish moving. Do I put the main program in a loop waiting for the event? And does the serial port need to be declared with a special option to handle the event?

Thanks!
Haj
0
 
LVL 11

Accepted Solution

by:
AkisC earned 500 total points
Comment Utility
Q:"1) Can your DataReceived subroutine handle ReadLine as well as ReadByte?"
As you see in the code use ReadByte and is converted to string aCHR = ChrW(bufASCII) 'valid for ascii chars only
I beleive it is safest to read one-byte-at-atime

Q:"2) What do I put in my main routine that sent out my WriteLine string and is waiting for the response?The response may take as long as 45 seconds before the mechanical components finish moving. Do I put the main program in a loop waiting for the event? "
You do not have to wait. When dataReceived you have it there. You can have a (hidden) label to alert your user like label1.text="waiting response...":label1.visible=true. When you receive the response.label1.visible=false

Q:"And does the serial port need to be declared with a special option to handle the event?"
No just put the sub as above. When data is recevied fires...
Drop a SerialPort control (Tool box->Components) name it mySerialPort as sub above (Handles mySerialPort.DataReceive) and then rename it from control properties to any name you want (VB2005 translates automatically all your code)

Q:"3) Is it better to open the COM port once or open and close it before/after each command?"
I paste the code I use (you will have some errors when you paste it in your code, since I use some public variables-but-I guess it is to grasp the idea)

On formLoad I open it once like
Disconnect_COMPort()
'Initialize_UI()  '////initialize my user interface -you have your own
Connect_COMPort()

Have fun coding... ;)
Private Sub Connect_COMPort()

        Try

            If Not mySerialPort.IsOpen Then

                If MyConfiguredSerialPort.Length > 0 Then

                    Dim strCOM As String() = Split(MyConfiguredSerialPort, ",")

                    PortNameCOM1 = strCOM(0)

                    Disconnect_COMPort() 'Disconnect_mySerialPort

                    'Set ComPort Rates

                    mySerialPort.PortName = PortNameCOM1

                    mySerialPort.BaudRate = CInt(strCOM(1))

                    mySerialPort.Parity = CType([Enum].Parse(GetType(Ports.Parity), strCOM(2)), Ports.Parity)

                    mySerialPort.DataBits = CInt(strCOM(3))

                    mySerialPort.Handshake = CType([Enum].Parse(GetType(Ports.Handshake), strCOM(4)), Ports.Handshake)

                    mySerialPort.DtrEnable = True

                    mySerialPort.RtsEnable = True

                    If Not mySerialPort.IsOpen Then

                        mySerialPort.ReadBufferSize = 2

                        mySerialPort.ReadTimeout = 5000

                        mySerialPort.WriteTimeout = 5000

                        mySerialPort.Encoding = ASCII

                        Try

                            mySerialPort.Open()

                            defender.Pause(0.4)

                        Catch ex1 As Exception

                            CatchThisError(FRM_HEADER & "Connect_COMPort:" & PortNameCOM1 & Chr(251) & ex1.ToString)

                            If ShowErrorMessages Then MsgBox("Sub ->Connect_COMPort" & ex1.ToString, , PortNameCOM1)

                        End Try

                    End If

                End If

            End If

            update_mySerialPort_IconsAndLabels()

        Catch ex2 As Exception

            CatchThisError(FRM_HEADER & "Connect_COMPort:" & PortNameCOM1 & Chr(251) & ex2.ToString)

            If ShowErrorMessages Then MsgBox("Sub ->Connect_COMPort " & ex2.ToString, , PortNameCOM1)

        End Try

    End Sub

    Private Sub Disconnect_COMPort()

        Try

            If Me.mySerialPort.IsOpen Then

                mySerialPort.RtsEnable = False

                mySerialPort.DtrEnable = False

                mySerialPort.Close()

                Application.DoEvents()

                Sleep(1000)

            End If

        Catch ex As Exception

            CatchThisError(FRM_HEADER & "_" & "Disconnect_COMPort" & PortNameCOM1 & Chr(251) & ex.ToString)

            If ShowErrorMessages Then MsgBox("Sub ->Disconnect_COMPort " & PortNameCOM1 & ex.ToString, , FRM_HEADER)

        End Try

    End Sub

Open in new window

0
 

Author Comment

by:hsano6294
Comment Utility
Thanks for your help AkisC. My apologies- the system just deployed to the customer and we are in the middle of final integration and installation. I used portions of your suggestions in the final solution. There were additional issues that arose related to how the instrument communicates with its own internal components.
I have attached key portions of the code.
Thanks again, and my apologies for not posting this in a more timely manner.
    Private WithEvents _serialPort As IO.Ports.SerialPort
 
 

Friend Function Send(ByVal Data As String) As String

        ' Function will return to calling method as soon as it receives the processed return message.

        Dim returnMessage As String = String.Empty

        Try
 

            'Read the port buffer, timeout is set in New

            Dim received As String = String.Empty

            Try

                _serialPort.Write(Data)
 

                ' Read incoming reply

                Dim incomingMessage As String = String.Empty

                Dim _continue As Boolean = True

                Do

                    Thread.Sleep(30) ' Wait 30 msec before checking cmdIndex
 

                     If cmdIndex = 0 Then 'key at beginning, received processed message from DataReceived

                        returnMessage = inMessageBuffer

 

                        Return returnMessage ' Return before another incoming message disrupts things

                         _continue = False

                    End If

                Loop While (_continue)

            Catch ex As Exception

 

                'return has timed out - received will be blank

                Throw

            End Try
 

        Catch ex As Exception

            

            Throw
 

        End Try

 

        Return returnMessage

    End Function

-------------------------------

    ' Event Handler for incoming data

    Private Sub DataReceived( _

        ByVal sender As Object, _

        ByVal e As System.IO.Ports.SerialDataReceivedEventArgs) _

        Handles _serialPort.DataReceived
 

        Dim ValidMessage As Boolean = False

        inMessage = _serialPort.ReadLine
 

        inMessageBuffer = String.Empty

        cmdIndex = -999

        ' Dim key As String = "P"

        cmdIndex = inMessage.IndexOf(cmdKey)
 

        'If cmdIndex < 0 Then ' key not found, check if valid reply is embedded

        If (inMessage.Contains("PDCOMP")) Then ' Destack complete
 

            inMessageBuffer = "PDCOMP"

            cmdIndex = 0

            ValidMessage = True
 

        ElseIf (inMessage.Contains("PMCOMP")) Then ' MoveToStation or ReRead complete
 

            inMessageBuffer = "PMCOMP"

            cmdIndex = 0

            ValidMessage = True
 

	ElseIf ' check for other valid return messages
 

        End If
 

        'End If

        If (ValidMessage) Then
 

            ClosePort() ' Call ClosePort to immediately close serial port and prevent additional traffic from interrupting post-processing.
 

        End If
 

    End Sub

-------------------------------

    Friend Sub ClosePort()

        Try

            'Close serial port
 

            If (_serialPort IsNot Nothing) AndAlso _serialPort.IsOpen Then
 

                _serialPort.Close()

            Else

                ' Port is not open

            End If
 
 

        Catch ex As Exception
 

            Throw

        End Try

    End Sub

Open in new window

0
 
LVL 11

Expert Comment

by:AkisC
Comment Utility
Hi hsano6294
Nice you got it working!
If you face any issues let me know

0

Featured Post

6 Surprising Benefits of Threat Intelligence

All sorts of threat intelligence is available on the web. Intelligence you can learn from, and use to anticipate and prepare for future attacks.

Join & Write a Comment

Parsing a CSV file is a task that we are confronted with regularly, and although there are a vast number of means to do this, as a newbie, the field can be confusing and the tools can seem complex. A simple solution to parsing a customized CSV fi…
A long time ago (May 2011), I have written an article showing you how to create a DLL using Visual Studio 2005 to be hosted in SQL Server 2005. That was valid at that time and it is still valid if you are still using these versions. You can still re…
Internet Business Fax to Email Made Easy - With eFax Corporate (http://www.enterprise.efax.com), you'll receive a dedicated online fax number, which is used the same way as a typical analog fax number. You'll receive secure faxes in your email, fr…
When you create an app prototype with Adobe XD, you can insert system screens -- sharing or Control Center, for example -- with just a few clicks. This video shows you how. You can take the full course on Experts Exchange at http://bit.ly/XDcourse.

762 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

12 Experts available now in Live!

Get 1:1 Help Now