『7x24小时有问必答』
  系统学习

人生就像一场马拉松,偶尔停下来摸摸鱼,才能走得更远

---

小莫=w 王工=M
W:  小莫啊,愁眉苦脸的,又被 Modbus 卡住了?
M:  王工救命啊!设备死活不响应我的读取命令,我查了接线、波特率都对,感觉问题出在我发的报文上。可那一串十六进制数字,我看着就头大...
W:  哈哈,报文就是 Modbus 设备间的“对话语言”。别怕,把它拆开看,其实很简单。来,坐下,我泡壶茶,咱慢慢聊。Modbus 的核心思想就是“主从问答”。你(主站)发个“问题”(请求报文),设备(从站)回个“答案”(响应报文)或者执行个动作。报文就是包裹这个“问题”或“答案”的“信封”。
M:  “信封”?这个比喻好!那这个“信封”里都装了啥?
W:  问得好!Modbus 报文最基本、最重要的几个部分就是:
地址 (Address):  就像收件人门牌号。总线上可能挂了很多设备(从站),这个地址(1个字节,范围 1-247)告诉报文是发给谁的。地址 0 是广播地址(所有从站都听,但不回复)。
功能码 (Function Code):  这是“问题”的核心!告诉从站你要它干什么(1个字节)。比如:
0x01:读线圈状态 (Read Coils) - 读一堆开关量输出(DO)的状态(ON/OFF)。
0x02:读离散量输入 (Read Discrete Inputs) - 读一堆开关量输入(DI)的状态。
0x03:读保持寄存器 (Read Holding Registers) - 读一堆可读写的模拟量数据(比如温度设定值、电机转速)。
0x04:读输入寄存器 (Read Input Registers) - 读一堆只读的模拟量数据(比如实际温度、压力)。
0x05:写单个线圈 (Write Single Coil) - 改变一个 DO 的状态(强制 ON 或 OFF)。
0x06:写单个寄存器 (Write Single Register) - 改变一个保持寄存器的值。
0x0F:写多个线圈 (Write Multiple Coils) - 一次性改变多个 DO 的状态。
0x10:写多个寄存器 (Write Multiple Registers) - 一次性改变多个保持寄存器的值。
数据 (Data):  这个部分根据功能码不同,内容和长度都变。它包含具体操作的细节。比如:
起始地址 (Starting Address):  从哪个线圈/寄存器开始写?(2字节,高位在前)
数量 (Quantity):  要写多少个线圈/寄存器?(2字节,高位在前)
字节计数 (Byte Count):  后面紧跟着的实际数据有多少个字节?(1字节)
输出值/寄存器值 (Outputs Value/Registers Value):  要写入的多个值(N 字节)。线圈状态会按位打包(8个线圈压成1个字节),寄存器值则每个占2字节。
输出地址/寄存器地址 (Output Address/Register Address):  你要写哪个线圈/寄存器?(2字节,高位在前)
输出值/寄存器值 (Output Value/Register Value):  你要把它写成什么值?(2字节。对于线圈,0xFF00  表示 ON,0x0000  表示 OFF;对于寄存器就是数值)。
起始地址 (Starting Address):  你要从哪个线圈/寄存器开始读?(2字节,高位在前)
数量 (Quantity):  你要连续读多少个线圈/寄存器?(2字节,高位在前)
对于“读”请求 (0x01,  0x02,  0x03,  0x04):数据区通常包含:
对于“写单个”请求 (0x05,  0x06):数据区包含:
对于“写多个”请求 (0x0F,  0x10):数据区包含:
错误校验 (Error Check):  这个就像“信封”的封口漆和防伪码,确保报文在传输过程中没被干扰出错。有两种主要形式:
CRC (循环冗余校验):  用在  Modbus RTU/ASCII  串行链路上(RS-232/485)。对地址、功能码、数据区所有字节进行计算,得到2个字节的校验码,附加在报文最后。RTU 模式最常用。
LRC (纵向冗余校验):  用在  Modbus ASCII  链路上。计算得到一个字节的校验码(转换为两个ASCII字符表示)。
(对于 Modbus TCP):  TCP/IP 协议栈底层(如以太网)已经有很强的校验机制(CRC32),所以  Modbus TCP 报文本身不需要额外的 CRC/LRC!  它用另一种机制保证完整性(见后面)。
M:  哦!我好像有点感觉了!地址找对人,功能码说明要干啥,数据区说明具体怎么干,最后加个校验保安全。那... 我常用的 RTU 和 TCP 报文结构一样吗?
W:  非常好!抓住了核心要素。RTU 和 TCP 的“应用数据单元”是一样的(就是地址+功能码+数据),但它们的“包装”不同:
Modbus RTU 报文 (串行 RS-232/485):
[ 从站地址 (1 Byte) ] [ 功能码 (1 Byte) ] [ 数据 (N Bytes) ] [ CRC 校验 (2 Bytes) ]
特点:  紧凑,所有数据都是二进制,直接传输效率高。
关键点:  报文之间必须由至少  3.5个字符时间  的静默间隔来分隔。否则设备分不清哪是头哪是尾。波特率越高,这个静默时间越短。
示例 (读保持寄存器):  主站请求读取从站地址 1 的设备,从保持寄存器地址 40001(对应协议地址 0x0000)开始,连续读 2 个寄存器。
地址: 0x01

功能码: 0x03 (读保持寄存器)

数据: 起始地址高字节 0x00, 起始地址低字节 0x00, 数量高字节 0x00, 数量低字节 0x02

CRC: (计算前面 01 03 00 00 00 02 这6个字节的CRC) -> 假设是 0xC4 0x0B

完整RTU请求帧 (十六进制): `01 03 00 00 00 02 C4 0B`
正常响应:  如果成功,从站 1 回复:

地址: 0x01 (我是谁)

功能码: 0x03 (你要我读寄存器)

数据: 字节计数 (后面数据有多少字节) 0x04 (因为2个寄存器 * 2字节/寄存器=4字节),

         寄存器1值高字节, 寄存器1值低字节,

         寄存器2值高字节, 寄存器2值低字节 (假设值分别是 0x1234 和 0x5678)

CRC: (计算前面 01 03 04 12 34 56 78 这7个字节的CRC)

完整RTU响应帧 (十六进制): `01 03 04 12 34 56 78 [CRC Hi] [CRC Lo]`
异常响应:  如果出错(比如寄存器地址不存在),功能码最高位置 1 (即原功能码 + 0x80),数据区只有一个字节的错误码。
地址: 0x01

功能码: 0x83 (0x03 + 0x80)

数据: 异常码 (1 Byte) 例如 0x02 (非法数据地址)

CRC: (计算 01 83 02 的CRC)

完整RTU异常响应帧: `01 83 02 [CRC Hi] [CRC Lo]`

Modbus TCP 报文 (基于以太网):
Modbus TCP 报文 (基于以太网):
[ MBAP 头 (7 Bytes) ] [ 从站地址 (1 Byte) ] [ 功能码 (1 Byte) ] [ 数据 (N Bytes) ]
事务处理标识 (Transaction Identifier) (2 Bytes):  由主站生成,用于匹配请求和响应。同一个事务ID的请求响应是一对。非常重要!
协议标识符 (Protocol Identifier) (2 Bytes):  固定为  0x00 0x00,表示 Modbus 协议。
长度 (Length) (2 Bytes):  指示后面还有多少个字节(从站地址 + 功能码 + 数据)。注意:不包括 MBAP 头自身的长度。
单元标识符 (Unit Identifier) (1 Byte):这就是 RTU 报文里的“从站地址”!  在 TCP 下,它通常用于标识连接在网关、网桥后面的串行网络上的具体 Modbus 从站设备。如果直接连接 TCP 设备,这个字段可能由设备定义其用途(有时也直接当从站地址用)。
特点:  运行在 TCP/IP 网络上(通常是端口 502)。去掉了 CRC 校验,增加了 MBAP 头 (Modbus Application Protocol Header)。
MBAP 头详解 (7字节):
示例 (同样的读请求 - Modbus TCP):  主站请求(事务ID假设为 0x0001)读取单元标识符(相当于从站地址)为 1 的设备,同样读 2 个保持寄存器 (0x0000 开始)。

MBAP头:

   事务ID: 0x00 0x01

   协议ID: 0x00 0x00

   长度: 0x00 0x06 (后面有 01(地址) + 03(功能码) + 00 00(起始地址) + 00 02(数量) = 6字节)

   单元ID: 0x01

应用数据单元:

   地址/单元ID: 0x01 (这里和MBAP里的单元ID一致)

   功能码: 0x03

   数据: 起始地址 0x00 0x00, 数量 0x00 0x02

完整TCP请求帧 (十六进制): `00 01 00 00 00 06 01 03 00 00 00 02`
正常响应:

MBAP头:

   事务ID: 0x00 0x01 (和请求匹配!)

   协议ID: 0x00 0x00

   长度: 0x00 0x05 (后面有 01(地址) + 03(功能码) + 04(字节计数) + 4字节数据 = 5字节? 等等... 计算:01+03+04+[4字节数据]=8字节? 这里有个关键点!)

   **修正:** MBAP 头里的 `长度` = 单元标识符(1) + 功能码(1) + 数据区长度。

   数据区长度 = 字节计数(1) + 实际数据字节数(4) = 5字节。

   所以整个 PDU 长度 = 1(单元ID) + 1(功能码) + 5(数据区) = 7字节? 不对。

   **关键:** MBAP 长度字段 = 后面跟随的字节数,即 **从 `单元标识符` 字节开始,一直到报文结束的所有字节数**。

   响应帧结构: [MBAP] + [单元ID 1B] + [功能码 1B] + [字节计数 1B] + [寄存器数据 4B] -> 后面部分共 1+1+1+4=7 字节。

   所以长度字段应该是 `00 07` (0x0007)。

MBAP头 (修正后): `00 01 00 00 00 07 01`

应用数据单元:

   单元ID: 0x01

   功能码: 0x03

   数据: 字节计数 0x04, 寄存器1值 0x12 0x34, 寄存器2值 0x56 0x78

完整TCP响应帧 (十六进制): `00 01 00 00 00 07 01 03 04 12 34 56 78`
异常响应:  功能码+0x80,数据区一个字节错误码。长度字段计算类似。
M:  哇!这下清楚多了!TCP 那个 MBAP 头,特别是事务 ID 和长度,原来是干这个用的。单元标识符其实就是串行网络里的从站地址换了个地方。那... 我最初的问题,设备不响应,可能出在哪里?
W:  根据报文结构,结合你的情况,重点排查:
地址:  你请求报文里的从站地址 (RTU) 或单元标识符 (TCP)  真的是目标设备的地址吗?确认设备设置的地址。
功能码:  你读的线圈/寄存器,设备支持这个功能码吗?比如你试图用  0x03  去读只允许  0x04  读的输入寄存器,设备会报错(异常响应)。查设备手册!
数据区 - 起始地址:  Modbus 协议地址通常是  0-based(从0开始)。但很多软件/手册用  1-based(如 40001 对应协议地址 0x0000)。务必搞清楚你用的软件和设备的约定!  这是超级大坑!你发的是 40001 的地址还是 0x0000?
数据区 - 数量:  你请求的数量超过设备允许的范围了吗?比如设备只有10个寄存器,你请求从地址0开始读11个,也会出错。
错误校验 (RTU):  你计算(或者软件自动计算)的  CRC 正确吗?用在线CRC计算器验证下你的请求报文。设备端校验失败会直接丢弃报文,不会有任何响应。
格式 (RTU):  波特率、数据位、停止位、校验位(None/Even/Odd)主站和从站设置完全一致吗?特别是校验位。RTU 报文之间是否有足够的 3.5 字符静默时间?
连接 (TCP):  TCP 连接建立成功了吗?能 Ping 通设备 IP 吗?防火墙(尤其是工控机上的)是否放行了端口 502?设备作为 TCP Server 在监听吗?主站作为 TCP Client 连接上了吗?
MBAP 长度 (TCP):  你的 TCP 请求帧,MBAP 头里的长度字段计算正确吗?后面跟的字节数算对了吗?常见的错误来源。
M:  明白了王工!太感谢了!我这就拿报文分析软件(或者串口助手/Wireshark)抓一下实际收发的数据,对着报文结构一条条核对,重点看地址、功能码、起始地址、数量、CRC(RTU)/MBAP长度(TCP)。有这结构图在手,心里有底多了!
W:  这就对了!记住几个关键点:
主从问答:  主问,从答(或执行)。
核心四件套 (应用层):  地址、功能码、数据、校验(MBRTU)/MBAP头(MBTCP)。
地址陷阱:  0-based vs 1-based (40001 vs 0x0000)。
功能码:  读 (01,02,03,04) vs 写 (05,06,0F,10)。
RTU 要点:  二进制、紧凑、CRC3.5字符间隔
TCP 要点:MBAP头  (事务ID匹配、长度计算、单元ID)、无CRC、走网络 (IP:Port 502)。
异常响应:  功能码 + 0x80 + 错误码。
M:  记下了!王工,下次调试我请您吃饭!
W:  哈哈,吃饭就免了,把设备调通了就是最好的谢礼。去吧,按这个思路查,准能搞定!遇到具体抓到的报文看不懂再随时来问。

---

授人以鱼不如授人以渔

1.webp

喜欢就给个点赞+在看
2.gif

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

上一主题上一主题         下一主题下一主题
QQ手机版小黑屋粤ICP备17165530号

关于我们·投诉举报· 用户帮助· 联系我们 · 本站服务 · 版权声明· 隐私政策 · 投搞指南

法律保护:PLC技术网,plcjs.com,plcjs.net等字样
Copyright 2010-2030. All rights reserved. 


微信公众号二维码 抖音二维码 百家号二维码 今日头条二维码哔哩哔哩二维码