MODBUS 入门学习

355 阅读12分钟

MODBUS是一种应用层消息协议。自1979年以来,一直属于工业自动化的一项标准协议。

背景

研究MODBUS是源于之前对MQTT的研究,所以产生了对[工业]物联网协议的兴趣。

事实上,MODBUS是一个标准的工业自动化设备通信协议,协议规范中所用到的术语还是和工业设备有关联的。

因此,对待MODBUS协议(即:用代码实现它),建议从工业控制程序入门(PLC),从而更精准的理解其规范的含义。如果仅仅做的是[工业]物联网协议转换的后台服务,可以不用深入了解,因为MODBUS数据结构就是两部分组成:1.功能码 (Function codes) 2. 数据 (Data)

1. 官方信息

1.1 协议规范列表

modbus.org/specs.php

1.2 协议规范说明

版本: V1.1b3

目标:描述MODBUS协议框内的“功能码”使用规范

链接: modbus.org/docs/Modbus…

版本: V1_0b

目标:描述MODBUS协议消息基于TCP/IP传输方法

链接:modbus.org/docs/Modbus…

版本:V1_02

目标:描述串行线路上的MODBUS协议

modbus.org/docs/Modbus…

2. 前置基础知识

收获关键词:离散(Discretes),线圈(Coils),寄存器(Register)

在MODBUS的数据模型描述中,会用到这些关键词,如果之前一直是在做纯软件开发,可能会感觉到难以理解。

2.1 PLC

开始学习MODBUS之前,有一个概念需要介绍一下:PLC

可编程逻辑控制器(Programmable Logic Controller,PLC),一种具有微处理器的数字电子设备,用于自动化控制的数字逻辑控制器,可以将控制指令随时加载存储器内存储与运行。

PLC出现于1964年,源于增效节本,替换之前“硬接线”方式的继电器。

比如:通过PLC控制电灯

PLC-operated-light.webp

图片来源

关于PLC的详细内容,可自行搜索,这里仅仅是为了在学习MODBUS之前,了解一点PLC相关的内容,有助于理解MODBUS中提到的一些概念名词

如果对PLC编程有兴趣,推荐几个链接

v2-839f171c993f7901d4599a62df410649_r.jpg

图片来源

2.2 PLC梯形图编程

注意

PLC编程语言有5种,这里介绍的是梯形图编程。

梯形图(LD)、指令表(IL)、功能模块图(FBD)、顺序功能流程图(SFC)及结构化文本(ST)

来源

梯形图是一种PLC编程语言,也被称为梯形逻辑(Ladder Logic)。之所以称为梯形图,是因为 这种程序由一条条水平线构成,看起来很像梯子

注意 梯形图编程的工具是PLC设计商提供的。

  • 1 : 横线 / 梯级
  • -||- : 触点(输入)
  • -()- : 线圈(输出)

触点(contacts)和线圈(coils)是离散编程元件,处理布尔变量状态。

“触点”和“线圈”依然沿用了继电器种的概念

在PLC中,“触点” 和 “线圈” 操作的其实是PLC 寄存器

output-latch-ladder-logic.gif

PLC程序运行模拟图,共分为三个阶段:1. 扫描输入 2. 运行 3. 输出

ladder-logic-inputs-outputs.gif

PLC控制电灯实例

开关按下,发送信号给PLC, PLC输出信号控制灯泡

instrumentationtools.com_plc-example.webp

图片来源

与PLC连接的设备分为两类:1. 输入设备 2.输出设备

  1. 输入设备为PLC提供信号或者数据

  2. 输出设备等待PLC提供信号或者数据

所有的输入&输出设备有两种基本类型:1. 离散(数字) 2.模拟

离散设备是指只有开和关两种状态的输入和输出, 模拟设备是指有无数种状态的输入和输出

PLC 中的线圈(coil)类似于电气世界中的单个离散继电器(即继电器线圈),读取时返回单个开或关(0 或 1)值或状态。PLC 中的线圈可以是内部线圈,也可以是离散输出。内部线圈(也称为逻辑线圈、虚线圈等)是 PLC 梯形逻辑程序中使用的软件线圈,与外界没有直接联系。离散输出线圈是 PLC 梯形逻辑程序线圈,通过软件连接到 PLC I/O 中的物理离散输出点。在 Modicon PLC 中,线圈寻址为 0xxxx,"x "的数量是 Modicon 处理器的功能。

保持寄存器(holding register)通常类似于数值。在 Modicon 术语中(我认为这是 "保持寄存器 "一词的来源),保持寄存器是一个 16 位字,通常包含 15 位有符号整数(+/-32767),有时也包含 16 位数。在较早的 Modicon PLC(如 484)中,保持寄存器最高只能到 999,而一些 Modicon 584 和早期的 984 最高只能到 9999。在 Modicon PLC 中,保持寄存器的地址为 4xxxx,"x "的数量是 Modicon 处理器的功能。

保持寄存器通常包含整数,如定时器预置、定时器累计值、计数器预置、计数器累计值、数学运算结果、模拟值等。有些系统将多个连续的保持寄存器组合在一起

摘自

3. MODBUS

  • PDU:协议数据单元
  • ADU:应用数据单元

3.1 数据格式

协议核心数据结构:

  1. 功能码
  2. 数据
modbus_application_protocol.png

协议通用数据结构, 规范“V1_1b3”

  1. 地址
  2. 功能码
  3. 数据
  4. 错误检查

WX20240321-174747@2x.png


基于串口通信的MODBUS数据格式, 规范“V1_02”

示意图中的 MODBUS SERIAL LINE PDU 指的就是协议通用数据结构的ADU(即应用数据) WX20240321-175026@2x.png


基于TCP/IP通信的MODBUS数据格式, 规范“V1_0b”

WX20240321-175310@2x.png

3.2 通信架构

MODBUS 基于三种方式实现通信:

  1. 互联网 - TCP/IP
  2. 异步串口 - 采用 “主从模式”,介质分为 a.有线:RS232,RS485;b.光纤 c.无线
  3. 高级链路控制

WX20240321-203445@2x.png

MODBUS协议可以在所有的网络架构中轻松实现通信

WX20240321-203618@2x.png

3.3 数据结构定义

ADU 在串口线上的每帧最大传输数据承载量256个字节 PDU 在串口线上的每帧最大传输数据承载量=256 - 1(地址) - 2(校验码) = 253 个字节

PDU是MODBUS的协议数据,在任何通信方式下,其都应该有固定的尺寸规则,因此PDU最大尺寸永远是253个字节

基于TCP的 MODBUS ADU尺寸 = 253 + 7(MBAP) = 260 个字节


3.3.1 基于串口通信

在串口通信中,MODBUS的数据传输有两种类型,MODBUS_RTU 和 MODBUS_ASCII

数据传输采用“大端模式”

3.3.1.1 RTU类型帧

占用字节的示意图

WX20240321-220744@2x.png

3.3.1.2 ASCII类型帧

占用字节的示意图

消息(message)编码规则:每8bit的字节会被拆分为两个字符的字节

例如:0x98, 根据ASCII表得知0x98中的9对应的16进制为0x39, 同理,0x98中的8对应的16进制为0x38

从上例可知,一个16进制表示字节,会被分拆为两个字符(如 '9' , '8'), 然后再将‘9’ & ‘8’转换为16进制

WX20240321-220833@2x.png

3.3.2 基于TCP/IP通信

MODBUS消息基于TCP/IP通信时,默认端口为502

数据传输采用“大端模式”

WX20240321-234329@2x.png

3.4 功能码(Function/Function code)

通过上边内容的了解,不管是基于TCP/IP还是串口来实现MODBUS,都会包含一个PDU-协议数据单元(功能码+数据 [即,Function Code + Data] )

在MODBUS协议中已明确定义了19个功能码,其作用详情见规范“V1_1b3”

  1. 0x01 -读取 线圈(Read Coils)
  2. 0x02 -读取 离散输入(Read Discrete Inputs)
  3. 0x03 -读取 保持寄存器(Read Holding Registers)
  4. 0x04 -读取 输入寄存器 (Read Input Registers)
  5. 0x05 -写入 单个线圈(Write Single Coil)
  6. 0x06 -写入 单个寄存器(Write Single Register)
  7. 0x07 -读取 异常状态(Read Exception Status)
  8. 0x08 -诊断
  9. 0x0b -读取 通信事件计数器
  10. 0x0c -读取 通信事件日志
  11. 0x0f -写入 多个线圈
  12. 0x10 -写入 多个寄存器
  13. 0x11 -上报多个服务器ID
  14. 0x14 -读取 文件记录
  15. 0x15 -写入 文件记录
  16. 0x16 -屏蔽写寄存器
  17. 0x17 -读取/写入 多个寄存器
  18. 0x18 -读取FIFO队列
  19. 0x2B -封装接口传输

如果对这些功能码感觉有点难以理解,可以先不用深究,从协议规范角度来讲,它就是一个常量值,属于PDU(协议数据单元)的一部分。

功能码其实不止这19种,因为协议规范中,已明确定义了功能码的分类和值范围

  • 类型:1.公共功能码(PUBLIC function codes) 2.用户功能码(User Defined Function codes) 3.预留功能码
  • 值范围

功能码范围:1~255,128 - 255用于预留码和异常码(规范“V1_1b3”)

The function code field of a MODBUS data unit is coded in one byte. Valid codes are in the range of 1 ... 255 decimal (the range 128 – 255 is reserved and used for exception responses).

WX20240322-093529@2x.png

注意规范“V1_1b3”中的"Annex A (Informative)"部分,提到有一些功能码/子功能码不属于协议规范,这些码被保留下来,用于特殊用途。8/19; 8/21-65535, 9, 10, 13, 14, 41, 42, 43/0-12, 43/15-255,90, 91, 125, 126, 127

4.1 子功能码

协议中还定义了“子功能码”,仅出现在串口通信网络中,作用:为功能码提供更多操作。在PDU结构(功能码+数据)中,位于“数据”部分

子功能码占用2个字节,值范围是0 - 65535(即十六进制 0x0000 - 0xffff)

和功能码一样,MODBUS协议已明确定义了15个子功能码

规范 “V1_1b3”-6.8.1章节 WX20240322-104657@2x.png

3.5 数据模型

MODBUS的数据模型是基于不同类型的四张表构建的。

Slave设备的信息存储在四个不同的表中。两个表存储 "开/关" 离散值(线圈),两个表存储数子值(寄存器)。线圈和寄存器各有一个只读表和读写表。

数据模型

Primary tablesObject typeType ofComments
Discretes Input(离散输入)Single bitRead-OnlyThis type of data can be provided by an I/O system.
Coils(线圈)Single bitRead-WriteThis type of data can be alterable by an application program.
Input Registers(输入寄存器)16-bit wordRead-OnlyThis type of data can be provided by an I/O system
Holding Registers(保持寄存器)16-bit wordRead-WriteThis type of data can be alterable by an application program.

MODBUS是工业通信协议,因此在这个数据模型中的表格命名,其实非常切合工业控制场景中的说法。这就是为什么在前边需要了解一下PLC相关内容。

但是这个数据模型在英文模式下,感觉还是很难理解,下边是“恩艾 (中国) 仪器有限公司”的总结

WX20240322-130624@2x.png 摘自

“恩艾 (中国) 仪器有限公司” 将Primary tables意译为“内存区块”,这个叫法和规范中所提供示意图更加能对的上,并且也符合实操看到现象。

WX20240322-131300@2x.png

3.6 地址模型

MODBUS 应用协议精确定义了PDU地址规则。

  • 在MODBUS PDU中,每个数据的地址范围为0至65535
  • 数据模型是由4个类型块组成,每个类型块由若干个从1到n编号的元素组成

MODBUS数据模型必须与设备应用程序绑定, MODBUS数据模型与设备应用程序之间的映射完全取决于供应商的设备.

WX20240322-132722@2x.png

为了更好的理解供应商对数据模型和设备应用程序的作用,这里以 西门子S7-300 来做一说明

3.6.1 西门子 S7-300

S7-300 操作文档

文档 “GOULD-MODBUS 协议概述” 中详细对S7-300的数据模型和地址模型做了说明

从“地址表示”中,可以十分清楚的看到“数据类型”对应的地址

WX20240322-134238@2x.png

WX20240322-134333@2x.png

3.7 功能码使用

读取Slave modbus的线圈状态,分别从规范,西门子s7-300,模拟软件,modbustools.com 四个来源展示一下 0x01 功能码的用法

3.7.1 根据 规范“V1_1b3”

读取20-38编号的线圈状态

线圈的默认地址从0开始,对应的编号为1

因此,编号20对应起始地址0x0013, 编号38对应的终止地址为0x0026 = 0x0013(起始地址)+0x0013(线圈数量)

同理返回值27-20,代表 0x001b地址-0x0013地址, 总共8位,其值为0xCD, 二进制 1100 1101

WX20240322-152837@2x.png

3.7.2 根据 西门子S7-300

章节“6.1 功能代码01”

由于S7-300支持的是RTU帧类型传输,所以“请求”命令中有“从站地址”。

WX20240322-155956@2x.png

3.7.3 根据 模拟软件

www.simplymodbus.ca/RTUmaster.h…

在示意图中,找到“SEND”按钮,按钮左边便是功能码命令.

如图所示

  • Master选择的是RTU模式
  • Slave地址:0x0A
  • 功能码:0x03
  • 起始地址:0x0000
  • 数量:0x0014
  • 校验码:0x44be

screenshotmaster80.png

3.7.4 根据 modbustools.com样例

样例来源:www.modbustools.com/modbus.html…

读取13个线圈,线圈编号为11-23,起始地址4

WX20240322-164822@2x.png

3.8 进阶

通过了解MODBUS协议的PDU,数据模型之后,如果还有兴趣继续进一步研究。可以根据你的方向从两个文档入手

  1. 基于串口通信的modbus modbus.org/docs/Modbus…
  2. 基于TCP/IP的modbus modbus.org/docs/Modbus…

3.8 MODBUS 开源代码

Java版本: jamod.sourceforge.net/

C版本:libmodbus.org/

4 总结

  1. MODBUS协议数据结构是非常简单的
  2. 大多数公司不用从0实现MODBUS协议
  3. 了解MODBUS协议,会阅读MODBUS规范,熟练掌握MODBUS设备供应商手册

入行关键词

  • MODBUS 设备组网
  • MODBUS RS232 RS485
  • MODBUS 传感器
  • MODBUS PLC
  • RJ45 RS232 RS485
  • PLC Coils Register

祝好运!