 YModbus寄存器类型转换:int、float、double和字节序)
GitHub 项目地址https://github.com/lidecong133/YModbus保持寄存器读出来是ushort[]。这很符合 Modbus 协议但不太符合人看数据的习惯。现场你想看的通常不是16856这种原始寄存器值而是温度23.5、压力1.25、计数器123456。这些值在设备内部可能是整数缩放也可能是float还可能是 32 位或 64 位整数。所以读寄存器只是第一步。第二步是把寄存器解释成人能看懂的值。先确认它到底是什么类型一个 Modbus 寄存器是 16 位也就是 2 个字节。类型常见占用short/ushort1 个寄存器int/uint2 个寄存器float/Single2 个寄存器long/ulong4 个寄存器double4 个寄存器如果手册写温度是float32一般就要读两个寄存器。ushort[]registersawaitclient.ReadHoldingRegistersAsync(100,2);如果手册写温度是UINT16倍率 0.1那就只读一个寄存器然后自己除以 10。这两种处理方式完全不同。比例系数不是字节序这个坑很常见。设备显示23.5寄存器读出来是235这时候不要急着按float解析。它很可能只是整数缩放ushortraw(awaitclient.ReadHoldingRegistersAsync(100,1))[0];decimaltemperatureraw/10m;字节序解决的是“几个字节怎么拼成一个类型”。比例系数解决的是“原始数值怎么换算成工程量”。这两个问题不要混在一起。混在一起以后现场就会开始盲试一会儿换字节序一会儿换地址一会儿换功能码最后哪个改动生效都说不清。再看字序和字节序如果设备确实存的是float或int32那就要看顺序。一个float占 4 个字节比如41 BC 00 00放进两个寄存器时可能是寄存器 0: 41 BC 寄存器 1: 00 00也可能是寄存器 0: 00 00 寄存器 1: 41 BCYModbus 把它拆成两个参数参数说明ModbusWordOrder多个 16 位寄存器之间哪个字在前ModbusByteOrder一个寄存器内部高字节还是低字节在前常见组合是HighWordFirstBigEndian但不能假定所有设备都这样。直接读成 typed 值如果你只想读一个 typed 值可以用扩展方法。读floatusingYModbus.Clients;usingYModbus.Protocol;floattemperatureawaitclient.ReadHoldingRegisterSingleAsync(startAddress:100,wordOrder:ModbusWordOrder.HighWordFirst,byteOrder:ModbusByteOrder.BigEndian);读int32intcounterawaitclient.ReadHoldingRegisterInt32Async(startAddress:120,wordOrder:ModbusWordOrder.LowWordFirst,byteOrder:ModbusByteOrder.BigEndian);输入寄存器也有对应方法floatpressureawaitclient.ReadInputRegisterSingleAsync(startAddress:0,wordOrder:ModbusWordOrder.HighWordFirst,byteOrder:ModbusByteOrder.BigEndian);区别只是功能码不同。保持寄存器走03输入寄存器走04。批量读完再转换实际项目里我更常用另一种方式先读一段寄存器再按地址表解析。例如你一次读 100 个保持寄存器里面有温度、压力、状态字、计数器。每个字段单独发请求效率低也不方便保持同一时刻的数据。这时候用RegisterConverterusingYModbus.Protocol;ushort[]registersawaitclient.ReadHoldingRegistersAsync(100,20);floattemperatureRegisterConverter.ToSingle(registers.AsSpan(0,2).ToArray(),ModbusWordOrder.HighWordFirst,ModbusByteOrder.BigEndian);intcounterRegisterConverter.ToInt32(registers.AsSpan(2,2).ToArray(),ModbusWordOrder.LowWordFirst,ModbusByteOrder.BigEndian);写入时也可以反过来转ushort[]valuesRegisterConverter.FromSingle(23.5F,ModbusWordOrder.HighWordFirst,ModbusByteOrder.BigEndian);awaitclient.WriteMultipleRegistersAsync(100,values);怎么判断顺序对不对最稳的办法是找一个已知值。比如设备屏幕显示温度23.5你读出来两个寄存器。然后用不同组合解析看哪个结果接近23.5。可以临时这样试foreach(ModbusWordOrderwordOrderinEnum.GetValuesModbusWordOrder()){foreach(ModbusByteOrderbyteOrderinEnum.GetValuesModbusByteOrder()){floatvalueRegisterConverter.ToSingle(registers,wordOrder,byteOrder);Console.WriteLine(${wordOrder},{byteOrder}:{value});}}如果某个组合解析出来是4.17E-41这种离谱值基本就不是它。但这个办法只是为了确认设备格式。确认以后最好把格式写进地址表不要每次靠试。地址表里要记录格式一个可维护的地址表至少应该记录这些东西字段示例地址100功能码03数据类型Float32字序HighWordFirst字节序BigEndian比例系数1.0单位℃名称当前温度只记录“地址”和“名称”是不够的。等项目过几个月再维护谁也不记得当时这个值为什么要低字在前为什么要除以 10。写入 typed 值要更谨慎读错通常只是显示不对。写错可能会改设备参数。YModbus 有 typed write helperawaitclient.WriteHoldingRegisterSingleAsync(startAddress:100,value:23.5F,wordOrder:ModbusWordOrder.HighWordFirst,byteOrder:ModbusByteOrder.BigEndian);写int32awaitclient.WriteHoldingRegisterInt32Async(startAddress:120,value:123456,wordOrder:ModbusWordOrder.LowWordFirst,byteOrder:ModbusByteOrder.BigEndian);真正对设备写之前先确认地址可写、数值范围安全、字序字节序和手册一致。最好写完以后再读回一次确认设备里实际变成了什么。到这里寄存器转换不是 YModbus 特有的问题是 Modbus 本身就这样。YModbus 能帮你做两件事直接用 typed helper 读写int、float、double用RegisterConverter在批量读出的寄存器上做转换但设备到底是整数缩放、还是 IEEE754 浮点数字序怎么排比例系数是多少还是要回到设备手册和现场验证。