ModbusTCP协议分析
ModbusTCP与ModbusUDP的报文格式是一样的,它们之间的区别其实就是TCP与UDP的区别,因此下面就针对ModbusTCP的协议进行分析,ModbusTCP与ModbusRtu(ModbusASCII)之间的区别如下图:
从上图可以看出,ModbusTCP在Modbus串行通信的基础上,去除了校验(由于TCP本身就带有校验和)和设备地址(ModbusTCP弱化了设备地址,用IP地址来取代),再加上MBAP报文头(占7 bytes),下面针对MBAP进行分析说明:
域 | 长度 | 说明 | 客户机 | 服务器 |
事务处理标识符 | 2字节 | Modbus请求/响应事务处理的标识 | 客户机启动 | 复制响应 |
协议标识符 | 2字节 | 0=Modbus协议 | 客户机启动 | 复制响应 |
长度 | 2字节 | 长度之后的字节总数 | 客户机启动 | 服务器启动 |
单元标识符 | 1字节 | 串行链路或其它总线的从站识别 | 客户端启动 | 复制响应 |
下面针对具体报文进行分析,Modbus协议在以太网链路上的报文格式如下所示:
事务处理标识符 | 协议标识符 | 长度 | 单元标识符 | 功能码 | 数据 |
2 bytes | 2 bytes | 2 bytes | 1 byte | 1 byte | N bytes |
有了以上理论基础之后,下面针对各个功能码进行详细分析:
读取输出线圈
发送报文格式如下:
发送报文含义:读取服务器1号从站输出线圈,起始地址为0x13=19,对应地址为00020,线圈数量为0x1B=27,即读取1号从站输出线圈,地址从00020-00046,共27个线圈的状态值。
这里值得注意一下,协议中的起始地址指的是索引,后面的地址指的是具体地址,对于任意一个存储区,索引都是从0开始的,但是对应的具体地址,与存储区是相关的,比如输出线圈,0对应00001;输入线圈,0对应10001;输入寄存器,0对应30001;保持寄存器,0对应40001。
返回报文格式如下:
返回报文含义:返回服务器1号从站输出线圈00020-00046,共27个线圈的状态值,返回字节数为4个,分别为CD 6B B2 05。
CD=1100 1101 对应 00020-00027
6B=0110 1011 对应 00028-00035
B2=1011 0010 对应 00036-00043
05=0000 0101 对应 00044-00046
读取输入线圈
发送报文格式如下:
发送报文含义:读取服务器1号从站输入线圈,起始地址为0xC4=196,对应地址为10197,线圈数量为0x1D=29,即读取1号从站输入线圈,地址从10197-10225,共29个线圈的状态值。
返回报文格式如下:
返回报文含义:返回服务器1号从站输入线圈10197-10225,共29个线圈的状态值,返回字节数为4个,分别为CD 6B B2 05。
CD=1100 1101 对应 10197-10204
6B=0110 1011 对应 10205-10212
B2=1011 0010 对应 10213-10220
05=0000 0101 对应 10221-10225
读取保持寄存器
发送报文格式如下:
发送报文含义:读取服务器1号从站保持寄存器,起始地址为0x6B=107,对应地址为40108,寄存器数量为0x02=2,即读取1号从站保持寄存器,地址从40108-40109,共2个寄存器的数值。
返回报文格式如下:
返回报文含义:返回服务器1号从站保持寄存器40108-40109,共2个寄存器的数值,返回字节数为4个,分别为02 2B 01 06,40108对应数值为0x022B,40109对应数值为0x0106。
读取输入寄存器
发送报文格式如下:
发送报文含义:读取服务器1号从站输入寄存器,起始地址为0x6B=107,对应地址为30108,寄存器数量为0x02=2,即读取1号从站保持寄存器,地址从30108-30109,共2个寄存器的数值。
返回报文格式如下:
返回报文含义:返回服务器1号从站输入寄存器30108-30109,共2个寄存器的数值,返回字节数为4个,分别为02 2B 01 06,30108对应数值为0x022B,30109对应数值为0x0106。
预置单线圈
发送报文格式如下:
发送报文含义:预置服务器1号从站单个线圈的值,线圈地址为0x00AC=172,对应地址为00173,断通标志0xFF00表示置位,0x000表示复位,即置位1号从站输出线圈00173。
返回报文格式如下:
返回报文含义:预置单输出线圈原报文返回。
预置单寄存器
发送报文格式如下:
发送报文含义:预置服务器1号从站单个保持寄存器的值,寄存器地址为0x0087=135,对应地址为40136,写入值为0x039E,即预置1号从站保持寄存器40136值为0x039E。
返回报文格式如下:
返回报文含义:预置单保持寄存器原报文返回。
预置多线圈
发送报文格式如下:
发送报文含义:预置服务器1号从站多个线圈的值,线圈地址为0x0013=19,对应地址为00020,线圈数为0x0A=10,写入值为0xCD00,即预置1号从站线圈00020-00027=0xCD=1100 1101,00028-00029=0x00=0000 0000。
返回报文格式如下:
返回报文含义:预置多输出线圈返回报文是在原报文基础上除去字节数及具体字节后返回。
预置多寄存器
发送报文格式如下:
发送报文含义:预置服务器1号从站多个寄存器的值,寄存器地址为0x0087=135,起始地址为40136,寄存器数量为0x02=2,结束地址为40137,写入值为0xCD00和0x0A10,即预置1号从站寄存器40136=0x0105,40137=0x0A10。
返回报文格式如下:
返回报文含义:预置多保持寄存器返回报文是在原报文基础上除去字节数及具体字节后返回。
异常错误码说明
数据类型 | 功能描述 | 功能码 | 功能码(十六进制) | 异常功能码 | |
比特访问 | 物理离散量输入 | 读输入离散量 | 02 | 0x02 | 0x82 |
内部比特或者物理线圈 | 读线圈 | 01 | 0x01 | 0x81 | |
写单个线圈 | 05 | 0x05 | 0x85 | ||
写多个线圈 | 15 | 0x0F | 0x8F | ||
16比特访问 | 输入存储器 | 读输入寄存器 | 04 | 0x04 | 0x84 |
内部存储器或物理输出存储器(保持寄存器) | 读多个寄存器 | 03 | 0x03 | 0x83 | |
写单个寄存器 | 06 | 0x06 | 0x86 | ||
写多个寄存器 | 16 | 0x10 | 0x90 | ||
读/写多个寄存器 | 23 | 0x17 | 0x97 | ||
屏蔽写寄存器 | 22 | 0x16 | 0x96 | ||
文件记录访问 | 读文件记录 | 20 | 0x14 | ||
写文件记录 | 21 | 0x15 |
其中物理离散量输入和输入寄存器只能有I/O系统提供的数据类型,即只能是由I/O系统改变离散量输入和输入寄存器的数值,而上位机程序不能改变的数据类型,在数据读写上表现为只读,而内部比特或者物理线圈和内部寄存器或物理输出寄存器(保持寄存器)则是上位机应用程序可以改变的数据类型,在数据读写上表现为可读可写。
错误代码表
代码 | 名称 | 含义 |
01 | 非法功能 | 对于服务器(或从站)来说,询问中接收到的功能码是不可允许的操作,可能是因为功能码仅适用于新设备而被选单元中不可实现同时,还指出服务器(或从站)在错误状态中处理这种请求,例如:它是未配置的,且要求返回寄存器值。 |
02 | 非法数据地址 | 对于服务器(或从站)来说,询问中接收的数据地址是不可允许的地址,特别是参考号和传输长度的组合是无效的。对于带有100个寄存器的控制器来说,偏移量96和长度4的请求会成功,而偏移量96和长度5的请求将产生异常码02。 |
03 | 非法数据值 | 对于服务器(或从站)来说,询问中包括的值是不可允许的值。该值指示了组合请求剩余结构中的故障。例如:隐含长度是不正确的。modbus协议不知道任何特殊寄存器的任何特殊值的重要意义,寄存器中被提交存储的数据项有一个应用程序期望之外的值。 |
04 | 从站设备故障 | 当服务器(或从站)正在设法执行请求的操作时,产生不可重新获得的差错。 |
05 | 确认 | 与编程命令一起使用,服务器(或从站)已经接受请求,并且正在处理这个请求,但是需要长持续时间进行这些操作,返回这个响应防止在客户机(或主站)中发生超时错误,客户机(或主机)可以继续发送轮询程序完成报文来确认是否完成处理。 |
07 | 从属设备忙 | 与编程命令一起使用,服务器(或从站)正在处理长持续时间的程序命令,当服务器(或从站)空闲时,客户机(或主站)应该稍后重新传输报文。 |
08 | 存储奇偶性差错 | 与功能码20和21以及参考类型6一起使用,指示扩展文件区不能通过一致性校验。服务器(或从站)设备读取记录文件,但在存储器中发现一个奇偶校验错误。客户机(或主机)可重新发送请求,但可以在服务器(或从站)设备上要求服务。 |
0A | 不可用网关路径 | 与网关一起使用,指示网关不能为处理请求分配输入端口值输出端口的内部通信路径,通常意味着网关是错误配置的或过载的。 |
0B | 网关目标设备响应失败 | 与网关一起使用,指示没有从目标设备中获得响应,通常意味着设备未在网络中。 |