Modbus_tcp

目录

一:modbus起源

1.起源

[2. 分类:](#2. 分类:)

[3. 优势:](#3. 优势:)

[4. 应用场景:](#4. 应用场景:)

5.ModbusTCP特点(掌握):

[二、 ModbusTCP的协议](#二、 ModbusTCP的协议)

[1. 报文头](#1. 报文头)

[2. 寄存器](#2. 寄存器)

[1. 线圈(Coils)](#1. 线圈(Coils))

[2. 离散量输入(Discrete Inputs)](#2. 离散量输入(Discrete Inputs))

[3. 输入寄存器(Input Registers)](#3. 输入寄存器(Input Registers))

总结

3.功能码

01功能码分析

05功能码分析

0F功能码分析

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


一: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,一般只有通讯量比较少时才会考虑它。

注:在ASCII模式下,每个8位的字节被拆分成两个ASCII字符进行发送,比如十六进制0xAF(1010 1111),会被分解成ASCII字符"A"(0100 0001)和"F"(0100 0110)进行发送,其发送量显然比RTU增加一倍。

3)Modbus TCP

运行在以太网上的协议

3. 优势:

免费、简单、容易使用

4. 应用场景:

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

5.ModbusTCP特点(掌握):

1)采用主从问答式通信

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

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

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

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

3)Modbus TCP端口号默认502

二、 ModbusTCP的协议

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

MBAP:Modbus Application Protocol (modbus报文头)

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

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

1. 报文头

包含7个字节

2. 寄存器

1. 线圈(Coils)

  • 定义:线圈在Modbus协议中通常被类比为开关量,每一个bit都对应一个信号的开关状态。它主要用于控制IO设备的开关状态,如继电器、阀门等。
  • 访问类型:可读可写。通过发送特定的功能码(如01H、05H、0FH),可以读取或修改线圈的状态。
  • 应用场景:用于控制外部设备的开/关状态,如控制灯的亮灭、电机的启停等。

2. 离散量输入(Discrete Inputs)

  • 定义:离散量输入寄存器相当于线圈寄存器的只读模式,每个bit表示一个开关量,但只能读取输入的开关信号,不能修改。
  • 访问类型:只读。通过发送功能码02H,可以读取离散量输入寄存器的状态。
  • 应用场景:用于读取外部设备的状态,如按钮是否被按下、开关是否处于打开状态等。

3. 输入寄存器(Input Registers)

  • 定义:输入寄存器与保持寄存器类似,但它是只读的。每个输入寄存器占据两个byte的空间,可以存储16位的数据。
  • 访问类型:只读。通过发送功能码04H,可以读取输入寄存器的值。
  • 应用场景:用于读取工业设备的模拟量输入值,如温度、压力、流量等传感器的读数。
  1. 保持寄存器(Holding Registers)
  • 定义:保持寄存器是Modbus协议中最重要的数据类型之一,它既可以读取也可以修改。每个保持寄存器同样占据2个byte的空间,可以存储16位的数据。
  • 访问类型:可读可写。通过发送功能码03H、06H或10H,可以读取或修改保持寄存器的值。
  • 应用场景:用于存储和修改设备的设定值、状态值等,如设置温度控制器的目标温度、读取设备的运行时间等。

总结

数据类型 定义 访问类型 功能码 应用场景
线圈 开关量,每个bit对应一个信号的开关状态 可读可写 01H, 05H, 0FH 控制外部设备的开/关状态
离散量输入 类似线圈的只读模式,每个bit表示一个开关量 只读 02H 读取外部设备的状态
输入寄存器 类似保持寄存器的只读模式,每个寄存器占两个byte 只读 04H 读取工业设备的模拟量输入值
保持寄存器 既可以读取也可以修改,每个寄存器占两个byte 可读可写 03H, 06H, 10H 存储和修改设备的设定值、状态值等

3.功能码

01功能码分析


05功能码分析


0F功能码分析


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

cs 复制代码
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <string.h>
#include <stdlib.h>
void read_registers(uint8_t *p, uint16_t addr, uint16_t num, uint8_t *dest, int sockfd)
{
    p[8] = addr >> 8;
    p[9] = addr;
    p[10] = num >> 8;
    p[11] = num;
    send(sockfd, p, 12, 0);
    int ret = recv(sockfd, dest, 24, 0);
    if (ret < 0)
    {
        perror("recv err");
        close(sockfd);
        return;
    }
    else
    {
        for (int i = 0; i < ret; i++)
            printf("%02x ", dest[i]);
    }
    printf("\n");
}
void write_coil(uint8_t *p, uint16_t addr, int op, uint8_t *dest, int sockfd)
{
    p[8] = addr >> 8;
    p[9] = addr;
    p[11] = 0;
    if (op == 1)
    {
        p[10] = 0xFF;
    }
    else
    {
        p[10] = 0;
    }
    send(sockfd, p, 12, 0);
    int ret = recv(sockfd, dest, 24, 0);
    if (ret < 0)
    {
        perror("recv err");
        close(sockfd);
        return;
    }
    else
    {
        for (int i = 0; i < ret; i++)
            printf("%02x ", dest[i]);
    }
    printf("\n");
}
int main(int argc, char const *argv[])
{
    char buf[128] = {0};
    // 1.创建套接字(socket)------------》有手机
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd < 0)
    {
        perror("socket err");
        return -1;
    }
    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]);
    // 3.连接(connect)-------------------》拨打电话
    if (connect(sockfd, (struct sockaddr *)&saddr, sizeof(saddr)) < 0)
    {
        perror("connect err");
        return -1;
    }
    printf("connect okk\n");
    uint8_t data_reg[12] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x01, 0x03};
    uint8_t data_reg1[24] = {};
    uint16_t first;
    int num;
    printf("请输入读取保持寄存器起始地址:");
    scanf("%hx", &first);
    printf("请输入要查询的寄存器个数:");
    scanf("%d", &num);
    read_registers(data_reg, first, num, data_reg1, sockfd);
    uint8_t data_coil[12] = {0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0x01, 0x05};
    uint8_t data_coil1[24] = {};
    printf("请输入写入线圈起始地址:");
    scanf("%hx", &first);
    printf("请选择设置通断状态:");
    scanf("%d", &num);
    write_coil(data_coil, first, num, data_coil1, sockfd);
    close(sockfd);
    return 0;
}
相关推荐
幽兰的天空8 小时前
介绍 HTTP 请求如何实现跨域
网络·网络协议·http
lisenustc8 小时前
HTTP post请求工具类
网络·网络协议·http
心平气和️8 小时前
HTTP 配置与应用(不同网段)
网络·网络协议·计算机网络·http
心平气和️8 小时前
HTTP 配置与应用(局域网)
网络·计算机网络·http·智能路由器
Gworg9 小时前
网站HTTP改成HTTPS
网络协议·http·https
Mbblovey9 小时前
Picsart美易照片编辑器和视频编辑器
网络·windows·软件构建·需求分析·软件需求
北顾南栀倾寒10 小时前
[Qt]系统相关-网络编程-TCP、UDP、HTTP协议
开发语言·网络·c++·qt·tcp/ip·http·udp
GZ_TOGOGO10 小时前
PIM原理与配置
网络·华为·智能路由器
7ACE10 小时前
Wireshark TS | 虚假的 TCP Spurious Retransmission
网络·网络协议·tcp/ip·wireshark·tcpdump
大丈夫立于天地间11 小时前
ISIS基础知识
网络·网络协议·学习·智能路由器·信息与通信