modbus协议及modbus TCP协议

一、Modbus协议
  • 1.起源

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

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

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

2.分类

1)Modbus RTU

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

2)Modbus ASCII

运行在串口上的协议,采用ASCII码进行传输,并且每个字节的开始和结束都有特殊字符作为标志,传输效率远远低于Modbus RTU,一般只有通讯量比较少时才会考虑它。

3)Modbus TCP

运行在以太网上的协议

3.优势:

免费、简单、容易使用

4.应用场景:

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

二、Modbus tcp协议

1.特点

1)采用主从问答式通信

(一个主机可以对应多个从机,可以发出请求采集数据,发出请求控制设备)

2)Modbus TCP是应用层协议,基于传输层的TCP进行通信的

3)Modbus TCP端口号默认502

2.组成

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

报文头7个字节,功能码1个字节,Modbus TCP/IP协议最大数据帧长度为260字节,最大252,(1 byte = 8 bit)

1)报文头

包含一个7字节报文头

事物处理标识符:没有限制,主机按照啥发,从机按照什么回

协议标识符:为啥是4个0?写一个0行不行?每一位都是16进制数 一位十六进制由4位2进制组成,8位2进制为一个字节

长度:

单元标识符:从机id

注意:报文头包含内容个数顺序均不可调换

2)寄存器

Modbus TCP通过寄存器的方式存储数据。

一共有四种类型的寄存器,分别是:离散量输入、线圈、输入寄存器、保持寄存器

离散量和线圈其实就是位寄存器(每个寄存器数据占1字节),工业上主要用于控制IO设备输入和保持寄存器是字寄存器(每个寄存器数据占2个字节),工业上主要用于存储工业设备的值

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

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

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

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

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

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

3)功能码(16进制)

寄存器PLC地址和寄存器的对应关系:

00001-09999 :线圈

10001-19999:离散量输入

30001-39999:输入寄存器

40001-49999:保持寄存器

点亮一个灯:05

读温湿度:04 03

具体协议分析:

实例分享 | ModbusTCP报文详解

4)总结

读数据:

主机--》从机:

报文头--功能码--起始地址--数量

从机--》主机:

报文头(长度可能发生变化)--功能码(不变)--字节计数--数据

在读数据和写单个的时候,字节长度都是0x06( 一个字节的单元标识符,一个字节的功能码,两个字节的地址,两个字节的数据)

写数据:

写单个:

主机--》从机:

报文头--功能码--地址--数据/断通标志(保持寄存器:数据:线圈:断通标志)

从机--》主机:

原文返回

写多个:

主机--》从机:

报文头----功能码----起始地址----数量----字节计数----数据

从机--》主机:  

原文返回

5)练习

练习一

主机询问数据流

00 03 00 00 00 06 01 03 00 63 00 02

00 00:事务处理标识符 

00 00:协议标识符

00 06 :字节 长度

03:功能码(保持寄存器)

0063:起始地址:6*16+3=99,40100

0002:寄存器的数量  40100-40101

00 03 00 00 00 07 01 03 04 05 83 00 76

00 00:事务处理标识符

00 00:协议标识符

00 07:字节长度

03:功能码

04:字节计数

0583:40100寄存器的数据

0076:40101寄存器的数据

练习2

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

00 00 00 00  00 06 01 03 00 60 00 01

00 00 00 00  00 05 01 03 02 00 62

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

线圈单个置零:0000 置一:FF00

00 00 00 00  00 06 01 05 00 60 FF 00

00 00 00 00  00 06 01 05 00 60 FF 00

  • 工具安装
    1. Modbus Salve/Poll

1)安装过程:软件默认安装

2)破解:点击connection-》connect,输入序列号(序列号在SN.txt)

3)使用

从机:

先设置,先打开salve端,

再连接:点击connection-》connect

主机:

    1. 网络调试助手

    1. Wireshark

捕获器选择:

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

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

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

或Adapter for loopback traffic capture

过滤条件:

过滤端口:tcp.port==502

过滤IP:ip.addr == 192.168.3.11(windows 的ip)

  1. 练习

在虚拟机写程序实现poll端功能,编写客户端实现和Slave通信。

发送:发送Modbus TCP协议   03功能码

发送:uint8_t data[12]={0x00,0x00,0x00,0x00,0x00,0x06,0x01,0x03,0x00,0x00,0x00,0x02};

接收:recv

#include <stdio.h>
#include <stdlib.h> // atoi
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <unistd.h>int main(int argc, char const *argv[])
{uint8_t data[12] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x06,0x01, 0x03, 0x00, 0x00, 0x00, 0x03};uint8_t buf[32] = {0};//1.创建套接字int sockfd = socket(AF_INET, SOCK_STREAM, 0);if (sockfd < 0){perror("socket err");return -1;}//2.填充结构体struct sockaddr_in caddr;
    caddr.sin_family = AF_INET;
    caddr.sin_port = htons(502);
    caddr.sin_addr.s_addr = inet_addr(argv[1]);//3.链接if (connect(sockfd, (struct sockaddr *)&caddr, sizeof(caddr)) < 0){perror("connect err");return -1;}//4.发送send(sockfd, data, sizeof(data), 0);//5.接收recv(sockfd, buf, sizeof(buf), 0);for (int i = 0; i < buf[8]; i++)printf("%#x ", buf[9+i]);putchar(10);//6.关闭close(sockfd);return 0;
}

作业:

  1. 复习今天内容
  2. 完成05功能码  ,写一个线圈
  3. 封装函数。。

/*客户端创建代码 */
#include <stdio.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h> /* superset of previous */
#include <arpa/inet.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
int sockfd;
uint8_t buf[32];
void writecoil(uint8_t *p, int addr, int num)
{
    p[7] = 0x05;
    p[8] = addr >> 8;   //寄存器高位
    p[9] = addr & 0xff; //寄存器低位
    if (num == 1)
    {
        p[10] = 0xff; //置1
    }
    else if (num == 0)
    {
        p[10] = 0x00; //置0
    }
    p[11] = 0x00;
    for (int j = 0; j < 12; j++)
    {
        printf("%#x ", *(p+j));
    }
    send(sockfd, p, 12, 0);
    recv(sockfd, buf, sizeof(buf), 0);
    for (int i = 0; i < 12; i++)
    {
        printf("%#x ", buf[i]);
    }
    putchar(10);
}
void readregster(uint8_t *p, int addr, int num)
{
    p[7] = 0x03;
    p[8] = addr >> 8;   //寄存器高位
    p[9] = addr & 0xff; //寄存器低位
    p[10] = num >> 8;   //数量高位
    p[11] = num & 0xff; //数量低位
    for (int j = 0; j < 12; j++)
    {
        printf("%#x ", *(p + j));
    }
    putchar(10);
    send(sockfd, p, 12, 0);
    recv(sockfd, buf, sizeof(buf), 0);    for (int i = 0; i < buf[8]; i++)
    {
        printf("%#x ", buf[9 + i]);
    }
    putchar(10);
}
void set_selve_id(uint8_t *p)
{
    p[6] = 0x01;
}
int main(int argc, char const *argv[])
{
    if (argc < 3)
    {
        printf("input err\n");
        return -1;
    }
    //创建套接字
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd < 0)
    {
        perror("socket err.");
        return -1;
    }
    //填充结构体
    struct sockaddr_in caddr;
    caddr.sin_family = AF_INET;
    caddr.sin_port = htons(atoi(argv[2]));
    caddr.sin_addr.s_addr = inet_addr(argv[1]);
    socklen_t len = sizeof(caddr);
    //链接
    if (connect(sockfd, (struct sockaddr *)&caddr, len) < 0)
    {
        perror("connect err.");
        return -1;
    }
    // uint8_t data[12] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x01, 0x01, 0x00, 0x07, 0xFF, 0x00};
    // uint8_t data1[12] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x01, 0x01, 0x00, 0x00, 0x00, 0x03};
    uint8_t data[12] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x06};
    int th = sizeof(data);
    int addr = 0x0101;
    int num = 0x0000;
    set_selve_id(data);
    // readregster(data, 0, 2);
    writecoil(data, 6, 1);
    return 0;
}

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

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

相关文章

springboot前后端分离项目配置https接口(ssl证书)

文章目录 说明vue.js前端部署vue.js项目axios请求配置本地创建日志文件创建Dockerfile文件配置ssl证书nginx.confvue项目打包上传创建容器部署 后端springboot项目部署配置ssl证书打包部署 补充&#xff1a;jsk证书和pfx证书补充&#xff1a;两种证书的转化JKS转PFXPFX 转 JKS …

Elasticsearch:将最大内积引入 Lucene

作者&#xff1a;Benjamin Trent 目前&#xff0c;Lucene 限制 dot_product (点积) 只能在标准化向量上使用。 归一化迫使所有向量幅度等于一。 虽然在许多情况下这是可以接受的&#xff0c;但它可能会导致某些数据集的相关性问题。 一个典型的例子是 Cohere 构建的嵌入&#x…

使用 Lhotse 高效管理音频数据集

Lhotse 是一个旨在使语音和音频数据准备更具灵活性和可访问性的 Python 库&#xff0c;它与 k2 一起&#xff0c;构成了下一代 Kaldi 语音处理库的一部分。 主要目标&#xff1a; 1. 以 Python 为中心的设计吸引更广泛的社区参与语音处理任务。 2. 为有经验的 Kaldi 用户提供…

SpringBoot——启动类的原理

优质博文&#xff1a;IT-BLOG-CN SpringBoot启动类上使用SpringBootApplication注解&#xff0c;该注解是一个组合注解&#xff0c;包含多个其它注解。和类定义SpringApplication.run要揭开SpringBoot的神秘面纱&#xff0c;我们要从这两位开始就可以了。 SpringBootApplicati…

Spring实例化对象

默认proxyBeanMethods true&#xff0c;这种方法是用的代理模式创建对象&#xff0c;每次创建都是同一个对象&#xff0c;如果改为false每次都是不同的对象 FactoryBean的使用 定义的类A&#xff0c;造出来一个类B&#xff0c;可以在创造bean之前做一些自己的个性化操作

MFS分布式文件系统

目录 集群部署 Master Servers ​Chunkservers ​编辑Clients Storage Classes LABEL mfs高可用 pacemaker高可用 ​编辑ISCSI 添加集群资源 主机 ip 角色 server1 192.168.81.11 Master Servers server2 192.168.81.12 Chunkservers server3 192.168.81.13 Chunkserver…

【产品安全平台】上海道宁与Cybellum将整个产品安全工作流程整合到一个专用平台中,保持构建的互联产品的网络安全和网络合规性

Cybellum将 整个产品安全工作流程 整合到一个专用平台中 使设备制造商能够 保持他们构建的互联产品的 网络安全和网络合规性 产品安全性对 每个人来说都不一样 每个行业的系统、工作流程和 法规都存在根本差异 因此&#xff0c;Cybellum量身定制了 Cybellum的平台和技…

为何内存不够用?微服务改造启动多个Spring Boot的陷阱与解决方案

在生产环境中我们会遇到一些问题&#xff0c;此文主要记录并复盘一下当时项目中的实际问题及解决过程。 背景简述 最初系统上线后都比较正常风平浪静的。在系统运行了一段时间后&#xff0c;业务量上升后&#xff0c;生产上发现java应用内存占用过高&#xff0c;服务器总共64…

爱创科技总裁谢朝晖荣获“推动医药健康产业高质量发展人物”

中国医药市场规模已经成为全球第二大医药市场&#xff0c;仅次于美国。近年来&#xff0c;随着中国经济的持续增长和人民生活水平的提高&#xff0c;医药市场需求不断扩大。政府对医疗卫生事业的投入也在不断加大&#xff0c;为医药行业的发展创造了良好的政策环境。为推动医药…

结构体与指针_sizeof_static_extern_函数指针数组_函数指针_回调函数

一、结构体与指针 #include <stdint.h> #include <stdlib.h> #include <stdio.h> #define up_to_down(uuu) (downdemo_t *)(uuu->beg) #define __plc__ typedef struct updemo_s{uint8_t *head;uint8_t *beg;uint8_t *end; }updemo_t; typedef struct do…

陪玩圈子系统APP小程序H5,详细介绍,源码交付,支持二开!

陪玩圈子系统&#xff0c;页面展示&#xff0c;源码交付&#xff0c;支持二开&#xff01; 陪玩后端下载地址&#xff1a;电竞开黑陪玩系统小程序&#xff0c;APP&#xff0c;H5: 本系统是集齐开黑&#xff0c;陪玩&#xff0c;陪聊于一体的专业APP&#xff0c;小程序&#xff…

Linux进程通信——共享内存

概念 共享内存&#xff08;Shared Memory&#xff09;&#xff0c;指两个或多个进程共享一个给定的存储区。 特点 共享内存是最快的一种 IPC&#xff0c;因为进程是直接对内存进行存取。 因为多个进程可以同时操作&#xff0c;所以需要进行同步。 信号量共享内存通常结合在一…

Open3D (C++) 计算两点云之间的最小距离

目录 一、 算法原理二、代码实现三、结果展示本文由CSDN点云侠原创,原文链接。如果你不是在点云侠的博客中看到该文章,那么此处便是不要脸的爬虫与GPT。 一、 算法原理 Open3D中ComputePointCloudDistance函数提供了计算从源点云到目标点云的距离的方法,计算点云的距离。也…

【C语法学习】26 - strcmp()函数

文章目录 1 函数原型2 参数3 返回值4 比较机制5 示例5.1 示例1 1 函数原型 strcmp()&#xff1a;比较str1指向的字符串和str2指向的字符串&#xff0c;函数原型如下&#xff1a; int strcmp(const char *str1, const char *str2);2 参数 strcmp()函数有两个参数str1和str2&a…

HCIP-四、MUX-vlanSuper-vlan+端口安全

四、MUX-vlan&Super-vlan端口安全 MUX-vlan实验拓扑实验需求及解法1. 在SW1/2/3分别创建vlan10 20 30 402. SW1/2/3之间使用trunk链路&#xff0c;仅允许vlan10 20 30 40 通过。3. SW与PC/Server之间使用access链路。4. ping验证&#xff1a; Super-vlan端口安全实验拓扑实…

【腾讯云云上实验室-向量数据库】腾讯云开创新时代,发布全新向量数据库Tencent Cloud VectorDB

前言 随着人工智能、数据挖掘等技术的飞速发展&#xff0c;海量数据的存储和分析越来越成为重要的研究方向。在海量数据中找到具有相似性或相关性的数据对于实现精准推荐、搜索等应用至关重要。传统关系型数据库存在一些缺陷&#xff0c;例如存储效率低、查询耗时长等问题&…

CentOS使用docker安装OpenGauss数据库

1.搜索OpenGauss docker search opengauss 2.选择其中一个源拉取 docker pull docker.io/enmotech/opengauss 3.运行OpenGauss docker run --name opengauss --privilegedtrue --restartalways -d -e GS_USERNAMEpostgres -e GS_PASSWORDmyGauss2023 -p 5432:5432 docker.…

黑马React18: ReactRouter

黑马React: ReactRouter Date: November 21, 2023 Sum: React路由基础、路由导航、导航传参、嵌套路由配置 路由快速上手 1. 什么是前端路由 一个路径 path 对应一个组件 component 当我们在浏览器中访问一个 path 的时候&#xff0c;path 对应的组件会在页面中进行渲染 2. …

2023年中国高压驱动芯片分类、市场规模及发展趋势分析[图]

高压驱动芯片是一种能在高压环境下工作的集成电路&#xff0c;主要用于控制和驱动各种功率器件&#xff0c;如继电器、电磁阀、电机、变频器等。高压驱动芯片根据其输出电流的大小和形式可分为两类恒流型和开关型。 高压驱动芯片分类 资料来源&#xff1a;共研产业咨询&#x…

蓝桥杯算法双周赛心得——迷宫逃脱(记忆化搜索)

大家好&#xff0c;我是晴天学长&#xff0c;非常经典实用的记忆化搜索题&#xff0c;当然也可以用dp做&#xff0c;我也会发dp的题解&#xff0c;需要的小伙伴可以关注支持一下哦&#xff01;后续会继续更新的。&#x1f4aa;&#x1f4aa;&#x1f4aa; 1) .迷宫逃脱 迷官逃脱…