Modbus RTU、Modbus 库函数

Modbus RTU

与 Modbus TCP 的区别

一般在工业场景中,使用 Modbus RTU 的场景更多一些,Modbus RTU 基于串行协议进行收发数据,包括 RS232/485 等工业总线协议。采用主从问答式(master / slave)通信。
与 Modbus TCP 不同的是,RTU 没有报文头 MBAP 字段,但是在尾部增加了两个 CRC 检验字节(CRC16),因为网络协议中自带校验,所以在 TCP 协议中不需要使用 CRC 校验码。
RTU 和 TCP 的总体使用方法基本一致,只是在创建 Modbus 对象时有所不同。TCP 需要传入网络socket 信息;而 RTU 需要传入串口相关信息。

特点

通信

采用主从问答式(master / slave)通信,由主机发起,一问一答。

设置串口参数

波特率:9600
数据位:8
停止位:1
无流控

协议格式(地址码 + 功能码 + 数据 + 校验码)

Modbus RTU 数据帧包含:地址码、功能码、数据、校验码。
地址码: 从机 ID
功能码: 同 Modbus TCP
数据: 起始地址、数量、数据
CRC 校验码: 两个字节,对 地址码、功能码、数据 进行校验,可以通过函数自动生成

报文详解

(👆 链接至另一博主,放心跳转)

以 03 功能码为例:

主机 ——> 从机:

在这里插入图片描述

从机 ——> 主机:

在这里插入图片描述

模拟器的安装、配置、使用

实际硬件产品成本较高,可以使用一系列 Modbus 软件模拟器,进行数据模拟,从而分析 Modbus RTU 协议。

所用工具

Modbus Slave、vspd 虚拟串口、UartAssist 串口调试工具、虚拟机

安装与配置

一)vspd 虚拟串口的安装

1)将压缩包解压后,双击 vspd.exe 文件进行安装;
在这里插入图片描述

2)打开软件,添加 COM1 和 COM2 端口(用完之后记得删除端口);
在这里插入图片描述

3)打开设备管理器,出现如下图所示即可;
在这里插入图片描述

4)可以汉化,将 Cracked 下的文件复制到软件安装目录即可。
在这里插入图片描述

二)虚拟机绑定端口

1)VMware 虚拟机(注意不是 ubuntu)在系统关机(必须是关机状态,挂起不行)状态下,
点击:虚拟机 ——> 设置 ——> 硬件 ——> 添加串行端口,添加 COM1;
在这里插入图片描述

2)添加完成后,第一次使用需要将电脑重启;
3)重启之后,打开虚拟机,点击虚拟机 ——> 可移动设备 ——> 串行端口 ——> 连接;
在这里插入图片描述

4)在终端输入dmesg|grep tty,查看对应的设备文件,其中默认的会有 ttyS0 文件,
其余一个(ttyS1 或 ttyS2)就是虚拟串口对应的设备文件。
在这里插入图片描述

三)测试通信

1)Windows 下打开串口调试工具,选择好串口 COM2 ——> COM1,设置对应的波特率;
在这里插入图片描述

2)以下步骤在虚拟机下完成,在虚拟机安装 minicom 软件;sudo apt-get install minicom
在这里插入图片描述

3)在终端执行 sudo minicom -s ,选择 Serial port setup;
在这里插入图片描述

4)设置设备文件,波特率,关闭流控;(按 Ctrl + 相应字母)
在这里插入图片描述

5)回车,保存修改,选择 Save setup as dfl;
在这里插入图片描述

6)可以在以下界面输入字符,查看串口助手的显示情况;
在这里插入图片描述

7)测试通信(终端输入不可见);
在这里插入图片描述

8)退出:Ctrl + A,然后按 Z,在弹出的界面里输入X,即可退出。
在这里插入图片描述

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

虚拟机绑定 COM1 端口,Modbus Slave 连接 COM2 端口,虚拟机通过编程测试串口通信;
在这里插入图片描述

五)可能遇到的问题

虚拟串口完成主机与 vmware 下虚拟机进行串口通信
VSPD 虚拟串口工具 —— 从此告别硬件串口调试
vmware 虚拟机检测不到 vspd 虚拟串口问题
(👆 链接至其他博主,放心跳转)

Modbus 库

库的安装

安装与配置

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 			   // 安装

在这里插入图片描述
在这里插入图片描述

5)执行完成后会在 install 文件夹下产生对应的头文件、库文件。

使用

1、一般操作:

gcc xxx.c -I ./install/include/modbus -L ./install/lib -lmodbus
./a.out

-I : 后需要指定出头文件的路径(大写的i)
-L : 后需要指定库的路径
-l : 后需要指定库名(小写的L)

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

sudo  cp install/include/modbus/*.h  /usr/include 
sudo  cp install/lib/*  -r /lib -d 

在这里插入图片描述

后期编译时,就可以直接 gcc xxx.c -lmodbus,
头文件默认搜索路径:/usr/include、/usr/local/include
库文件默认搜索路径:/lib、/usr/lib

函数接口

0x01(modbus_read_bits)

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

0x02(modbus_read_input_bits)

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

0x03(modbus_read_registers)

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

0x04(modbus_read_input_registers)

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

0x05(modbus_write_bit)

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

0x06(modbus_write_register)

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

0x0F(modbus_write_bits)

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

0x10(modbus_write_registers)

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

编程流程

1)创建实例(modbus_new_tcp / modbus_new_rtu)

modbus_t *modbus_new_tcp(const char *ip, int port); 功能:以 TCP 方式创建 Modbus 实例,并初始化
参数:ip  :	ip 地址port:	端口号
返回值:成功:Modbus 实例失败:NULL
modbus_t *modbus_new_rtu(const char *device, int baud, char parity, int data_bit, int stop_bit);功能:用于创建一个用于 Modbus RTU 通信的 modbus_t 结构体实例
参数:device:	要打开的串口设备的路径(例如:"/dev/ttyUSB0")baud:		波特率(如 9600、19200 等)parity:	校验位(可选值:'N' - 无校验、'E' - 偶校验、'O' - 奇校验)data_bit:	数据位(常用值为 8)stop_bit:	停止位(常用值为 1)返回值:成功:Modbus 实例失败:NULL

2)设置从机地址(modbus_set_slave)

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

3)建立连接(modbus_connect)

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

4)各种操作(见函数接口)

5)关闭套接字(modbus_close)

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

6)释放实例(modbus_free)

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

练习:

// 和 Slave 通信,读保持寄存器的三个值#include <stdio.h>
#include <modbus.h>
#include <stdlib.h>
#include <string.h>
#include <modbus-rtu.h>int main(int argc, char const *argv[])
{   if (argc != 3){printf("Please input %s <ip> <port>. \n", argv[0]);return -1;}modbus_t *ctx;ctx = modbus_new_tcp(argv[1], atoi(argv[2]));// ctx = modbus_new_rtu("/dev/ttyS1", 9600, N, 8, 1);if (ctx == NULL){perror("Failed to modbus_new_tcp");			// "Failed to modbus_new_rtu"return -1;}if (modbus_set_slave(ctx, 1) < 0){perror("Failed to modbus_set_slave");return -1;}if (modbus_connect(ctx) < 0){perror("Failed to modbus_connect");return -1;}uint16_t dest[32] = {};if (modbus_read_registers(ctx, 0, 3, dest) < 0){perror("Failed to modbus_read_registers");return -1;}for (int i = 0; i < 3; i++)printf("%#x ", dest[i]);putchar(10);for (int i = 0; i < 3; i++)printf("%d ", dest[i]);putchar(10);modbus_close(ctx);modbus_free(ctx);return 0;
}

运行结果如下:
在这里插入图片描述

注意:

1、使用 Modbus TCP 协议时,将 slave 的 connect 设置为“Modbus TCP/IP”。
2、使用 Modbus RTU 协议时,将 slave 的 connect 设置为“Serial Port”。
在这里插入图片描述

小目标:

编程实现采集传感器数据和控制硬件设备(传感器和硬件通过 slave 模拟)。
传感器:2个,光线传感器、加速度传感器(x \ y \ z);
硬件设备:2个,LED灯、蜂鸣器。
要求:
1、多任务编程:多线程、多进程
2、循环 1s 采集一次数据,并将数据打印至终端
3、同时从终端输入指令控制硬件设备
0 1:LED 灯开
0 0:LED 灯关
1 1:蜂鸣器开
1 0:蜂鸣器关

// 同步实现#include <stdio.h>
#include <modbus.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <unistd.h>
#include <semaphore.h>modbus_t *ctx;
sem_t sem1, sem2;void *collector(void *arg){uint16_t *dest = (uint16_t *)arg;while (1){sleep(5);sem_wait(&sem1);if (modbus_read_registers(ctx, 0, 4, dest) < 0){perror("Failed to modbus_read_registers");return NULL;}for (int i = 0; i < 4; i++)printf("%d ", dest[i]);putchar(10);sem_post(&sem2);}pthread_exit(0);
}void *control(void *arg){uint8_t writer[2];while (1){sem_wait(&sem2);printf("Please set status of LED or BUZZER: ");for (int i = 0; i < 2; i++)scanf("%hhu", &writer[i]);modbus_write_bit(ctx, writer[0], writer[1]);sem_post(&sem1);}pthread_exit(0);
}int main(int argc, char const *argv[])
{   if (argc != 3){printf("Please input %s <ip> <port>. \n", argv[0]);return -1;}ctx = modbus_new_tcp(argv[1], atoi(argv[2]));if (ctx == NULL){perror("Failed to modbus_new_tcp");return -1;}if (modbus_set_slave(ctx, 1) < 0){perror("Failed to modbus_set_slave");return -1;}if (modbus_connect(ctx) < 0){perror("Failed to modbus_connect");return -1;}uint16_t dest[32] = {};pthread_t tid1, tid2;sem_init(&sem1, 0, 1);sem_init(&sem2, 0, 0);if (pthread_create(&tid1, NULL, collector, dest)){perror("Failed to create a thread named collector");return -1;}pthread_detach(tid1);if (pthread_create(&tid2, NULL, control, NULL)){perror("Failed to create a thread named input");return -1;}pthread_detach(tid2);while (1);modbus_close(ctx);modbus_free(ctx);return 0;
}

实现效果如下:
在这里插入图片描述

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

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

相关文章

基于springboot实现实习管理系统的设计与实现项目【项目源码+论文说明】计算机毕业设计

基于sprinmgboot实现实习管理系统的设计与实现演示 摘要 随着信息化时代的到来&#xff0c;管理系统都趋向于智能化、系统化&#xff0c;实习管理也不例外&#xff0c;但目前国内仍都使用人工管理&#xff0c;市场规模越来越大&#xff0c;同时信息量也越来越庞大&#xff0c;…

普通平衡树

题意&#xff1a;略&#xff0c;题中较清晰。 用二叉查找树来存储数据&#xff0c;为了增加效率&#xff0c;尽量使左子树和右子树的深度差不超过一&#xff0c;这样可以时间控制在logn&#xff0c;效率比较高。 右旋和左旋&#xff0c;目的是为了维护二叉树的操作&#xff0…

Dubbo引入Zookeeper等注册中心简介以及DubboAdmin简要介绍,为后续详解Dubbo各种注册中心做铺垫!

文章目录 一&#xff1a;Dubbo注册中心引言 1&#xff1a;什么是Dubbo的注册中心&#xff1f; 2&#xff1a;注册中心关系图解 3&#xff1a;引入注册中心服务执行流程 4&#xff1a;Dubbo注册中心好处 5&#xff1a;注册中心核心作用 二&#xff1a;注册中心实现方案 …

Springboot+vue的新冠病毒密接者跟踪系统(有报告)。Javaee项目,springboot vue前后端分离项目

演示视频&#xff1a; Springbootvue的新冠病毒密接者跟踪系统(有报告)。Javaee项目&#xff0c;springboot vue前后端分离项目 项目介绍&#xff1a; 本文设计了一个基于Springbootvue的新冠病毒密接者跟踪系统&#xff0c;采用M&#xff08;model&#xff09;V&#xff08;v…

景区智慧旅游智能化系统方案:PPT全文58页,附下载

关键词&#xff1a;智慧景区解决方案&#xff0c;智慧文旅解决方案&#xff0c;智慧旅游解决方案&#xff0c;智慧文旅综合运营平台 一、景区智慧旅游智能化系统建设背景 近年来&#xff0c;随着信息技术的快速发展和普及&#xff0c;以及旅游市场的不断扩大和升级&#xff0…

电脑自动删除文件怎么办?如何恢复?

在数字化时代&#xff0c;电脑已经成为人们不可或缺的工具之一。然而&#xff0c;由于各种原因&#xff0c;我们有时会遇到电脑自动删除文件的情况&#xff0c;这给我们的工作和生活带来了很多不便。那么&#xff0c;当电脑自动删除文件时&#xff0c;我们应该如何处理呢&#…

【Python爬虫】8大模块md文档从0到scrapy高手,第8篇:反爬与反反爬和验证码处理

本文主要学习一下关于爬虫的相关前置知识和一些理论性的知识&#xff0c;通过本文我们能够知道什么是爬虫&#xff0c;都有那些分类&#xff0c;爬虫能干什么等&#xff0c;同时还会站在爬虫的角度复习一下http协议。 Python爬虫和Scrapy全套笔记直接地址&#xff1a; 请移步这…

数据结构与算法编程题14

设计一个算法&#xff0c;通过一趟遍历在单链表中确定值最大的结点。 #include <iostream> using namespace std;typedef int Elemtype; #define ERROR 0; #define OK 1;typedef struct LNode {Elemtype data; //结点保存的数据struct LNode* next; //结构体指针…

RedHat NTP时间服务器配置Chrony(所有节点时间跟主节点时间同步)

NTP NTP&#xff08;Network Time Protocol&#xff09;是一种用于在计算机网络中同步时钟的协议。它的主要目的是确保网络中的各个设备具有准确的时间参考&#xff0c;以便协调事件顺序、安全通信和日志记录等应用。它通过分层体系结构、时间同步算法和准确的时间参考源来确保…

Linux设置静态IP

Linux设置静态IP 使用ip addr查看ip&#xff0c;如下所示就是动态IP 1、什么是静态IP&#xff1f; 静态ip就是固定的ip&#xff0c;需要手动设置。静态IP地址&#xff08;又称固定IP地址&#xff09;是长期分配给一台计算机或网络设备使用的 IP 地址。一般来说&#xff0c;一…

738. Monotone Increasing Digits 968. Binary Tree Cameras

738. Monotone Increasing Digits An integer has monotone increasing digits单调递增数字 if and only if each pair of adjacent digits x and y satisfy x < y. Given an integer n, return the largest number that is less than or equal to n with monotone increa…

TypeScript 学习笔记 第三部分 贪吃蛇游戏

尚硅谷TypeScript教程&#xff08;李立超老师TS新课&#xff09; 1. 创建开发环境 创建工程&#xff0c;使用学习笔记的第二部分安装css部分 npm i -D less less-loader css-loader style-loader对css部分处理&#xff0c;能够运行在低版本浏览器 npm i -D postcss postcss…

音色逼真、韵律自然的AI人声克隆限时福利!

声音&#xff0c;为数字人注入灵魂。 2023云栖大会上&#xff0c;阿里云视频云接受了CCTV-2财经频道的采访&#xff0c;分享并演示了如何利用云端智能剪辑&#xff0c;一站式完成数字人渲染及视频精编二创。 正如视频开头所呈现的AI重现演员“原声”&#xff0c;近年来&#x…

基于SpringBoot的图书管理系统

基于SpringBoot的图书管理系统 图书管理系统开发技术功能模块代码结构数据库设计运行截图源码获取 图书管理系统 开发技术 技术&#xff1a;SpringBoot、MyBatis-Plus、MySQL、Beetl、Layui。 框架&#xff1a;基于开源框架Snowy-Layui开发。 工具&#xff1a;IDEA、Navicat等…

【Linux】进程间通信——进程间通信的介绍和分类、管道、匿名管道、命名管道、匿名管道与命名管道的区别

文章目录 进程间通信1.进程间通信的介绍1.1目的和发展 2.进程间通信分类3.管道3.1匿名管道3.1.1匿名管道的原理&#xff08;文件角度&#xff09;3.1.2匿名管道的原理&#xff08;内核角度&#xff09;3.1.3管道读写规则3.1.4管道特点 3.2命名管道3.2.1创建命名管道3.2.2命名管…

谈思生物医疗直播 | 利用类器官模型研究肺的发育与稳态

类器官是一种三维细胞培养物&#xff0c;其在细胞类型&#xff0c;空间结构及生理功能上能够模拟对应器官&#xff0c;从而提供一个高度生理相关的系统。自2009年小肠类器官首次建立至今&#xff0c;类器官研究已经延伸到多个组织系统&#xff0c;并成为当下生命科学领域最热门…

美创获IDC数据库安全市场代表厂商推荐,一路引领数据库安全

近日&#xff0c;全球领先的IT市场研究和咨询公司IDC发布《IDC Persepctive&#xff1a;中国数据库安全市场洞察&#xff0c;2023》报告。 凭借多年的技术积累和丰富的产品体系与行业实践&#xff0c;美创科技获「代表厂商」推荐&#xff0c;再次彰显专业领先能力&#xff01; …

Mybatis一级缓存和二级缓存原理剖析与源码详解

Mybatis一级缓存和二级缓存原理剖析与源码详解 在本篇文章中&#xff0c;将结合示例与源码&#xff0c;对MyBatis中的一级缓存和二级缓存进行说明。 MyBatis版本&#xff1a;3.5.2 文章目录 Mybatis一级缓存和二级缓存原理剖析与源码详解⼀级缓存场景一场景二⼀级缓存原理探究…

部署项目时常用的 Linux 命令

目录 1 前言2 SSH登录命令3 SCP传输命令4 CP拷贝命令5 MV移动命令6 TAR解压命令7 DU查看文件夹/文件大小8 TAIL查看日志9 NOHUP后台运行10 结语 1 前言 在应用部署过程中&#xff0c;Linux命令是必不可少的工具。它们能够帮助我们管理文件、连接服务器、拷贝文件、查看日志以及…

vite项目配置vite.config.ts在打包过程中去除日志

在生产环境上&#xff0c;务必要将日志清除干净&#xff0c;其因有二&#xff0c;在webgis系统中&#xff0c;有很多几何数据&#xff0c;体积大、数量多&#xff0c;很容易引起系统卡顿&#xff1b;清除log后&#xff0c;系统看着舒服&#xff0c;协同开发有很多无聊的日志&am…