基于libmodbus库实现modbus TCP/RTU通信

      最后更新:2022-08-03 14:41:24 手机定位技术交流文章

      文章目录

      • One.Modbus TCP
        • 1.1标题MBAP
        • 1.2框架结构PDU
        • 1.3 库函数调用
        • 1.4写入基本类封装的基本函数
      • 二、Modbus RTU
        • 2.1 使用示例
        • 2.2 编写在基本类包中的基本函数
        • 2.3虚拟串行终端
      • 安装modbuslib库
        • 3.对1个modbuslib库的介绍
        • 3.2 Modbuslib库安装
        • 3.3modbuslib库函数

      One.Modbus TCP

      Modbus由MODICON于1979年开发,是工业现场总线协议的标准。1996年,Schneider推出了基于Ethernet TCP/IP的Modbus协议:ModbusTCP。
      Modbus协议是一个应用程序级的报告协议,它包含三个报告类型: ASCII、RTU和TCP。
      标准的Modbus协议物理层接口包括RS232、RS422、RS485和Ethernet(modbus TCP)接口,并使用主机/slave通信。
      ModbusTCP数据框架:
      ModbusTCP数据框架可以分成两个部分: MBAP+PDU。

      1.1标题MBAP

      MBAP是一个长7个字符的标题,内容如下:
      在这里插入图片描述

      内容 解释
      事务处理标识 一个可以理解为消息的序列数,通常在每个通信之后添加1来区分不同的通信数据消息
      协议标识符 00 00 表示 ModbusTCP 协议
      长度 表示下一个数据长度,单位为字节
      单元标识符 可以理解为设备地址

      例如,一个单帧modbus TCP消息如下:00 01 00 00 00 06 0106 08 00 12 34
      第一个七个字符(红色部分)是为标题MBAP。

      1.2框架结构PDU

      PDU由函数代码+数据组成,函数代码为1字节,而数据长度取决于特定函数。
      功能码:
      Modbus有四个操作对象:一个循环,一个离散输入,一个保持注册,和一个输入注册。
      在这里插入图片描述
      PDU详细结构:
      1. 0x01:读循环状态
      从车站读取1至200个连续环, ON = 1, OFF = 0
      请求:MBAP函数代码初始地址H初始地址L数量H数量L(12字节)
      答复:MBAP函数代码数据长度数据(一个地址数据是1位)
      例如,从0x01站读取循环数据的起始地址0x002读取0x008位元
      00 01 00 00 00 06 01 01 00 02 00 08
      返回:数据长度为0x01字节,数据为0x01,第一个环是开,其余是关
      00 01 00 00 00 04 01 01 01 01

      2. 0x02:读离散输入
      从一个站点读取1至200个连续离散输入状态
      请求:MBAP函数代码初始地址H初始地址L数量H数量L(12字节)
      答复:MBAP函数代码数据长度数据(长度:9+天线(编号/8))
      从地址0x00开始读取0x0012离散输入
      00 01 00 00 00 06 01 02 00 00 00 12
      返回:数据长度为0x03字节,数据为0x01 04 00,表示第一个离散输入和第11个离散输入是开的,其余是关的
      00 01 00 00 00 06 01 02 03 01 04 00

      3.0x03:阅读和保持注册表
      从远程设备读取维护记录的连续块内容
      请求: MBAP函数码 起始地址 H 起始地址 L 登记册数 H 登记册数 L (12 字节)
      答复:MBAP函数码数据长度内存数据(长度:9+内存数x2)
      初始地址为0x00,存档数为0x003
      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

      4.0x04:读取输入注册表
      从远程设备读取1至200个连续输入记录
      请求: MBAP函数码 起始地址 H 起始地址 L 登记册数 H 登记册数 L (12 字节)
      答复:MBAP函数码数据长度内存数据(长度:9+内存数x2)
      读取初始地址为0x002,登记数据的数目为0x005
      00 01 00 00 00 06 01 04 00 02 00 05
      返回:数据长度为0x0A,第一个登记数据为0x0c,其余为0x00
      00 01 00 00 00 0D 01 04 0A 00 0C 00 00 00 00 00 00 00 00

      5.0x05:写一个单环
      从一个站点写出输出到开或关,0xFF00请求输出到开,0x00请求输出到关
      请求:MBAP函数代码输出地址H输出地址L输出值H输出值L(12字节)
      答复:MBAP函数代码输出地址H输出地址L输出值H输出值L(12字节)
      例如:设置地址的循环为0x003到ON
      00 01 00 00 00 06 01 05 00 03 FF 00
      回:写入成功
      00 01 00 00 00 06 01 05 00 03 FF 00

      6.0x06:写单行登记
      在远程设备上写一个保持登记
      请求:MBAP 功能码 寄存器地址H 寄存器地址L 寄存器值H 寄存器值L(共12字节)
      响应:MBAP 功能码 寄存器地址H 寄存器地址L 寄存器值H 寄存器值L(共12字节)
      例如,将数据写入0x00地址0x00A的注册表
      00 01 00 00 00 06 01 06 00 00 00 0A
      回:写入成功
      00 01 00 00 00 06 01 06 00 00 00 0A

      7.0x0F(15):写多个循环
      执行从站点发出的循环的序列中每个循环的开关或关闭,并在数据区域配置的位请求的相应输出位点上开关,并在0位置配置的位请求的响应上关闭
      请求: MBAP函数代码 开端地址 H 开端地址 L 输出数 H 输出数 L 字符长度 输出值 H 输出值 L
      答复: MBAP函数代码 开端地址 H 开端地址 L 输出号码 H 输出号码 L

      8.0x10(16):写多个保持者
      在远程设备中编写连续注册表块(1~123个注册表)
      请求: MBAP 函数码 起始地址 H 起始地址 L 登记台数 H 登记台数 L 字符长 登记台数 (13+ 登记台数 x 2)
      答复:MBAP函数码开端地址H开端地址L登记册数H登记册数L(12字节)
      如:向起始地址为0x0000,数量为0x0001的寄存器写入数据,数据长度为0x02,数据为0x000F
      00 01 00 00 00 09 01 10 00 00 00 01 02 00 0F
      回:写入成功
      00 01 00 00 00 06 01 10 00 00 00 01

      1.3 库函数调用

      下列函数分别执行上述八项函数,并给出发送数据的格式:

      1.4写入基本类封装的基本函数

      为方便与其他客户通话,上述函数通过将它们包入一个基本类来实现,在基础类中执行上面8项中的读写操作,当客户端需要调用Modbus功能时,只需要继承上面的基类,并扩展相关的函数功能,简单易用。下面给出了modbus_tcp和modbus_tcp.cpp程序。
      modbus_tcp.hpp

      modbus_tcp.cpp

      main.cpp

      编译 & 执行:

      g++ main.cpp modbus_tcp.cpp -lmodbus -std=c++17
      ./a.out

      输出结果:
      在这里插入图片描述

      二、Modbus RTU

      在一般工业场景中,使用modbus RTU场景较多,基于序列协议的modbus RTU方法数据,包含工业总线协议,如RS232/485。与modbus TCP不同,RTU没有消息头的MBAP字段。然而,最后我们添加了两个CRC测试字符(CRC16),由于网络协议中的自跟踪,因此在TCP协议中不需要CRC校正代码。RTU和TCP的通用使用基本上是相同的.当创建一个modbus对象时,它只是不同的,TCP需要传递网络插座信息;RTU需要传递串行相关信息。

      2.1 使用示例

      当您需要给远程主机写一个值,例如给01地址的设备,0x0105将保持主机写一个数据:0x0190例如,您需要构造一个数据框架如下:

      主要发射器:01 06 01 05 01 90 99 CB
      01表示从机地址,06功能码表示写单个保持寄存器,0105表示寄存器地址,0190表示写入寄存器的数值,99 CB为CRC校验值。
      如果机器正确接收数据,则将返回数据帧:

      机器回答: 01 06 01 05 01 90 99 CB
      因此作为主机,编写数据的过程是:

      • 构造一个Modbus-RTU数据框架
      • 等待从机响应的数据
      • 如果响应数据正确, 写成功, 否则写失败.

      2.2 编写在基本类包中的基本函数

      为方便与其他客户通话,上述函数通过将它们包入一个基本类来实现,在基础类中执行上面8项中的读写操作,当客户端需要调用Modbus功能时,只需要继承上面的基类,并扩展相关的函数功能,简单易用。Modbus_rtu 是下面的.hpp 和 modbus_rtu.cpp 程序。
      modbus_rtu.hpp

      modbus_rtu.cpp

      main.cpp

      编译 & 执行:

      g++ main.cpp modbus_tcp.cpp -lmodbus -std=c++17
      ./a.out

      2.3虚拟串行终端

      在构造modbus_rtu对象时,可以看到输入参数,包括"/dev/pts/4",这表示串行输出端口的文件名,RS232/485通信通常使用USB接收器进行(使用硬件芯片进行电平转换,如CH340),因此,生成的串行通信端口通常是“/dev/ttyUSB0”类型的。在我的测试中,因为没有USB端口工具,因此,测试以虚拟序列方式进行,索卡特工具在这里使用。
      安装索卡特工具:
      sudo apt install socat
      在虚拟循环打开之前,在设备下面检查/dev/pts:
      在这里插入图片描述
      开启虚拟串口:
      socat -d -d PTY PTY
      在这里插入图片描述
      打开虚拟循环后,请在/dev/pts下查看设备:
      在这里插入图片描述
      如果虚拟循环总是有效,你必须让苏卡特继续跑。使用"minicom -D /dev/pts/5 -b 9600",打开一个串口终端,使用"minicom -D /dev/pts/6 -b 9600"打开另一个串行端口。在 minicom命令中,输入命令,它可以在另一个小型电话终端接收。

      安装modbuslib库

      3.对1个modbuslib库的介绍

      libmodbus,基于C语言实现的Modbus驱动程序库,作者是史蒂芬,支持Linux、Mac OS X、FreeBSD、QNX和Win32操作系统,主要应用在PC上,用来开发上位机,也可以交叉编译源代码,以适配更多的平台,例如,ARM Linux。源代码开源,遵照LGPL-2.1许可。目前的版本是3.1.6,Github仓库提交的最后期限是2021年5月21日。

      官方网站:

      www.libmodbus.org

      开源地址:

      github.com/stephane/libmodbus

      3.2 Modbuslib库安装

      1.获取源代码
      使用Git工具将GitHub代码仓库源本地下载,这样你就可以得到最新的libmodbus代码,但也有一些错误。

      git clone https://github.com/stephane/libmodbus/

      如果下载速度很慢,你可以下载到我的吉蒂仓库:

      git clone https://gitee.com/whik/libmodbus

      或从官方仓库下载最新的稳定版本v3。1.6:

      libmodbus.org/releases/libmodbus-3.1.6.tar.gz

      下载完成后,它 locally被解压缩,Linux系统可以使用tar -zxvf libmodbus-3.0.6.Tar.gz命令行解压缩:
      libmodbus支持下列功能:

      3.3modbuslib库函数

      • 支持Modbus-RTU和Modbus-TCP
      • 支持01/02/03/04/05/06/07/0F/10/11/16/17等通用功能代码
      • 支持行类型读写、登记读写、离散读写等。
      • 支持广播地址0,从机器地址1-247
      • 支持浮点数和格式化数据转换, 大和小端等。
      • 参数是基于Modbus_Application_Protocol_V1_1b.PDF官方标准文档设计,例如读写线圈的最大数目、读写注册表的最大数目等。
      • 源代码是用C语言编写的,并且易于跨平台移植,只有11个文件。

      本文由 在线网速测试 整理编辑,转载请注明出处,原文链接:https://www.wangsu123.cn/news/31420.html

          热门文章

          文章分类