简介面向 Modbus 开发调试场景的实用说明文档主要讲解 ModSim32 与 ModScan32 两款调试工具的使用方法适合工业自动化、HMI 组态开发及 Modbus 协议调试人员参考。文档以图文方式介绍 Modbus RTU/TCP 下的主从设备模拟流程包括连接设置、功能码读写规则以及同一台电脑上模拟数据收发与验证的方法并专门指出 ModScan32 对 1、3 功能码地址只读、对 0、4 功能码可读写的差异能帮助初学者快速规避常见配置错误。文档从实操角度整理联调步骤可在真实设备接入前先用本机模拟验证。压缩包共 1 个文件类型为 doc 文档大小仅 14KB内容精炼便于随时查阅。目前已有 1665 人浏览学习适合需要掌握 Modbus 调试环境搭建、理解从站模拟与主站轮询过程、并希望快速上手这两款工具的开发与测试人员。1. 两台工具一台电脑ModScan32 与 ModSim32 的主从自测玩法拿到一份新的智能仪表或者还没有接线的 HMI 画面最怕的不是协议复杂而是你根本不知道设备回什么数据。ModScan32 和 ModSim32 就是干这个的前者模拟主站主动发指令后者模拟从站按地址回数据两个工具装在同一台 Windows 电脑上用 Modbus TCP 走 127.0.0.1 就能自测一整套收发流程不用接任何硬件。对做组态开发、设备调试和协议联调的人来说这套组合是“先把软件链路打通、再上真机”的标准起手式。本文按我实际的联调习惯把从连接、地址映射到写权限、抓包验证的所有细节和翻车点一次讲完。2. 先立个骨架功能码、数据模型与 Modbus TCP 报文2.1 主从模型谁发指令、谁回数据Modbus 是典型的主从式通信链路上只有一个主站其余都是从站。主站负责发起请求从站收到请求后执行对应的读或写操作再回一帧响应。日常使用最多的两种载体是 Modbus RTU串口基于 RS-232/RS-485和 Modbus TCP/IP以太网这两种工具默认场景就是 TCP。ModScan32 的角色是主站它把请求帧发给从站从站响应的数据会直接显示在界面上所以验证“设备有没有按协议回数”用它最直观。ModSim32 的角色是从站它模拟一台 Modbus 智能终端组态软件或者 ModScan32 往它发请求时它会按你预设的寄存器值回复。两个工具在同一台电脑上跑就是最简单的闭环测试环境ModScan32 发请求ModSim32 回数据中间不需要真实设备。2.2 四个数据模型与功能码0/1/3/4 背后是什么Modbus 协议把数据分为四个区ModScan32 和 ModSim32 界面上那些数字按钮就是对应这四个区数据模型界面编号对应标准功能码读写属性常见用途线圈 Coil00x01 读、0x05 写单、0x0F 写多可读可写开关量输出、继电器离散输入 Discrete Input10x02 读只读开关量输入、限位信号输入寄存器 Input Register30x04 读只读模拟量采集、仪表测量值保持寄存器 Holding Register40x03 读、0x06 写单、0x10 写多可读可写参数设定、累计量、控制字这里要特别留意一个地方工具界面上写的数字“0、1、3、4”是数据模型的快捷按钮不是线路上真正传输的功能码。你按下“4”这个按钮时工具实际发出的是 0x03 读保持寄存器请求。很多第一次用的人在这里被绕晕后面避坑章我会再展开。选型上日常联调 90% 的场景集中在保持寄存器和输入寄存器仪表测量值一般放输入寄存器或保持寄存器设定值和控制字放保持寄存器。线圈和离散输入用于开关逻辑做 HMI 联动时也会用到。2.3 Modbus TCP 报文502 端口与 MBAP 长度字段既然走 TCP就绕不开端口 502这是 Modbus 协议的保留端口。Modbus TCP 报文在标准 Modbus PDU 基础上套了一层 MBAP 头格式如下事务标识(2字节) | 协议标识(2字节) | 长度(2字节) | 单元标识(1字节) | 功能码(1字节) | 数据(N字节)举一个实际例子主站请求读取从站保持寄存器从地址 0 开始读 2 个寄存器。十六进制报文是00 01 00 00 00 06 01 03 00 00 00 02逐字节拆开看00 01是事务标识用于匹配请求和响应00 00是协议标识Modbus 固定为 000 06是长度字段表示后面从单元标识开始还有 6 个字节01是单元标识也就是 Device Id03是功能码读保持寄存器00 00是起始地址00 02是寄存器个数。响应帧会把这 2 个寄存器的值放在数据区返回。这段报文结构解释了联调时的三个硬性要求端口要一致且默认 502两边的 Device Id 必须相同起始地址和长度要能对上。否则从站要么不响应要么报异常功能码。理解了这帧结构后面用 Wireshark 验帧时也能一眼看出问题在哪。3. 30 秒联调ModSim32 挂从站、ModScan32 连主站3.1 ModSim32 做从站三步把服务挂起来ModSim32 的默认界面像一个表格有四类数据区标签页每个地址一格白色格子可以在线改值灰色格子只读。启动后按这个顺序操作菜单栏选择Connection - Connect。在弹出的连接方式里选Modbus/TCP Svr。端口保持默认的 502点击 OK。完成这三步后窗口底部状态栏会显示监听状态此时这台电脑上就有了一个监听 502 端口的 Modbus 从站。ModSim32 几乎不需要额外配置因为它同时开了全部四个数据区任何区被读取都能直接响应。如果你本机 502 被占用可以在这一步改成其他端口比如 1502但后面 ModScan32 那边的 Service 端口必须跟着改成 1502两边都要一致。提示如果只是自己本机测试IP 填 127.0.0.1 就行如果是 VMware 虚拟机里跑从站、物理机跑主站ModSim32 的监听 IP 必须填虚拟机网卡的局域网地址且要关闭防火墙拦截。3.2 ModScan32 做主站连接参数要对齐ModScan32 启动后默认界面是地址和数据区先别急着点 Read把连接配置做对菜单栏选择Connection - Connect。连接方式选择Remote TCP/IP Server。IP Address 填 127.0.0.1同一台电脑或填 ModSim32 所在机器的局域网 IP。Service 端口填 502和 ModSim32 一致。点击 OK。连接成功后右侧面板开始显示数据。接着需要配置请求参数核心是四个值Device Id、功能码、起始地址、长度。这四个值必须和从站侧对齐比如 Device Id 两边都填 1功能码都选同一个区起始地址都从同一个数开始长度一致。下面这张表是常见参数组合的对照参考参数常见取值说明Device Id1TCP 默认两边必须一致RTU 下等于站号功能码0 / 1 / 3 / 4按你要读的数据模型选起始地址0 或 1注意 1-based 与 0-based 换算见第 4 章长度1 ~ 125一次最多读 125 个寄存器轮询周期1000 ms默认 1 秒轮询一次联调可改快3.3 首轮读写验证观察与写入的操作路径连接建立后ModScan32 默认会按当前配置持续轮询界面上的数值会不停刷新这就说明主从链路通了。此时可以手动验证写操作在 ModScan32 中切到可写的数据区双击某个地址弹出写入对话框输入值后确认ModSim32 对应地址的格子会立刻变成相同的值。反过来在 ModSim32 的格子里直接填数ModScan32 下一轮轮询就会显示新值。注意 ModScan32 界面里写入按钮的可用状态受数据区读写属性约束有些区按钮是灰的这是正常现象不是工具坏了。标准 Modbus 协议里线圈和保持寄存器可写离散输入和输入寄存器只读但原文档里明确提到“ModScan32 只能对 0 和 4 功能代码的地址进行写操作不能对 1 和 3 进行写操作”这是汉化版本对各区编号的习惯叫法建议你以实际界面上可点击的写入按钮为准。不同版本的按钮编号可能不一样我见过同一个版本在不同汉化包下按钮含义都不同动手时留意界面右侧的读写状态提示即可。4. 地址映射与 HMI 联动把 30001 写进去、画面动起来4.1 1-based 与 0-based地址差一的真正根源Modbus 协议内部地址从 0 开始计数这是 0-based。但很多设备手册、组态软件和仪表说明书习惯用 PLC 风格的 1-based 地址地址前面再加一个区号数字表示数据模型比如 30001、40001。这种编号方式里30001 表示输入寄存器区的第 1 个寄存器对应协议内部地址 040001 表示保持寄存器区的第 1 个寄存器同样对应协议内部地址 0。换算关系固定在下面这张表里区号前缀含义起始显示地址对应协议内部地址0线圈0000101离散输入1000103输入寄存器3000104保持寄存器400010所以 30001 就是协议地址 030002 对应协议地址 130003 对应协议地址 2。原文档里的例子“在 ModSim32 中分别对地址 30001、30002、30003 写入 99、123、2217”对应的就是输入寄存器区的前三个寄存器。而这个例子在 ModScan32 中能原样看到前提是两边都选输入寄存器区、起始地址都设置为一致的位置。4.2 用 30001 例子跑通跨工具读写按原文档的示例完整走一遍验证过程如下ModSim32 切到输入寄存器Input Register区左侧能看到 30001 开头的地址列。在 30001、30002、30003 三个格子里分别填 99、123、2217。ModSim32 所有数据区都允许直接填值。ModScan32 连接成功后把功能码切到输入寄存器区起始地址设为与 ModSim32 侧对应的位置长度设为 3。观察 ModScan32 的数据列三个地址显示 99、123、2217与 ModSim32 完全一致。这里最容易犯错的是第 3 步的起始地址设置。ModScan32 界面上地址列显示的是 30001 这种带区号的地址你填起始地址时也要按这个编号习惯填如果你在 ModSim32 里写的是 30001在 ModScan32 里就不能填 0而要填 30001 对应的显示地址。注意ModScan32 和 ModSim32 的地址显示体系并不总是相同。ModSim32 的表格地址列偏底层有些版本直接显示 0、1、2ModScan32 偏好显示 30001、40001 这种带区号的地址。两边对不上时先换算到协议内部地址再对齐。4.3 接到 HMI 组态模拟从站状态变化的套路ModSim32 在 HMI 组态开发里的典型用法是充当虚拟设备。组态软件里建一个 Modbus TCP 从站连接IP 指向运行 ModSim32 的机器端口 502然后定义变量绑定到对应地址。运行时组态软件作为主站持续轮询 ModSim32ModSim32 格子里的值变化HMI 画面的数显、趋势曲线、报警状态就跟着变。这套验证套路分三步在组态软件里建好变量表把每个变量映射到 Modbus 地址注意按 4.1 节的表格做 1-based 和 0-based 换算。启动 ModSim32 并监听 502再启动组态软件的运行环境观察变量是否都能读到值。依次修改 ModSim32 里对应地址的值逐个核对画面上的每个控件是否正确响应。比如把地址 40001 的值从 0 改成 50画面上的液位条高度应随之变化。反过来如果 HMI 画面上有输入控件在画面上写入数值ModSim32 对应地址也会更新这用来验证“上位机下发参数”的链路同样有效。5. 避坑清单地址差一、写权限与 502 端口的五个翻车点5.1 连上了但数据错位一位地址差一的坑现象两个工具都显示连接正常ModScan32 能收到响应但读出来的值和 ModSim32 里填的对不上总是错开一格比如 ModSim32 的 30001 是 99ModScan32 显示 30002 才是 99。原因两边对地址的理解不一致。ModScan32 的显示地址从 1 开始编号而 ModSim32 有些版本内部按 0 开始。起始地址填 1 时实际请求的是内部地址 0 对应的寄存器看起来像是整体偏移了一个位置。解决先把两边的地址统一换算成协议内部地址。要访问显示地址 30001协议内部地址填 0要访问 30002填 1。在 ModScan32 里填起始地址时按显示地址和内部地址的映射关系换算好后再填然后用 4.2 节的方法先读 3 个连续地址做校验确认对齐后再扩展。5.2 写操作按钮灰色或写入无效读写属性的坑现象ModScan32 里双击地址想写入时弹不出输入框或者能弹出来但写入后 ModSim32 里数值没变。原因你选中的数据区本身是只读模型。标准 Modbus 协议里离散输入区和输入寄存器区是只读的线圈区和保持寄存器区才可读可写。原文档说的“ModScan32 只能对 0 和 4 功能代码的地址进行写操作”指的是它的界面按钮里 0 区和 4 区允许写1 区和 3 区只读不同汉化版本对此的描述顺序经常不一致容易让人把 3 和 4 搞混。解决要验证写操作先切到线圈区或保持寄存器区。ModScan32 切对应按钮ModSim32 切到相同数据区两边地址对齐后再写。如果按钮还是灰的检查界面上的写使能选项确认没有勾选“Read Only”之类的保护开关。5.3 连接被拒或一直超时502 端口起不来的坑现象ModScan32 点击 OK 后提示连接失败或者状态栏一直转圈超时但 ModSim32 明明已经启动。原因常见有三种。一是 ModSim32 没有真正切到 Modbus/TCP Svr 模式只停留在启动初始界面二是 502 端口被其他程序占用比如另一个 Modbus 从站工具或者调试服务三是 Windows 防火墙拦截了本机回环或局域网访问。解决先回 ModSim32 的 Connection 菜单确认已经选到 Modbus/TCP Svr 且状态栏有监听提示。然后打开命令行执行netstat -ano | findstr 502有输出说明端口被占记下最后的 PID到任务管理器里找到对应进程结束掉或者干脆把两边端口一起改成 1502 避开冲突。防火墙方面临时关闭防火墙测试一次能通就说明是拦截问题再按程序放行 502 端口的入站规则。5.4 返回异常码且数据全空Device Id 对不上的坑现象ModScan32 能建立 TCP 连接但数据区一直是 0 或者显示异常响应状态栏出现错误码。原因Modbus TCP 报文里的单元标识也就是 Device Id在两台工具里不一致。ModScan32 连接对话框里有 Device Id 输入框ModSim32 侧也有对应的单元号设置两边不相等时从站会回异常响应主站把异常码显示出来。协议里常见的异常码 0x02 表示非法地址0x03 表示非法数据0x0B 表示从站忙。解决把两边的 Device Id 统一。TCP 模式下默认填 1 最省事两边都填 1。如果是通过网关转 RTU 的场景这个值要和网关里配置的从站站号一致。出现异常码时不要只看数据区先记下异常码再检查 Device Id、起始地址、长度三项。5.5 数据能读到但是乱数类型与字节序的坑现象寄存器里明明写的是 2217ModScan32 读出来却是很大的整数或者带小数的异常值比如读到 0.0 或者 -32768。原因Modbus 寄存器本身只是 16 位的存储单元没有类型概念。同一个 0xFFFF按无符号整数读是 65535按有符号整数读是 -1两个寄存器拼成 32 位浮点数又是另一个值。ModScan32 和 ModSim32 默认按无符号整数显示但很多智能仪表的数据手册要求按 IEEE 754 浮点解析而且字节序可能是大端或小端读法不同结果完全不同。解决先在两个工具里确认把显示格式设为无符号整型排除显示格式干扰。如果设备手册明确说数据是 32 位浮点那么长度必须设 2一次读取两个连续寄存器再按浮点解析。解析时先按大端试试如果数值明显不合理再切成小端。这类问题在联调阶段很容易误判成“设备坏了”其实只是数据解释方式不对先用 ModSim32 填一个已知值再用 ModScan32 读回来对比一次就能定位出字节序规则。6. 收尾技巧抓包验帧与用 Python 裸跑 Modbus TCP6.1 Wireshark 抓包一帧报文定乾坤两个工具联调时界面上能看到数据但协议层到底发生了什么还是个黑匣子。我的习惯是同时开着 Wireshark 抓本机回环流量过滤器直接写tcp.port 502这样能看到每一帧请求和响应的完整字节。重点看三处请求帧里的功能码、起始地址、长度是否和你界面配置一致响应帧里的异常码是什么事务标识是否一一对应。比如你在 ModScan32 里填了起始地址 30001抓包却发现请求帧里的地址字段是 0x0000那就说明工具已经帮你把显示地址换算成了内部地址两边差一的问题根源就找到了。抓包验完几帧对工具的地址换算规则就有底了比猜可靠得多。6.2 Python 裸发请求把验证变成脚本联调最怕改一次参数就手点一遍界面。我一般会在链路跑通后用 Python 直接构造请求帧做回归验证脚本很简单import socket import struct sock socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.connect((127.0.0.1, 502)) def read_hold_registers(device_id, start_addr, quantity): # 事务标识固定用 1协议标识固定 0长度 单元标识(1) 功能码(1) 起始地址(2) 数量(2) transaction_id 1 protocol_id 0 length 6 request struct.pack(HHHBBHH, transaction_id, protocol_id, length, device_id, 0x03, start_addr, quantity) sock.send(request) response sock.recv(256) # 响应结构: 事务标识(2)协议标识(2)长度(2)单元标识(1)功能码(1)字节数(1)数据 data response[9:] values struct.unpack( H * (len(data) // 2), data) return values print(read_hold_registers(1, 0, 3))这段代码构造的就是 2.3 节那帧报文只是起始地址和数量改成了参数。用struct.pack按大端打包各字段HHHBBHH对应两个两字节、两个单字节和两个两字节的格式。连接地址填 127.0.0.1 和 502对应 ModSim32 的监听位置。跑一次能读到 3 个寄存器的值就等于把 ModScan32 的读操作脚本化了。从那以后我每次做 Modbus 联调都强制走一遍固定流程先用 Wireshark 抓几帧确认请求参数没被工具“翻译”错再用 ModSim32 和 ModScan32 做一轮双向读写验证最后用上面的 Python 脚本做批量回归。等真机接上来时链路层的问题早就在软件层踩完了剩下要排查的只有现场接线和设备本身的参数了。这套方法帮我省了不少冤枉时间希望帮到你。本文还有配套的精品资源点击获取