modpoll 是一个命令行的 Modbus 主站模拟器与测试工具,由 proconX / FOCUS Software 基于自家 FieldTalk 协议栈开发,当前版本 3.16。它是一个单文件二进制,Windows、Linux、树莓派都能跑,免费使用(按附带的 LICENSE-FREE 条款,可自由使用与分发,但不开放源码)。

调试 Modbus 有两条路:一条是图形工具,点鼠标、填表格、看数据网格;另一条是命令行,一行命令发出请求,结果打在终端里。图形工具适合坐在现场对着设备琢磨;命令行工具适合脚本、批量、定时、无人值守——而后者恰恰是实际工程里最缺的。


一、为什么是它

先看它能替掉什么:

你现在的做法

换成 modpoll

图形工具手点,读一个寄存器看一眼

modpoll -r 1 -c 10 192.168.1.200

想定时采集,只能人工盯着界面截图

-l 1000 循环轮询,输出重定向到日志文件

服务器、网关、树莓派上没有图形界面

单文件二进制,SSH 进去直接跑

想把 Modbus 检查并入自动化测试

文本输出可解析,退出码可判断

出差在外想快速确认对方设备通不通

装个 200 KB 的 exe,不用装几百兆的套件

一句话:它把 Modbus 请求变成了一个可以放进脚本、放进管道、放进 crontab 的东西。

先理清三个容易混的名字

搜 "Modbus 调试工具",会出现三个读音很像的东西,它们不是同一个软件:

名字

出品方

形态

授权

说明

modpoll

FieldTalk / proconX

命令行

免费,闭源

本篇主角

Modbus Poll

modbustools.com

图形界面

商业付费

完全不同的产品,别搞混

mbpoll

epsilonrt 等

命令行

GPL 开源

命令语法刻意仿 modpoll,但源码完全独立

mbpoll 的官方说明里明确写了这件事:

mbpoll 的输出语法和命令行选项语法与 proconX 发布的原版 modpoll 程序相似。但 mbpoll 是一个完全独立的项目,源码与原版 modpoll 不同。

这意味着:两者参数长得像,但不能混着抄。 拿 mbpoll 的教程去调 modpoll,或者反过来,很容易在某些选项上得到意外的行为。

关于更新节奏,如实说明

我查了官方下载文件的服务器时间戳:

文件

最后修改时间

modpoll.zip

2025-04-03

modpoll.tgz

2025-04-03

距今约一年半。这不算"高频迭代",但要放在正确的语境里看:Modbus 协议本身从 1979 年至今没有发生颠覆性变化,一个功能已经完整的命令行工具,迭代慢是正常的。它和"厂商跑路、版本停在 2014 年"完全是两回事——官网在,下载可用,二进制仍在维护线上。

如果你需要的是频繁更新、可读源码、活跃社区,那 mbpoll(GPL,基于 libmodbus)更合适;如果你要的是参数成熟、开箱即用、跨平台单文件,modpoll 更省事。两者可以都装着,按场景挑。


二、安装

modpoll 没有安装程序,就是下载、解压、加进 PATH。

Windows

下载 modpoll.zip,解压到任意目录,进到 win 子目录,用命令提示符运行:

cd modpoll\win
modpoll -h

想在任何目录直接调用,就把 modpoll\win 目录加进系统环境变量 Path

Linux

tar xzf modpoll.tgz

压缩包里是多个架构的二进制,挑一个匹配你机器的加进 PATH。以 ARM 平台(树莓派、BeagleBoard 等)为例:

export PATH=$PWD/modpoll/linux_arm-eabihf:$PATH
modpoll -h

x86_64 的机器则用 x86_64-linux-gnu 目录。

验证安装

modpoll -h

能打出完整的参数列表就说明就绪了。建议第一次装完就先把 -h 的输出存一份——这个帮助信息本身就是最准确的参数手册,比任何二手教程都可靠。


三、命令行长什么样

modpoll 的命令行结构非常简单,只有三个部分:

modpoll [OPTIONS] SERIALPORT|HOST [WRITEVALUES...]

部分

含义

[OPTIONS]

一堆以 - 开头的参数:协议、地址、数量、数据类型

SERIALPORT\|HOST

串口(COM3/dev/ttyUSB0)或 IP 地址/主机名

[WRITEVALUES...]

要写入的值。不写这个,就是读

最后这一条是理解 modpoll 的关键:读和写用的是同一个命令,区别只在于尾部有没有跟数值。

# 读:尾部没有值
modpoll -r 100 -c 5 192.168.1.200

# 写:尾部跟上要写的值(写入 1201 号寄存器)
modpoll -r 1201 -c 1 192.168.1.200 1234

还有一个很省事的默认行为:协议可以自动推断

  • 第一个参数里包含 /\COM → 按 RTU 处理;

  • 其他情况(IP 或主机名)→ 按 TCP 处理。

所以 modpoll -r 1 192.168.1.200 不用写 -m tcp 也能工作。但建议还是显式写上,脚本里显式声明比依赖隐式推断更安全。


四、参数速查

通用选项

参数

含义

默认值

-m ascii

Modbus ASCII 协议

-m rtu

Modbus RTU 协议

参数含 \COM 时默认

-m tcp

Modbus TCP

IP 形式时默认

-m udp

Modbus UDP

-m enc

封装 RTU over TCP(网关常用)

-a #

从站地址(串口 1-247,TCP 0-255)

1

-r #

起始参考地址(1-65536)

1

-c #

读取数量(1-125)

1

-0

起始地址用 0 基(PDU 寻址)

关闭

-1

只轮询一次

关闭(持续轮询)

-l #

轮询间隔(毫秒)

1000

-o #

超时(秒,0.01-10.0)

1.0

-u

按无符号整数显示

关闭

数据类型(-t

-t 后面的数字就是设备手册上的寄存器区号,记忆起来很直接:

写法

区号含义

对应功能码

-t 0

离散输出(线圈)

FC01

-t 1

离散输入

FC02

-t 3

输入寄存器(3xxxx 区)

FC04

-t 4

保持寄存器(4xxxx 区)

FC03,默认

在每个区号后面可以加类型后缀,一次读多个寄存器拼成一个值:

后缀

含义

:hex

十六进制显示

:i32

32 位有符号整数(占 2 个寄存器)

:i64

64 位有符号整数(占 4 个寄存器)

:mod

32 位 module 10000 类型

:f32

32 位单精度浮点(占 2 个寄存器)

:f64

64 位双精度浮点(占 4 个寄存器)

例如 -t 4:f32 就是"保持寄存器区,按 32 位浮点解释"。

另外有两个特殊类型用于协议探测:

写法

用途

-t id

读设备识别对象(FC43/14),能问出厂商名、型号、版本——不认识的新设备,先试这个

-t file

读文件记录(FC20/21),配合 -n # 指定文件号

数值字节序

这几个参数是 32/64 位数据读出来"数值离谱"时的解药:

参数

含义

-i

从站按大端处理 32/64 位整数

-f

从站按大端处理 32/64 位浮点

-e

Daniel/Enron 单寄存器 32 位模式(隐含 -i-f

-x

Lufkin ELAM 扩展(仅 RTU 和 RTU over TCP)

网络选项

参数

含义

默认值

-p #

IP 端口号

502

注意非标端口:标准 Modbus TCP 用 502,但很多设备厂家会用 10001、10002、10003 这类端口。不写 -p 就会连到 502 上去,然后超时。

串口选项

参数

含义

默认值

-b #

波特率

19200

-d #

数据位(ASCII 协议 7 或 8,RTU 用 8)

-s #

停止位(1 或 2)

1

-p none / -p even / -p odd

校验位

even

-4 #

RS-485 模式,发送期间 RTS 拉高,之后保持 # 毫秒

串口默认值是 19200 / even 校验,这个要注意。 而市面上大量设备的出厂设置是 9600 / 无校验。不改参数直接读,表现就是一直超时——这跟线接错、设备不给响应的现象一模一样,很容易误判。


五、五个真实场景

场景 1:读一台 TCP 设备的前 10 个保持寄存器

modpoll -m tcp -p 502 -a 1 -r 1 -c 10 192.168.1.200

按 1 秒一次持续轮询,Ctrl+C 停止。想跑一次就退出,加 -1

modpoll -m tcp -p 502 -a 1 -r 1 -c 10 -1 192.168.1.200

脚本里一定要加 -1,否则进程不会自己退出,会一直占着。

场景 2:一次读满 100 个寄存器,扫描整张点表

单次读取的上限是 125 个寄存器,所以 100 个可以一次读完:

modpoll -m tcp -p 10001 -a 1 -r 1 -c 100 -1 192.168.1.200

输出是带 [地址] : 值 的逐行列表,可以直接喂给 grep / awk 做判断

# 检查第 37 个寄存器的值是否为 1(0 基协议地址 36)
modpoll -m tcp -p 10001 -a 1 -r 37 -c 1 -1 192.168.1.200 | tail -1

如果设备寄存器超过 125 个,要分段读:

for start in 1 126 251; do
  modpoll -m tcp -p 10001 -a 1 -r $start -c 125 -1 192.168.1.200
done

场景 3:读 32 位浮点,以及"数值离谱"时怎么办

设备手册写着"温度,Float,地址 40001",对应命令是:

modpoll -m tcp -p 10001 -a 1 -r 1 -c 2 -t 4:f32 -1 192.168.1.200

注意 -c 2——一个 32 位浮点占两个 16 位寄存器,只读 1 个是拼不出来的。

如果读出来是 1.09e-19 这种量级,或者是个几十亿的怪数,通常不是通信故障,而是字序不对。依次试:

modpoll -m tcp -p 10001 -a 1 -r 1 -c 2 -t 4:f32 -i -f -1 192.168.1.200

-i -f 表示从站按大端处理。仍然不对的话,就要考虑设备用的是字节交换(BADC/CDAB)这类非标准排法——modpoll 没有直接对应的开关,得用 -t 4:hex 读出原始两个寄存器,自己按字节重排。

场景 4:写寄存器——FC06 还是 FC16

写操作的关键在于用哪个功能码,由 -c 决定:

# 不写 -c:默认走 FC16(写多个寄存器)
modpoll -m tcp -p 10001 -a 1 -r 1201 192.168.1.200 1234

# 加 -c 1:强制走 FC06(写单个寄存器)
modpoll -m tcp -p 10001 -a 1 -r 1201 -c 1 192.168.1.200 1234

官方参数说明里写得很清楚:-c 1 to force FC5 or FC6有些老设备只实现了 FC06,不接受 FC16,这时候不加 -c 1 就会收到异常响应——这个坑值记住。

写多个连续的寄存器,就是尾部跟多个值,配 -c

modpoll -m tcp -p 10001 -a 1 -r 10 -c 3 192.168.1.200 100 200 300

场景 5:定时轮询 + 落盘,做长期记录

Linux 上,把输出接给时间戳脚本,追加进日志:

while true; do
  echo "=== $(date '+%F %T') ===" >> /var/log/modbus-10001.log
  modpoll -m tcp -p 10001 -a 1 -r 1 -c 100 -1 192.168.1.200 >> /var/log/modbus-10001.log 2>&1
  sleep 10
done

也可以让 modpoll 自己以 5 秒间隔持续轮询(注意这样不会自己退出):

modpoll -m tcp -p 10001 -a 1 -r 1 -c 100 -l 5000 192.168.1.200 | tee -a modbus.log

Windows 批处理同理:

:loop
echo === %date% %time% === >> modbus-10001.log
modpoll.exe -m tcp -p 10001 -a 1 -r 1 -c 100 -1 192.168.1.200 >> modbus-10001.log
timeout /t 10 /nobreak > nul
goto loop

长期采集一定要考虑日志体积——100 个寄存器每 10 秒一次,一天就是几十万行。配合 logrotate 或者按天切文件。


六、和图形工具的字段对照

如果你之前用的是带界面的 Modbus 工具,迁移到 modpoll 几乎是零成本,因为界面上的每个输入框都对应一个命令行参数

图形界面上的字段

modpoll 参数

说明

Device ID / Slave ID

-a 1

从站地址

Address / Start

-r 1

起始地址

Length / Quantity

-c 100

读取数量

Point Type(03/04/01/02)

-t 4 / -t 3 / -t 0 / -t 1

区号对应

数据类型(Float/Int/Hex)

:f32 / :i32 / :hex

加在 -t 后面

IP / Port

-p 10001 + IP

端口单独给

串口 / 波特率 / 校验

-b -d -s -p

串口模式

Scan Rate

-l 1000

轮询间隔毫秒

显示格式

-u:hex

无符号、十六进制

最值得注意的一条是地址:很多图形工具和手册上显示的地址是 1 基(1-100),而 modpoll 的 -r 默认也是 1 基。两边都是 1 基时你直接照抄就行,不要自作聪明去减 1。 反过来,如果对方给的是 0 基的协议地址(常见于 PLC 组态软件导出的清单),才需要加 -0


七、常见坑

现象

原因

解法

一直没输出、最后超时

端口不对。设备用 10001,命令没写 -p 就默认连了 502

显式加 -p 10001

脚本卡住不结束

忘了 -1,它在持续轮询

-1 只跑一次

读浮点得到天文数字

字序不匹配

-i -f;仍不对就用 :hex 读原始值自己排

读浮点只给一个寄存器

-c 给了 1,32 位值需要 2 个寄存器

32 位用 -c 2,64 位用 -c 4

串口一直超时

默认 19200/even,设备可能是 9600/none

显式写 -b 9600 -p none

写入报异常

老设备只支持 FC06,而默认走了 FC16

-c 1 强制 FC06

一次读 200 个寄存器失败

单次上限 125

分段读

拿 mbpoll 的参数来用

两者语法相似但实现独立

modpoll -h 为准

想改代码或二次分发

modpoll 免费但不开源

需要源码就用 GPL 的 mbpoll


八、上手路径

阶段

做什么

达成什么

第 1 步

下载解压,modpoll -h 通读一遍参数

知道它有哪些能力

第 2 步

-r 1 -c 10 -1 读通一台真实设备

确认网络、端口、从站号都对

第 3 步

-t id 问设备身份

不用查手册也能知道对面是谁

第 4 步

把点表按 -t/:f32 配好,一次读满

一张命令就是一份点表快照

第 5 步

写进定时脚本,落盘 + 切片

变成能长期跑的采集程序

真正需要练的只有一件事:遇到"要定时读一批寄存器"的需求时,想到用 modpoll 而不是打开图形界面。


九、参考

  • 官方主页与下载:https://www.modbusdriver.com/modpoll.html

  • 官方免费许可条款:https://www.modbusdriver.com/info/LICENSE-FREE

  • FieldTalk 协议栈:https://www.modbusdriver.com/fieldtalk

  • 命令语法相似的开源替代(GPL,基于 libmodbus):https://github.com/epsilonrt/mbpoll

本文基于 modpoll 3.16 编写,二进制发布日期为 2025-04-03。参数以 modpoll -h 的输出为准;Modbus 协议本身稳定,但不同设备对功能码、字节序的支持差异很大,遇到异常优先怀疑设备实现而不是工具。