西门子s7通信协议
1.OPC UA2.C# OPC UA服务器 3.Omron-Fins协议4.FINS协议分析5.ModbusTcpSlaveTool6.C#编写OPC客户端读取OPC服务器的数据(最高效简洁版)7.信捷PLC Modbus通讯 (Modbus_TCP与Modbus_RTU)8.三菱Q系列PLC与通讯软件MX Component的使用9.c#基于TCP/IP、CIP协议的欧姆龙PLC通信10.EtherNet/IP实现欧姆龙NX系列PLC通信11.NModbus4的使用12.基于OPC的UG与PLC通信13.KepServer作为OPC UA服务器以及建立OPC UA客户端14.NModbus网口使用MODBUSTCP字符串、浮点数读写
15.西门子s7通信协议
西门子s7通信协议
S7Comm(S7 Communication)是西门子专有的协议,是西门子S7通讯协议簇里的一种。 S7通信协议是西门子S7系列PLC内部集成的一种通信协议,是S7系列PLC的精髓所在。 它是一种运行在传输层之上的(会话层/表示层/应用层)、经过特殊优化的通信协议,其信息传输可以基于MPI网络、 PROFIBUS网络或者以太网 s7在TCP连接上后还需要进行两次握手 S7协议的TCP/IP实现依赖于面向块的ISO传输服务。S7协议被封装在TPKT和ISO-COTP协议中,这使得PDU(协议数据单元) 能够通过TCP传送。
S7协议帧结构
1 TPKT 会话层 主要设置版本号 预留号 报文总长度
2 COPT 表示层 设置PDU类型
3 s7协议 应用层 设置协议头和协议参数等
s7协议的使用
使用tcp连接时需要进行五次握手,其中有三次是tcp客户端与服务器的基有链接,然后需要再通过s7协议发送两次请求连接,共为五次握手。
TCP三次握手(TCP连接时进行) => COTP连接(第一次握手连接) => S7连接(第二次握手) => 数据的读写
连接
TCP三次握手(TCP连接时进行) => COTP连接(第一次握手连接) => S7连接(第二次握手) => 数据的读写
COTP连接(第一次握手)报文
S7连接(第二次握手)报文
使用tcp五次握手进行连接
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 | public partial class Form1 : Form { public Form1() { InitializeComponent(); } /// <summary> /// 五次握手 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void button1_Click( object sender, EventArgs e) { // s7协议 // 1 需要通过socket三次握手,不用写握手过程 // 目前提供设备型号s71200 cpu:1212c 电压是24vDC TcpClient client = new TcpClient(); client.Connect( "192.168.107.202" ,102); // 连接服务器 receiveData(client); // 2 第一请求连接 发送请求帧为 // 总共22个字节 byte [] bs1 = new byte [] { 0x03, // 1字节版本号 默认是03 0x00, // 1字节 保留值 默认0 0x00, 0x16, // 2 字节 报文的总长度 0x11, // 1字节从该字节往后字节个数 十进制是17 0xE0, // PDU 类型 0x00,0x00, // DST引用 默认值 0x00,0x01, // src引用 0x00, // 采用默认值 0xc1, // 上位机擦书 0x02, // 上位机长度 0x10,0x00, // 0x01代表双边通信 0x00机架号和插槽号 0xC2, // plc参数 0x02, // 长度 0x03,0x01, // 0x01和0x00 共同控制机架号和插槽 0xC0,0x01, 0x0a }; client.GetStream().Write(bs1,0,bs1.Length); // 发送第一次请求帧 // 3 第二次请求连接 发送请求帧为 bs1 = new byte [] { 0x03, // 1字节版本号 默认是03 0x00, // 1字节 保留值 默认0 0x00, 0x19, // 2 字节 报文的总长度 0x02, // 当前字节后的字节数 0xF0, // PUD类型 数据传输 0x80, // 最高是十进制128 0x32, // 协议ID,固定值 0x01, // 工作类型 0x01 主站发送请求 0x00,0x00, 0x00,0x00, 0x00,0x08, // 参数长度 0x00,0x00, // 数据长度 0xF0, // 功能码 0x00, // Reserved保留值 0x00,0x03, // 允许操作最大工作队列 0x00,0x03, 0x03,0xc0, // 允许处理最大字节数组 }; client.GetStream().Write(bs1,0,bs1.Length); MessageBox.Show( "连接成功" ); } /// <summary> /// 接收响应数据集 /// </summary> /// <param name="tcpClient"></param> public void receiveData(TcpClient tcpClient) { Task.Run(() => { byte [] bytes = new byte [1024]; while (tcpClient.Connected) { // ONE int count = tcpClient.GetStream().Read(bytes,0,bytes.Length); if (count == 0) return ; Console.WriteLine(BitConverter.ToString(bytes, 0, count) + "\r\n" ); // TWO byte [] s = new byte [count]; Array.Copy(bytes,s,count); Console.WriteLine( string .Join( "," ,s)); } }); } } |
读取和写入报文格式
数据的读取
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | /// <summary> /// 读取M区 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void button2_Click( object sender, EventArgs e) { // 发送请求帧 请求M区地址从00开始 读取一个数据 // 读取数据时的请求帧 byte [] data = new byte [] { // TPKT: 版本号 预留号 总字节长度 0x03, // 版本号 0x00, // 预留号 0x00,0x01F, // 总字节长度 // COTP: 0x02, // 往下的长度 0xF0, // PDU类型 0x80, // 目标引用 // s7-header s7头 0x32, // 协议ID 默认 0x01, // 主站开始发请求 0x00,0x00, // 预留位置 0x03,0x7b, // 随机生成的数字 每次在基础之上递增 0x00,0x0e, // 参数长度 0x00,0x00, // 数据长度 // s7-参数部分 0x04, // 功能码 读取功能 重点 0x01, // 如果涉及多读时候 设置为1, 0x12, // 结构表示 一般默认12 0x0a, // 往后的字节长度 0x10, // 寻址模式 0x02, // 读取的数据类型 02是字节类型 0x00,0x01, // 读取长度 重点 0x00,0x00, // 读取不是DB区 重点 0x83, // 0x83 M存储区,0x84DB块 重点 0x00,0x00,0x70, // 开始数据起始地址 重点 // 列如M30000, 实际地址是30000*8=24 00000,把转成山歌字节,转成16进制3a980 对应三个地址,0x03,0xA9 0x80 // 列如数据DB块的数据,DB21234,4000,其中DB号是21234 转成16进制0x52F2,DB区改为0x52,0xF2 // 40000*8=,2000,转成16进制7d00 转成三个字节变成 0x00,0x7d,0x00 }; socket.Send(data); } |
接收数据的响应
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | /// <summary> /// 接收响应数据 /// </summary> void startReceive() { Task.Run(() => { byte [] bytes = new byte [1024]; while ( true ) { int count = socket.Receive(bytes); if (count == 0) break ; // 转为16进制的字符串 Console.WriteLine( "十六进制打印:" +BitConverter.ToString(bytes,0,count)); // 转为十进制打印 byte [] datas = new byte [count]; Array.Copy(bytes,datas,count); Console.WriteLine( "十进制打印:" + string .Join( "," ,datas)); Invoke( new Action(() => { try { this .label1.Text = datas[25].ToString(); } catch (Exception ex) { Console.WriteLine(ex); } })); /* 响应的数据 * 连接的响应 * 03-00-00-16-11-D0-00-01-00-08-00-C0-01-0A-C1-02-10-00-C2-02-03-01 * 03-00-00-1B-02-F0-80-32-03-00-00-00-00-00-08-00-00-00-00-F0-00-00-03-00-03-00-F0 * * 读取数据的响应 * 03-00-00-1A-02-F0-80-32-03-00-00-03-7B-00-02-00-05-00-00-04-01-FF(读取成功的标志)-04(读取的数据类型)-00-08(数据的长度)-0C(数据) */ } }); } |
数据的写入
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 | /// <summary> /// 写入M14 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void button1_Click( object sender, EventArgs e) { byte [] value = BitConverter.GetBytes( uint .Parse(textBox1.Text)); // 生成写的报文 byte [] bs = new byte [] { // TPKT部分 0x03, // 版本号 0x00, // 预留号 // 0x00,0x24, // 报文总长度36 0x00,0x27, // 报文总长度39 // TOPT 0x02, // 长度 0xF0, // PDU类型 0xB0, // 目标引用 // s7 header 0x32, // 协议id 0x01, // 主站开始请求 0x00,0x00, // 预留部分 0x03,0x7d, // 随机生成 0x00,0x0E, // 参数长度 0x00,0x08, // 参数数据长度 // s7 参数 0x05, // 05代表写入,04代表读取 0x01, // 通信项数 可以支持多写 0x12, // 变量指定 0x0A, // 后面的长度 0x10, 0x02, // 传输数据类型 字节 // 0x00,0x01, // 操作数据的长度 0x00,0x04, // 操作数据的长度 0x00,0x00, // M区 不是DB区 0x83, // M区 // 0x00,0x00,0x70, // 开始写入的地址M14 0x00,0x3e,0x80, // 开始写入的地址M2000 0x00, 0x04, // 字节类型 // 0x00,0x80, // 写入的长度 8位=1字节 0x00,0x20, // 写入的长度 8位=1字节 (写入4个数据 4*8=32转16进制为20) //byte.Parse(textBox1.Text) value[3],value[2],value[1],value[0] }; tcp.Send(bs); Type = RequestType.Write; } |
完整代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 | public partial class Form1 : Form { TcpClientHelper tcp; public Form1() { InitializeComponent(); } private void Tcp_OnClose(TcpClientHelper obj) { MessageBox.Show( "客户端关闭" ); } enum RequestType { Write, // 写入请求 Read, // 读取请求 Connect // 连接的请求 } RequestType Type; private void Tcp_OnMessage( byte [] arg1, TcpClientHelper arg2) { // 获取数据即可 自动触发 BeginInvoke( new Action(() => { switch (Type) { case RequestType.Write: // 写入数据的响应 Console.WriteLine( "写入数据的响应" +BitConverter.ToString(arg1) ); break ; case RequestType.Read: // 读取数据的响应 Console.WriteLine( "读取数据的响应" + BitConverter.ToString(arg1)); this .label1.Text = arg1[arg1.Length-1].ToString(); break ; case RequestType.Connect: Console.WriteLine( "连接时的响应" + BitConverter.ToString(arg1)); break ; } })); } /// <summary> /// 写入M14 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void button1_Click( object sender, EventArgs e) { byte [] value = BitConverter.GetBytes( uint .Parse(textBox1.Text)); // 生成写的报文 byte [] bs = new byte [] { // TPKT部分 0x03, // 版本号 0x00, // 预留号 // 0x00,0x24, // 报文总长度36 0x00,0x27, // 报文总长度39 // TOPT 0x02, // 长度 0xF0, // PDU类型 0xB0, // 目标引用 // s7 header 0x32, // 协议id 0x01, // 主站开始请求 0x00,0x00, // 预留部分 0x03,0x7d, // 随机生成 0x00,0x0E, // 参数长度 0x00,0x08, // 参数数据长度 // s7 参数 0x05, // 05代表写入,04代表读取 0x01, // 通信项数 可以支持多写 0x12, // 变量指定 0x0A, // 后面的长度 0x10, 0x02, // 传输数据类型 字节 // 0x00,0x01, // 操作数据的长度 0x00,0x04, // 操作数据的长度 0x00,0x00, // M区 不是DB区 0x83, // M区 // 0x00,0x00,0x70, // 开始写入的地址M14 0x00,0x3e,0x80, // 开始写入的地址M2000 0x00, 0x04, // 字节类型 // 0x00,0x80, // 写入的长度 8位=1字节 0x00,0x20, // 写入的长度 8位=1字节 (写入4个数据 4*8=32转16进制为20) //byte.Parse(textBox1.Text) value[3],value[2],value[1],value[0] }; tcp.Send(bs); Type = RequestType.Write; } /// <summary> /// 读取M14 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void button2_Click( object sender, EventArgs e) { // 读取的报文31字节 byte [] bs = new byte [] { 0x03, 0x00, 0x00,0x1f, 0x02, 0xf0, 0xb0, // 协议参数 0x32, 0x01, 0x00,0x00, 0x03,0x7d, 0x00,0x0e, 0x00,0x00, // 读取操作 为0 0x04,0x01, 0x12,0x0a, 0x10, 0x02, //0x00,0x01, // 读取长度 0x00,0x04, // M2000 读取四个字节 0x00,0x00, 0x83, // 0x00, 0x00,0x70 0x00,0x3e,0x80, }; tcp.Send(bs); Type = RequestType.Read; } /// <summary> /// 连接 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void Form1_Load( object sender, EventArgs e) { // 1 创建客户端对象 tcp = new TcpClientHelper(); tcp.Connect( "192.168.107.202" , 102); // 连接服务器 // 2 获取数据 tcp.OnMessage += Tcp_OnMessage; // 3 关闭连接 tcp.OnClose += Tcp_OnClose; // 第一次连接请求 byte [] bs = new byte [] { 0x03, // 1字节版本号 默认是03 0x00, // 1字节 保留值 默认0 0x00, 0x16, // 2 字节 报文的总长度 0x11, // 1字节从该字节往后字节个数 十进制是17 0xE0, // PDU 类型 0x00,0x00, // DST引用 默认值 0x00,0x01, // src引用 0x00, // 采用默认值 0xc1, // 上位机擦书 0x02, // 上位机长度 0x10,0x00, // 0x01代表双边通信 0x00机架号和插槽号 0xC2, // plc参数 0x02, // 长度 0x03,0x01, // 0x01和0x00 共同控制机架号和插槽 0xC0,0x01, 0x0a }; tcp.Send(bs); // 第二次请求连接 bs = new byte [] { 0x03, // 1字节版本号 默认是03 0x00, // 1字节 保留值 默认0 0x00, 0x19, // 2 字节 报文的总长度 0x02, // 当前字节后的字节数 0xF0, // PUD类型 数据传输 0x80, // 最高是十进制128 0x32, // 协议ID,固定值 0x01, // 工作类型 0x01 主站发送请求 0x00,0x00, 0x00,0x00, 0x00,0x08, // 参数长度 0x00,0x00, // 数据长度 0xF0, // 功能码 0x00, // Reserved保留值 0x00,0x03, // 允许操作最大工作队列 0x00,0x03, 0x03,0xc0, // 允许处理最大字节数组 }; tcp.Send(bs); Type = RequestType.Connect; MessageBox.Show( "连接成功" ); } } |
合集:
C#采集设备数据
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?