云计算百科
云计算领域专业知识百科平台

一文看懂 Modbus:从零到能用

文章目录

  • 一文看懂 Modbus:从零到能用
    • Modbus 是啥?
    • 四类“对象”、编号与功能码(核心概念)
      • 参考号 vs 实际地址(偏移)
    • RTU/ASCII/TCP 报文长啥样?
      • RTU(串口,二进制)
      • ASCII(串口,文本)
      • TCP(以太网)
    • 读写能带多少数据?(常用上限)
    • 真·一眼会的实战例子
      • 例子 1:读保持寄存器 40100(一个寄存器)
      • 例子 2:写线圈 00020 为 ON
      • 例子 3:连续读取离散输入 10001–10016(16 位)
      • 例子 4:读取 32 位浮点数(占两个 HR)
    • 连接与布线小抄(RS-485)
    • 小细节 & 易踩坑合集
    • 报文长相(看一眼就会)
    • 选 RTU 还是 TCP?
    • 上手代码(Python,pymodbus)
    • 调试与工具清单
    • 结语

一文看懂 Modbus:从零到能用

这篇给刚入门的你:什么是 Modbus、RTU/ASCII/TCP 有啥区别、寄存器和功能码怎么对上、报文长啥样、线缆怎么接、常见坑怎么避。看完你就能读/写设备数据,少踩 80% 的坑。


Modbus 是啥?

Modbus 是一种主从(Client/Server)式工业通信协议,用来在 PLC、变频器、仪表、传感器之间读写位或寄存器。它轻量、简单、跨品牌通用,至今仍是“现场总线界的英语”。

三种常见形态:

  • Modbus RTU:跑在串口(常见 RS-485),二进制帧,效率高。
  • Modbus ASCII:也跑串口,文本帧,易读但效率低。
  • Modbus TCP:跑以太网(TCP 502 端口),好接入上位机/网络。

四类“对象”、编号与功能码(核心概念)

Modbus 操作的是 4 种对象(可理解为 4 个抽屉)——每类对象固定对应功能码(要干什么)和地址空间(在哪儿干):

参考号写法对象类型数据粒度读/写用的功能码
0xxxx(如 00001) Coils(线圈) 位,读写 读 01 / 写单点 05 / 写多点 15
1xxxx(如 10001) Discrete Inputs(离散输入) 位,只读 读 02
3xxxx(如 30001) Input Registers(输入寄存器) 16 位,只读 读 04
4xxxx(如 40123) Holding Registers(保持寄存器) 16 位,读写 读 03 / 写单个 06 / 写多个 16

为啥“看地址就能知道功能码”?因为很多手册里的“地址”其实是带分类的参考编号(首位 0/1/3/4 已经说明了它属于哪个抽屉),而各抽屉的读写动作在协议里固定映射成上述功能码。

参考号 vs 实际地址(偏移)

真正进报文用的是0 基偏移地址,不是“40001”这种参考号。换算规则:

  • Coil:偏移 = 参考号 − 1(00020 → 19)
  • DI:偏移 = 参考号 − 10001
  • IR:偏移 = 参考号 − 30001
  • HR:偏移 = 参考号 − 40001

有些手册直接给“Address/Offset=99(0x0063)”这类0 基偏移,这时不用再减。以手册的列名为准。


RTU/ASCII/TCP 报文长啥样?

RTU(串口,二进制)

  • 帧:[从站地址][功能码][数据…][CRC16]
  • 以静默间隔划帧:帧前后至少 3.5 个字符时间(俗称 T3.5),帧内字节间不超过 1.5 个字符时间(T1.5)。
  • 广播地址 0 只用于写(无应答,常用于同时下发)。

ASCII(串口,文本)

  • 帧:: 开头,内容是 ASCII 十六进制,结尾 CRLF,LRC 校验。调试友好,效率较低。

TCP(以太网)

  • 帧:[MBAP 头 7B][功能码][数据…] MBAP 头含事务 ID、长度、Unit ID(网关场景下映射到下挂 RTU 从站地址;直连设备通常忽略或设成 1/0xFF)。
  • 无 CRC(由 TCP 保证)。

读写能带多少数据?(常用上限)

  • 读 Coils/DI:最多 2000 位
  • 读 IR/HR:最多 125 个寄存器
  • 写多个 Coils(FC15):最多 1968 位
  • 写多个 HR(FC16):最多 123 个寄存器

这些上限来自 Modbus PDU 的最大长度限制,绝大多数设备遵守。个别厂家可能更小——看手册。


真·一眼会的实战例子

例子 1:读保持寄存器 40100(一个寄存器)

  • 类型:HR → 功能码 03
  • 偏移:40100 − 40001 = 99
  • 数量:1
  • RTU 命令关键字段:[addr][03][00 63][00 01][CRC]

例子 2:写线圈 00020 为 ON

  • 类型:Coil → 功能码 05
  • 偏移:20 − 1 = 19
  • 数据:ON=0xFF00(OFF=0x0000)

例子 3:连续读取离散输入 10001–10016(16 位)

  • 类型:DI → 02
  • 偏移:10001 − 10001 = 0
  • 数量:16

例子 4:读取 32 位浮点数(占两个 HR)

  • 文档说“40100(低字) 和 40101(高字) 组成一个浮点”
  • 读参数:FC 03,偏移 99,数量 2
  • 解析时注意字/字节序(常见:高低字交换 / 大小端不一致)。

连接与布线小抄(RS-485)

  • 拓扑:总线型(菊花链),避免星形。
  • 终端电阻:两端各 120 Ω;偏置电阻在一处提供。
  • 屏蔽与接地:屏蔽层一端接地,注意共模干扰与等电位。
  • A/B 命名混乱:不同厂家对 A/B(D+/D−)定义可能反着来;不亮就对调试试。
  • 波特率:9600 很常见;更高波特率对线缆与干扰更敏感。
  • 设备数量:传统 32 单位负载,现代收发器可更多(看手册)。

小细节 & 易踩坑合集

  • 40001 并不是“真实地址” 真实的是偏移(0 基)。参考号只是带分类的“人类友好编号”。

  • -1 不是通吃 只有 0xxxx(Coil)是 −1;其余要减 10001/30001/40001。 个别厂商用 40000/30000 起步,是否 −1 以手册为准。

  • 从站地址 ≠ 寄存器地址 从站地址(Unit/Slave ID)是哪台设备;寄存器地址是那台设备里的哪个点。

  • 32 位/浮点的“字节序/字序” 两个 16 位寄存器拼 32 位可能需要 word swap / byte swap。不同厂家不同——看“字节序”说明或示例值。

  • 广播写(地址 0)无应答 只在 RTU/ASCII;只能用于写类功能码(如 05/06/15/16),不要指望有响应。

  • 异常码(错误码)读得懂,排错快 常见:

    • 01:Illegal Function(功能不支持)
    • 02:Illegal Data Address(地址不存在/越界)
    • 03:Illegal Data Value(数量/参数不合法)
    • 04:Server Device Failure(设备内部错误)
    • 06:Server Device Busy(忙)
    • 0A/0B:网关路径/下位机无响应(TCP→RTU 网关场景)
  • 安全性 Modbus 无认证/加密。TCP 场景下请隔离网络、用 VPN/防火墙/网关做访问控制。


  • 报文长相(看一眼就会)

    以“从站 1 读取 3 个 HR,自偏移 0x006B(=寄存器 40001+0x006B=40108)”为例:

    • RTU 请求:01 03 00 6B 00 03 CRClo CRChi
    • RTU 应答:01 03 06 02 2B 00 00 00 64 CRClo CRChi 其中 06 表示后面有 6 个数据字节(3 个 16 位寄存器)。

    你不必死记硬背,知道结构就能看懂:[地址][功能码][起始地址][数量] / [地址][功能码][字节数][数据…]。


    选 RTU 还是 TCP?

    • 已有很多串口设备/距离不长/干扰可控 → RTU(成本低)
    • 要进上位机/云/广域网/易集成 → TCP(好接入、好管理)
    • 混合:用 TCP→RTU 网关 把老设备接入以太网。

    上手代码(Python,pymodbus)

    RTU 读两个保持寄存器(从 40001 起)

    from pymodbus.client import ModbusSerialClient

    client = ModbusSerialClient(
    port='COM3', baudrate=9600, bytesize=8, parity='N', stopbits=1, timeout=1
    )
    client.connect()
    res = client.read_holding_registers(address=0, count=2, slave=1) # 40001-40002
    if not res.isError():
    print(res.registers) # [reg0, reg1]
    client.close()

    TCP 写单个保持寄存器(40100 = 1234)

    from pymodbus.client import ModbusTcpClient

    client = ModbusTcpClient(host='192.168.1.10', port=502)
    client.connect()
    res = client.write_register(address=99, value=1234, slave=1) # 40100 → 偏移 99
    print(res.isError())
    client.close()

    提示:很多设备 TCP 场景下会忽略 slave(Unit ID),但网关需要这个值来路由到下挂 RTU 从站。


    调试与工具清单

    • 串口参数:波特率/数据位/校验/停止位必须一致(常见 9600 8N1)。
    • 测试软件:Modbus Poll/QModMaster(主站)、Modbus Slave(从站仿真)。
    • 抓包:TCP 用 Wireshark;串口用串口监听工具或 USB 逻辑分析仪。
    • 逐步法:先“读一个”成功→再“读一批”→最后“写入”。

    结语

    Modbus 的精髓就是:四类对象固定映射功能码,报文里用 0 基偏移。认清“参考号 vs 偏移”的区别,你就能在各家设备间自如切换。

    赞(0)
    未经允许不得转载:网硕互联帮助中心 » 一文看懂 Modbus:从零到能用
    分享到: 更多 (0)

    评论 抢沙发

    评论前必须登录!