ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Modbus TCP调试避坑指南:字节序问题深度解析

Modbus TCP调试避坑指南:字节序问题深度解析 做现场调试这些年Modbus TCP 是我用得最多的协议之一。它和 Modbus RTU 比起来确实省心不用折腾 485 接线不用管收发切换直接把网线插上IP 配好就能通讯。但我发现一个有意思的现象越是觉得它简单越容易在一个地方栽跟头。前段时间去一个现场处理触摸屏和仪表通讯的问题数据能读上来数值却怎么也对不上一会儿是正的一会儿是负的排查了大半天最后定位到问题出在字节序上。这个坑藏得是真深它不会给你报错也不影响通讯状态就是你读上来的数据不对而且不同设备表现还不一样。这篇文章我把 Modbus TCP 最常见的坑整理了一遍重点会放在藏得最深的字节序问题上然后结合威纶通触摸屏、汇川 AM 系列、KingSCADA 这些实际场景把配置和排查方法完整过一遍。1. 先看清 Modbus TCP 的全貌才知道坑藏在哪里1.1 Modbus TCP 与 RTU 的本质区别Modbus TCP 和 RTU 在应用层的数据模型、功能码、寄存器定义是完全一致的最大的区别在传输层。RTU 走的是串口报文里没有 IP 和端口的概念用 CRC 校验保证数据完整性TCP 走的是以太网报文前面多了一个 7 字节的 MBAP 报文头去掉 CRC 校验换成 TCP/IP 自带的校验机制。这个区别带来一个实际问题很多人习惯了 RTU 的报文结构和地址计算方式转成 TCP 之后还是按老思路去填参数结果就掉坑里了。比如 RTU 报文里从站地址是 1 个字节直接填在功能码前面而在 TCP 报文里这个地址变成了 MBAP 头部的单元标识符Unit ID并且它和 IP 地址并不是一一对应的关系。有些设备同一个 IP 后面可以带多个 Modbus 从站靠 Unit ID 区分而有些设备直接把 Unit ID 忽略掉这就给通讯联调增加了变数。再说端口号。Modbus TCP 的标准端口是 502绝大多数设备默认监听这个端口。但有些设备支持自定义端口如果现场有两套系统都用 502 端口就可能需要把其中一套改成其他端口这时候上位机和触摸屏里对应的地方也得改。我在一个项目里遇到过组态软件默认填 502但设备实际监听 1502结果连了半天连不上查到最后才发现是端口不匹配。1.2 一张表看清 Modbus TCP 最常见的坑把平时见过的坑梳理一下基本逃不出下面这几类坑的类型表现隐蔽程度字节序问题数据能读到数值错误最高通讯一切正常就是数据不对地址偏移读到相邻地址的数据高只差一位不容易发现Unit ID 配置错误网关或设备无响应中看设备实现有的不校验有的不理会功能码混淆读上来的数据全为 0 或报异常码中按 03 读不了输入寄存器轮询超时和重连机制通讯偶发中断中取决于现场网络质量寄存器数据类型搞错16 位数当 32 位读数值异常高规约文档写得清楚但没人细看这里头字节序问题最坑因为它不会让通讯报错数据链路完全正常你甚至在触摸屏上能看到数值在变化只是数值的大小和正负号不对。很多工程师在这个问题上耗了一两天才反应过来。2. 藏得最深的那个坑字节序问题2.1 为什么字节序会把你坑到怀疑人生要理解字节序问题得先清楚 Modbus 寄存器的数据模型。Modbus 寄存器是 16 位2 个字节一个寄存器可以存一个 0 到 65535 的整数或者 -32768 到 32767 的带符号整数。如果需要传输 32 位的整数或者浮点数就得连续占用两个寄存器。问题就出在这个“连续占用”上。两个寄存器一共 4 个字节这 4 个字节按什么顺序组合成最终的数值不同厂商的设备处理方式不一样而 Modbus 协议本身并没有强制规定 32 位数据的字节序规则。这就好比两个人约定用四个数字表示一个数字但一个人习惯从左往右读另一个人习惯从右往左读两边都没错凑在一起就对不上。拿一个温控仪表读取温度举例假设实际温度是 100.0 摄氏度在 IEEE 754 浮点数格式下这个值用十六进制表示是 0x42C80000四个字节依次是 42 C8 00 00。如果设备按大端序发送读上来的两个寄存器是 0x42C8 和 0x0000上位机按大端序组合成果 0x42C80000转换后就是 100.0没问题。但如果上位机默认按小端序处理组合成 0x000042C8转换后就是 2.98E-41一个几乎等于 0 的数。更麻烦的是有的设备还会把高低 16 位反过来寄存器显示为 0x0000 和 0x42C8处理不好又是一套完全不同的数值。16 位整数也存在字节序问题但相对简单因为只有一个寄存器最多就是高低 8 位交换。比如仪表读上来的值是 0x1234正常解析是 4660但如果字节颠倒读出来就是 0x3412也就是 13330。如果你只是在做 16 位整数通讯那你要关心的就是“字内字节交换”这个选项。32 位数据则要复杂得多因为存在四种常见组合方式大端字节序加大端字序、大端字节序加小端字序、小端字节序加小端字序以及各种混合排列。很多触摸屏和组态软件在定义 32 位变量时都会提供“双字高字节在前/低字节在前”“字内字节交换”这类选项就是用来适配不同设备的字节序规则。2.2 怎么判断设备的字节序规则判断字节序最靠谱的办法是找设备说明书里的寄存器表看里面有没有关于“数据格式”或“字序”的说明。但现实是很多设备的说明书只给了寄存器地址压根不提字节序遇到这种情况就只能实测。实测的方法很简单。第一步找一个已知的寄存器最好是那种当前值固定的量比如设备型号、固件版本号、量程上限等。第二步用 Modbus Poll 之类的工具去读这个寄存器观察返回的原始十六进制数值。第三步把读回来的寄存器和设备文档上的实际值做对照就能推断出设备的字节序规则。举个例子设备文档说地址 0x0000 存的是压力值单位是 kPa当前实际压力是 25.6 kPa浮点数表示为 0x41CCCCCD。用 Modbus Poll 读回来的两个寄存器如果显示为 0x41CC 和 0xCCCD说明设备按大端序发送从高位字到低位字如果显示为 0xCCCD 和 0x41CC说明高低字交换了上位机需要做对应的字序调整。还有一种情况读回来的寄存器本身内部的高低字节也换了比如 0xCC41那就不光要交换高低字还要做字节交换。实际调试的时候我习惯先把一个已知的整数写到设备里再读回来对比这样比盲猜快很多。比如向保持寄存器写入 0x1234然后读回来如果读出来也是 0x1234说明 16 位字节序没问题如果读出 0x3412那就要在触摸屏或组态软件的“字内字节交换”选项上打勾。2.3 不同上位机和触摸屏里的字节序处理威纶通触摸屏在走 Modbus TCP 时处理 32 位数据的地方在系统寄存器 LW 和扩展寄存器 RW 的类型设置里。新建变量时如果数据类型选的是 32 位 Float 或 32 位 Signed Integer触摸屏默认按自己的规则解析但如果设备不是按同样的规则发送就需要去设备属性中找“字组排列”相关的选项。威纶通的设置入口一般是在系统参数——设备属性的“高级”选项里里面有关于 32 位数据排列顺序的设置不同版本叫法可能不一样有的叫“双字顺序”有的叫“4x 格式”实际在配置时要看清楚说明再选。KingSCADA 连接 Modbus TCP 设备时变量定义对话框里通常会有一个“数据类型”下拉框对于 32 位数据可以选择“2 个寄存器组合”然后手动指定高低字序。我用的版本里有“高字在前”和“低字在前”两个选项分别对应大端字序和小端字序。除此之外还有一个单独的“字节交换”选项这是用来处理寄存器内部高低字节颠倒的情况。这三个选项组合起来基本能覆盖所有设备的字节序习惯。汇川 AM 系列 PLC 做 Modbus TCP Server 时字节序问题出在内部寄存器的排列方式上。AM 系列本身是按小端序存储数据的如果直接用 MOV 指令把浮点数写入 Modbus 映射区而上位机按大端序解析那读出来的数值就是反的。这时候需要在编写服务端程序时把浮点数拆成字节再重新排列或者在上位机端做一次高低字交换二选一即可。说到底字节序这个问题没有统一规范全靠现场适配。最稳妥的办法是调试时先读已知值确认字节序规则后再批量配置变量不要等几百个点位都建好了才去测通讯到时候光调字节序就能调到怀疑人生。3. 除了字节序这几个坑也不容忽视3.1 地址偏移40001 还是 0Modbus 协议里数据地址是从 0 开始的也就是协议层的地址 0x0000 对应第一个保持寄存器。但在 PLC 的世界里第一个保持寄存器的地址是 40001两者差了一个“1”。这个偏移量在触摸屏和组态软件里一般已经被处理好了你不需要关心但也有例外情况。比如直接用 Socket 发原始 Modbus TCP 报文调试如果你把起始地址填成 40001实际发到设备里的是地址 40001对应协议层的 0x9C41这通常会指向一个错误的寄存器或者设备直接返回异常码 02非法数据地址。组态软件里也要小心。有一些软件的设备地址栏填的是协议地址不是 PLC 地址。比如你要读 40001协议地址就应该填 0如果填了 1读到的就是 40002。这一类问题在联调时很容易被忽略因为不会报错只是读到相邻的一个地址的数据如果恰好那个地址有数据不细看真发现不了。我在写自定义通讯脚本时一般会强制自己使用十六进制地址比如 0x0000、0x000A这样能避免十进制的 0 和 1 搞混。同时会在代码里加注释标明 0x0000 对应 PLC 地址 40001这样过几个月后回头维护代码不用再重新推导一遍。3.2 Unit ID 不是摆设Modbus TCP 报文头的 Unit ID 字段很多人在配置时根本没注意过直接填了 0 或者 255结果在大部分设备上也能正常通讯。因为对于纯 TCP/IP 直连的设备来说IP 地址已经唯一确定了访问目标Unit ID 没有实际意义设备直接忽略。但当你通过网关把 Modbus TCP 转换成 Modbus RTU 时Unit ID 就变得至关重要。网关收到 TCP 请求后会解析报文里的 Unit ID然后把它当作 RTU 从站地址转发到串口总线上。如果你在 TCP 端填的 Unit ID 是 0而 Modbus RTU 设备地址是 1网关就会把请求发到地址 0 的从站结果当然是无响应。解决办法是先确认现场是不是有协议转换网关。如果有Unit ID 必须和从站设备的 RTU 地址保持一致。如果没有网关直接连设备Unit ID 可以填 255广播地址或者设备文档推荐的默认值。为保险起见最好先查一下设备手册有些设备对 Unit ID 有严格校验填错了直接拒绝连接并返回异常码。3.3 功能码选错数据读上来也是错的Modbus 功能码就那么几个常用的03 读保持寄存器、04 读输入寄存器、06 写单个寄存器、16 写多个寄存器。很多人在配置点位时不管设备文档怎么写的统一选 03因为保持寄存器用得最多。但设备的模拟量输入通道通常映射在输入寄存器区只能通过功能码 04 读取如果你用 03 去读设备大概率会返回异常码 01非法功能码或者返回全 0。功能码混淆比较好排查因为一般会报错不会让你稀里糊涂地把错误数据采上去。但它也容易在上位机点位数量多的时候引起混乱因为一个设备不同数据区可能用不同功能码如果你在配置时图省事全部用 03那么所有输入寄存器的数据都会读不出来。另外写操作也需要注意。06 功能码只能写一个 16 位寄存器如果你要写 32 位浮点数必须用 16 功能码连续写两个寄存器。有些触摸屏的宏指令里如果用了错误的写指令会直接把两个寄存器的值分开写虽然最终数据也可能是对的但存在一个瞬间的数据不一致窗口在实时控制场景下这一点很重要。4. 实操从威纶通到汇川把配置过程走一遍4.1 威纶通触摸屏通过网线走 Modbus TCP 的建站方法威纶通触摸屏连接支持 Modbus TCP 的设备比如上位机板卡、现场仪表新建工程时的重点步骤其实不多但每一步都有讲究。第一步打开 EasyBuilder Pro新建工程时选择触摸屏型号然后在“系统参数管理”里添加设备。添加设备时设备类型选择“Modbus TCP/IP”这个选项在“设备列表”的“Modbus”分类下面。如果你用的威纶通软件版本不同设备名称可能是“Modbus Slave”或者“Modbus TCP”本质上一样。第二步设置设备的 IP 地址。在设备属性的“通讯设置”里填入目标设备的 IP 地址端口默认是 502如果设备用的是非标端口这里要改。要注意的是这里填的 IP 必须是目标设备的实际 IP不是本机触摸屏的 IP填反了或者只填了触摸屏的 IP通讯必然失败。第三步确定站号Slave ID。威纶通这里默认值一般是 1但 Modbus TCP 设备的 Unit ID 约定各不相同。大多数板卡和设备默认是 255 或者是 0如果你不确定先去设备文档里查一下或者干脆用 Modbus Poll 测出正确的 Unit ID 再填到触摸屏里。第四步设置元件的地址。在画面上放置数值显示元件地址的格式是“功能码 地址编号”比如 4x10 表示保持寄存器地址 10PLC 地址 400103x5 表示输入寄存器地址 5PLC 地址 30005。对于 32 位浮点数需要在“读取格式”里选择“32-Bit Float”并根据设备实际的字节序设置“双字排列顺序”。威纶通这里有几个选项AABB高字在前、BBAA低字在前、以及加入字内字节交换的组合选项具体选哪个按我前面说的实测方法来定。第五步下载工程到触摸屏然后用“查看数据”窗口监视地址的实时值确认数值正确后再做画面对接。4.2 汇川 AM 系列做 Modbus TCP Server 的编程思路汇川 AM 系列 PLC 支持通过 InoProShop 软件配置 Modbus TCP 通讯可以作为客户端Client主动读取设备数据也可以作为服务端Server被上位机访问。这里说一下做 Server 的编程思路。在 InoProShop 中新建工程选择对应的 AM 系列 CPU然后在左侧工程树的“通讯”或“Modbus”分类中添加 Modbus TCP Server 配置。需要设置本地端口通常 502、最大连接数以及地址映射区。AM 系列做 Server 的底层逻辑是把 PLC 的数据区如 M 区、D 区映射到 Modbus 地址空间上位机访问这些 Modbus 地址实际上就是在读写 PLC 的 D 寄存器。映射关系可以在配置界面直接指定比如把 D100 开始的一块区域映射成保持寄存器区的 40001 起始地址。编写程序时要注意地址重叠和数据格式。如果上位机要访问 32 位浮点数而数据在 PLC 里是以两个相邻 D 寄存器存储的那你需要确认相邻 D 寄存器的顺序也就是高字在前还是低字在前。AM 系列 CPU 里32 位数据默认存储方式是低字在前小端序也就是说 D100 存低 16 位D101 存高 16 位。如果上位机按大端序解析读出来的字序是反的。解决方式有两种一种是在 PLC 程序里把浮点数拆开重排字节另一种是直接在上位机的变量设置里把高低字交换打开。我一般情况下倾向于尽量用上位机的设置去适配因为 PLC 端做字节重排要额外写代码还容易引入扫描周期内的数据不一致问题。另外汇川 AM 系列做 Server 时连接数量也是有限制的默认可能只支持 4 个客户端同时连接。如果现场有触摸屏、上位机、数据采集系统同时访问超过了连接数限制后来的连接就会被拒绝现象就是通讯断开或一直连不上。4.3 组态软件 KingSCADA 连接 Modbus TCP 需要注意的地方KingSCADA 通过 Modbus TCP 驱动连接设备时配置路径是“工程——IO 通讯——新建设备”设备驱动选“Modbus TCP”然后填 IP 地址和端口号。关键点在变量定义。KingSCADA 的变量数据包中需要为每个变量指定关联的寄存器地址、数据类型和读写属性。对于 32 位数据这里有几个选项一是变量类型选择比如定义成“Signed 32 位”还是“Float 32 位”选错了解析出来的数值就不对。二是双字字节序设置。KingSCADA 的变量条目里通常有“4x 数据格式”或“寄存器类型”的下拉框里面会列出 “ABCD”、“BADC”、“CDAB”、“DCBA” 这几种组合分别对应不同的字节序规则。很多人看到这几个选项直接懵了其实只要知道设备的实际字节序照着选就行。比如设备按大端序发送 32 位浮点数就选 ABCD设备低字在前就选 CDAB。三是采集频率的设置。KingSCADA 默认的采集周期是 1000ms如果现场点位很多建议根据实际需求调整采集分组和周期。Modbus TCP 虽然带宽比 RTU 大很多但如果一次采集几百个寄存器报文会被分片设备处理不过来可能返回异常码点位状态就会显示通讯失败。组态软件还有一个容易忽略的地方是“写操作确认”。KingSCADA 写变量时默认是直接发写命令如果你的设备只需要读取不需要写入最好在变量属性里把写入权限关掉避免屏上的操作误修改了现场设备的参数。4.4 NX-CIF105 的 Modbus TCP 通讯设置补充热词里有人提到 NX-CIF105这是欧姆龙 NX 系列的一个以太网通讯单元支持多种工业以太网协议其中就包括 Modbus TCP。它通常是插在 NX 系统上配套使用的通过 Sysmac Studio 进行配置。NX-CIF105 做 Modbus TCP 从站时需要在 Sysmac Studio 里把单元设置为“Modbus TCP Server”模式然后配置端口号和保持寄存器映射关系。这个单元的特别之处在于它内部的保持寄存器和欧姆龙的 CIO 区或者 DM 区之间存在映射关系你可以通过分配变量地址来控制哪些数据可以被外部上位机访问。实际操作中我见过不少人在 NX-CIF105 通讯不正常时先怀疑程序但其实是 Sysmac Studio 里的 IP 地址设置和实际 PLC 的 IP 地址不一致。NX 系列的 IP 地址可以在硬件设置里分配也可以在系统管理里分配如果两边不一致上位机根本找不到这个设备连 Ping 都 Ping 不通。5. 排查实录遇到数据不对怎么一步步定位5.1 用 Modbus Poll 验证通讯链路现场调试时我一般先用 Modbus Poll 这个工具验证一下通讯链路是否正常。这个软件是 Windows 下的经典 Modbus 调试工具支持 TCP 和 RTU 两种模式。用 Modbus Poll 做连接测试时先配置好从站参数。在“Connection”里选“Modbus TCP/IP”填目标 IP 和端口然后在“Setup”里选择功能码和起始地址。比如要读保持寄存器的前 10 个寄存器功能码选 03起始地址填 0长度填 10然后点连接。如果通讯正常窗口中会显示出 10 个寄存器的原始数值这些数值是十六进制显示的。通过观察这组原始值可以快速判断很多东西如果数值全为 0先别急着怀疑地址或功能码可能是设备当前输出就是 0也可能是采集的数据类型不对。如果某些寄存器值有变化说明链路是通的问题可能出在上位机的解析上。如果连 Modbus Poll 都读不到数据那说明问题出在 IP 配置、端口、Unit ID 或者设备的 Modbus 功能配置上得先解决链路问题再谈数据解析。Modbus Poll 还有一个好处是可以手动输入十六进制值进行写寄存器测试这样能精确验证某个寄存器的读写行为不用在上位机里反复配置整个工程来测试。5.2 抓包看报文让问题无处可藏Modbus Poll 能解决“能不能通讯”的问题但它不能告诉你“通讯报文到底是什么样的”。如果现场遇到的是字节序或者地址偏移这类问题最好的办法是直接用 Wireshark 抓包分析。Wireshark 抓 Modbus TCP 包的方法很简单选择对应网卡启用过滤器 tcp.port 502然后触发一次通讯就能看到完整的请求和响应报文。看报文时重点关注几个字段请求报文里Modbus 协议头的事务 IDTransaction ID是随机或递增的协议 IDProtocol ID固定为 0长度Length字段表示后续字节数单元 IDUnit ID就是前面说的站号。再往后是功能码和起始地址、寄存器数量。响应报文里功能码如果是 03数据区第一个字节是字节数后面就是寄存器数据。比如返回 04 41 CC 00 00说明后面有 4 个字节的数据也就是两个寄存器十六进制是 0x41CC 和 0x0000。通过抓包你可以看到设备实际返回的原始字节是什么然后和期望值做对比。如果你从报文中看到数据字节是 CC 41 而期望值是 41 CC那问题一目了然——字节序错了。抓包另一个作用是排查“通讯偶发中断”的问题。如果报文显示有 TCP 重传、连接被复位或者 KeepAlive 异常那基本可以判断是网络层面的问题比如网线松动、交换机端口不稳、或者防火墙拦截了空闲连接。Modbus TCP 本身不会自动重连如果上位机软件没有正确处理断线重连一旦链路断过一次可能就一直停在断线状态直到人工干预。5.3 常见现象与原因对照速查把日常调试中最常遇到的现象和对应原因整理成一张表遇到问题可以直接对照现象排查步骤常见原因完全连不上Ping 目标 IP确认网线、IP 网段一致网段不一致、端口错误能 Ping 通但读不到数检查 Unit ID、功能码、防火墙Unit ID 错误、防火墙拦截 502 端口通讯正常但数据为 0换 04 功能码试试查设备数据区用 03 读输入寄存器数值不对正负相反检查数据类型有无符号有符号变量定义成无符号了数值不对和实际差很多重点查字节序设置双字顺序、字节交换没配置好数值瞬时跳变查上位机扫描频率和设备响应时间浮点数高低字被分两次读取造成中间态通讯偶发断开抓包看 TCP 断开原因设备连接数限制、空闲超时第 6 条值得展开说一下因为这是一个很隐蔽的工程问题。上位机读取 32 位浮点数时如果一条报文没有完整读取两个寄存器而是被分成两条报文分别读取那么在两次读取之间设备里的数据可能发生了变化上位机组合出来的数值就会出现瞬时跳变。比如一个 PID 调节器的输出值下半秒是 45.6%上半秒变成了 45.7%如果两次读取跨越了这个变化点组合出来的可能是 45.7% 的整数部分和 45.6% 的小数部分组合完就是 45.6% 或者一些奇怪的值。解决方法是把 32 位数据放在相邻的连续地址上并确保上位机一次性读取两个寄存器。威纶通触摸屏元件属性里有“连续读取寄存器”的选项组态软件里要确保变量组合的寄存器地址是相邻的且不跨组。6. 调试 Modbus TCP 的通用流程照着做能省一半时间把这些年的经验总结一下我调试 Modbus TCP 基本会走下面这几个步骤已经成了习惯。第一步确认网络层通不通。用 Ping 命令测试设备 IP如果 Ping 不通先检查网线、IP 网段、子网掩码。这一步排除的是最底层的物理连接问题。第二步确认 Modbus 层通不通。用 Modbus Poll 连一下设备如果能正常读写说明设备侧没有限制连接Modbus 功能配置正确。如果连不上优先查端口、Unit ID、防火墙。很多 Windows 上位机软件的防火墙默认拦截外部连接请求第一次调试时经常被这个坑到可以先把防火墙临时关闭测试。第三步确认数据层对不对。用 Modbus Poll 读取设备的已知寄存器对比十六进制原始值和实际物理量确定字节序和数据格式。这一步是重点决定了后面上位机变量定义是否正确。第四步配置上位机。根据第三步得到的字节序规则在上位机里逐个定义变量先定义几个关键测试变量确认数值正确后再批量配置剩余点位。第五步长时间运行验证。配置完成后不要急着离开现场至少运行一两个小时观察通讯是否稳定。重点看有没有偶发断线、数据跳变、写命令失败等问题。按这个流程走下来绝大多数现场问题都能在半小时之内定位到方向不会出现那种从下午调到深夜还摸不着头脑的情况。最后说一个经验之谈。Modbus 协议出现数据问题时我最先问自己的问题永远是“原始字节是什么”而不是“上位机设置对不对”。字节序的坑之所以藏得深就是因为它要你把注意力放在最底层的原始数据上这一步跳过了后面所有推断都可能建立在错误假设上。只要能拿到原始报文或原始寄存器值90% 的 Modbus TCP 问题都能在十分钟内定位到原因。做自动化的同行这个习惯建议从今天开始养成。
返回列表