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

网络高级day01(Modbus 通信协议:Modbus TCP)

目录

1》modbus分类

1> Modbus RTU

2> Modbus ASCLL

3> Modbus TCP

 2》Modbus TCP的特点

 3》Modbus TCP协议

 1> 报文头(一共7个字节)

 2> 寄存器

3> 功能码

 4> 数据

 01H  功能码分析

 05H 功能码分析

0FH 功能码分析


1》modbus分类

1> Modbus RTU

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

2> Modbus ASCLL

 运行在串口上的协议,采用ASCLL码进行传输,并且每个字节的开始和结束都有特殊字符作为标志,传输效率远远低于Modbus RTU,一般只有通讯量比较少的时候才会考虑它。注:在ASCII模式下,每个8位的字节被拆分成两个ASCII字符进行发送,比如十六进制0xAF(1010 1111),会被分解成ASCII字符“A”(0100 0001)和”F”(0100 0110)进行发送,其发送量显然比RTU增加一倍。

3> Modbus TCP

 运行在以太网上的协议

 2》Modbus TCP的特点

1> 主从应答式通信

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

注:更好的理解网络模型的分层特点:

各层之间独立,每一层不需要知道下一层如何实现

当任何一层发生变化时,只要层间接口关系保持不变,则这层以上或以下层不受影响。

3> Modbus TCP 端口号默认为502

 3》Modbus TCP协议

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

MBAP:Modbus Application Protocol (modbus报文头)

PDU:Protocol Data Unit(协议数据单元)

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

 1> 报文头(一共7个字节)

 2> 寄存器

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

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

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

线圈寄存器,类比为开关量。 线圈寄存器支持读也支持写,写在功能码里面又分为写单个线圈寄存器和写多个线圈寄存器。

读单个或多个 写单个 写多个 3个功能码实现

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

对应功能码1个 读单个或多个

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

保持寄存器,这个寄存器的单位不再是bit而是两个byte,也就是可以存放具体的数据量的,并且是可读写的。比如空调检测到的室温,这是不可以修改的,因为室温是根据实际的物理环境决定的。

功能码有对应的三个

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

对应的功能码也就一个

3> 功能码

 4> 数据

 01H  功能码分析

读数据:

主机-》从机

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

从机-》主机

报文头---功能码---字节计数---数据

 05H 功能码分析

写单个:

主机--》从机

报文头---功能码---地址---通断标志/数据

从机--》主机

原文返回

0FH 功能码分析

写多个:

主机-->从机

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

从机--->主机

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

练习

封装函数实现03,05功能码的作用

#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>
void set_slaveid(uint8_t *p, uint8_t id);
int sockfd;
void read_registers(uint8_t *p, uint16_t addr, uint16_t num, uint8_t *dest);
void write_coil(uint8_t *p, uint16_t addr, int op, uint8_t *dest);
int main(int argc, char const *argv[])
{

    if (argc != 2)
    {
        printf("usage:<ip>\n");
        return -1;
    }
    // 1.创建套接字
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    printf("sockfd:%d\n", sockfd);
    // 2.填充结构体
    struct sockaddr_in saddr;
    saddr.sin_family = AF_INET;
    saddr.sin_port = htons(502);
    saddr.sin_addr.s_addr = inet_addr(argv[1]);
    socklen_t addrlen = sizeof(saddr);
    // 3.连接
    if (connect(sockfd, (struct sockaddr *)&saddr, addrlen) < 0)
    {
        perror("connect err");
        return -1;
    }
    // 创建数组
    uint8_t req[32] = {0};
    uint8_t date[128] = {0};
    set_slaveid(req, 0x01);
    read_registers(req, 0x0000, 0x0002, date);//03
    for (int i = 0; i < date[8]; i++)
    {
        printf("%02x ", date[9 + i]);
    }
    putchar(10);
    write_coil(req,0x0000,1,date);//05
    printf("seq:%02x\n",date[1]);
    return 0;
}

void set_slaveid(uint8_t *p, uint8_t id)
{
    p[6] = id;
}
void read_registers(uint8_t *p, uint16_t addr, uint16_t num, uint8_t *dest)
{
    p[5] = 0x06;
    p[7] = 0x03;
    p[9] = addr;
    p[8] = addr >> 8;
    p[11] = num;
    p[10] = num >> 8;
    send(sockfd, p, 12, 0);
    recv(sockfd, dest, 128, 0);
}
void write_coil(uint8_t *p, uint16_t addr, int op, uint8_t *dest)
{   
    p[1]=0x01;
    p[5] = 0x06;
    p[7] = 0x05;
    p[9] = addr;
    p[8] = addr >> 8;
    if (op == 1)
    {
        p[10] = 0xFF;
    }
    else
    {
        p[10] = 0x00;
    }
    p[11] = 0x00;
    send(sockfd, p, 12, 0);
    recv(sockfd, dest, 128, 0);
}

今天的分享就到这里结束啦,如果有哪里写的不好的地方,请指正。
如果觉得不错并且对你有帮助的话点个关注支持一下吧!


http://www.kler.cn/news/312699.html

相关文章:

  • 【类型黑市】指针
  • Angular面试题四
  • 如何使用ssm实现企业人事管理系统+vue
  • monorepo基础搭建教程(从0到1 pnpm+monorepo+vue)
  • 最新版本TensorFlow训练模型TinyML部署到ESP32入门实操
  • matlab模拟时间有负数的信号频谱
  • 前端在网络安全攻击问题上能做什么?
  • jacoco-maven-plugin使用
  • Qt中文乱码解决
  • 动手学深度学习(pytorch土堆)-06损失函数与反向传播、模型训练、GPU训练
  • 零基础玩转实在Agent -- 基础篇|实在Agent研究
  • 算法leecode笔记
  • 基于PHP的电脑线上销售系统
  • 小米电视,无需U盘,直接通过ADB远程安装APK,很方便!
  • 【Verilog学习日常】—牛客网刷题—Verilog快速入门—VL16
  • 修改状态的标准模版
  • 【移动端】Flutter与uni-app:全方位对比分析
  • MATLAB绘图:2.plot函数
  • vulnhub靶场 DC-3
  • axios二次封装
  • web基础—dvwa靶场(十二)JavaScript Attacks
  • 从数据到资产:企业数据治理经济价值实现的策略框架
  • Dubbo SPI源码
  • Linux 开发工具(vim、gcc/g++、make/Makefile)+【小程序:进度条】-- 详解
  • 新媒体运营
  • 非守护线程会阻止JVM的终止吗
  • 大联大诠鼎集团推出基于联咏科技、思特威和TDK产品的电子防抖(EIS)摄像头方案
  • 面试复盘与 AI 大模型学习
  • BitLocker硬盘加密的详细教程分享
  • 【简单点】docker如何部署tomcat