探索:Modbus TCP协议(应用层)

news2024/11/25 15:21:03

目录

一,Modbus起源

ModbusTCP协议格式

1,报文头

2,寄存器

3,功能码

练习:

工具软件使用

三方库的使用

【1】库的安装

1.库的安装配置

2.库的使用

【2】函数接口

【3】编程流程

代码实战


一,Modbus起源

  1. 起源:

     Modbus由Modicon公司于1979年开发,是一种工业现场总线协议标准。

     Modbus通信协议具有多个变种,其中有支持串口,以太网多个版本,其中最著名的是Modbus RTU、Modbus ASCII和Modbus TCP三种

     其中Modbus TCP是在施耐德收购Modicon后1997年发布的。

  1. 分类:

1)Modbus RTU:运行在串口上的协议,采用二进制表示形式以及紧凑的数据结构,通信效率较高,应用比较广泛

2)Modbus ASCII:运行在串口上的协议,采用ASCII码传输,利用特殊字符作为字节开始和结束的标志,传输效率较低,只有在传输数据量较少的时候才会考虑它

3)Modbus TCP:运行在以太网上的协议

  1. 优势:

     免费、简单、容易使用

  1. 应用场景:

Modbus协议是现在国内工业领域应用最多的协议,不只PLC设备,各种终端设备,比如水控机、水表、电表、工业秤、各种采集设备

  1. Modbus TCP特点

1)采用主从问答方式进行通信

2)Modbus Tcp是应用层协议,基于传输层TCP协议实现

3)Modbus Tcp端口号默认502

ModbusTCP协议格式

ModbusTcp协议包含三部分:报文头、功能码、数据

Modbus TCP/IP协议最大数据帧长度为260字节

1,报文头

共7字节,分别是:

2,寄存器

包含四种寄存器,分别是线圈、离散量输入、保持寄存器、输入寄存器

  1. 离散量和线圈其实就是位寄存器(每个寄存器数据占1字节),工业上主要用于控制IO设备。

线圈寄存器,类比为开关量,每一个bit都对应一个信号的开关状态。所以一个byte就可以同时控制8路的信号。比如控制外部8路io的高低。 线圈寄存器支持读也支持写,写在功能码里面又分为写单个线圈寄存器和写多个线圈寄存器。

对应上面的功能码也就是:0x01  0x05  0x0f

离散输入寄存器,离散输入寄存器就相当于线圈寄存器的只读模式,他也是每个bit表示一个开关量,而他的开关量只能读取输入的开关信号,是不能够写的。比如我读取外部按键的按下还是松开。

所以功能码也简单就一个读的 0x02

  1. 输入和保持寄存器是字寄存器(每个寄存器数据占2个字节),工业上主要用于存储工业设备的值。

保持寄存器,这个寄存器的单位不再是bit而是两个byte,也就是可以存放具体的数据量的,并且是可读写的。比如我我设置时间年月日,不但可以写也可以读出来现在的时间。写也分为单个写和多个写

所以功能码有对应的三个:0x03 0x06 0x10

输入寄存器,这个和保持寄存器类似,但是也是只支持读而不能写。一个寄存器也是占据两个byte的空间。类比我我通过读取输入寄存器获取现在的AD采集值

对应的功能码也就一个 0x04

3,功能码

根据四种不同的寄存器设置了8种功能码

练习:

读传感器数据,读1个寄存器数据,写出主从数据收发协议。

主机给从机:

|-事务处理标识符|-协议类型-|-字节长度-|-从机ID-|-功能码-|-起始地址-|-寄存器个数-|

0x0000 0000 0006 01 03 0000 0001

从机给主机:

|-事务处理标识符|-协议类型-|-字节长度-|-从机ID-|-功能码-|-数据长度-|--数据--|

0x0000 0000 0005 01 03 02  0102

练习:写出控制IO设备开关的协议数据,操作1个线圈。

主机给从机:

|----------MBAP报文头----------|-功能码-|-起始地址-|-断通标志-|

0x0000  0x0000 0x0006 0x11   0x05     0x000b     0xFF00

从机回主机:

|----------MBAP报文头----------|-功能码-|-起始地址-|-断通标志-|

0x0000  0x0000 0x0006 0x11   0x05     0x000b     0xFF00

 

工具软件使用

  1. ModbusSlave/Poll
  1. 软件默认安装
  2. 破解

点击connection->connect,输入序列号即可

3)使用

先设置后连接

后连接(连接时注意先开启slave端(相当于服务器),后起poll端(相当于客户端))

查询主机ip:win + r 然后输入cmd 然后输入ipconfig   即可查询主机ip

  1. 网络调试助手

  1. Wireshark使用

安装使用wireshark时注意把杀毒软件和防火墙关闭

捕获器选择:

windows如果连接有线网络,选择本地连接/以太网

     如果连接无线网络,选择WLAN

如果只是在本机上的通信,选择NPCAP Loopback apdater

或Adapter for loopback traffic capture

过滤条件:

过滤端口:tcp.port == 502

过滤IP:ip.addr == 192.168.1.156(自己的ip地址)

 

三方库的使用

【1】库的安装

1.库的安装配置

1. 在linux中解压压缩包

将库压缩包复制到linux下,进行解压

tar -xvf libmodbus-3.1.7.tar.gz

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

cd libmodbus-3.1.7

mkdir install

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

./configure --prefix=$PWD/install

  1. 执行make和make install

make//编译

make install//安装

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

2.库的使用

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

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

【2】函数接口

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 :得到的状态值
int  modbus_read_input_bits(modbus_t *ctx, int addr, int nb, uint8_t *dest)
功能:读取输入状态,可读取多个连续输入的状态(对应功能码为0x02)
参数:
    ctx   :Modbus实例
    addr :寄存器起始地址
    nb   :寄存器个数
    dest :得到的状态值
返回值:成功:返回nb的值
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:线圈状态
返回值:成功:0
      失败:-1
int  modbus_write_bits(modbus_t *ctx, int addr, int nb, const uint8_t *src);
功能:写入多个连续线圈的状态(对应功能码为15)
参数:
    ctx     :Modbus实例
    addr  :线圈地址
    nb     :线圈个数
    src    :多个线圈状态
返回值:成功:0
      失败:-1
int  modbus_write_register(modbus_t *ctx, int addr, int value);
功能:  写入单个寄存器(对应功能码为0x06)
参数: 
    ctx    :Modbus实例
    addr  :寄存器地址
    value :寄存器的值 
返回值:成功:0
       失败:-1
int  modbus_write_registers(modbus_t *ctx, int addr, int nb, const uint16_t *src);
功能:写入多个连续寄存器(对应功能码为16)
参数:
    ctx    :Modbus实例
    addr  :寄存器地址
    nb     :寄存器的个数
    src    :多个寄存器的值 
返回值:成功:0
      失败:-1

【3】编程流程

  1. 创建实例

modbus_new_tcp

  1. 设置从机ID

modbus_set_slave

  1. 和从机进行连接

modbus_connect

  1. 寄存器操作

功能码对应的函数

  1. 关闭套接字

modbus_close

  1. 释放实例

modbus_free

代码实战

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

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

modbus_t *ctx;
uint16_t modbout_dest[128] = {};
void *mythread(void *arg)
{
    while (1)
        {
            modbus_read_registers(ctx, 0, 4, modbout_dest);
            for (int i = 0; i < 4; i++)
                {
                    if (i == 0)
                    {
                        printf("光线传感器:");
                        printf("%#x\n", modbout_dest[i]);
                    }
                    else if (i == 1)
                    {
                        printf("加速度传感器x:%#x\n", modbout_dest[i]);
                    }
                    else if (i == 2)
                    {
                        printf("加速度传感器y:%#x\n", modbout_dest[i]);
                    }
                    else if (i == 3)
                    {
                        printf("加速度传感器z:%#x\n", modbout_dest[i]);
                    }
                    putchar(10);
                    sleep(1);
                }
        }

    pthread_exit(NULL);
}

int main(int argc, char const *argv[])
{

    // if (argc != 3) //从终端输入ip和端口
    // {
    //     printf("please input %s <ip,port>\n", argv[0]);
    //     return -1;
    // }
    //1,创建实例
    //modbus_t *ctx = modbus_new_tcp(argv[1], atoi(argv[2]));
    modbus_t *ctx = modbus_new_tcp("192.168.196.1", 502);
    //2,设置从机ID
    modbus_set_slave(ctx, 1);
    //3,和主机进行连接
    modbus_connect(ctx);
    pthread_t tid;
    if (pthread_create(&tid, NULL, mythread, NULL) != 0)
    {
        perror("pthread_create err");
        return -1;
    }
    pthread_detach(tid);

    //从终端输入
    printf("o1:led灯打开***00:led灯关闭***11:蜂鸣器开***10:蜂鸣器关");
    printf("请从终端输入指令控制硬件设备");

    while (1)
        {

            int a, b;
            while (1)
                {
                    scanf("%d", &a);
                    scanf("%d", &b);
                    if (a == 0 && b == 1)
                    {
                        printf("led灯打开");
                    }
                    else if (a == 0 && b == 0)
                    {
                        printf("led灯关闭");
                    }
                    else if (a == 1 && b == 1)
                    {
                        printf("蜂鸣器开");
                    }
                    else if (a == 1 && b == 0)
                    {
                        printf("蜂鸣器关");
                    }
                    modbus_write_bit(ctx, a, b);
                }
        }
    //6,释放Modbus实例
    modbus_free(ctx);
    //7,关闭套接字
    modbus_close(ctx);

    return 0;
}

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

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

相关文章

【数据分享】1901-2022年1km分辨率逐月平均气温栅格数据(全国/分省/免费获取)

气温数据是我们最常用的气象指标之一&#xff0c;之前我们给大家分享过1950-2022年0.1 x 0.1精度的逐月平均气温栅格数据和逐年平均气温栅格数据&#xff08;均可查看之前的文章获悉详情&#xff09;&#xff01; 本次我们分享的是精度更高的气温栅格数据——1901-2022年1km分…

【数据分享】全国地级市1999—2020年工业企业数(Shp/Excel格式)

在之前的文章中&#xff0c;我们分享过基于2000-2022年《中国城市统计年鉴》整理的1999-2021年地级市的人口相关数据、各类用地面积数据、污染物排放和环境治理相关数据、房地产投资情况和商品房销售面积、社会消费品零售总额和年末金融机构存贷款余额&#xff08;可查看之前的…

[Golang] Viper原理以及详细使用案例

文章目录 什么是 Viper&#xff1f;基础配置引入依赖&#xff1a;动态监听原理分析&#xff1a;监听原理分析 Config.yaml文件配置Viper文件配置 什么是 Viper&#xff1f; 介绍&#xff1a;用于处理配置文件中解析和读取配置文件 优点&#xff1a;支持多种配置格式&#xff0…

办公软件巨头CCED、WPS迎来新挑战,新款办公软件已形成普及之势

办公软件巨头CCED、WPS的成长经历 众所周知&#xff0c;CCED和WPS是中国办公软件行业的两大知名品牌。 但它们的成长经历不是一蹴而就的&#xff0c;都是经历了漫长的发展过程的。 CCED是中国大陆早期的一款文本编辑器&#xff0c;它在上个世纪80年代末和90年代初非常流行。 …

Flutter Widget Life Cycle 组件生命周期

Flutter Widget Life Cycle 组件生命周期 视频 前言 了解 widget 生命周期&#xff0c;对我们开发组件还是很重要的。 今天会把无状态、有状态组件的几个生命周期函数一起过下。 原文 https://ducafecat.com/blog/flutter-widget-life-cycle 参考 https://api.flutter.dev/f…

低代码和零代码有哪些区别?

低代码开发的概念 低代码开发是一种新兴的软件开发方法&#xff0c;其核心是通过使用图形用户界面和可视化建模工具&#xff0c;来减少编写代码的工作量和技能要求。低代码开发平台通常提供了丰富的预定义组件和模板&#xff0c;可以帮助开发人员快速构建应用程序。开发人员只…

【雕爷学编程】Arduino动手做(173)---SG90舵机双轴云台模块

37款传感器与执行器的提法&#xff0c;在网络上广泛流传&#xff0c;其实Arduino能够兼容的传感器模块肯定是不止这37种的。鉴于本人手头积累了一些传感器和执行器模块&#xff0c;依照实践出真知&#xff08;一定要动手做&#xff09;的理念&#xff0c;以学习和交流为目的&am…

新思的DW ECC计算模块

参考文件&#xff1a;synopsys学习资料-dw_ecc.pdf 设计的模块&#xff1a;

一些需要用到的网址

https://npm.taobao.org/mirrors/chromedriver/ (谷歌浏览器镜像网址) 欢迎使用Markdown编辑器 你好&#xff01; 这是你第一次使用 Markdown编辑器 所展示的欢迎页。如果你想学习如何使用Markdown编辑器, 可以仔细阅读这篇文章&#xff0c;了解一下Markdown的基本语法知识。…

Neo4j 集群和负载均衡

Neo4j 集群和负载均衡 Neo4j是当前最流行的开源图DB。刚好读到了Neo4j的集群和负载均衡策略&#xff0c;记录一下。 1 集群 Neo4j 集群使用主从复制实现高可用性和水平读扩展。 1.1 复制 集群的写入都通过主节点协调完成的&#xff0c;数据先写入主机&#xff0c;再同步到…

word中将合并后的多行拆分为原先的行数

word中将已经合并的多行拆分为原先的行数&#xff0c;我们不用刻意去数应该是多少行&#xff0c; 只需将拆分的行数不断增加&#xff0c;word会默认最大增加到合并前的行数。

redis 第二章

目录 1.持久化 2.主从复制 3.总结 1.持久化 通过 aof 和 rdb 将内存里的数据放到磁盘中 aof: rdb: 2.主从复制 将一台 redis 服务器的数据&#xff0c;复制到其他的 redis 服务器 3.总结 主从复制是高可用 redis 的基础&#xff0c;哨兵和集群都是在主从复制基础上实现高可…

UE UMG补充

Widget Switcher&#xff08;UI选择&#xff09; 通过该组件可以在同一个地方显示不同UI 可以通过蓝图改变显示的UI 在UI界面直接设置 ProgressBar(进度条) 可以用动画实现增长&#xff0c;更加自然 Animation 有绑定功能

具身智能controller---RT-1(Robotics Transformer)(上---方法介绍)

具身智能controller---RT-1&#xff08;Robotics Transformer&#xff09;&#xff08;上---方法介绍&#xff09; 相关链接摘要和简介相关工作与预备知识系统概述模型 RT-1: ROBOTICS TRANSFORMER模型 相关链接 github链接 主页链接&#xff08;包括论文和训练数据集&#xf…

fpga开发——蜂鸣器

蜂鸣器的原理 有源蜂鸣器和无源蜂鸣器 无源蜂鸣器利用电磁感应现象&#xff0c;为音圈接入交变电流后形成的电磁铁与永磁铁相吸或相斥而推动振膜发声&#xff0c;接入直流电只能持续推动振膜而无法产生声音&#xff0c;只能在接通或断开时产生声音。无源蜂鸣器的工作原理与扬声…

LeetCode152.Maximum-Product-Subarray<乘积最大的子数组>

题目&#xff1a; 思路&#xff1a; 一开始是使用的每次乘积的最大值. 遍历. 然后因为有负数所以还是差点.看了答案,发现还需要保存一个负数的值.当当前的值是负数的时候 互换位置.然后获得当前最大值. 代码是&#xff1a; //codeclass Solution { public:int maxProduct(vec…

哈希表与布隆过滤器

文章目录 一、哈希函数是什么&#xff1f;哈希函数的应用场景哈希函数的构造方法 二、哈希表哈希表的底层设计题型 三、布隆过滤器布隆过滤器优点布隆过滤器缺陷布隆过滤器使用场景 一致性哈希算法.位图 3. 海量数据面试题 一、哈希函数是什么&#xff1f; 1 : 哈希函数没有随…

二.安装helm

1.什么是helm Kubernetes 包管理器 Helm 是查找、分享和使用软件构件 Kubernetes 的最优方式。 Helm 管理名为 chart 的 Kubernetes 包的工具。Helm 可以做以下的事情&#xff1a; 从头开始创建新的 chart将 chart 打包成归档(tgz)文件与存储 chart 的仓库进行交互在现有的 K…

【数据结构】_4.List接口实现类LinkedList与链表

目录 1.链表的结构与特点 1.1 链表的结构&#xff1a; 1.2 链表的特点&#xff1a; 2. 不带头单链表的模拟实现 3. 单链表OJ 3.1 题目1&#xff1a;移除链表元素: 3.2 题目2&#xff1a;反转一个单链表 3.3 题目3&#xff1a;返回链表的中间结点 3.4 题目4&#xff1…