Modbus RTU、Modbus 库函数

news2024/11/26 14:23:11

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.coloradmin.cn/o/1267385.html

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

相关文章

软件测评中心▏软件集成测试和功能测试之间的区别和联系简析

软件集成测试是在软件开发周期的后期阶段进行的测试活动&#xff0c;旨在验证系统各个组件之间的接口和交互是否正常工作。而功能测试是一种验证软件系统是否按照需求规格说明书所规定的功能进行正确实现的测试。接下来&#xff0c;我们来分别探讨一下软件集成测试和功能测试有…

可以在电脑桌面展示工作计划表的软件

很多上班族都表示自己在工作时&#xff0c;会面临大量且复杂的工作任务&#xff0c;这时候就会拖延工作&#xff0c;或者感觉时间不够用&#xff0c;所以需要有明确的工作计划来指导自己如何分类时间和精力&#xff0c;确保每项工作任务都能够按时完成。如果需要制定每天的工作…

mysql bin-log日志导出

一、mysql bin-log简介 1.1 什么是bin-log&#xff1f; MySQL bin-log是二进制日志文件&#xff0c;用于记录MySQL数据库中所有更改操作&#xff08;如插入、更新、删除等&#xff09;的详细信息。bin-log文件由MySQL服务器自动创建和维护&#xff0c;并记录了每个更改操作的…

JS:获取当前日期是本年度的第几周

问题 根据当前的日期&#xff08;比如年月日&#xff09;&#xff0c;来得到当前日期属于本年度的第几周 解决 代码&#xff1a; // 获取当前日期是本年的第几周 //参数&#xff1a; a为年 b为月 c为日 function getYearWeek(a, b, c) {var date1 new Date(a, parseInt(b)…

沈阳互联网医院|互联网医院系统|线上医疗发展现状

互联网医院系统已经成为了现代医疗行业中的新趋势&#xff0c;它不仅提供了线上诊疗、药品配送、在线咨询等服务&#xff0c;还为患者提供了更加便捷的医疗服务。那么&#xff0c;互联网医院系统的优势是什么呢&#xff1f; 1、互联网医院系统提供了线上诊疗服务&#xff0c;患…

给企业做公众号运营你都有哪些宝贵经验?

运营企业公众号需要长期的坚持和不断的创新&#xff0c;如何运营好一个企业公众号&#xff0c;使其成为企业与受众互动、传递价值、提升品牌形象的平台&#xff0c;是许多企业所面临的挑战。但只要不断学习&#xff0c;总结经验&#xff0c;就一定能够找到适合自己企业的公众号…

Kotlin基础——类型系统

? 对于如下Java函数&#xff0c;可传递null或者值为null的String int strLen(String s) {return s.length(); }而在Kotlin中&#xff0c;如下函数不能传递null或值为null的String&#xff0c;否则会在编译期报错&#xff0c;保证了永远不会在运行时报空指针异常 fun strLen…

服务器修复

服务器修复 主要服务器漏洞展示未禁用sync、shutdown、halt默认账户。未创建系统管理员、审计管理员、安全管理员账户设置系统管理员设置安全管理员 设置审计管理员配置PASS_MAX_DAYS 99999、PASS_MIN_LEN 5未配置TMOUT值配置HISTSIZE0未配置登录失败/密码复杂度策略umask值022…

【带头学C++】----- 八、C++面向对象编程 ---- 8.10 函数的默认参数

8.10 函数的默认参数 C在声明函数原型的时可为一个或者多个参数指定默认(缺省)的参数值&#xff0c;当函数调用的时候如果没有指定这个值&#xff0c;编器会自动用默认值代替。 通过为函数参数指定默认值&#xff0c;可以在调用函数时省略相应的参数&#xff0c;而该参数将使用…

喜报 | 再获影响力产品奖!擎创科技实力亮相GOPS全球运维大会

10月26日-27日&#xff0c;为期两天&#xff0c;共1100余人签到的 GOPS 全球运维大会 2023 上海站已经圆满落幕。 此次会议的“2023 IT技术领导力年度颁奖典礼”中&#xff0c;擎创夏洛克AIOps数智运维管理平台凭借成熟的产品能力及广泛且优异的落地实践效益&#xff0c;得到了…

【寒武纪(6)】MLU推理加速引擎MagicMind,最佳实践(二)混合精度

混合精度在精度损失范围内实现数倍的性能提升。 支持的量化特性 构建混合精度的流程 构建混合精度的流程如下&#xff0c;支持浮点或半精度编程&#xff0c;以及量化精度编程两种方式。 浮点或半精度 无需提供tensor分布量化编程需要设置tensor分布。 网络粒度和算子粒度的设…

【智能算法】季节优化算法Seasons optimization algorithm【2023最新智能优化算法合集】

本文介绍了一种基于成吉思汗鲨鱼(Genghis Khan shark&#xff0c;GKS)行为的自然启发的元启发式算法(MA)&#xff0c;称为成吉思汗鲨鱼优化器(Genghis Khan shark optimizer&#xff0c;GKSO)&#xff0c;用于数值优化和工程设计。GKSO的灵感来自于GKS的捕食和生存行为。该成果…

【分布式系统学习】CAP原理详解

CAP原理详解 前言CAP一张图 一、概念1.1 关键词解读1.2 关于CAP&#xff08;拆分解读&#xff09;1.3 CAP原理精髓 二、CAP模拟场景举例理解三、CAP原理证明为什么不能同时满足&#xff08;下面举例说明&#xff09;3.1 必须满足分区容错性P下的处理方式3.2 不是必须满足分区容…

自定义Windows服务启动失败

文章目录 自定义Windows服务启动失败报错内容解决方案管理员身份运行cmd进入到InstallUtil.exe的路径&#xff0c;使用cd命令。使用InstallUtil.exe工具安装服务。 自定义Windows服务启动失败 报错内容 “无法从命令行或调试器启动服务&#xff0c;必须首先安装Windows服务(使…

《opencv实用探索·四》Mat图像数据类型转换和归一化显示

一种数据类型转为另一种数据类型&#xff0c;不改变图像大小&#xff0c;但每个像素值可能会变 src.convertTo(dst, type, scale, shift);Scale和shitf默认为0&#xff08;这两个参数也相当于对比度和亮度&#xff09; 现在有个8位图像&#xff0c;把8位转成32位 可以看到像素…

【EI稳定检索】第三届绿色能源与电力系统国际学术会议(ICGEPS 2024)

第三届绿色能源与电力系统国际学术会议&#xff08;ICGEPS 2024&#xff09; 2024 3rd International Conference on Green Energy and Power Systems 绿色能源是指可以直接用于生产和生活的能源。它包括核能和“可再生能源”。随着世界各国能源需求的不断增长和环境保护意识…

人工智能 -- 技术概览

1、我们身处人工智能的时代 人们从早期做web开发&#xff0c;到移动端的开发&#xff1b;之后随着数据量的增大&#xff0c;人们开始研究高并发的问题&#xff1b;当数据量不断的增大&#xff0c;而人们希望数据不被浪费时&#xff0c;产生了大数据的技术&#xff0c;包括&…

国标GB28181协议/RTSP视频监控汇聚平台EasyCVR(V.3.4)页面UI大更新

为提高用户体验&#xff0c;增强平台功能&#xff0c;旭帆科技的Easy系列平台也在不断优化更新中。在最新的EasyCVR&#xff08;V.3.4&#xff09;中&#xff0c;其最显著的区别即为首页UI的调整。 其亮点是在【配置中心】-【基础配置】-【展示信息】中&#xff0c;首页UI可分…

Spark经典案例分享

Spark经典案例 链接操作案例二次排序案例 链接操作案例 案例需求 数据介绍 代码如下&#xff1a; package base.charpter7import org.apache.hadoop.conf.Configuration import org.apache.hadoop.fs.{FileSystem, Path} import org.apache.spark.SparkContext import org.a…

品牌全渠道营销系统如何与不同经销商ERP打通

品牌商在与各经销商ERP系统打通方面面临的挑战。传统的ERP系统往往使得数据收集和合作变得繁琐且低效&#xff0c;导致市场响应迟缓&#xff0c;影响整体的供应链管理和市场决策。我们的解决方案旨在破解这一难题&#xff0c;提供一个全渠道营销系统&#xff0c;它能自动与各类…