网络编程(Modbus进阶)

news2025/6/13 2:13:55

思维导图

Modbus RTU(先学一点理论)

概念

Modbus RTU 是工业自动化领域 最广泛应用的串行通信协议,由 Modicon 公司(现施耐德电气)于 1979 年推出。它以 高效率、强健性、易实现的特点成为工业控制系统的通信标准。 包括RS232/485等工业总线协议。

与Modbus TCP区别

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

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

特点

1.遵循主从问答的通信方式

2.采用串口的方式进行通信

设置串口参数时要求:(了解,后续还会用)

波特率为9600(波特率是指每秒钟传输的比特数)

8位数据位 (数据位是指每个字符中包含的比特数)

1位停止位 (停止位是指在每个字符传输结束后添加的比特数)

无流控 (流控是指在数据传输过程中控制数据流量的一种机制,无流控表示在该设置下没有额外的控制机制来控制数据流量)

modbus rtu协议格式

地址码 功能码 数据 校验码

地址码(1字节):从机ID

功能码(1字节):和modbus tcp一样(01 02 03 04 05 06 0f 10H)

数据:起始地址、地址、数量、数据、字节计数;和modbus tcp一样。

校验码(2字节):对地址码、功能码、数据进行校验,由函数生成,循环冗余校验 (低字节在前)

其实modbus rtu协议的格式和modbus tcp是很像,就是把tcp的MBAP报文头去掉,只保留了一个字节的主机ID,最后结尾加上了两个字节的校验码。(校验码没有实际意义,是函数生成的不用管

 以01发送的数据格式为例,可以看到数据位是一样的,上图就是tcp和rtu协议格式的区别。数据接收也是和tcp一样的,所以就不再讲了。

modbus 库

官方文档:libmodbus

1库的安装

第一步和第二步都要运行,第一步是为了安装配置,第二步是为了让你使用这个库更方便,把它放在你的C语言库里。​​​​​

1.1库的安装配置(共四步)

通过网盘分享的文件:sqlite-autoconf-3460000.tar.gz
链接: https://pan.baidu.com/s/1ro8-xbsFitDSEEK6mSYzwQ?pwd=3521 提取码: 3521

直接复制命令,别手打,按顺序

1. (先下载压缩包,CtrlC+V复制到虚拟机任意路径下)在linux中解压压缩包

tar -xvf libmodbus-3.1.7.tar.gz

2. 进入源码目录

cd libmodbus-3.1.7

3.创建文件夹(存放头文件、库文件)

mkdir install

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

./configure --prefix=$PWD/install

5. 执行make

make                        //编译

6.执行make install

make install                //安装

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

1.2.库的使用

要想编译方便,可以将头文件和库文件放到系统路径下(直接复制命令,别手打,按顺序

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

sudo cp lib/* -r /lib -d

后期编译时,可以直接gcc xx.c -lmodbus(和编译有关线程代码一样)

头文件默认搜索路径:/usr/include/usr/local/include(之前文章库里的内容

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

2.函数接口

在上面的官方文档里包含所有的函数接口,以下是常用的modbus tcp函数接口,上个文章尝试自己写函数,这里就是使用这些别人写好的库函数(更方便)。

modbus_t* modbus_new_tcp(const char *ip, int port)

功能:以TCP方式创建Modbus实例,并初始化

参数:ip :ip地址

           port:端口号

返回值:成功:Modbus实例

失败:NULL

int modbus_set_slave(modbus_t *ctx, int slave)

功能:设置从机ID

参数:ctx :Modbus实例

           slave:从机ID

返回值:成功:0

               失败:-1

int modbus_connect(modbus_t *ctx)

功能:和从机(slave)建立连接

参数:ctx:Modbus实例

返回值:成功:0

               失败:-1

void modbus_free(modbus_t *ctx)

功能:释放Modbus实例

参数:ctx:Modbus实例

void modbus_close(modbus_t *ctx)

功能:关闭套接字

参数:ctx:Modbus实例

int modbus_read_bits(modbus_t *ctx, int addr, int nb, uint8_t *dest)

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

参数:ctx :Modbus实例

           addr :寄存器起始地址

           nb :寄存器个数

           dest :得到的状态值

返回值:成功:读到的数量

               失败:-1

int modbus_read_input_bits(modbus_t *ctx, int addr, int nb, uint8_t *dest)

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

参数:ctx :Modbus实例

           addr :寄存器起始地址

           nb :寄存器个数

           dest :得到的状态值

返回值:成功:返回nb的值

               失败:-1

int modbus_read_registers(modbus_t *ctx, int addr, int nb, uint16_t *dest)

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

参数:ctx :Modbus实例

           addr :寄存器起始地址

           nb :寄存器个数

           dest :得到的寄存器的值

返回值:成功:读到寄存器的个数

               失败:-1

int modbus_read_input_registers(modbus_t *ctx, int addr, int nb, uint16_t *dest)

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

参数:ctx :Modbus实例

           addr :寄存器起始地址

           nb :寄存器个数

           dest :得到的寄存器的值

返回值:成功:读到寄存器的个数

               失败:-1

int modbus_write_bit(modbus_t *ctx, int addr, int status);

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

参数:ctx :Modbus实例

           addr :线圈地址

           status:线圈状态

返回值:成功:1

               失败:-1

int modbus_write_bits(modbus_t *ctx, int addr, int nb, const uint8_t *src);

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

参数:ctx :Modbus实例

           addr :线圈地址

           nb :线圈个数

           src :多个线圈状态

返回值:成功:写入的数量

               失败:-1

int modbus_write_register(modbus_t *ctx, int addr, int value);

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

参数: ctx :Modbus实例

            addr :寄存器地址

            value :寄存器的值

返回值:成功:1

               失败:-1

int modbus_write_registers(modbus_t *ctx, int addr, int nb, const uint16_t *src);

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

参数:ctx :Modbus实例

           addr :寄存器地址

           nb :寄存器的个数

           src :多个寄存器的值

返回值:成功:写入的数量

               失败:-1

有关读取浮点的就不全举例了,感兴趣可以去查看官方文档

float modbus_get_float_dcba(const uint16_t *src)

功能:读取浮点类型的数据

参数:src:读到数据的存放数组

返回值:转换后的浮点类型

编程

从上往下写就可以连接到Modbus Slave,ip要写主机的IP地址,不要写成虚拟机的IP地址。,你要是在虚拟机运行的Modbus Slave,那就可以写虚拟机地址。

编程步骤

1.创建实例

2.设置从机ID

3.建立连接

4.寄存器操作(按需选择)

5.关闭套接字

6.释放实例

编程实现

1.基础步骤实现:
#include <modbus.h>
#include <stdio.h>

int main(int argc, char const *argv[])
{
    char ip[128] = {"192.168.50.224"};         //IP
    int port = 502;                            //端口号
    int slave = 1;                             //从机地址
    int addr = 0x0000;                         //寄存器地址
    int nb = 0x0002;                           //寄存器数
    uint16_t dest[12] = {0};                   //接收数组
    // 创建实例
    // IP与端口号可作为命令行传参
    modbus_t *modbus = modbus_new_tcp(ip, port);
    if (modbus == NULL)
    {
        perror("err\n");
        return -1;
    }
    //设置从机ID
    modbus_set_slave(modbus, slave);   
    //建立连接
    int con = modbus_connect(modbus);
    if (con == -1)
    {
        perror("con:\n");
        return -1;
    }
    //寄存器操作
    int read = modbus_read_registers(modbus, addr, nb, dest);
    if (read == -1)
    {
        perror("read:\n");
        return -1;
    }
    for (int i = 0; i < read; i++)
    {
        printf("%d ", dest[i]);
    }
    putchar(10);
    //关闭套接字
    modbus_close(modbus);
    //释放实例
    modbus_free(modbus);
    return 0;
}
2.数据采集小项目:

编程实现采集传感器数据和控制硬件设备(传感器和硬件通过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 <unistd.h>
#include <pthread.h>

void *handler1(void *arg){            //内不含阻塞,相当于后台运行
	uint8_t dest1[32] = {0};
	uint16_t dest2[32] = {0};
	modbus_t *modbusid = (modbus_t *)arg;
	int size = 0;
	while(1){								
		//读取线圈状态
		size = modbus_read_bits(modbusid,0,2,dest1);
		if(size == -1){					//容错判断
			perror("modbus_read_registers err");
			break;
		}
		printf("LED:%02x 蜂鸣器:%02x\n",dest1[0],dest1[1]);
		//查询寄存器数值
		size = modbus_read_registers(modbusid,0,2,dest2);
		if(size == -1){					//容错判断
			perror("modbus_read_registers err");
			break;
		}
		printf("温度传感器:%02x 加速度传感器:%02x\n",dest2[0],dest2[1]);
		sleep(5);                        //5秒打印一次
	}
}

void *handler2(void *arg){                    //用于执行写操作,需要输入指令
	modbus_t *modbusid = (modbus_t *)arg;
	int addr,nb,status;
	int a = 0;				        //标志操作05,06
	while(1){
		scanf("%d",&a);				//选择操作
		if(a == 5){					//操作单个线圈
			scanf("%d %d",&addr,&status);
			modbus_write_bit(modbusid,addr,status);
		}
		else if(a == 6){			//操作单个寄存器
			scanf("%d %d",&addr,&nb);
			modbus_write_register(modbusid,addr,nb);
		}
		if(a == -1)
			break;
	}
}

int main(int argc, const char *argv[])
{
	//创建实例
	modbus_t *modbusid = modbus_new_tcp("192.168.43.148",502);
	if(modbusid == NULL){
		perror("modbus_new_tcp err");
		return -1;
	}
	//设置从机ID
	int slave = 1;
	if(modbus_set_slave(modbusid,slave) == -1){
		perror("modbus_set_slave err");
		return -1;
	}
	//建立连接
	if(modbus_connect(modbusid) == -1){
		perror("modbus_connect err");
		return -1;
	}
	//寄存器操作
	pthread_t ptid1;
	pthread_create(&ptid1,NULL,handler1,modbusid);        //创建第一个线程
	pthread_detach(ptid1);
	pthread_t ptid2;
	pthread_create(&ptid2,NULL,handler2,modbusid);        //创建第二个线程
	pthread_join(ptid2,NULL);

	//关闭套接字
	modbus_close(modbusid);
	//释放实例
	modbus_free(modbusid);
	return 0;
}

五秒打印一次,终端还可以输入命令取改变寄存器和线圈值

 

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

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

相关文章

UE5 学习系列(二)用户操作界面及介绍

这篇博客是 UE5 学习系列博客的第二篇&#xff0c;在第一篇的基础上展开这篇内容。博客参考的 B 站视频资料和第一篇的链接如下&#xff1a; 【Note】&#xff1a;如果你已经完成安装等操作&#xff0c;可以只执行第一篇博客中 2. 新建一个空白游戏项目 章节操作&#xff0c;重…

IDEA运行Tomcat出现乱码问题解决汇总

最近正值期末周&#xff0c;有很多同学在写期末Java web作业时&#xff0c;运行tomcat出现乱码问题&#xff0c;经过多次解决与研究&#xff0c;我做了如下整理&#xff1a; 原因&#xff1a; IDEA本身编码与tomcat的编码与Windows编码不同导致&#xff0c;Windows 系统控制台…

利用最小二乘法找圆心和半径

#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …

使用docker在3台服务器上搭建基于redis 6.x的一主两从三台均是哨兵模式

一、环境及版本说明 如果服务器已经安装了docker,则忽略此步骤,如果没有安装,则可以按照一下方式安装: 1. 在线安装(有互联网环境): 请看我这篇文章 传送阵>> 点我查看 2. 离线安装(内网环境):请看我这篇文章 传送阵>> 点我查看 说明&#xff1a;假设每台服务器已…

XML Group端口详解

在XML数据映射过程中&#xff0c;经常需要对数据进行分组聚合操作。例如&#xff0c;当处理包含多个物料明细的XML文件时&#xff0c;可能需要将相同物料号的明细归为一组&#xff0c;或对相同物料号的数量进行求和计算。传统实现方式通常需要编写脚本代码&#xff0c;增加了开…

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造&#xff0c;完美适配AGV和无人叉车。同时&#xff0c;集成以太网与语音合成技术&#xff0c;为各类高级系统&#xff08;如MES、调度系统、库位管理、立库等&#xff09;提供高效便捷的语音交互体验。 L…

(LeetCode 每日一题) 3442. 奇偶频次间的最大差值 I (哈希、字符串)

题目&#xff1a;3442. 奇偶频次间的最大差值 I 思路 &#xff1a;哈希&#xff0c;时间复杂度0(n)。 用哈希表来记录每个字符串中字符的分布情况&#xff0c;哈希表这里用数组即可实现。 C版本&#xff1a; class Solution { public:int maxDifference(string s) {int a[26]…

【大模型RAG】拍照搜题技术架构速览:三层管道、两级检索、兜底大模型

摘要 拍照搜题系统采用“三层管道&#xff08;多模态 OCR → 语义检索 → 答案渲染&#xff09;、两级检索&#xff08;倒排 BM25 向量 HNSW&#xff09;并以大语言模型兜底”的整体框架&#xff1a; 多模态 OCR 层 将题目图片经过超分、去噪、倾斜校正后&#xff0c;分别用…

【Axure高保真原型】引导弹窗

今天和大家中分享引导弹窗的原型模板&#xff0c;载入页面后&#xff0c;会显示引导弹窗&#xff0c;适用于引导用户使用页面&#xff0c;点击完成后&#xff0c;会显示下一个引导弹窗&#xff0c;直至最后一个引导弹窗完成后进入首页。具体效果可以点击下方视频观看或打开下方…

接口测试中缓存处理策略

在接口测试中&#xff0c;缓存处理策略是一个关键环节&#xff0c;直接影响测试结果的准确性和可靠性。合理的缓存处理策略能够确保测试环境的一致性&#xff0c;避免因缓存数据导致的测试偏差。以下是接口测试中常见的缓存处理策略及其详细说明&#xff1a; 一、缓存处理的核…

龙虎榜——20250610

上证指数放量收阴线&#xff0c;个股多数下跌&#xff0c;盘中受消息影响大幅波动。 深证指数放量收阴线形成顶分型&#xff0c;指数短线有调整的需求&#xff0c;大概需要一两天。 2025年6月10日龙虎榜行业方向分析 1. 金融科技 代表标的&#xff1a;御银股份、雄帝科技 驱动…

观成科技:隐蔽隧道工具Ligolo-ng加密流量分析

1.工具介绍 Ligolo-ng是一款由go编写的高效隧道工具&#xff0c;该工具基于TUN接口实现其功能&#xff0c;利用反向TCP/TLS连接建立一条隐蔽的通信信道&#xff0c;支持使用Let’s Encrypt自动生成证书。Ligolo-ng的通信隐蔽性体现在其支持多种连接方式&#xff0c;适应复杂网…

铭豹扩展坞 USB转网口 突然无法识别解决方法

当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…

未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?

编辑&#xff1a;陈萍萍的公主一点人工一点智能 未来机器人的大脑&#xff1a;如何用神经网络模拟器实现更智能的决策&#xff1f;RWM通过双自回归机制有效解决了复合误差、部分可观测性和随机动力学等关键挑战&#xff0c;在不依赖领域特定归纳偏见的条件下实现了卓越的预测准…

Linux应用开发之网络套接字编程(实例篇)

服务端与客户端单连接 服务端代码 #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <pthread.h> …

华为云AI开发平台ModelArts

华为云ModelArts&#xff1a;重塑AI开发流程的“智能引擎”与“创新加速器”&#xff01; 在人工智能浪潮席卷全球的2025年&#xff0c;企业拥抱AI的意愿空前高涨&#xff0c;但技术门槛高、流程复杂、资源投入巨大的现实&#xff0c;却让许多创新构想止步于实验室。数据科学家…

深度学习在微纳光子学中的应用

深度学习在微纳光子学中的主要应用方向 深度学习与微纳光子学的结合主要集中在以下几个方向&#xff1a; 逆向设计 通过神经网络快速预测微纳结构的光学响应&#xff0c;替代传统耗时的数值模拟方法。例如设计超表面、光子晶体等结构。 特征提取与优化 从复杂的光学数据中自…

网络六边形受到攻击

大家读完觉得有帮助记得关注和点赞&#xff01;&#xff01;&#xff01; 抽象 现代智能交通系统 &#xff08;ITS&#xff09; 的一个关键要求是能够以安全、可靠和匿名的方式从互联车辆和移动设备收集地理参考数据。Nexagon 协议建立在 IETF 定位器/ID 分离协议 &#xff08;…

AI-调查研究-01-正念冥想有用吗?对健康的影响及科学指南

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; &#x1f680; AI篇持续更新中&#xff01;&#xff08;长期更新&#xff09; 目前2025年06月05日更新到&#xff1a; AI炼丹日志-28 - Aud…

第19节 Node.js Express 框架

Express 是一个为Node.js设计的web开发框架&#xff0c;它基于nodejs平台。 Express 简介 Express是一个简洁而灵活的node.js Web应用框架, 提供了一系列强大特性帮助你创建各种Web应用&#xff0c;和丰富的HTTP工具。 使用Express可以快速地搭建一个完整功能的网站。 Expre…