学校门户网站开发,湛江快速网站建设在哪里做,全媒体广告策划营销,网络平台推广方案一、Modbus协议 1.起源  Modbus由Modicon公司于1979年开发#xff0c;是一种工业现场总线协议标准。 Modbus通信协议具有多个变种#xff0c;其中有支持串口#xff0c;以太网多个版本#xff0c;其中最著名的是Modbus RTU#xff08;通信效率最高#xff0c;基于串口是一种工业现场总线协议标准。 Modbus通信协议具有多个变种其中有支持串口以太网多个版本其中最著名的是Modbus RTU通信效率最高基于串口、Modbus ASCII和Modbus TCP基于以太网三种 其中Modbus TCP是在施耐德收购Modicon后1997年发布的。 2.分类 1Modbus RTU 运行在串口上的协议采用二进制表现形式以及紧凑的数据结构通信效率较高应用比较广泛 2Modbus ASCII 运行在串口上的协议采用ASCII码进行传输并且每个字节的开始和结束都有特殊字符作为标志传输效率远远低于Modbus RTU一般只有通讯量比较少时才会考虑它。 3Modbus TCP 运行在以太网上的协议 3.优势 免费、简单、容易使用 4.应用场景 Modbus协议是现在国内工业领域应用最多的协议不只PLC设备各种终端设备比如水控机、水表、电表、工业秤、各种采集设备 二、Modbus tcp协议 1.特点 1采用主从问答式通信 (一个主机可以对应多个从机可以发出请求采集数据发出请求控制设备) 2Modbus TCP是应用层协议基于传输层的TCP进行通信的 3Modbus TCP端口号默认502 2.组成 ModbusTcp协议包含三部分报文头、功能码、数据          报文头7个字节功能码1个字节Modbus TCP/IP协议最大数据帧长度为260字节最大2521 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*1639940100 0002寄存器的数量  40100-40101 00 03 00 00 00 07 01 03 04 05 83 00 76 00 00事务处理标识符 00 00协议标识符 00 07字节长度 03功能码 04字节计数 058340100寄存器的数据 007640101寄存器的数据  练习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 写出控制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 工具安装 Modbus Salve/Poll 1)安装过程软件默认安装 2)破解点击connection-》connect输入序列号序列号在SN.txt) 3使用 从机 先设置先打开salve端   再连接点击connection-》connect  主机     网络调试助手   Wireshark 捕获器选择 windows如果连接有线网络选择本地连接/以太网      如果连接无线网络选择WLAN 如果只是在本机上的通信选择NPCAP Loopback apdater 或Adapter for loopback traffic capture 过滤条件 过滤端口tcp.port502 过滤IPip.addr  192.168.3.11(windows 的ip)  练习 在虚拟机写程序实现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.hint 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[9i]);putchar(10);//6.关闭close(sockfd);return 0;
}   作业 复习今天内容完成05功能码  写一个线圈封装函数。。   /*客户端创建代码 */
#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 , *(pj));}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;
}