1 Modbus 概述
1.1 模式——ASCII、RTU
1、对于modbus ASCII 模式,使用的是高位字节在前,低位字节在后。使用LRC校验。又称作命令行实现
2、对于modbus rtu 模式,使用的是低位字节在前,高位字节在后。使用CRC校验。又称作代码实现
1.2 单主/多从
Modbus协议是一种单主/多从的通信协议,其特点是在同一时间,总线上只能有一个主设备,但可以有一个或者多个(最多247个 ip地址1-247)从设备,每一个从设备一个ip地址。
- 在请求的报文中请求的地址为0则为广播模式,248-255保留
- 请求方为主设备,应答方为从设备
- 从设备之间是不能直接通信的,原因是协议不支持
主设备通过两种方式向从设备发送请求报文,即单播模式和广播模式
- 单播模式:主设备仅仅寻址单个从设备,从设备返回一个响应报文。
- 广播模式:主设备向所有的从设备发送请求指令,从设备收到指令后,各自处理,不要求返回应答;
这种模式下,请求指令必须是Modbus标准功能中的写指令;比如 0x06 指令 (写单个保持寄存器)
1.3 Modbus 通用帧
ADU: 应用数据单元
PDU: 协议数据单元
- 第一个是地址域,每一个从机都有一个地址,主机在访问从机的时候依靠这个唯一的地址识别,多个从机接收到主机的数据的时候,匹配接收到的数据和自身的地址,匹配上的那个从机作出响应,其他的都忽略。
- 第二个是功能码,决定这一帧数据主要是干嘛的,输入数据,输出数据,读取控制量等。
- 第三是数据,可以有也可以没有。
- 第四是差错校验,用来对之前发送的数据校验,防止发送过程中因为电磁干扰,数据出错。
1.4 功能码
当主机向从句发送信息时,功能代码向从机说明应执行的动作。
当从机响应主机时,功能代码可说明从机正常响应或出现错误(即不正常响应),正常响应时,从句简单返回原始功能代码;
不正常响应时,从机返回与原始代码相等效的一个码,并把最高有效位设定为“1”。
如,主机要求从机读一组保持寄存器时,则发送信息的功能码为:
0000 0011 (十六进制03)
若从机正确接收请求的动作信息后,则返回相同的代码值作为正常响应。发现错时,则返回一个不正常响信息:
1000 0011(十六进制83)
不正常代码
2 示例
2.1 01X 读取一组逻辑线圈的当前状态(ON/OFF)
请求:MBAP 功能码 起始地址H 起始地址L 数量H 数量L(共12字节)
响应:MBAP 功能码 数据长度 数据(一个地址的数据为1位)
发送包
byte[0] byte[1] 00 02 为消息号,随便指定,服务器返回的数据的前两个字和这个一样
byte[2] byte[3] 00 00 为modbus标识,强制为0即可
byte[4] byte[5] 00 06 值在06 之后所有字节的个数,大家也可以数一数哈
byte[6] 01 为站号、随便指定。
byte[7] 01 为功能码(这个是决定了要干什么事)
byte[8] byte[9] 00 00为起始地址,比如我们我们想读地址0的数据就为00,读1000地址为03 E8
byte[10] byte[11] 00 80为指定读取数据的长度,跟地址规则一样
回包
byte[0] byte[1] 消息号,我们之前写发送指令的时候,是多少,这里就是多少。
byte[2] byte[3] 同上
byte[4] byte[5] 指后面的字节数
byte[6] 站号
byte[7] 功能码
byte[8] 指示在byte[8]后面的字节数量 在byte[8]后面就是真实数据
byte[9] 到结尾都是我们读取到的数据 因为字节是8位所以是16(0-127所占了128个bit 128/8 得出16)
列子:
读5个线圈状态
发送:00 00 00 00 00 06 01 01 00 00 00 05
接收:00 00 00 00 00 04 01 01 01 1F
2.2 0x02 读取一组开关输入的当前状态(ON/OFF)
请求:MBAP 功能码 起始地址H 起始地址L 数量H 数量L(共12字节)
响应:MBAP 功能码 数据长度 数据(长度:9+ceil(数量/8))
发包
其实大家自己看与0x01是基本一样的。只是多了个Bit Count这一个
Bit Count所代表的意思就是 读取十个数据 即 10bit 但需要占用2byte
回包
例子
读5个输入状态
发送:00 00 00 00 00 06 01 02 00 00 00 05
接收:00 00 00 00 00 04 01 02 01 15
2.3 0x03 读取一个或多个保持寄存器的数值
请求:MBAP 功能码 起始地址H 起始地址L 寄存器数量H 寄存器数量L(共12字节)
响应:MBAP 功能码 数据长度 寄存器数据(长度:9+寄存器数量×2)
2.4 0x04 读取一个或多个输入寄存器的数值
请求:MBAP 功能码 起始地址H 起始地址L 寄存器数量H 寄存器数量L(共12字节)
响应:MBAP 功能码 数据长度 寄存器数据(长度:9+寄存器数量×2)
2.5 0x05 强置一个逻辑线圈的通断状态
请求:MBAP 功能码 输出地址H 输出地址L 输出值H 输出值L(共12字节)
响应:MBAP 功能码 输出地址H 输出地址L 输出值H 输出值L(共12字节)
05 是功能码, 00 00是我们指定的地址,如果我们想写地址1000,那么就为 03 E8,后四位是规定线圈的通断状态。
那么上面发包的意思就是 在00 00这个位置 指定线圈通断状态为 断开。
举个例子:
写入地址100为通: 00 00 00 00 00 06 FF 05 00 64 FF 00
写入地址1000为断:00 00 00 00 00 06 FF 05 03 E8 00 00
2.6 0x06 写单个保持寄存器
请求:MBAP 功能码 寄存器地址H 寄存器地址L 寄存器值H 寄存器值L(共12字节)
响应:MBAP 功能码 寄存器地址H 寄存器地址L 寄存器值H 寄存器值L(共12字节)
发包
在00 00位置 写入 00 00
回包 与发包一致,服务器重复发送
2.7 0x10 写多个保持寄存器
请求:MBAP 功能码 起始地址H 起始地址L 寄存器数量H 寄存器数量L 字节长度 寄存器值(13+寄存器数量×2)
响应:MBAP 功能码 起始地址H 起始地址L 寄存器数量H 寄存器数量L(共12字节)
发送
Word count为 要写的个数
Byte count 为 要写入的数目 1个word等于2个bytes
后面 00 00 每组为写入的数值
2.8 0x2b 读取设备ID
回包发包一致
byte[0]byte[1] 消息号 随便指定
byte[2]byte[3] modbus的标识
byte[4]byte[5] 在此之后的长度
byte[6] 站号
byte[7] 功能码
byte[8] 功能类型 这里是读取设备ID
byte[9] 读什么 这里是读设备标识
byte[10] 设备名称.
3 Modbus 模拟器