You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

VB.NET读取Modbus RTU非标准帧中UInt16类型PV值的技术问题

解决VB.NET读取自定义Modbus RTU帧提取PV值的问题

问题背景

使用VB2019编写VB.NET程序,通过RS485 Modbus RTU连接老式PID控制器。控制器不支持标准03功能码,需使用自定义发送帧81 81 52 00 00 53读取寄存器。当前收到的响应帧为98 42 01 80 00 10 A0,手册说明PV值对应帧中98 42(十进制17048),但程序提取结果为32768,需修正代码获取正确值。

控制器帧格式说明

发送帧结构

发送数据结构
自定义发送帧对应手册中的地址、功能码等字段,固定为81 81 52 00 00 53。

接收帧结构

接收数据结构
响应帧前两个字节为PV值,字节顺序为低字节在前,高字节在后(即98是低字节,42是高字节),计算方式为:高字节 × 256 + 低字节 = 0x42 ×256 +0x98 = 66×256+98=17048。

现有代码问题分析

  1. 数据提取位置错误:当前代码从响应帧索引3开始复制数据,实际PV值位于响应帧前两个字节(索引0和1)。
  2. DataType转换逻辑错误:Word.ToArray方法的数组长度计算错误,导致循环取值异常;同时字节顺序处理不符合控制器的帧格式要求。

修改后的核心代码

替换原btnReadHoldingRegisters_Click中的响应处理部分:

If serialPort.BytesToRead > 1 Then ' PV值占2字节,只需判断至少2字节即可
    Dim buffRecei As Byte() = New Byte(serialPort.BytesToRead - 1) {} ' 修正数组长度,避免空元素
    serialPort.Read(buffRecei, 0, buffRecei.Length)
    txtReceiMsg.Text = Me.DisplayValue(buffRecei)

    ' 提取前2字节作为PV值的字节数组
    Dim pvBytes As Byte() = New Byte(1) {}
    Array.Copy(buffRecei, 0, pvBytes, 0, 2)

    ' 转换为正确的PV值
    Dim pvValue As UInt16 = DataType.Word.FromByteArray(pvBytes)
    txtResult.Text = String.Format("PV值:{0}", pvValue)
End If

修正DataType.Word类

修复ToArray方法的数组长度和循环逻辑,同时确保FromByteArray的字节顺序符合控制器要求:

Imports System.Collections.Generic
Imports System.Text

Namespace DataType
    Public Class Word
#Region "将字节数组转换为Word类型"
        ''' <summary>
        ''' 将字节数组转换为Word类型(低字节在前,高字节在后)
        ''' </summary>
        ''' <param name="bytes">字节数组,长度为2</param>
        ''' <returns>Word类型值</returns>
        Public Shared Function FromByteArray(bytes As Byte()) As UInt16
            ' bytes(0) = 低字节,bytes(1) = 高字节
            Return FromBytes(bytes(0), bytes(1))
        End Function

        ''' <summary>
        ''' 将高低字节转换为Word类型
        ''' </summary>
        ''' <param name="lowByte">低字节</param>
        ''' <param name="highByte">高字节</param>
        ''' <returns>Word类型值</returns>
        Public Shared Function FromBytes(lowByte As Byte, highByte As Byte) As UInt16
            Return CType(highByte * 256 + lowByte, UInt16)
        End Function
#End Region

#Region "将Word类型转换为字节数组"
        Public Shared Function ToByteArray(value As UInt16) As Byte()
            Dim array1 As Byte() = BitConverter.GetBytes(value)
            Array.Reverse(array1)
            Return array1
        End Function

        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>
        ''' 将字节数组转换为Word数组(低字节在前,高字节在后)
        ''' </summary>
        ''' <param name="bytes">输入字节数组</param>
        ''' <returns>Word数组</returns>
        Public Shared Function ToArray(bytes As Byte()) As UInt16()
            If bytes.Length Mod 2 <> 0 Then
                Throw New ArgumentException("字节数组长度必须为偶数")
            End If
            Dim values As UInt16() = New UInt16(bytes.Length \ 2 - 1) {}
            Dim counter As Integer = 0
            For cnt As Integer = 0 To bytes.Length - 1 Step 2
                values(counter) = FromByteArray(New Byte() {bytes(cnt), bytes(cnt + 1)})
                counter += 1
            Next
            Return values
        End Function
#End Region
    End Class
End Namespace

测试验证

修改后运行程序,点击读取按钮:

  • 发送帧应为81 81 52 00 00 53
  • 接收帧显示98 42 01 80 00 10 A0
  • 文本框将显示正确的PV值:PV值:17048

内容的提问来源于stack exchange,提问作者Mwanachingwala Judge Munyama

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.08 15:12:03