【1】Modbus
1. 起源
- Modbus由Modicon公司于1979年开发,是全球第一个真正用于工业现场的总线协议
- 在中国,Modbus 已经成为国家标准,并有专业的规范文档,感兴趣的可以去查阅相关的文件,详情如下:标准编号为:GB/T19582-2008文件名称:《基于 Modbus 协议的工业自动化网络规范》
- Modbus通信协议具有多个变种,其中有支持串口,以太网多个版本,其中最著名的是Modbus RTU、Modbus ASCII和Modbus TCP三种,其中Modbus TCP是在施耐德收购Modicon后1997年发布的。
2. 分类
1)Modbus RTU
运行在串口上的协议,采用二进制表现形式以及紧凑的数据结构,通信效率较高,应用比较广泛
2)Modbus ASCII
运行在串口上的协议,采用ASCII码进行传输,并且每个字节的开始和结束都有特殊字符作为标志,传输效率远远低于Modbus RTU,一般只有通讯量比较少时才会考虑它。
3)Modbus TCP
是一种基于以太网的协议,使用 TCP/IP 协议栈进行通信。它使用以太网帧作为数据传输的封装,通过 IP 地址和端口号来标识设备。
3. 优势
简单、免费、容易使用
4. 应用场景
Modbus协议是现在国内工业领域应用最多的协议,不只PLC设备,各种终端设备,比如水控机、水表、电表、工业秤、各种采集设备
问答
在面试中回答“Modbus 协议”相关问题时,可从协议定位、核心架构、传输方式、通信流程、实际应用、技术价值等维度组织内容,既体现全面性,又贴合工业/嵌入式场景:
一、协议定位与核心价值
Modbus 是工业自动化领域应用广泛的开源通信协议,旨在解决工业现场不同设备(PLC、传感器、执行器、网关等)间的“数据互通与指令协同”问题,让分散的设备能像“统一系统”一样工作,是工业物联网(IIoT)底层设备互联的核心协议之一。
二、核心架构:主从式通信
采用“一主多从”的主从架构:
- 主设备(如 PLC、工业网关、上位机)主动发起通信请求;
- 从设备(如温湿度传感器、电机驱动器、阀门控制器)仅响应主设备的请求,不主动发起通信。
这种架构简化了通信冲突处理,适合工业现场“中心控制+分散设备”的管理模式。
三、主流传输版本与差异
Modbus 针对不同“传输介质”衍生出 3 类核心版本,适配从“串口总线”到“以太网”的场景:
1. Modbus RTU(串行总线,如 RS - 485/RS - 232)
- 数据格式:二进制紧凑传输,每个字节为 8 位,用 CRC(循环冗余校验) 保证数据完整性;
详述循环冗余校验CRC
一道题带你搞懂CRC循环冗余校验是如何纠错的
- 传输效率:适合长距离、低带宽的工业现场(如车间产线设备互联),但通信速率受串口波特率限制(常见 9600bps、19200bps);
- 典型场景:工厂产线的传感器(温湿度、压力)与 PLC 通信,单主设备可带最多 247 个从设备。
2. Modbus ASCII(串行总线)
- 数据格式:ASCII 字符传输(每个字节拆为 2 个 ASCII 字符),用 LRC(纵向冗余校验);
- 特点:可读性强(便于串口调试工具抓包分析),但传输效率低于 RTU(相同数据,ASCII 长度是 RTU 的 2 倍),多用于对“调试友好性”要求高的场景(如实验室设备联调)。
3. Modbus TCP(以太网)
- 传输层:基于 TCP/IP 协议,把 Modbus 应用数据封装在 TCP 数据包中传输;
- 特点:突破串口“距离与速率”限制,支持跨网络、高速通信(千兆以太网下可近实时传输);
- 典型场景:工业物联网云平台与边缘网关通信、智能工厂多车间设备的跨区域集中管理。
四、通信流程(以“主设备读从设备数据”为例)
以 Modbus RTU 为例,核心流程分“请求 - 响应 - 错误处理”:
- 主设备请求:主设备发送“请求帧”,包含: 从设备地址(指定和哪个从设备通信);
- 功能码(如 0x03 表示“读保持寄存器”,0x01 表示“读线圈状态”);
- 数据地址(要读的寄存器/线圈起始地址);
- 数据长度(读多少个寄存器/线圈);
- CRC 校验(确保帧传输无错)。
- 从设备响应:从设备收到请求后,先校验地址和 CRC: 若匹配且能执行操作,返回“响应帧”:包含从设备地址、功能码、实际读取的数据、CRC 校验;
- 若出错(如地址不存在、功能不支持),返回“异常响应帧”:功能码最高位设为 1(表示异常),并附带“异常码”(说明错误原因,如 0x01 表示“不支持的功能码”)。
- 错误处理:主设备收到异常响应后,可根据异常码重试、切换从设备或报警。
五、在工业/嵌入式场景的典型应用
Modbus 是工业设备互联的“通用语言”,典型应用场景包括:
- 设备数据采集:PLC 通过 Modbus 读取车间温湿度传感器、压力变送器的实时数据,为生产决策(如空调启停、产线速度调整)提供依据;
- 设备远程控制:上位机通过 Modbus TCP 向边缘网关下发指令,网关再通过 Modbus RTU 控制车间电机转速、阀门开关;
- 多系统集成:把采用不同通信协议的设备(如老产线的 Modbus 传感器、新系统的 Profinet 执行器)通过“Modbus 网关”桥接,实现跨协议协同。
六、技术优势(为什么工业领域广泛用 Modbus)
- 开源免费:无授权费用,降低工业企业/嵌入式开发者的使用成本;
- 易实现:协议格式简单,嵌入式端(如 STM32、Arduino)可通过串口库+CRC 校验库快速开发从设备/主设备;
- 兼容性强:几乎所有工业设备厂商(西门子、施耐德、国产 PLC 等)都支持 Modbus,不同品牌设备能“即连即用”。
【2】Modbus TCP协议
1. 特点
- 遵循主从问答协议 (主机 从机 主从问答:采集信息 控制)
- (主机问---从机答247 1-247:从机 0:广播 248-255:保留 )
- Modbus TCP协议是应用层协议,基于传输层TCP通信
- Modbus TCP的默认端口号是502
2. 组成
Modbus TCP协议包含三部分:报文头、功能码、数据
报文头有7个字节,功能码有1个字节,Modbus TCP协议最大数据帧长度为260个字节,数据最多为252个字节。
2.1 报文头:7个字节
事务处理标识符:包的标识,没有什么限制,一般主机发什么从机回什么
协议标识符:0x0000 两个字节
长度:长度后面的字节个数,必须占2个字节
单元标识符:从机地址,从机ID
2.2 寄存器(存储数据) ******
Modbus TCP通过寄存器的方式存储数据。
一共有四种类型的寄存器,分别是:离散量输入寄存器、线圈寄存器、输入寄存器、保持寄存器。
1) 离散量和线圈其实就是位寄存器(每个寄存器数据占1字节),工业上主要用于控制IO设备。
线圈寄存器,类比为开关量,每一个bit都对应一个信号的开关状态。所以一个byte就可以同时控制8路的信号。比如控制外部8路io的高低。 线圈寄存器支持读也支持写,写在功能码里面又分为写单个线圈寄存器和写多个线圈寄存器。
对应的功能码也就是:0x01 0x05 0x0f
离散输入寄存器,离散输入寄存器就相当于线圈寄存器的只读模式,他也是每个bit表示一个开关量,而他的开关量只能读取输入的开关信号,是不能够写的。比如我读取外部按键的按下还是松开。
所以功能码也简单就一个读的 0x02
2) 输入和保持寄存器是字寄存器(每个寄存器数据占2个字节),工业上主要用于存储工业设备的值。
保持寄存器,这个寄存器的单位不再是bit而是两个byte,也就是可以存放具体的数据量的,并且是可读写的。比如我设置时间年月日,不但可以写也可以读出来现在的时间。写也分为单个写和多个写
所以功能码有对应的三个:0x03 0x06 0x10
输入寄存器,这个和保持寄存器类似,但是也是只支持读而不能写。一个寄存器也是占据两个byte的空间。类比我通过读取输入寄存器获取现在的AD采集值
对应的功能码也就一个 0x04
2.3 功能码(记住)
寄存器PLC地址和寄存器的对应关系:
线圈: 00001-09999
离散量输入:10001-19999
输入寄存器:30001-39999
保持寄存器:40001-49999
开灯:05
读温度传感器数据:03 04
具体协议分析:
总结
读数据:
主机--》从机:
报文头(7字节)+功能码(1)+起始地址(2)+数量(2)
从机--》主机:
报文头(7)+功能码(1)+字节计数(1)+数据(?)
写数据:
写单个:
主机--》从机:
报文头(7)+功能码(1)+地址(2)+断通标志/数据(2)
(线圈:断通标志 0x0000:复位 0xff00:置位 保持寄存器:数据)
从机--》主机:
原文返回
对于读数据和写单个主机--》从机来说,报文头中字节长度固定为0x0006,(1个字节的单元标识符+1个字节的功能码+4个字节的数据)
写多个:
主机--》从机:
报文头(7)功能码(2)+起始地址(2)+数量(2)+字节计数(1)+数据(?)
从机--》主机:
报文头(7)功能码(2)+起始地址(2)+数量(2)(原文返回)
练习
主机--》从机:
12 34 00 00 00 06 01 03 00 63 00 03
从机--》主机:
12 34 00 00 00 09 01 03 06 45 69 11 11 66 66
1.读传感器数据,读1个寄存器数据,写出主从数据收发协议。
主机--》从机:12 34 00 00 00 06 11 03 00 01 00 01
从机--》主机:12 34 00 00 00 05 11 03 02 12 34
2. 写出控制IO设备开关的协议数据,操作1个线圈,置1。
主机--》从机:12 34 00 00 00 06 01 05 00 63 ff 00
从机--》主机:12 34 00 00 00 06 01 05 00 63 ff 00
【3】工具使用
1.Modbus Slave、Poll安装
1)默认安装
2)破解:点击connection-》connect,输入序列号(序列号在SN.txt)
3)使用:
从机:Modbus Slave
先设置:
再连接:点击connection-》connect
主机:
先设置
再连接:(一定要先开启从机(salve端),再开启主机(poll端))
2. 网络调试助手
linux下编程
在虚拟机写程序实现poll端功能,编写客户端实现和Slave通信,实现03功能码。
uint8_t data[12]={0x00,0x00,0x00,0x00,0x00,0x06,0x01....};
主机要先建立连接---》发送协议(modbus tcp协议)---》接收数据(modbus tcp协议)--》打印数据
1. 功能码0x01(读线圈状态)
示例1:读取地址0x0000的两个线圈
主机->从机:00 01 00 00 00 06 01 01 00 00 00 02
从机->主机:00 01 00 00 00 04 01 01 01 03
# 解析:线圈0状态为1(ON),线圈1状态为1(ON),其余线圈状态无效
示例2:读取地址0x0003的四个线圈
主机->从机:00 02 00 00 00 06 01 01 00 03 00 04
从机->主机:00 02 00 00 00 04 01 01 01 0F
# 解析:二进制0F转换为十进制为15,即00001111,线圈3至6状态为1(ON)(有效位为1)
2. 功能码0x02(读离散输入)
示例1:读取地址0x0001的三个离散输入
主机->从机:00 03 00 00 00 06 01 02 00 01 00 03
从机->主机:00 03 00 00 00 04 01 02 01 05
# 解析:二进制05转换为十进制为5,即00000101,输入1状态为1(ON),输入3状态为1(ON)
示例2:读取地址0x0005的两个离散输入
主机->从机:00 04 00 00 00 06 01 02 00 05 00 02
从机->主机:00 04 00 00 00 04 01 02 01 01
# 解析:二进制01转换为十进制为1,即00000001,仅输入5状态为1(ON)
3. 功能码0x03(读保持寄存器)
示例1:读取地址0x0002的两个保持寄存器
主机->从机:00 05 00 00 00 06 01 03 00 02 00 02
从机->主机:00 05 00 00 00 07 01 03 04 12 34 56 78
# 解析:寄存器2的值为0x1234,寄存器3的值为0x5678
示例2:读取地址0x000A的一个保持寄存器
主机->从机:00 06 00 00 00 06 01 03 00 0A 00 01
从机->主机:00 06 00 00 00 05 01 03 02 FF FF
# 解析:寄存器10的值为0xFFFF(典型错误状态码)
4. 功能码0x04(读输入寄存器)
示例1:读取地址0x0000的三个输入寄存器
主机->从机:00 07 00 00 00 06 01 04 00 00 00 03
从机->主机:00 07 00 00 00 09 01 04 06 00 64 01 F4 FF FF
# 解析:寄存器0的值为100(0x0064),寄存器1的值为500(0x01F4),寄存器2的值为65535(0xFFFF)
示例2:读取地址0x0005的两个输入寄存器
主机->从机:00 08 00 00 00 06 01 04 00 05 00 02
从机->主机:00 08 00 00 00 07 01 04 04 80 00 40 00
# 解析:寄存器5的值为32768(0x8000),寄存器6的值为16384(0x4000)
5. 功能码0x05(写单个线圈)
示例1:开启地址0x0063的线圈
主机->从机:00 09 00 00 00 06 01 05 00 63 FF 00
从机->主机:00 09 00 00 00 06 01 05 00 63 FF 00
# 说明:0xFF00表示置ON,成功时返回相同数据
示例2:关闭地址0x001A的线圈
主机->从机:00 0A 00 00 00 06 01 05 00 1A 00 00
从机->主机:00 0A 00 00 00 06 01 05 00 1A 00 00
# 说明:0x0000表示置OFF
6. 功能码0x06(写单个寄存器)
示例1:写地址0x0003的寄存器值为0x55AA
主机->从机:00 0B 00 00 00 06 01 06 00 03 55 AA
从机->主机:00 0B 00 00 00 06 01 06 00 03 55 AA
# 成功时回显写入值
示例2:写地址0x0010的寄存器值为0x1234
主机->从机:00 0C 00 00 00 06 01 06 00 10 12 34
从机->主机:00 0C 00 00 00 06 01 06 00 10 12 34
7. 功能码0x0F(写多个线圈)
示例1:写地址0x0010的3个线圈为ON
主机->从机:00 0D 00 00 00 08 01 0F 00 10 00 03 01 07
从机->主机:00 0D 00 00 00 06 01 0F 00 10 00 03
# 解析:二进制07转换为十进制为7,即00000111,线圈16至18置ON
示例2:写地址0x0020的5个线圈(二进制10101)
主机->从机:00 0E 00 00 00 08 01 0F 00 20 00 05 01 15
从机->主机:00 0E 00 00 00 06 01 0F 00 20 00 05
# 解析:二进制15转换为十进制为15,即00010101,仅偶数位生效(线圈32、34、36置ON)
8. 功能码0x10(写多个寄存器)
示例1:写地址0x0004的2个寄存器(值0xAABB和0xCCDD)
主机->从机:00 0F 00 00 00 0B 01 10 00 04 00 02 04 AA BB CC DD
从机->主机:00 0F 00 00 00 06 01 10 00 04 00 02
# 数据部分以四字节表示两个寄存器的值
示例2:写地址0x0010的三个寄存器(十进制值100、200、300)
主机->从机:00 10 00 00 00 0D 01 10 00 10 00 03 06 00 64 00 C8 01 2C
从机->主机:00 10 00 00 00 06 01 10 00 10 00 03
# 数值转换:100转换为十六进制为0x0064,200转换为十六进制为0x00C8,300转换为十六进制为0x012C