在实际自动化工作中,我们发现西门子PLC与上位机触摸屏通信很方便的,PLC端不用写代码。用的是什么协议呢?
一.今天我们就来解析S7协议。
1.硬件环境:S71215DCDCDC
2.软件环境:Kepserver、VS2019、Wireshark(网络抓包工具)、TIA V16
3.说S7协议之前我们首先说说tcp协议。为什么要说tcp协议呢?因为s7协议是在tcp协议基础上封 装的一层应用协议,与之对应还有,modbustcp,其底层都是tcp。
4.为什么要用tcp协议呢?因为它是一个可靠的连接。我们知道tcp协议都有三次握手?
5.有没有想过为什么是三次握手?两次四次行不行?
6.两次模拟:
客户端:服务端你能听到我说话吗?
服务端:我能听到客户端说话。你能听到我说话吗?
如果两次,服务端无法确定客户端收到它的消息,如果服务端先说话,可能客户端收不到他的话
7.四次:模拟
客户端:服务端你能听到我说话吗?
服务端:我能听到客户端说话。你能听到我说话吗?
客户端:我可以听到服务端说话。你能听到我说话吗?
服务端:靠,不想跟傻子说话...
8.三次:模拟
客户端:服务端你能听到我说话吗?
服务端:我能听到客户端说话。你能听到我说话吗?
客户端:我可以听到服务端说话,今晚去喝酒
连接OK
以上,通俗理解,如果想更深入了解,可以查看TCP通讯原理.这里先重点分析S7协议
二.S7协议分析
- 当我们用kepserver连接PLC时,出现以下报文信息。
2.如果看到当我们用S7协议连接PLC时,首先进行的是3次TCP握手,再分别有两次往返握手COTP,S7COM确认。
3.从TKPT开始是我们需要封装的报文,如红色区域,前面是IP报文头,我们不用管,底层会帮我们处理。
4.TKPT返回报文红色区域是有效数据。
5.然后就是S7COMM握手如图:
6.S7的返回报文其中有一项是PDU length,什么意思呢?这个就是我们一次允许通讯的报文字节数,我这里是240;如博图F1帮助,S7通讯1200对应的字节是240.
三.撸代码实现
1.我们创建S7-1200.cs类,代码如下。
Socket S7TcpClient;//创建socket连接对象
public ushort PDU { get;private set; }//对外开放PDU只读属性
/// <summary>
/// 连接S71200
/// </summary>
/// <param name="ip">PLC的IP地址</param>
/// <returns>是否连接成功</returns>
public bool Connect(string ip)
{
S7TcpClient = new Socket( AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);//创建sokcet tcp连接对象
IAsyncResult asyncResult=S7TcpClient.BeginConnect( new IPEndPoint(IPAddress.Parse(ip),102 ),null,null);//异步连接
asyncResult.AsyncWaitHandle.WaitOne(3000,true);//阻塞最多等待3秒
if (!asyncResult.IsCompleted)//异步操作没完成 就关掉tcp连接 返回失败
{
S7TcpClient.Close();
return false;
}
//上面的socket连接相当于执行了tcp的三次握手
byte[] cotp = new byte[] //cotp报文封装
{
0x03,0x00,0x00,0x16,0x11,0xE0,0x00,0x00,0x00,0x01,0x00,0xC0,0x01,0x0A,0xC1,
0x02,0x01,0x02,0xC2,0x02,0x01,0x00
};
byte[] s7comm = new byte[]//s7comm报文封装
{
0x03, 0x00, 0x00,0x19, 0x02, 0xf0,0x80,0x32,0x01,0x00,0x00,0xff,0xff,0x00,
0x08,0x00, 0x00, 0xf0, 0x0, 0x0, 0x3, 0x0, 0x3, 0x07, 0x80
};
try
{
//第一次握手
S7TcpClient.Send(cotp, SocketFlags.None);
Thread.Sleep(10);//等待10ms给CPU反应时间
byte[] buffer = new byte[S7TcpClient.Available];
S7TcpClient.Receive(buffer,buffer.Length,SocketFlags.None);
//第二次握手
S7TcpClient.Send(s7comm, SocketFlags.None);
Thread.Sleep(10);//等待10ms给CPU反应时间
buffer = new byte[S7TcpClient.Available];
S7TcpClient.Receive(buffer, buffer.Length, SocketFlags.None);
if (buffer.Length == 27)//正确返回报文字节数
{
PDU = (ushort)(buffer[25] * 256 + buffer[26]);//获得此CPU允许单次通讯的字节数,因为占了两个字节,高8位位权256所以乘256
}
return true;
}
catch (Exception)
{
return false;
}
}
3.今天的分析就到这里,如果对您有帮助,请关注,点赞。下期我们讲如果操作DB区