Link to home
Start Free TrialLog in
Avatar of hsano6294
hsano6294Flag for United States of America

asked on

Reading asynchronous serial port data in VB 2005

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

Avatar of AkisC
AkisC
Flag of Greece image

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

Avatar of hsano6294

ASKER

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
ASKER CERTIFIED SOLUTION
Avatar of AkisC
AkisC
Flag of Greece image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
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

Hi hsano6294
Nice you got it working!
If you face any issues let me know