当前位置: 首页 > web >正文

网络编程(Modbus进阶)

思维导图

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"};         //IPint 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;}//设置从机IDmodbus_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,06while(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;}//设置从机IDint 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.xdnf.cn/news/13351.html

相关文章:

  • Manus 框架与 COKE 框架解析及完整 Demo
  • Unreal从入门到精通之使用 CheatManager 自定义控制台命令
  • 操作系统的一些名词
  • 期末考试复习总结-第一章《HarmonyOS介绍》
  • ​计算机网络原理超详解说​
  • 2025-03-14-Google检索技巧
  • 华为云Flexus+DeepSeek征文 | 基于ModelArts Studio、DeepSeek大模型和Dify搭建网站智能客服助手
  • 深度学习——简介
  • Ubuntu下挂载NTFS格式磁盘
  • 访问服务器项目,服务器可以ping通,但是端口访问不到
  • C++ mutex 锁的使用
  • JavaScript BOM 详细介绍
  • 重温经典算法——二分查找
  • 借助AI识别测试盲区:从需求文档中挖掘遗漏场景
  • CVPR 2025 MIMO: 支持视觉指代和像素grounding 的医学视觉语言模型
  • 深度学习:概念、特点和发展史
  • Admin.Net中的消息通信SignalR解释
  • 基于OpenCV的风格迁移:图像金字塔方法
  • jupyterhub的浅浅使用-重点在解决无法登录
  • GD32-开发工程搭建
  • 超短脉冲激光自聚焦效应
  • 人脸识别技术应用备案找不找第三方
  • CppCon 2015 学习:Practical Move Semantics
  • SpringBoot+Vue+MySQL全栈开发实战:前后端接口对接与数据存储详解
  • 【算法篇】逐步理解动态规划模型5(子序列问题)
  • 隐藏wordpress后台登陆地址 让wordpress网站更安全
  • 【VBA】使用脚本把doc/docx转换为pdf格式
  • 消息消费类型和具体实现
  • nsswitch.conf配置文件内容解析
  • 生产安全与设备管理如何分清界限?如何正确用设备管理系统?