Modbus是 OSI 模型第 7 层上的应用层报文传输协议,它在连接至不同类型总线或网络的设备之间提供客户机/服务器通信。包括了ASCII、RTU、TCP三种报文类型,协议本身并没有定义物理层,定义了控制器能够认识和使用的消息结构,不管它们是经过何种网络进行通信的。Modbus协议使用串口传输时可以选择RTU或者ASCII模式,并规定了消息、数据结构、命令和应答方式,且需要对数据进行校验。ASCII模式采用LRC校验,RTU模式采用16位CRC校验。通过以太网传输时使用TCP,这种模式下不使用校验,因为TCP协议是一个面向连接的可靠协议。
MODBUS 协议允许在各种网络体系结构内进行简单通信。每种设备(PLC、HMI、控制面板、驱动程序、动作控制、输入/输出设备)都能使用 MODBUS协议来启动远程操作。
Modbus有下列三种通信方式:
1、ASCII模式
当控制器设为在Modbus网络上以ASCII模式通信,在消息中的每个8Bit字节都作为两个ASCII字符发送。这种方式的主要优点是字符发送的时间间隔可达到1秒而不产生错误。
2、RTU模式
当控制器设为Modbus网络上以RTU(远程终端单元)模式通信,在消息中的每个8Bit字节包含两个4Bit的十六进制字符。这种方式的主要优点是:在同样的波特率下,可比ASCII方式传送更多的数据。
3、Modbus TCP
在Modbus TCP/IP协议中,串行链路中的主/从设备分别演变为客户端/服务器端设备。即客户端相当于主站设备,服务器端相当于从站设备。基于TCP/IP网络的传输特性,串行链路上一主多从的构造也演变为多客户端/多服务器端的构造模型。Modbus TCP/IP服务器端通常使用端口502作为接收报文的端口, IANA(Internet Assigned Numbers Authority,互联网编号分配管理机构)给Modbus协议赋予TCP端口号为502,这是目前在仪表与自动化行业中唯一分配到的端口号。
其通信过程:
ModbusTCP数据帧:MBAP+PDU
内容 | 长度 | 解释 |
---|---|---|
00 00 | 2字节 | 可以理解为报文的序列号,一般每次通信之后就要加1以区别不同的通信数据报文。 |
00 00 | 2字节 | 00 00表示ModbusTCP协议。 |
00 06 | 2字节 | 表示接下来的数据长度,单位为字节。 |
01 | 1字节 | 可以理解为设备地址。以上七个字节也被称为Modbus报文头 |
PDU帧结构:PDU由功能码+数据组成。功能码为1字节,数据长度不定,由具体功能决定。
PDU结构详解-举个例子
0x03:读保持寄存器
从远程设备中读保持寄存器连续块的内容
请求:MBAP 功能码 起始地址H 起始地址L 寄存器数量H 寄存器数量L(共12字节)
响应:MBAP 功能码 数据长度 寄存器数据(长度:9+寄存器数量×2)
如:00 01 此次通信事务处理标识符,00 00 标识modbus TCP协议,00 06为数据长度,01 为设备地址,03 为功能码此时代表读取保持寄存器,00 00代表起始地址,00 03为寄存器数量
00 01 00 00 00 06 01 03 00 00 00 03
回:数据长度为0x06,第一个寄存器的数据为0x21,其余为0x00
00 01 00 00 00 09 01 03 06 00 21 00 00 00 00
其它PDU结构:
功能码 | 含义 | 位操作/字操作 | 操作数量 | 长度 | 例子 |
0x01 | 读线圈 | 位操作 | 单个或多个 | 12字节 | 如:在从站0x01中,读取开始地址为0x0002的线圈数据,读0x0008位 |
0x05 | 写单个线圈 | 位操作 | 单个 | 12字节 | 如:将地址为0x0003的线圈设为ON 00 01 00 00 00 06 01 05 00 03 FF 00 |
0x0F | 写多个线圈 | 位操作 | 多个 | 12字节 | 如:将线圈03之后写入4个数据都是ON,01代表后面数据占用一个字节(F对应的二进制是1111) 046B00000008010F 0003 0004 01 0F |
0x02 | 读离散量输入 | 位操作 | 单个或多个 | 12字节 | 从地址0x0000开始读0x0012个离散量输入 00 01 00 00 00 06 01 02 00 00 00 12 |
0x04 | 读输入寄存器 | 字操作 | 单个或多个 | 12字节 | 读起始地址为0x0002,数量0x0005的寄存器数据 00 01 00 00 00 06 01 04 00 02 00 05 |
0x03 | 读保持寄存器 | 字操作 | 单个或多个 | 12字节 | 起始地址是0x0000,寄存器数量是 0x0003 00 01 00 00 00 06 01 03 00 00 00 03 |
0x06 | 写单个保持寄存器 | 字操作 | 单个 | 12字节 | 向地址是0x0000的寄存器写入数据0x000A 00 01 00 00 00 06 01 06 00 00 00 0A |
0x10 | 写多个保持寄存器 | 字操作 | 多个 | 13+寄存器数量×2 | 向起始地址为0x0000,数量为0x0001的寄存器写入数据,数据长度为0x02,数据为0x000F 00 01 00 00 00 09 01 10 00 00 00 01 02 00 0F |
Modbus是一主多从的通信协议,控制器相互之间、或控制器经由网络(如以太网)可以和其它设备之间进行通信。Modbus协议使用的是主从通讯技术,即由主设备主动查询和操作从设备。一般将主控设备方所使用的协议称为Modbus Master,从设备方使用的协议称为Modbus Slave。典型的主设备包括工控机和工业控制器等;典型的从设备如PLC可编程控制器等。
Modbus的工作方式是请求/应答,每次通讯都是主站先发送指令,可以是广播,或是向特定从站的单播;从站响应指令,并按要求应答,或者报告异常。当主站不发送请求时,从站不会自己发出数据,从站和从站之间不能直接通讯 。
Modbus通讯物理接口可以选择串口,也可以选择以太网。它的通信遵循以下过程:
关于Java的开源库
Java中能用来做什么
server端: 作为服务端(slave端、从端)接收数据:指的是java作为报文的接收者,接收硬件设备的反馈消息
client端: 作为客户端(master端、主动端)发送数据数据:指的是Java作为报文的发送者,来达到控制设备
参考资料:
modbus协议中的线圈、寄存器等的解释:
如上所示一共8种功能码。这其中有涉及到线圈、离散输入、保持、输入四种寄存器。
线圈寄存器:实际上就可以类比为开关量(继电器状态),每一个bit对应一个信号的开关状态。所以一个byte就可以
同时控 制8路的信号。比如控制外部8路io的高低。 线圈寄存器支持读也支持写,写在功能码里面又分为写单个线圈寄存器和写多个 线圈寄存器。对应上面的功能码也就是:0x01 0x05 0x0f
离散输入寄存器:如果线圈寄存器理解了这个自然也明白了。离散输入寄存器就相当于线圈寄存器的只读模式,他也是每个 bit表示一个开关量,而他的开关量只能读取输入的开关信号,是不能够写的。比如我读取外部按键的按下还是松开。所以功 能码也简单就一个读的 0x02
保持寄存器:这个寄存器的单位不再是bit而是两个byte,也就是可以存放具体的数据量的,并且是可读写的。一般对应参数 设置,比如我我设置时间年月日,不但可以写也可以读出来现在的时间。写也分为单个写和多个写,所以功能码有对应的三 个:0x03 0x06 0x10
输入寄存器:这个和保持寄存器类似,但是也是只支持读而不能写,一般是读取各种实时数据。一个寄存器也是占据两个 byte的空间。类比我我通过读取输入寄存器获取现在的AD采集值。对应的功能码也就一个 0x04
为什么要使用驱动库?我们知道了Modbus是一种总线协议,它可以基于串口或网口,以基于串口的Modbus-RTU为例,我们需要在Windows或Linux下实现一个上位机,上位机的功能是读写Modbus接口传感器设备的数据,或者是和单片机等从设备进行交互。
所以作为主机,写数据的流程是:
读数据也是同样的流程,我们可以基于串口发送、串口接收函数、定时器等,自己写一个Modbus驱动库,来实现对从设备的读写。当然,也可以直接使用别人写好的Modbus驱动库,比如libmodbus,本文将介绍如何使用libmodbus驱动库,实现Modbus主机和从机。
传送门:CentOS7下编译安装libmodbus库_雪域迷影的博客-CSDN博客【CentOS7下编译安装libmodbus库】