从串口(ModibusRTU)响应中提取UInt16

问题描述 投票:0回答:1

我(编程新手)正在尝试在 VB2019 上使用 VB.net(脚本)来查询老式 PID 控制器。它通过 PIC 卡 RS485 Modbus RTU 连接到我的桌面。虽然它是 Modbus 连接,但控制器不使用像

F03
这样的标准功能代码来读取其寄存器,而是使用数组
81 81 52 00 00 53
(查看下图)..

example of vb.net

我修改了在 YouTube 上找到的代码,以便能够发送接收数据;我收到

98 42 01 80 00 10 A0
;根据手册(wE49B),结果应该是
42 98 08 01 00 A0 10
;其中我最感兴趣的是(
98 42
),其中包含PV值(DEC:17048); 导入 System.IO.Ports 导入 System.Threading

公开课ReadHoldingRegistersForm

'Declare variables & constants
Private serialPort As SerialPort = Nothing

Private Sub ReadHoldingRegistersForm_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    Try
        serialPort = New SerialPort("COM4", 9600, Parity.None, 8, StopBits.Two)
        serialPort.Open() 'Open COM4
    Catch ex As Exception
        MessageBox.Show(Me, ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error)
    End Try
End Sub

Private Sub ReadHoldingRegistersForm_FormClosing(sender As Object, e As FormClosingEventArgs) Handles MyBase.FormClosing
    Try
        serialPort.Close() ' Close COM4.
    Catch ex As Exception
        MessageBox.Show(Me, ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error)
    End Try
End Sub

Private Sub btnReadHoldingRegisters_Click(sender As Object, e As EventArgs) Handles btnReadHoldingRegisters.Click
    Try
        Dim MeterAddress As Byte = 129
        Dim MeterAddress1 As Byte = 129
        Dim startAddress As Byte = 82
        Dim ReadOpMaker As Byte = 0
        Dim ReadData As Byte = 0
        Dim ReadData1 As Byte = 0
        Dim ReadPMaeker As Byte = 83
        Dim ReadPcode As Byte = ReadOpMaker

        Dim frame As Byte() = Me.ReadHolingRegisters(MeterAddress, MeterAddress1, startAddress, ReadOpMaker, ReadData, ReadData1, ReadPMaeker, ReadPcode)
        txtSendMsg.Text = Me.DisplayValue(frame) ' Diplays frame: send.
        serialPort.Write(frame, 0, frame.Length) ' Send frame to modbus slave.

        Thread.Sleep(100) ' Delay 100ms.

        If serialPort.BytesToRead > 5 Then
            Dim buffRecei As Byte() = New Byte(serialPort.BytesToRead) {}
            serialPort.Read(buffRecei, 0, buffRecei.Length) ' Read data from modbus slave.
            txtReceiMsg.Text = Me.DisplayValue(buffRecei) ' Display frame: received.

            Dim data As Byte() = New Byte(buffRecei.Length - 5) {}
            Array.Copy(buffRecei, 3, data, 0, data.Length)

            ' Convert byte array to word array
            Dim result As UInt16() = DataType.Word.ToArray(data) '

            'Display Result

            For Each item As UInt16 In result
                txtResult.Text += String.Format("{0:0} ", item)
            Next
        End If

    Catch ex As Exception
        MessageBox.Show(Me, ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error)
    End Try
End Sub

''' <summary>
''' Read holding registers
''' </summary>
''' <param name="MeterAddress">Slave Address</param>
''' <param name="MeterAddress1">Function</param>
''' <param name="startAddress">Starting Address</param>

''' <returns>Byte()</returns>
Private Function ReadHolingRegisters(MeterAddress As Byte, MeterAddress1 As Byte, startAddress As Byte, ReadOpMaker As Byte, ReadData As Byte, ReadData1 As Byte, ReadPMaeker As Byte, ReadPcode As Byte) As Byte()
    Dim frame As Byte() = New Byte(7) {} ' Total 8 Bytes
    frame(0) = MeterAddress ' Slave Address
    frame(1) = MeterAddress1 'Function
    frame(2) = startAddress  'Starting Address Hi.
    frame(3) = ReadOpMaker  'Starting Address Lo.
    frame(4) = ReadData  ' Quantity of Registers Hi.
    frame(5) = ReadData1  ' Quantity of Registers Lo.
    frame(6) = ReadPMaeker ' Error Check Lo
    frame(7) = ReadPcode ' Error Check Hi
    Return frame '
End Function

手册指出: Part of manual showing structure of receive data

并且: Part of manual showing structure of sent data

目前价值为32768???无论如何,我希望得到指导如何通过代码收集 PV(98 42)(DEC:17048) 并将其显示在文本栏上。以下是我相信我需要指导才能实现我的目标的部分代码。

        If serialPort.BytesToRead > 5 Then
            Dim buffRecei As Byte() = New Byte(serialPort.BytesToRead) {}
            serialPort.Read(buffRecei, 0, buffRecei.Length) ' Read data from modbus slave.
            txtReceiMsg.Text = Me.DisplayValue(buffRecei) ' Display frame: received.

            Dim data As Byte() = New Byte(buffRecei.Length - 5) {}
            Array.Copy(buffRecei, 3, data, 0, data.Length)

            ' Convert byte array to word array
            Dim result As UInt16() = DataType.Word.ToArray(data) '

            'Display Result

            For Each item As UInt16 In result
                txtResult.Text += String.Format("{0:0} ", item)
            Next
        End If

“数据类型”来自于此: 导入 System.Collections.Generic 导入 System.Text

命名空间数据类型 公开课词 #Region“Chuyển đổi mảng bytes thành kiểu Word。”

    ''' <summary>
    ''' Phương thức chuyển đổi mảng bytes thành kiểu Word.
    ''' </summary>
    ''' <param name="bytes">Mảng byte cần chuyển đổi</param>
    ''' <returns>Trả về giá trị kiểu Word</returns>
    Public Shared Function FromByteArray(bytes As Byte()) As UInt16
        ' bytes[0] -> HighByte
        ' bytes[1] -> LowByte
        Return FromBytes(bytes(1), bytes(0))
    End Function

    ''' <summary>
    ''' Phương thức chuyển đổi mảng bytes thành kiểu Word.
    ''' </summary>
    ''' <param name="LoVal">Giá trị byte thấp</param>
    ''' <param name="HiVal">Giá trị byte cao</param>
    ''' <returns>Trả về giá trị kiểu Word</returns>
    Public Shared Function FromBytes(LoVal As Byte, HiVal As Byte) As UInt16
        Return CType(HiVal * 256 + LoVal, UInt16)
    End Function

#结束区域

#Region“Chuyển đổi kiểu Word thành mảng 字节。”

    ''' <summary>
    ''' Phương thức chuyển đổi kiểu Word thành mảng bytes.
    ''' </summary>
    ''' <param name="value">Giá trị kiểu Word</param>
    ''' <returns>Trả về giá trị mảng kiểu byte</returns>
    Public Shared Function ToByteArray(value As UInt16) As Byte()
        Dim array1 As Byte() = BitConverter.GetBytes(value)
        Array.Reverse(array1)
        Return array1
    End Function

    ''' <summary>
    ''' Phương thức chuyển đổi mảng kiểu Word thành mảng bytes.
    ''' </summary>
    ''' <param name="value">Mảng kiểu Word</param>
    ''' <returns>Trả về mảng kiểu byte</returns>
    Public Shared Function ToByteArray(value As UInt16()) As Byte()
        Dim arr As New ByteArray()
        For Each val As UInt16 In value
            arr.Add(ToByteArray(val))
        Next
        Return arr.array
    End Function

    ''' <summary>
    ''' Phương thức chuyển đổi kiểu mảng bytes thành mảng word.
    ''' </summary>
    ''' <param name="bytes">Giá trị mảng bytes</param>
    ''' <returns>Trả về giá trị mảng kiểu Word</returns>
    Public Shared Function ToArray(bytes As Byte()) As UInt16()
        Dim values As UInt16() = New UInt16(bytes.Length \ 2 - 1) {}
        Dim counter As Integer = 0
        For cnt As Integer = 0 To values.Length - 1 Step 2
            values(cnt) = FromByteArray(New Byte() {bytes(cnt), bytes(cnt + 1)})
        Next
        Return values
    End Function

#结束区域

End Class

结束命名空间

vb.net modbus
1个回答
0
投票

让我们大大简化您的代码:

Dim buffRecei() as Byte= { &H98, &H42, &H01, &H80, &H00, &H10, &Ha0, &H00, &H00 }

Dim data As Byte() = New Byte(buffRecei.Length - 5) {}
Console.WriteLine(BitConverter.ToString(buffRecei).Replace("-"," "))
Array.Copy(buffRecei, 3, data, 0, data.Length)
Console.WriteLine(BitConverter.ToString(data).Replace("-"," "))

Dim result As UInt16() = ToArray(data)
Dim txtResult as string
For Each item As UInt16 In result
         txtResult += String.Format("{0} ", item.ToString())
Next
Console.WriteLine(txtResult)

输出:

98 42 01 80 00 10 A0 00 00
80 00 10 A0 00
32768 0 

希望这能表明正在发生的事情。您将使用

Array.Copy
丢弃前 3 个字节,然后将
80 00
转换为 int16(
0x8000
= 32768 十进制)。

要获得您想要的结果,您可以执行以下操作:

Dim data() as Byte= { &H98, &H42, &H01, &H80, &H00, &H10, &Ha0, &H00, &H00 }
Console.WriteLine(BitConverter.ToString(data).Replace("-"," "))

Dim result As UInt16() = ToArray(data)
Dim txtResult as string
For Each item As UInt16 In result
         txtResult += String.Format("{0} ", item.ToString())
Next
Console.WriteLine(txtResult)

' Alternative
dim val as UInt16 = BitConverter.ToUInt16(data, 0)
Console.WriteLine(val)

将输出:

98 42 01 80 00 10 A0 00 00
38978 0 384 0 
17048

那么为什么一个输出

38978
,另一个输出
17048
,答案是无尽

  • 0x9842 = 38978 十进制(大端)
  • 0x4298 = 17048 十进制(小端)

您可能会认为第一种方法(大端)是合乎逻辑的,但不同的芯片以不同的方式组织内存(并且通常以与内存中保存的顺序相同的顺序通过串行链路发送数据)。您使用的代码做出了一个不成立的假设(而

BitConverter
使用您的系统字节顺序,因此上述代码在不同的系统上可能会产生不同的结果)。

您展示的手册(请注意,这不是 Modbus;设备可能支持 Modbus,但它也支持其他内容!)明确指出第一个字节是

Low
;所以你需要按如下方式编辑函数:

' Old: values(cnt) = FromByteArray(New Byte() {bytes(cnt), bytes(cnt + 1)})
values(cnt) = FromByteArray(New Byte() {bytes(cnt+1), bytes(cnt)})

这里是我在在线游乐场中的代码(因此您可以使用它)。

© www.soinside.com 2019 - 2024. All rights reserved.