介绍
简介
Modbus 是一种应用层报文传输协议,由 Modicon 公司在 1979 年发布,是为了解决 PLC 通信而研发的协议。
因为 Modbus 是开源的且无著作权要求、易于部署维护、可靠性强的特性,所以 Modbus 已经成为工业领域通信协议事实上的业界标准,并且现在是工业电子设备之间常用的连接方式。
MODBUS 是一个请求/应答协议,包括ASCII、RTU、TCP等,是1979年开发出的一种工业通信协议,并没有规定物理层。此协议定义了控制器能够认识和使用的消息结构,而不管它们是通过何种硬件方式进行通信的
硬件接口
- RS-232
- RS-422
- RS-485
- 以太网
传输模式
- RTU
- ASII
- TCP
TCP和RTU比ASCII常见,其中TCP的使用频率更高一些
主站与从站
通讯只能由主站发起,从站回应
一个主站可以连接多个从站,且主站有且只能有一个,从站至少一个
tcp模式下:一般分为服务器与客户端,可认为客户端为主站,服务器为从站
协议及相关术语
存储区数据模型
在modbus中定义了4种不同的数据模型,具体如下
名称 | 数据类型 | 访问类型 | 操作码(功能码) |
---|---|---|---|
线圈 | 位(bit) | 读写 | 01H 05H 0FH |
离散量输入 | 位(bit) | 只读 | 02H |
保持寄存器 | 字(word,ushort) | 读写 | 03H 06H 10H |
输入寄存器 | 字(word,ushort) | 只读 | 04H |
对应英文(一个表有些拥挤)
中文 | 英文 |
---|---|
线圈 | Coils |
离散量输入 | Discrete Inputs |
保持寄存器 | Holding Registers |
输入寄存器 | Input Registers |
同许多规范一样,不同行业可能会有不同的称呼。如保持寄存器可以被称为输出寄存器
,而线圈可以被称为数字
或离散输出
。
功能码
一般常用到的有以下几种:
功能码 | 描述 | PLC地址 | 寄存器地址 | 位/字操作 | 操作数量 |
---|---|---|---|---|---|
01H | 读线圈 | 00001-09999 | 0000H-FFFFH | 位 | 单个或多个 |
02H | 读离散量输入 | 10001-19999 | 0000H-FFFFH | 位 | 单个或多个 |
03H | 读保持寄存器 | 40001-49999 | 0000H-FFFFH | 字 | 单个或多个 |
04H | 读输入寄存器 | 30001-39999 | 0000H-FFFFH | 字 | 单个或多个 |
05H | 写线圈 | 00001-09999 | 0000H-FFFFH | 位 | 单个 |
06H | 写保持寄存器 | 40001-49999 | 0000H-FFFFH | 字 | 单个 |
0FH | 写线圈 | 00001-09999 | 0000H-FFFFH | 位 | 多个 |
10H | 写保持寄存器 | 40001-49999 | 0000H-FFFFH | 字 | 多个 |
数据帧
RTU
从站地址 | 功能码 | 数据 | CRC |
---|---|---|---|
1byte(1字节)(8bit) | 1byte | 0-252byte | 2字节(CRC低|CRC高) |
数据位
一般请求为 2字节 表示要读取的 线圈(或寄存器)的起始地址 + 2字节数据表示要读取的线圈(或寄存器)的个数
应答数据位为 1字节表示数据的字节数 + N字节表示读取的线圈状态(或寄存器)数据
在读线圈时,如果读取到的线圈状态数据不是8的整数,则会在后面填充0使其满足 8 位的倍数
用表格展示(这里使用线圈
演示)
请求PDU
功能码 | 1个字节 | 0x01 |
---|---|---|
起始地址 | 2个字节 | 0x0000-0xFFFF |
线圈数量 | 2个字节 | 1-2000(0x7D0) |
响应PDU
功能码 | 1个字节 | 0x01 |
---|---|---|
字节数 | 1个字节 | *N(输入数量/8,余数不等于0,那么N = N+1) |
线圈状态 | n | n=N 或N+1 |
样例
线圈读取(0x01)样例
发送:
假设从站地址为01H,读取线圈寄存器的起始地址为0017H,读取38(十进制)个寄存器,指令结构如下表:
从站地址 | 功能码 | 起始地址高位 | 起始地址低位 | 寄存器数量高位 | 寄存器数量低位 | CRC高位 | CRC低位 |
---|---|---|---|---|---|---|---|
01 | 01 | 00 | 17 | 00 | 26 | 0D | D4 |
响应
各线圈的状态与数据内容的每个bit对应,1代表ON,0代表OFF.若查询线圈的数量不是8的倍数,则在最后一个字节的高位补0.
从站地址 | 功能码 | 返回字节数 | 数据1 | 数据2 | 数据3 | 数据4 | 数据5 | CRC高位 | CRC低位 |
---|---|---|---|---|---|---|---|---|---|
01 | 01 | 05 | CD | 6B | B2 | 0E | 1B | 44 | EA |
第一个字节CDH(0xCD)对应线圈0017H到001E的状态,转为二进制是11001101,其中bit0对应0017H,bit7对应001E,如下表:
001EH | 001DH | 001CH | 001BH | 001AH | 0019H | 0018H | 0017H |
---|---|---|---|---|---|---|---|
1 | 1 | 0 | 0 | 1 | 1 | 0 | 1 |
ON | ON | OFF | OFF | ON | ON | OFF | ON |
保持寄存器读取(0x03)样例
发送
有一个从站是温湿度传感器,从站地址为 1,它会将采集到的湿度写入保持寄存器的 40001 区块中;温度写入保持寄存器的 40002 区块中。此时我们发送读取保持寄存器请求去获取它的温湿度信息。
从站地址 | 功能码 | 起始地址高位 | 起始地址低位 | 寄存器数量高位 | 寄存器数量低位 | CRC高位 | CRC低位 |
---|---|---|---|---|---|---|---|
01 | 03 | 00 | 00 | 00 | 02 | C4 | 0B |
响应
从站地址 | 功能码 | 返回字节数 | 数据1 | 数据2 | 数据3 | 数据4 | CRC高位 | CRC低位 |
---|---|---|---|---|---|---|---|---|
01 | 03 | 04 | 01 | 46 | 01 | 3B | 5A | 59 |
前两个字节为湿度(换算成十进制为 326 ,即 32.6% ),后两个字节为温度(十进制为 315,即 31.5 摄氏度)
注:以上部分样例来源于网上公开数据