参考资料
- 官方文档: Modbus_Application_Protocol_V1_1b.pdf
- wiki: Modbus - Wikipedia (jinzhao.wiki)
- RTU与ASCII比较: Modbus RTU (ozeki.hu)
- 详细说明: Modicon Modbus Protocol Reference Guide
- modbus TCP: Modbus_Messaging_Implementation_Guide_V1_0b_tcp.pdf
名词解释
- ADU: Application Data Unit 应用数据单元
- PDU: Protocol Data Unit 协议数据单元
- RTU: Remote Terminal Unit
- CRC: Cyclic Redundancy Check 循环冗余校验
- LRC: Longitudinal Redundancy Check 纵向冗余校验
- 离散变量: 指其数值只能用自然数或整数单位计算的则为离散变量
协议简介
由Modicon (现在的施耐德电气)在1979年发布,用于PLC通信,已经成为事实上的工业领域通信协议的业界标准。
特点: 简单,应用广泛,请求/应答,功能码支持。
modbus支持多种通信栈传输
- TCP/IP over Ethernet, 端口为502
- Asynchronous serial 传输 over EIA/TIA-232-E 或 EIA/TIA-485 或 fiber, radio 等
- MODBUS PLUS, 一种高速令牌传输网络
- 其他
其中串口通讯最为常见。
总体结构
modbus是由 modbus ADU 构成,不同的通讯协议会对它做一定的封装,ADU的结构如下
Additinal address | Modbus PDU | Error Check |
---|---|---|
地址域 | 协议数据单元 | 差错校验 |
PDU的结构如下
Function code | Data |
---|---|
功能码 | 数据 |
PDU的格式将在下一段详细介绍。
modbus有很多种通讯协议,下面是最为常见的三种类型。
Modbus RTU
最常用, Modbus messages are framed (separated) by idle (silent) periods.数据密度高,用于RS-485/EIA-485 等异步串口
Name | Length (bits) | Function |
---|---|---|
Start | 3.5 x 8 | At least 3+1⁄2 character times (28 bits) of silence (mark condition) |
Address | 8 | Station address |
Function | 8 | Indicates the function code; e.g., read coils/holding registers |
Data | n × 8 | Data + length will be filled depending on the message type |
CRC | 16 | Cyclic redundancy check |
End | 3.5 x 8 | At least 3+1⁄2 character times (28 bits) of silence (mark condition) between frames |
Modbus ASCII
易读,有前导码和终止码,主要用于7-bits或8-bits的异步串口线,
Name | Length (bytes) | Function |
---|---|---|
Start | 1 | Colon : (ASCII value 0x3A) |
Address | 2 | Station address |
Function | 2 | Indicates the function codes like read coils / inputs |
Data | n × 2 | Data + length will be filled depending on the message type |
LRC | 2 | Checksum (Longitudinal redundancy check) |
End | 2 | Carriage return + line feed (CR/LF) pair (ASCII values 0x0D and 0x0A) |
RTU和ASCII发送同样的原始数据,区别如下
源数据 | RTU模式 | ASCII模式 |
---|---|---|
0x45 | 0x45 | 0x34 0x35 |
0xA1 | 0xA1 | 0x41 0x31 |
0xa1 | 0xA1 | 0x61 0x31 |
Modbus TCP
结构最复杂,运行在以太网上,TCP502端口。
Modbus/TCP应用层报文不需要校验(个人猜测是因为IP层/TCP层已经将校验做掉了)
modbus TCP的报文整体结构如下
Mac header | IP header | TCP header | Modbus ADU |
---|---|---|---|
Mac头 | IP头 | TCP头 | Modbus应用数据单元 |
下面是modbus中的ADU结构
Name | Length (bytes) | Function |
---|---|---|
Transaction identifier | 2 | For synchronization between messages of server and client |
Protocol identifier | 2 | 0 for Modbus/TCP |
Length field | 2 | Number of remaining bytes in this frame |
Unit identifier | 1 | Server address (255 if not used) |
Function code | 1 | Function codes as in other variants |
Data bytes | n | Data as response or commands |
Name | Length (bytes) | 说明 |
---|---|---|
Transaction identifier | 2 | 事务识别符,无限制,TC和TS一致 |
Protocol identifier | 2 | 协议识别符, 0 |
Length field | 2 | 后续长度, 2~254 |
Unit identifier | 1 | 单元识别符, 小于等于247或255 |
Function code | 1 | 见PDU结构章节 |
Data bytes | n | 见PDU结构章节 |
wireshark中将前7个固定字节成为MBAP header(Modbus Application Protocol),个人认为这相当于通用结构中的地址域(Additional address)。ADU总长度最大为260字节。
PDU
根据文档定义,功能码占一个字节,范围是1~127,分为3类
- 公共功能码:除去下面两种类型的功能码,均可使用,但是在文档中只有部分已定义
- 使用者自定义功能码:65~72, 100~110
- 保留功能码:被某些公司使用或者暂时不可用:8,9,10,13,14,41,42,90,91,125,126,127;
在具体解析功能码前,我们首先来了解一下功能类型和其作用的对象类型
功能类型
- Data Access 数据存取
- Diagnostics 诊断,用于查看链路状态和设备状态,只支持串口
- Other(Encapsulated Interface Transport?)
对象类型
Object type | 说明 | Access | Size | Address Space |
---|---|---|---|---|
Coil | 线圈 | Read-write | 1 bit | 00001 – 09999 |
Discrete input | 离散输入,相当于保持线圈 | Read-only | 1 bit | 10001 – 19999 |
Holding register | 保持寄存器 | Read-only | 16 bits | 30001 – 39999 |
Input register | 输入寄存器 | Read-write | 16 bits | 40001 – 49999 |
功能码与数据
本文解析的功能码只限于文档中出现的已定义公共功能码,如下表所示
Function type | Function name | Function code | Comment | ||
---|---|---|---|---|---|
Data Access | Bit Access | Physical Discrete Inputs | Read Discrete Inputs | 2 | |
Internal Bits or Physical Coils | Read Coils | 1 | |||
Write Single Coil | 5 | ||||
Write Multiple Coils | 15 | ||||
16-bit access | Physical Input Registers | Read Input Registers | 4 | ||
Internal Registers or Physical Output Registers | Read Multiple Holding Registers | 3 | |||
Write Single Holding Register | 6 | ||||
Write Multiple Holding Registers | 16 | ||||
Read/Write Multiple Registers | 23 | ||||
Mask Write Register | 22 | ||||
Read FIFO Queue | 24 | ||||
File Record Access | Read File Record | 20 | |||
Write File Record | 21 | ||||
Diagnostics | Read Exception Status | 7 | |||
Diagnostic | 8 | ||||
Get Com Event Counter | 11 | ||||
Get Com Event Log | 12 | ||||
Report Server ID | 17 | ||||
Read Device Identification | 43 | ||||
Other | Encapsulated Interface Transport | 43 |
下面根据开发需求将PDU的功能码分成4种类型
-
只含有功能码,无后续数据(4种)
-
基本读写功能码,长度固定为5字节(6种)
-
长度大于5字节的PDU(7种)
-
含有子功能码的PDU(2种)
单功能码
这几种功能码都是只支持串行线(serial line only),对应的PDU中只有功能码,无后续数据
function code | 定义 | 说明 |
---|---|---|
0x07 | read exception status | 读取远端设备8bit异常状态输出(因设备而异) |
0x0B | get common event count | 读取远端设备通信事件计数器 |
0x0C | get common event log | 时间计数 + 消息计数 + 事件描述 |
0x11 | report slave ID | 读取远端设备ID,运行状态和额外数据 |
基本读写
对应的PDU长度固定为5字节
类型 | 说明 | function code 1B | 地址 2B | 数量/数值 2B |
---|---|---|---|---|
read coils | 读线圈 | 0x01 | starting address | quantity of coils |
read discrete inputs | 读离散输入 | 0x02 | starting address | quantity of inputs |
read holding registers | 读保持寄存器 | 0x03 | starting address | quantity of registers |
read input registers | 读输入寄存器 | 0x04 | starting address | quantity of registers |
write single coil | 写单个线圈 | 0x05 | output address | output value |
write single registers | 写单个寄存器 | 0x06 | register address | register value |
注:
- 如果starting address为3,说明是第4个线圈开始
- input register是只读的,holding register可读可写
不定长
对应的PDU后续数据长度不定,功能码0x14和0x15后面的数据还可能存在多个请求
类型 | 说明 | function code 1B | 地址 2B | 数量 2B | 字节数 1B | 数值 NB |
---|---|---|---|---|---|---|
write multiple coils | 写多个线圈 | 0x0F | starting address | quantity of outputs | byte count | outputs value |
类型 | 说明 | function code 1B | 地址 2B | 数量 1B | 字节数 1B | 数值 2NB |
---|---|---|---|---|---|---|
write multiple registers | 写多个寄存器 | 0x10 | starting address | quantity of registers | byte count | registers value |
类型 | 说明 | function code 1B | 字节数 1B | 参考类型 1B | 文件号 2B | 记录号 2B | 记录长度 2B | 重复 7NB |
---|---|---|---|---|---|---|---|---|
read file record | 读取文件记录(读取通用参数) | 0x14 | byte count | 0x06(fixed) | file number | record number | record length | 4个字段一组 |
类型 | 说明 | function code 1B | 数据长度 1B | 参考类型 1B | 文件号 2B | 记录号 2B | 记录长度 2B | 记录数据 2NB | 重复 (7+2N)*MB |
---|---|---|---|---|---|---|---|---|---|
write file record | 写文件记录(读取通用参数) | 0x15 | request data length | 0x06(fixed) | file number | record number | record length | register data | 5个字段一组 |
类型 | 说明 | function code 1B | 地址 2B | 与掩码 2B | 或掩码 2B |
---|---|---|---|---|---|
mask write register | 掩写寄存器 | 0x16 | reference address | and_mask | or_mask |
类型 | 说明 | function code 1B | 起始读地址 2B | 读取数量 2B | 起始写地址 2B | 写数量 2B | 后续字节数 1B | 写寄存器值 2NB |
---|---|---|---|---|---|---|---|---|
read/write multiple registers | 读写多个寄存器 | 0x17 | read starting address | quantity to read | write starting address | quantity to write | write byte count | write registers value |
类型 | 说明 | function code 1B | 地址 2B |
---|---|---|---|
read FIFO queue | 读先进先出队列 | 0x18 | FIFO pointer address |
子功能码
含有子功能码的PDU格式如下
function code | sub-function code | data |
---|---|---|
功能码 1B | 子功能码 1B/2B | 数据 不定长 |
(0x08)Diagnostics(Serial line only)
子功能码长度为2B,范围为0~65535,其中 5~9, 19, 21~65535 保留功能码。已定义的子功能码对应的数据大部分为2字节,值为0;子功能码 0x00 对应数据为任意长度;子功能码 0x01 对应的数据有两种;子功能码 0x03 的数据为字符串’CHAR’ + 00;
类型 | 说明 | sub-function code 2B | data 不定长 |
---|---|---|---|
return query data | 发送若干数据,返回若干相同数据 | 0000 | any |
restart communication option | 清空事件计数器,Log是否保留可选 | 001 | 0000/FF00(clear log) |
return diagnostic register | 返回诊断寄存器 | 0002 | 0000 |
change ASCII input delimiter | 提示不需要换行符(Line Feed) | 0003 | CHAR 00 |
force listen only mode | 进入只听模式 | 0004 | 0000 |
clear counters and diagnostic register | 清空所有计数器和诊断寄存器 | 000A | 0000 |
return bus message count | 返回消息数量 | 000B | 0000 |
return bus communication error count | 返回CRC校验错误数量 | 000C | 0000 |
return bus exception error count | 返回异常回复数量 | 000D | 0000 |
return slave message count | 返回远端设备已处理消息数量 | 000E | 0000 |
return slave no response count | 返回远端设备未回复消息数量 | 000F | 0000 |
return slave NAK count | 返回远端设备未确认异常回复数量 | 0010 | 0000 |
return slave busy count | 返回远端设备忙的异常回复的数量 | 0011 | 0000 |
return bus character overrun count | 返回字符速度过快的异常回复数量 | 0012 | 0000 |
clear overrun counter and flag | 清空overrun计数器,重置标志 | 0014 | 0000 |
(0x2B)Encapsulated Interface Transport
本类型用于隧道服务和方法调用,下属子功能码的名称为MEI type(Modbus Encapsulated Interface),常用的有两种,其他均为保留
MEI type 1B | data NB |
---|---|
0x0D | 与CANopen 系统和设备进行交互 |
MEI type 1B | read device ID code 1B | object ID 1B |
---|---|---|
0x0E | 01/02/03/04 | 0x00~0xFF |