Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people, just like you, are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
Solved

Reading asynchronous serial port data in VB 2005

Posted on 2008-06-12
7
1,442 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
ID: 21776720
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
ID: 21861319
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
ID: 21862579
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
ID: 22145839
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
ID: 22145937
Hi hsano6294
Nice you got it working!
If you face any issues let me know

0

Featured Post

The New “Normal” in Modern Enterprise Operations

DevOps for the modern enterprise offers many benefits — increased agility, productivity, and more, but digital transformation isn’t easy, especially if you’re not addressing the right issues. Register for the webinar to dive into the “new normal” for enterprise modern ops.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

This article describes relatively difficult and non-obvious issues that are likely to arise when creating COM class in Visual Studio and deploying it by professional MSI-authoring tools. It is assumed that the reader is already familiar with the cla…
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…
Established in 1997, Technology Architects has become one of the most reputable technology solutions companies in the country. TA have been providing businesses with cost effective state-of-the-art solutions and unparalleled service that is designed…
With Secure Portal Encryption, the recipient is sent a link to their email address directing them to the email laundry delivery page. From there, the recipient will be required to enter a user name and password to enter the page. Once the recipient …

856 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