Modbus RTU协议及modbus库函数使用

一、与Modbus TCP的区别

在一般工业场景使用modbus RTU的场景还是更多一些,modbus RTU基于串行协议进行收发数据,包括RS232/485等工业总线协议。

与modbus TCP不同的是RTU没有报文头MBAP字段,但是在尾部增加了两个CRC检验字节(CRC16),因为网络协议中自带校验,所以在TCP协议中不需要使用CRC校验码。

RTU和TCP的总体使用方法基本一致,只是在创建modbus对象时有所不同,TCP需要传入网络socket信息;而RTU需要传入串口相关信息。

二、Modbus RTU特点

Modbus RTU也是主从问答协议,由主机发起,一问一答

Modbus RTU通过串口进行通信

设置串口参数:

设置串口参数时要求:

波特率为9600

8位数据位

1位停止位

无流控

三、Modbus RTU协议格式

ModbusRTU数据帧包含:地址码 功能码 数据 校验码

地址码:一个字节 从机ID

功能码:一个字节 同TCP(八种)

数据:起始地址 数量  数据

校验码:2个字节,对 地址码+功能码+数据进行校验,可以通过函数自动生成。

四、报文详解

03功能码为例

主机---》从机:

01 03 00 00 00 01 84 0a

01:地址码

03:功能码

00 00 :起始地址

00 01:读的数量

84 0a :校验码

从机--》主机:

01 03 02 00 14 b8 44

01:地址码

03:功能码

02:字节计数(只计算数据的个数)

00 14:数据

b8 44:校验码 

参考示例:

值得收藏 Modbus RTU 协议详解-CSDN博客

五、模拟器的使用

由于实际硬件产品成本较高,我们这里可以使用Modbus软件模拟器,进行数据模拟从而分析Modbus协议。

使用工具:

1. ModbusPoll(模拟主机)和ModbusSlave(模拟从机)

2. vspd虚拟串口

3. UartAssist串口调试工具

设置串口参数要求:波特率为9600   8位数据位  1位停止位 无流控  无校验

虚拟串口的使用:

  • 虚拟串口的安装
  1. 将压缩包解压后,双击vspd.exe文件进行安装

2.安装完成后,找到安装目录,将Cracked下的文件复制到软件安装目录

  1. 打开软件,添加COM1COM2端口(用完之后记得删除端口)

4.添加完端口后,打开设备管理器,这里出现如下图所示即可。

二、虚拟机绑定端口

  1.  将虚拟机在系统关机(必须是关机状态,挂起不行)状态下,点击虚拟机->设置->硬件->添加串行端口,添加COM1
  2. 添加完成后,第一次使用需要将电脑重启
  3. 重启之后,打开虚拟机,点击虚拟机->可移动设备->串行端口->连接
  4. 当连接上虚拟串口后,在终端输入dmesg | grep tty,可以查看到对应的设备文件,其中默认的会有ttyS0文件,剩下的就是虚拟串口对应的设备文件
  • 测试通信

1.Windows打开串口调试工具,选择好串口COM2->COM1,设置对应的波特率

2. 在虚拟机运行minicom

在虚拟机安装minicom软件

sudo apt-get install minicom

在终端执行sudo minicom -s

1)选择serial port setup,回车

2)设置设备文件,波特率,关闭流控,按如下图设置(文件改成自己的

dmesg | grep tty 查看文件名

3)修改完成后,回车,保存修改,选择save setup as dfl,敲回车,再次选择exit回车

4)退出后就可以和windows下的串口调试工具进行通信测试

5)也可以在这个界面输入字符,查看串口助手的显示情况。

6)退出:ctrl+AZ,在弹出的界面里输入X,即可退出。

四、将Modbus Slave模拟器作为RTU设备的从机

虚拟机绑定COM1端口,slave连接COM2端口,虚拟机通过编程测试串口通信

Modbus Slave端的配置如下:

五、可能会遇到的问题

1. 虚拟串口完成主机与vmware下虚拟机进行串口通信

虚拟串口完成主机与vmware下虚拟机进行串口通信_xcom2v2.0怎么用-CSDN博客

2. VSPD虚拟串口工具——从此告别硬件串口调试

『实用教程』VSPD虚拟串口工具——从此告别硬件串口调试_虚拟串口vspd-CSDN博客

3. vmware虚拟机检测不到vspd虚拟串口问题

vmware虚拟机检测不到vspd虚拟串口问题_vmware虚拟机 串口 vspd-CSDN博客

练习:代码完成通过串口读取slave端

串口初始化与校验码获取函数一起编译,管理员身份执行命令,

六、Modbus库

  1. 安装库
    1. 安装配置

1. 在linux中解压压缩包

tar -xvf libmodbus-3.1.7.tar.gz

2. 进入源码目录,创建文件夹(存放头文件、库文件)

cd libmodbus-3.1.7

mkdir install 

3. 执行脚本configure,进行安装配置(指定安装目录)

./configure --prefix=$PWD/install

4. 执行make和make install

make//编译

make install//安装

执行完成后会在install文件夹下生产对应的头文件、库文件件夹install,用于存放产生的头文件、库文件等

    1. 库的使用

要想编译方便,可以将头文件和库文件放到系统路径下

sudo  cp install/include/modbus/*.h  /usr/include 

sudo  cp install/lib/*  -r /lib -d

后期编译时,可以直接gcc xx.c -lmodbus

头文件默认搜索路径:/usr/include  、/usr/local/include

库文件默认搜索路径:/lib、/usr/lib

  1. 函数接口
  1. 以TCP方式创建Modbus实例,并初始化

modbus_t*   modbus_new_tcp(const char *ip, int port)
功能:以TCP方式创建Modbus实例,并初始化
参数:
    ip   :ip地址
    port:端口号
返回值:成功:Modbus实例
      失败:NULL

  1. 设置从机ID

int modbus_set_slave(modbus_t *ctx, int slave)
功能:设置从机ID
参数:
    ctx   :Modbus实例
    slave:从机ID
返回值:成功:0
       失败:-1

  1. 和从机(slave)建立连接

一个modbus实例只能链接一个从机:根据提供的引用内容,一个modbus实例只能连接一个从机ID。因为modbus通信协议是基于主从模式的,每个从机都有一个唯一的从机地址,主机通过从机地址来访问不同的从机。因此,一个modbus实例只能连接一个从机ID,如果需要连接多个从机,需要创建多个modbus实例。

int   modbus_connect(modbus_t *ctx)
功能:和从机(slave)建立连接
参数:
    ctx:Modbus实例
返回值:成功:0
       失败:-1

  1. 释放Modbus实例

void   modbus_free(modbus_t *ctx)
功能:释放Modbus实例
参数:ctx:Modbus实例

  1. 关闭套接字

void   modbus_close(modbus_t *ctx)
功能:关闭套接字
参数:ctx:Modbus实例

  1. 读取线圈状态,可读取多个连续线圈的状态(对应功能码为0x01)

int modbus_read_bits(modbus_t *ctx, int addr, int nb, uint8_t *dest)
功能:读取线圈状态,可读取多个连续线圈的状态(对应功能码为0x01)
参数:
    ctx   :Modbus实例
    addr :寄存器起始地址
    nb    :寄存器个数
    dest :得到的状态值

7)读取输入状态,可读取多个连续输入的状态(对应功能码为0x02

int  modbus_read_input_bits(modbus_t *ctx, int addr, int nb, uint8_t *dest)
功能:读取输入状态,可读取多个连续输入的状态(对应功能码为0x02)
参数:
    ctx   :Modbus实例
    addr :寄存器起始地址
    nb   :寄存器个数
    dest :得到的状态值
返回值:成功:返回nb的值

  1. 读取保持寄存器的值,可读取多个连续保持寄存器的值(对应功能码为0x03

int  modbus_read_registers(modbus_t *ctx, int addr, int nb, uint16_t *dest)
功能:读取保持寄存器的值,可读取多个连续保持寄存器的值(对应功能码为0x03)
参数:
    ctx   :Modbus实例
    addr :寄存器起始地址
    nb    :寄存器个数
    dest :得到的寄存器的值
返回值:成功:读到寄存器的个数
       失败:-1

  1. 读输入寄存器的值,可读取多个连续输入寄存器的值(对应功能码为0x04

int   modbus_read_input_registers(modbus_t *ctx, int addr, int nb, uint16_t *dest)
功能:读输入寄存器的值,可读取多个连续输入寄存器的值(对应功能码为0x04)
参数:
    ctx   :Modbus实例
    addr :寄存器起始地址
    nb    :寄存器个数
    dest :得到的寄存器的值
返回值:成功:读到寄存器的个数
       失败:-1

  1. 写入单个线圈的状态(对应功能码为0x05

int  modbus_write_bit(modbus_t *ctx, int addr, int status);
功能:写入单个线圈的状态(对应功能码为0x05)
参数:
    ctx     :Modbus实例
    addr  :线圈地址
    status:线圈状态
返回值:成功:0
      失败:-1

  1. 写入多个连续线圈的状态(对应功能码为15

int  modbus_write_bits(modbus_t *ctx, int addr, int nb, const uint8_t *src);
功能:写入多个连续线圈的状态(对应功能码为15)
参数:
    ctx     :Modbus实例
    addr  :线圈地址
    nb     :线圈个数
    src    :多个线圈状态
返回值:成功:0
      失败:-1

  1. 写入单个寄存器(对应功能码为0x06

int  modbus_write_register(modbus_t *ctx, int addr, int value);
功能:  写入单个寄存器(对应功能码为0x06)
参数: 
    ctx    :Modbus实例
    addr  :寄存器地址
    value :寄存器的值 
返回值:成功:0
       失败:-1

12)写入多个连续寄存器(对应功能码为16

int  modbus_write_registers(modbus_t *ctx, int addr, int nb, const uint16_t *src);
功能:写入多个连续寄存器(对应功能码为16
参数:
    ctx    :Modbus实例
    addr  :寄存器地址
    nb     :寄存器的个数
    src    :多个寄存器的值 
返回值:成功:0
      失败:-1

  1. 编程流程
  1. 创建实例 modbus_new_tcp
  2. 设置从机id modbus_set_slave
  3. 建立连接 modbus_connect
  4. 寄存器操作
  5. 关闭套接字 modbus_close
  6. 释放实例 modbus_free
  1. 练习

通过库函数实现03功能码采集数据

注意:编译不要忘了链接库、查看网络是否能用,查看slave端协议是否正确,查看slave端是否有对应的寄存器类型,查看slave id是否一致

gcc modbus.c -lmodbus

#include <stdio.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h> /* superset of previous */
#include <arpa/inet.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <modbus.h>
int main(int argc, char const *argv[])
{
    int ret;
    uint16_t buf[32];
    // 1.创建实例 modbus_new_tcp,端口号字符型转整型
    modbus_t* md = modbus_new_tcp(argv[1],atoi(argv[2]));
    // 2.设置从机id modbus_set_slave
    ret = modbus_set_slave(md, 1);
    if (ret < 0)
    {
        printf("set err\n");
    }
    // 3.建立连接 modbus_connect
    ret = modbus_connect(md);
    if (ret < 0)
    {
        printf("connect err.\n");
    }
    // 4.寄存器操作
    //从0开始读十个寄存器值
    ret = modbus_read_registers(md, 0, 10, buf);
    for (int i = 0; i < 10; i++)
    {
        printf("%#x ", buf[i]);
    }
    // 5.关闭套接字 modbus_close,先关闭套接字,再释放实例
    modbus_close(md);   
    // 6.释放实例 modbus_free
    modbus_free(md);
    return 0;
}

1.任务:编程实现采集传感器数据和控制硬件设备(传感器和硬件通过slave模拟)

传感器:2个,光线传感器、加速度传感器(x\y\z)

硬件设备:2个,led灯、蜂鸣器

要求:

1.多任务编程:多线程

2.循环1s采集一次数据,并将数据打印至终端

3.同时从终端输入指令控制硬件设备

0  1 :led灯打开

0  0:led灯关闭

1  1:蜂鸣器开

1 0 :  蜂鸣器关

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/206925.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【Web】UUCTF 2022 新生赛 个人复现

目录 ①websign ②ez_rce ③ez_upload ④ez_unser ⑤ezsql ⑥ezpop ⑦funmd5 ⑧phonecode ⑨ezrce ①websign 右键打不开&#xff0c;直接抓包发包看源码 ②ez_rce “反引号” 在PHP中会被当作SHELL命令执行 ?codeprintf(l\s /); ?codeprintf(ta\c /ffffffffffl…

特征变换1

编译工具&#xff1a;PyCharm 有些编译工具不用写print可以直接将数据打印出来&#xff0c;pycharm需要写print才会打印出来。 概念 1.特征类型 特征的类型&#xff1a;“离散型”和“连续型” 机器学习算法对特征的类型是有要求的&#xff0c;不是任意类型的特征都可以随意…

Spring RabbitMQ那些事(2-两种方式实现延时消息订阅)

目录 一、序言二、死信交换机和消息TTL实现延迟消息1、死信队列介绍2、代码示例(1) 死信交换机配置(2) 消息生产者(3) 消息消费者 3、测试用例 三、延迟消息交换机实现延迟消息1、安装延时消息插件2、代码示例(1) 延时消息交换机配置(2) 消息生产者(3) 消息消费者 3、测试用例 …

set和map + multiset和multimap(使用+封装(RBTree))

set和map 前言一、使用1. set(1)、模板参数列表(2)、常见构造(3)、find和count(4)、insert和erase(5)、iterator(6)、lower_bound和upper_bound 2. multiset3. map(1)、模板参数列表(2)、构造(3)、modifiers和operations(4)、operator[] 4. multimap 二、封装RBTree迭代器原理R…

科技与教育:未来教育的新趋势

在21世纪&#xff0c;科技的快速发展正在深刻地改变教育行业。从在线学习平台到虚拟现实教室&#xff0c;科技为教育带来了革命性的变化。本文将探讨科技如何影响现代教育&#xff0c;并预测未来教育的发展趋势。 一、科技在教育中的应用 在线学习平台&#xff1a;通过平台如C…

JSON 与 FastJSON

JSON 与 FastJSON JSON JavaScript Object Notation&#xff08;JavaScript 对象表示法&#xff09;是目前最常用的执行对象序列化的方式。 虽然 json 最初是为了在 JavaScript 语言中使用的&#xff0c;但实际上 json 本身跟语言没有任何关系&#xff0c;各种编程语言都可以使…

微服务--08--Seata XA模式 AT模式

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 分布式事务Seata 1.XA模式1.1.两阶段提交1.2.Seata的XA模型1.3.优缺点 AT模式2.1.Seata的AT模型2.2.流程梳理2.3.AT与XA的区别 分布式事务 > 事务–01—CAP理论…

Flutter使用flutter_gen管理资源文件

pub地址&#xff1a; https://pub.dev/packages/flutter_gen 1.添加依赖 在你的pubspec.yaml文件中添加flutter_gen作为开发依赖 dependencies:build_runner:flutter_gen_runner: 2.配置pubspec.yaml 在pubspec.yaml文件中&#xff0c;配置flutter_gen的参数。指定输出路…

msvcp140.dll的解决方法有哪些。详细解析五种可以修复msvcp140.dll丢失的方法

引言&#xff1a; 在日常使用电脑的过程中&#xff0c;我们可能会遇到一些错误提示&#xff0c;其中之一就是“msvcp140.dll丢失”。那么&#xff0c;什么是msvcp140.dll文件&#xff1f;它的作用是什么&#xff1f;当它丢失时会对电脑产生什么影响&#xff1f;本文将详细介绍…

使用elementPlus去除下拉框蓝色边框

// 下拉框去除蓝色边框 .el-select {--el-select-input-focus-border-color: none !important; }

仅仅通过提示词,GPT-4可以被引导成为多个领域的特定专家

The Power of Prompting&#xff1a;提示的力量&#xff0c;仅通过提示&#xff0c;GPT-4可以被引导成为多个领域的特定专家。微软研究院发布了一项研究&#xff0c;展示了在仅使用提策略的情况下让GPT 4在医学基准测试中表现得像一个专家。研究显示&#xff0c;GPT-4在相同的基…

【bug篇】Tomcat一直报错,但是代码没问题

代码都没有问题&#xff0c;就是报404错误&#xff0c;原因竟然是版本不兼容&#xff0c;搞了我好长时间&#xff0c;简直麻了&#xff01;&#xff01;&#xff01; 因为我的Tomcat是11版本的&#xff0c;所以导入的servlet和jsp依赖应该是下面这些&#xff1a; <!-- Serv…

c++之STL

首先我们来仔细研究string 首先我们需要实现string的构造函数和析构函数。有new就有delete. 然后我们实现size()和c_str()&#xff0c;其中c_str就是可以将string类型转换为char*类型返回。 通过运算符重载&#xff0c;我们就可以实现string的[]访问。 然后我们实现和append。 …

【机器学习】平滑滤波

平滑滤波技术 平滑滤波&#xff0c;顾名思义就是对信号进行处理使之整体显得更加平滑&#xff0c;降低噪声影响&#xff0c;提高信号质量&#xff0c;它常见于数字信号处理和图像处理&#xff0c;一般意义上的数字信号多体现于一维数据&#xff0c;图像信号多体现于二维数据。…

mysql:免费的GUI客户端工具推荐并介绍常用的操作

给大家推荐几个常用的 mysql 数据库客户端 sequel-pro sequel-ace 官网下载地址 免费 sequel-ace 可以理解为 Sequel Pro 的升级版&#xff0c;由于Sequel Pro官方不维护了&#xff0c;特别是对 MySQL 8.0 支持不好&#xff0c;所以现在由社区维护了新分支 sequel-ace&#x…

人力资源管理后台 === 角色管理

目录 1.组织架构-编辑部门-弹出层获取数据 2.组织架构-编辑部门-编辑表单校验 3.组织架构-编辑部门-确认取消 4.组织架构-删除部门 5.角色管理-搭建页面结构 6.角色管理-获取数据 7.角色管理-表格自定义结构 8.角色管理-分页功能 9.角色管理-新增功能弹层 10.角色管理…

Linux 中的 ls 命令使用教程

目录 前言 如何运用 ls 命令 1、列出带有所有权的文件和目录 2、获取以人类可读的方式显示的信息 3、列出隐藏文件 4、递归列出文件 5、在使用 ls 时对文件和目录做区分 6、列出指定扩展名的文件 7、基于大小对输出内容排序 8、根据日期和时间排序文件 让我们来总结…

Ansible的重用(include和import)

环境 管理节点&#xff1a;Ubuntu 22.04控制节点&#xff1a;CentOS 8Ansible&#xff1a;2.15.6 重用 Ansible提供四种可重用的工件&#xff1a; variable文件&#xff1a;只包含变量的文件task文件&#xff1a;只包含task的文件playbook&#xff1a;可包含play、变量、ta…

MVVM 模式与 MVC 模式:构建高效应用的选择

&#x1f90d; 前端开发工程师&#xff08;主业&#xff09;、技术博主&#xff08;副业&#xff09;、已过CET6 &#x1f368; 阿珊和她的猫_CSDN个人主页 &#x1f560; 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 &#x1f35a; 蓝桥云课签约作者、已在蓝桥云…

代码随想录算法训练营第六十天| 84.柱状图中最大的矩形

文档讲解&#xff1a;代码随想录 视频讲解&#xff1a;代码随想录B站账号 状态&#xff1a;看了视频题解和文章解析后做出来了 84.柱状图中最大的矩形 class Solution:def largestRectangleArea(self, heights: List[int]) -> int:heights.insert(0, 0)heights.append(0)st…