无法解码串口数据

Unable to decode serial port data

我试图通过串行端口从医疗 'Mindray bs 200' 设备接收数据。收到数据但不可读。找不到数据加密的种类。

这里是接收数据的代码

Private Sub comPort_DataReceived(ByVal sender As Object, ByVal e As System.IO.Ports.SerialDataReceivedEventArgs) Handles comPort.DataReceived
    Dim str As String = ""
    If e.EventType = SerialData.Chars Then
        Do
            Dim bytecount As Integer = comPort.BytesToRead
            If bytecount = 0 Then
                Exit Do
            End If
            Dim byteBuffer(bytecount) As Byte

            comPort.Encoding = Encoding.GetEncoding(28591)
            ' comPort.Encoding = Encoding.GetEncoding(1252)
            'comPort.Encoding = Encoding.GetEncoding("Windows-1252")
            comPort.Read(byteBuffer, 0, bytecount)
            str = str & System.Text.Encoding.ASCII.GetString(byteBuffer, 0, 1)
            ' The str looks like
        Loop
    End If
    RaiseEvent ScanDataRecieved(str)
End Sub

这是收到的数据 ???????????????????????????????????????? ???????????????????????????????????????? ???????????X???????????????

虽然不是针对此特定设备,this document 讨论了使用相同 RS232 通信协议的设备并推荐以下 SerialPort 设置:

  • 波特率:115200
  • 数据位:8
  • 停止位:1
  • 奇偶校验:None
  • 无流量控制

注意:最小波特率为57600。

根据this document

2.1 Message Grammar:

Each HL7 message is composed of segments that end with <CR>

3 Communication Process and Message Example:

A message of HL7 protocol is the format of: <SB> ddddd <EB><CR>

                                   ASCII value (HEX)

<SB> (start of message):           0B  (VT - vertical tab)
<EB> (end of message):             1C  (FS - file separator)
<CR> (carriage return)             0D  (carriage return)

在下面的代码中,我将展示如何在收到“消息开始”(十六进制:0B)时使用缓冲区收集数据,并填充缓冲区直到“消息结束”(十六进制:1C)收到回车 return (HEX: 0D) - 此时我们将引发一个事件 and/or 输出数据。

为了测试,我们还将数据写入 Documents 文件夹中的文件 - 每次程序启动时都会删除该文件,因此文件中仅包含当前程序执行的数据。数据的每个字节都输出为 2 位十六进制值,这使我们可以查看某个值是否为控制字符。

以下内容可能有助于解释这些值:


创建一个class(名称:HelperSerialPort.vb)

HelperSerialPort.vb

注意:方法 Port_DataReceived 中的代码(在 if-elseif 语句中)未经测试 - 我没有您所用的特定设备重新使用以测试它。

'(.NET Framework) - Add reference: Project => Add Reference => Assemblies => System.Management

'add using statement: Using System.Management;
'
'For >= .NET 5, install NuGet Package: System.IO.Ports and System.Management
'add Imports statements: Imports System.IO.Ports; Imports System.Management;
'
'

Imports System.Management
Imports System.IO.Ports

'specify valid baud rates
Public Enum PortBaudRate As Integer
    Baud57600 = 57600
    Baud76800 = 76800
    Baud115200 = 115200
End Enum

Public Class HelperSerialPort
    Implements IDisposable

    Private Const BufferSize As Integer = 4096 'this value may need to be changed
    Private Port As SerialPort = Nothing
    Private BytesReadMessage As Integer = 0 'used to hold message bytes read
    Private Buffer(BufferSize) As Byte 'used to hold data
    Private Filename As String = Nothing
    Private IsMessage As Boolean = False

    'events that can be subscribed to
    Public Event DataReceived(ByVal sender As Object, ByVal data As String)
    Public Event ErrorReceived(ByVal sender As Object, ByVal errMsg As String)

    Sub New()
        'set value
        'data will be written to this file for testing
        Filename = System.IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "Mindray BS200.txt")

        If Not String.IsNullOrEmpty(Filename) AndAlso System.IO.File.Exists(Filename) Then
            'delete existing file
            System.IO.File.Delete(Filename)
        End If

        System.Diagnostics.Debug.WriteLine("Filename: '" & Filename & "'")
    End Sub

    Public Function Connect(ByVal comPort As String, ByVal Optional baudRate As PortBaudRate = PortBaudRate.Baud115200) As String
        Dim errMsg As String = String.Empty
        Dim portName As String = String.Empty
        Dim result As String = String.Empty

        If String.IsNullOrEmpty(comPort) Then
            errMsg = "COM port not selected"
            Throw New Exception(errMsg)
        End If

        Try
            If Port Is Nothing Then

                'create new instance
                Port = New SerialPort(comPort)

                'subscribe to events (add event handlers)
                AddHandler Port.DataReceived, AddressOf Port_DataReceived
                AddHandler Port.ErrorReceived, AddressOf Port_ErrorReceived

            End If

            If Not Port.IsOpen Then

                'set properties
                Port.BaudRate = baudRate
                Port.Handshake = Handshake.None

                'if parity is even or odd, then set DataBits = 7
                'if parity is none, set DataBits = 8
                Port.Parity = Parity.None
                Port.DataBits = 8

                Port.StopBits = StopBits.One
                'Port.ReadTimeout = 500 'this value may need to be adjusted
                Port.WriteTimeout = 500 'this value may need to be adjusted
                Port.DtrEnable = True 'enable Data Terminal Ready
                'Port.RtsEnable = True 'enable Request to Send
                'Port.DiscardNull = True
                'Port.ReceivedBytesThreshold = 1 'number of bytes that causes 'DataReceived' event to be raised; default is 1

                'open port
                Port.Open()

                result = "Status: Connected"
            Else
                result = "Status: Already Connected"
            End If

        Catch ex As System.IO.IOException
            errMsg = "Error: " & ex.Message
            result = errMsg 'set value
            Debug.WriteLine(errMsg)

            Dispose()

        Catch ex As Exception
            errMsg = "Error: " & ex.Message
            result = errMsg 'set value
            Debug.WriteLine(errMsg)
            Throw ex
        End Try

        Debug.WriteLine(result)

        Return result
    End Function

    Public Function Disconnect() As String
        Dispose()

        Return "Status: Disconnected"
    End Function

    Public Sub Dispose() Implements System.IDisposable.Dispose
        If Port IsNot Nothing Then

            'unsubscribe from events (remove event handlers)
            RemoveHandler Port.DataReceived, AddressOf Port_DataReceived
            RemoveHandler Port.ErrorReceived, AddressOf Port_ErrorReceived

            Port.Dispose()
            Port = Nothing
        Else
            Debug.WriteLine("Info: Port is null")
        End If
    End Sub

    Public Function IsPortOpen() As Boolean
        If Port IsNot Nothing Then
            If Port.IsOpen Then
                Return True
            Else
                Try
                    Port.Dispose()
                Catch ex As Exception
                    'do nothing
                    Debug.WriteLine("Error (IsPortOpen): " & ex.Message)
                End Try
            End If

            Port = Nothing
            System.GC.Collect()
            System.GC.Collect()
        End If

        Return False
    End Function

    Private Sub Port_DataReceived(ByVal sender As Object, ByVal e As SerialDataReceivedEventArgs)
        'ToDo: add desired code

        Dim bytesRead As Integer = 0
        Dim bytesToRead As Integer = 1
        Dim errMsg As String = String.Empty

        'read SerialPort data
        Do
            'read 1 byte at a time
            bytesRead = Port.Read(Buffer, BytesReadMessage, bytesToRead)

            'when <SB> (HEX: 0B) is received, remove all existing data from buffer
            'this will result in the buffer only containing our desired data.
            'when <EB> (HEX: 1C) is received followed by <CR> (HEX: 0D), 
            'this is the end of the message, so we'll send the message and/or display it.
            'Buffer(BytesReadMessage) contains the last byte of data that was read
            '

            If Buffer(BytesReadMessage) = &HB Then
                '0x0B - VT (vertical tab); in the documentation this is '<SB>' - start of message

                System.Diagnostics.Debug.WriteLine("<SB> (start of message)")

                'remove existing data (get ready for new message)
                Array.Clear(Buffer, 0, Buffer.Length)

                're-initialize to -1. It's incremented below which will result in the value being 0
                BytesReadMessage = -1

                'set value - start saving data to the buffer
                IsMessage = True

            ElseIf IsMessage AndAlso Buffer(BytesReadMessage) = &HD AndAlso Buffer(BytesReadMessage - 1) = &H1C Then
                '0x1C - FS (file separator); in the documentation this is '<EB>' - end of message
                '0x0D - CR (carriage return); in the documentation this is '<CR>' 

                System.Diagnostics.Debug.WriteLine("<EB> (end of message)")

                'raise event to send data
                SendData(Buffer)

                'set value - stop saving data to the buffer
                IsMessage = False

            ElseIf Not IsMessage Then
                'for debugging/testing, display non-message data
                System.Diagnostics.Debug.WriteLine("Non-message data: " & Buffer(BytesReadMessage).ToString("X2"))
            End If

            'set value
            BytesReadMessage += bytesRead

            Debug.WriteLine("Info: BytesReadMessage: " & BytesReadMessage.ToString() & " bytesRead: " & bytesRead.ToString() & " Port.BytesToRead: " & Port.BytesToRead.ToString())

        Loop While (Port.BytesToRead > 0)

        Debug.WriteLine(String.Format("{0}---------------------------{0}", System.Environment.NewLine))
    End Sub

    Private Sub Port_ErrorReceived(ByVal sender As Object, ByVal e As SerialErrorReceivedEventArgs)
        'ToDo: add desired code

        Dim errMsg As String = e.EventType.ToString()

        Debug.WriteLine("Port_ErrorReceived: " & errMsg)

        'raise event
        RaiseEvent ErrorReceived(Me, errMsg)
    End Sub

    Public Sub SendData(buffer() As Byte)
        'ToDo: modify the code below and/or add desired code

        'convert to a string that's human-readable

        Dim data As String = String.Empty
        If buffer IsNot Nothing Then
            For i As Integer = 0 To BytesReadMessage Step 1
                If Not String.IsNullOrEmpty(data) Then
                    data += " " 'append space
                End If

                'for testing, convert to a 2-digit HEX value
                data += buffer(i).ToString("X2")
            Next

            'add newline
            data += System.Environment.NewLine
        End If

        System.Diagnostics.Debug.WriteLine("data: " & data)

        'for testing save to file
        If Not String.IsNullOrEmpty(Filename) Then
            'append data to file
            System.IO.File.AppendAllText(Filename, data)
        End If

        'raise event
        RaiseEvent DataReceived(Me, data)

        'Note: "When receiving the message, the LIS (Laboratory Information Management System) host first judges the legality and type of the message and then replies accordingly."
        'ToDo: A response needs to be sent for each message received (see documentation for information on how to create the appropriate message - "3 Communication Process and Message Example")

    End Sub

    Public Sub WriteToSerialPort(ByVal data As String)
        Dim errMsg As String = String.Empty

        Try
            If Port.IsOpen Then

                'convert string to Byte array
                Dim hexArr As Byte() = System.Text.Encoding.ASCII.GetBytes(data)

                For Each hexVal As Byte In hexArr

                    'convert byte to byte array
                    Dim tempArr As Byte() = New Byte() {hexVal}

                    'write 
                    Port.Write(tempArr, 0, 1)

                    'add 1 ms delay before writing next byte
                    System.Threading.Thread.Sleep(1)
                Next
            Else
                errMsg = "Error: Port is not open. Please open the connection and try again."
                Debug.WriteLine(errMsg)
                Throw New Exception(errMsg)
            End If
        Catch ex As Exception
            errMsg = "Error: " & ex.Message
            Debug.WriteLine(errMsg)
            Throw ex
        End Try
    End Sub
End Class

下面是方法 Port_DataReceived 的替代版本,它使用 ReadExisting() 代替 - 但是,它可能不适用于您的情况。

Port_DataReceived(使用 ReadExisting 的版本):

Private Sub Port_DataReceived(ByVal sender As Object, ByVal e As SerialDataReceivedEventArgs)
    'ToDo: add desired code

    'read SerialPort data
    Dim data As String = String.Empty

    data = Port.ReadExisting()
    'data = Port.ReadLine

    Debug.WriteLine("Port_DataReceived: " & data)

    'raise event
    RaiseEvent DataReceived(Me, data)
End Sub

Form1:

  • 添加一个按钮(名称:btnConnectDisconnect)
  • 双击按钮添加事件处理程序
  • 添加 RichTextBox(名称:RichTextBox1)

Form1.vb

注意:在下面的代码中,您需要使用正确的 COM 端口更新 helper.Connect("COM1")

Public Class Form1
    Private helper As HelperSerialPort = New HelperSerialPort()
    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load

    End Sub

    Private Sub Disconnect()
        If helper IsNot Nothing Then
            'unsubscribe from event(s)
            RemoveHandler helper.DataReceived, AddressOf Helper_DataReceived
            RemoveHandler helper.ErrorReceived, AddressOf Helper_ErrorReceived

            helper.Dispose()
            helper = Nothing
        End If
    End Sub

    Private Sub btnConnectDisconnect_Click(sender As Object, e As EventArgs) Handles btnConnectDisconnect.Click
        If helper Is Nothing Then
            'create new instance
            helper = New HelperSerialPort()
        End If

        If btnConnectDisconnect.Text = "Connect" Then
            'clear existing data
            RichTextBox1.Clear()

            'ToDo: change to your desired COM port
            helper.Connect("COM1")

            'subscribe to event(s)
            AddHandler helper.DataReceived, AddressOf Helper_DataReceived
            AddHandler helper.ErrorReceived, AddressOf Helper_ErrorReceived

            'set text
            btnConnectDisconnect.Text = "Disconnect"
            btnConnectDisconnect.Refresh()
        Else
            Disconnect()

            'set text
            btnConnectDisconnect.Text = "Connect"
            btnConnectDisconnect.Refresh()
        End If
    End Sub

    Private Sub Helper_DataReceived(ByVal sender As Object, ByVal data As String)
        'ToDo: add desired code
        System.Diagnostics.Debug.WriteLine(DateTime.Now.ToString("HH:mm:ss") & " - Helper_DataReceived: " & data)

        'append data to RichTextBox
        RichTextBox1.Invoke(New MethodInvoker(Sub()
                                                  RichTextBox1.AppendText(DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss.fff") & " - " & data)
                                              End Sub))
    End Sub

    Private Sub Helper_ErrorReceived(ByVal sender As Object, ByVal errMsg As String)
        'ToDo: add desired code
        System.Diagnostics.Debug.WriteLine(DateTime.Now.ToString("HH:mm:ss") & " - Helper_ErrorReceived: " & errMsg)

        'append error message to RichTextBox
        RichTextBox1.Invoke(New MethodInvoker(Sub()
                                                  RichTextBox1.AppendText(errMsg)
                                              End Sub))
    End Sub

    Private Sub Form1_FormClosed(sender As Object, e As FormClosedEventArgs) Handles MyBase.FormClosed
        Disconnect()
    End Sub
End Class

资源: