S7通信协议详解
西门子S7通信协议
引用:
https://blog.csdn.net/qq_64257614/article/details/137263665
https://zhuanlan.zhihu.com/p/456800341
https://blog.csdn.net/qq_35029061/article/details/128196236
https://www.itpub.net/thread-2052649-1-1.html?_dsign=f8584037
西门子和三菱的几个区别(通讯层面):
- 西门子PLC通讯端口固定102,但是可以连接多个PC端(客户端),三菱PLC通讯端口可以自定义,最多好像8个,但是每个端口只能连接一个客户端;
- 两者的读写指令类似,但是西门子在端口连接的时候,要做两个初始化指令交互后,才能正常读写处理; 如果中途有错误格式的指令,可能导致端口连接断开;
三菱PLC主要是以字为单位读写的
;西门子主要是以字节为单位读写
; 所以三菱相邻两个地址相差16bit,西门子相邻两个地址相差8bit;- 三菱PLC的数据块,
一般最小处理单位就是字
,很少拆成bit处理(或者把整个字当作0,1布尔类型处理,但是这样有点太浪费了),
而且上位PC端只能用字去读写,无法按位读写,如果真的要用bit处理,一般就用M点;
西门子这块比较灵活,可以按bit或byte去读写
;如果按byte,标识的样子是B10.B99 ;如果是bit,标识的样子是B10.X99.0~DB10.X99.7 - 三菱PLC的
数据块是固定的
,比如D0~D6000;西门子的数据块是通过西门子的编程工具初始化的
,也就是说,你可以把一片地址定义成DB10,也可以定义成DB50;
通俗的说:三菱PLC的数据库偏硬; 西门子的偏软,它的地址是映射的虚拟地址; - 三菱的数据位是
小端模式
从小到大的,比如某个双字,低位在前,高位在后;这是针对数字类型,但是如果是ascii码,因为一个字有两个字节,这时候却又是反的;
所以在三菱里面对数字和字符类型,要分两种顺序处理;
西门子是大端模式
从大到小的;这两种方法有什么区别呢; 简单来说:从小到大主要是计算机思考的方式; 从大到小是人的思考方式;
比如655539,它等于65536+3,转换成16进制是0x00010003 需要两个字 , 如果在三菱里存储的顺序就是先低位3,再高位1,也就是 03 00 01 00;
在西门子里存储的顺序从高到低,也就是00 01 00 03;
就像oracle在的数据在windows系统里的数据存储顺序是从小到大,在liunx系统里又是从大到小;
注意事项
对于西门子
字符串转字节数组传递时,需要在第一个字节填写字符串的总长度,第二个字节填写字符串的实际长度
如果字符串的实际长度大于博图给分配的长度,会导致PLC的CPU重启
/// <summary>
/// S7字符串转换成字节数组
/// </summary>
/// <param name="myself">需要转换的字符串</param>
/// <param name="length">字符串的长度</param>
/// <returns></returns>
public static byte[] GetByteArray(this string myself,byte length)
{
if (string.IsNullOrWhiteSpace(myself))
{
return null;
}
// 西门子PLC设置字符串的总长度
string count = Encoding.ASCII.GetString(new byte[] { length });
// 写入字符的长度
string head = Encoding.ASCII.GetString(new byte[] { (byte)(myself).Length });
// 转换完成的数组
var bytes = Encoding.ASCII.GetBytes((count + head + myself));
return bytes;
}
报文解析
2.报文的基本格式:
第1和第2个字节是:固定报文头03 00,这里我们就用到三种报文:
2.2 第3和第4个字节是:
整个报文的长度;
其它部分就是各种报文的个性化处理了;
下面分析大量报文的案例进行规律分析,为了便于对照,每种都用1200 和300 两种对照demo显示:
初始化
初始化分为两次交互
交互一
西门子1200:
PC发出报文 ( [A18]=0x01 =CPUSlot)
03 00 00 16 11 E0 00 00 00 01 00 C1 02 01 00 C2 02 01 01(cpuslot) C0 01 09
PLC回复报文( B[10]=0x06 可能 是西门子的小型号 B[22]=0x01=CPUSlot)
03 00 00 16 11 D0 00 01 00 06 00 C0 01 09 C1 02 01 00 C2 02 01 01 (cpuslot)
西门子300:
PC发出报文 ( A[18]=0x02 =CPUSlot)
03 00 00 16 11 E0 00 00 00 01 00 C1 02 01 00 C2 02 01 02(cpuslot) C0 01 09
PLC回复报文 (B[10]=0x04 可能 是西门子的小型号 B[22]=0x0=CPUSlot)
03 00 00 16 11 D0 00 01 00 04 00 C0 01 09 C1 02 01 00 C2 02 01 02
opc 对 1200 和 300 不用配置的不同点,就一个地方:1200: CPUSlot = 1 ,300: CPUSlot = 2;
PLC型号 | Slot值 |
---|---|
S200Smart | 03 |
S200 | 4D |
S300 | 02 |
S400 | 00 |
S1200 | 02 |
S1500 | 02 |
所以可以摸索规律是:
a.pc发起第一个初始化报文的时候,第18个字节标识了CPUSlot ;
b.plc回复报文和读取报文长度一样都是22个字节长度;
c.plc回复报文的最后一个字节也是CPUSlot ,这个可以用来校验;
d.plc回复的第10个字节一个是06,一个是04,这个好像是小型号的区别;
细节摸索下来:1200该字节是06,314是04,315是03;
交互二
PC发出报文
03 00 00 19 02 F0 80 32 01 00 00 FF FF 00 08 00 00 F0 00 00 01 00 01 07 80
PLC回复报文
03 00 00 1B 02 F0 80 32 03 00 00 FF FF 00 08 00 00 00 00 F0 00 00 01 00 01 00 F0
第二个初始化报文交互,通过1200 和314,315的对比,发现没有任何区别;
初始化流程
所以我们可以把这个交互完全固话;
到此,整个初始化处理就算结束了,正常在设计架构的时候,可以这么实现:
在ClentSocket的onConnect(即正常连接上)的瞬间,pc给plc发起第一个初始请求,得到回复后(为了简单,就仅仅判断长度为22即可);
立刻发起第二个固定的初始话请求,得到长度为24的报文后,就通过一个布尔变量通知整个系统可以正常读写;
读取报文
PC发出报文
报文解析
由上图可知:报文分为三部分
- TPKT
- COTP
- S7 Communication
TPKT
序号 | 报文 | 描述 | 备注 |
---|---|---|---|
A[0] | 03 | version | |
A[1] | 00 | reserved; | |
A[2]~A[3] | 00 1F | 整个读取请求长度为0x1F= 31 ; |
COTP
序号 | 报文 | 描述 | 备注 |
---|---|---|---|
A[4] | 02 | length | |
A[5] | F0 | type 标识类型,常见的值有: 0x01: ED Expedited Data,加急数据; 0x02: EA Expedited Data Acknowledgement,加急数据确认; 0x04: UD,用户数据; 0x05: RJ Reject,拒绝; 0x06: AK Data Acknowledgement,数据确认; 0x07: ER TPDU Error,TPDU错误; 0x08: DR Disconnect Request,断开请求; 0x0C: DC Disconnect Confirm,断开确认; 0x0D: CC Connect Confirm,连接确认; 0x0E: CR Connect Request,连接请求; 0x0F: DT Data,数据传输; |
|
A[6] | 80 | opt |
S7 Communication
序号 | 报文 | 描述 | 备注 |
---|---|---|---|
A[7] | 32 | 协议ID 一般指定为0x32 | |
A[8] | 01 | PDU类型 0x01 - JOB(Request: job with acknowledgement):作业请求。由主设备发送的请求(例如,读/写存储器,读/写块,启动/停止设备,设置通信) 0x02 - ACK(acknowledgement without additional field):确认响应,没有数据的简单确认(未遇到过由S7 300/400设备发送得) 0x03 - ACK_DATA(Response: acknowledgement with additional field):确认数据响应,这个一般都是响应JOB的请求 0x07 - USERDATA:原始协议的扩展,参数字段包含请求/响应ID(用于编程/调试,读取SZL,安全功能,时间设置,循环读取...); |
|
A[9]~A[10] | 00 00 | 冗余数据, | |
A[11]~A[12] | 00 1C | Protocol Data Unit Reference,HSL中默认用的是00 01 , |
|
A[13]~A[14] | 00 0E | 参数的总长度 也就是parameter的长度 | |
A[15]~A[16] | 00 00 | 数据的长度、也就是data部分数据的长度 如果无即为0 | |
A[17] | 04 | 功能码 0x00 CPU services CPU服务 0xf0 Setup communication 建立通信; 0x04 Read Var 读取值; 0x05 Write Var 写入值; 0x1a Request download 请求下载; 0x1b Download block 下载块; 0x1c Download ended 下载结束; 0x1d Start upload 开始上传; 0x1e Upload 上传; 0x1f End upload 上传结束; 0x28 PI-Service 程序调用服务; 0x29 PLC Stop 关闭PLC; |
|
A[18] | 01 | 代表了Item的个数 为1 即为一个 | |
A[19] | 12 | 结构标识通常都为0x12,代表变量规范 | |
A[20] | 0A | 长度规范、自此往后的长度 | |
A[21] | 10 | IDS的地址规范的格式类型 0x10: S7ANY Address data S7-Any pointer-like DB1.DBX10.2; 0x13: PBC-R_ID R_ID for PBC; 0x15: ALARM_LOCKFREE Alarm lock/free dataset; 0x16: ALARM_IND Alarm indication dataset; 0x19: ALARM_ACK Alarm acknowledge message dataset; 0x1a: ALARM_QUERYREQ Alarm query request dataset; 0x1c: NOTIFY_IND Notify indication dataset; 0xa2: DRIVEESANY seen on Drive ES Starter with routing over S7; 0xb2: 1200SYM Symbolic address mode of S7-1200; 0xb0: DBREAD Kind of DB block read, seen only at an S7-400; 0x82: NCK Sinumerik NCK HMI access; |
|
A[22] | 02 | 数据传输的大小 01-bit; 02-byte |
|
A[23]~A[24] | 00 11 | 两个字节,访问数据的个数,以byte为单位; | |
A[25]~A[26] | 00 0A | DB块的编号,比如DB50, 就是0x32=50, 两个字节,范围是0~65535 | |
A[27] | 84 | 访问数据块的类型 0x81-input(I存储区输入触点) 0x82-output(Q存储区输出触点) 0x83-flag(M存储区中间寄存器触点常用于存储标志位) 0x84-DB(这个最常见); |
|
A[28]~A[30] | 访问DB块的偏移量offset (地址+1以byte为单位); 3个字节,范围是0~16777216 |
摘要:程序设计的时候,其实主要关注最后4个信息,即:
- A[24]~A[25]: 访问byte个数
- A[26]~A[27]: DB块编号
- A[28] : 数据块类型
- A[29]~A[31] :访问地址偏移量;相当于首地址编号
PLC响应报文
报文解析
TPKT
序号 | 报文 | 描述 | 备注 |
---|---|---|---|
B[0] | 03 | ||
B[1] | 00 | 固定报文头; | |
B[2]~A[3] | 00 2A | 整个读取请求长度为0x1F= 31 ; |
COTP
序号 | 报文 | 描述 | 备注 |
---|---|---|---|
B[4] | 02 | length | |
B[5] | F0 | type 标识类型,常见的值有: 0x01: ED Expedited Data,加急数据; 0x02: EA Expedited Data Acknowledgement,加急数据确认; 0x04: UD,用户数据; 0x05: RJ Reject,拒绝; 0x06: AK Data Acknowledgement,数据确认; 0x07: ER TPDU Error,TPDU错误; 0x08: DR Disconnect Request,断开请求; 0x0C: DC Disconnect Confirm,断开确认; 0x0D: CC Connect Confirm,连接确认; 0x0E: CR Connect Request,连接请求; 0x0F: DT Data,数据传输; |
|
B[6] | 80 | opt |
S7 Communication
序号 | 报文 | 描述 | 备注 |
---|---|---|---|
B[7] | 32 | 整个读取回复报文长度:25+读取长度; | |
B[8] | 03 | PDU类型 0x01 - JOB(Request: job with acknowledgement):作业请求。由主设备发送的请求(例如,读/写存储器,读/写块,启动/停止设备,设置通信) 0x02 - ACK(acknowledgement without additional field):确认响应,没有数据的简单确认(未遇到过由S7 300/400设备发送得) 0x03 - ACK_DATA(Response: acknowledgement with additional field):确认数据响应,这个一般都是响应JOB的请求 0x07 - USERDATA:原始协议的扩展,参数字段包含请求/响应ID(用于编程/调试,读取SZL,安全功能,时间设置,循环读取...); |
|
B[9]~B[10] | 00 00 | 冗余数据 | |
B[11]~B[12] | 00 8E | 两个字节,标识序列号,回复报文相同位置和这个完全一样;范围是0~65535; | |
B[13]~B[14] | 00 02 | 两个字节,固定为00 02;对应读取位置是 00 0E;正好 02+0E=10 ;有点补码的感觉,其实不需要关注规律,反正是固定的; | |
B[15]~B[16] | 00 15 | ||
B[17] | 00 | 错误类型 | |
B[18] | 00 | 错误编码 | |
B[19] | 04 | 功能码 | |
B[20] | 01 | 代表了Item的个数 为1 即为一个 | |
B[21] | FF | FF 为返回码,返回码如下 0x00 Reserved 未定义,预留; 0x01 Hardware error 硬件错误; 0x03 Accessing the object not allowed 对象不允许访问; 0x05 Invalid address 无效地址,所需的地址超出此PLC的极限; 0x06 Data type not supported 数据类型不支持; 0x07 Data type inconsistent 日期类型不一致; 0x0a Object does not exist 对象不存在; 0xff Success 成功; |
|
B[22] | 04 | 04 为数据传输大小,data数据传输大小值如下: 0 NULL 3 BIT bit access, len is in bits 4 BYTE/WORD/DWORD byte/word/dword access, len is in bits 5 INTEGER integer access, len is in bits 6 DINTEGER integer access, len is in bytes 7 REAL real access, len is in bytes 9 OCTET STRING octet string, len is in bytes |
|
B[23]~B[24] | 00 88 | =178=请求数据长度 请求访问的byte个数8 ;其实就是以二进制为单位的个数;由此可以看出,一口气最多访问的地址个数是8192(=FFFF/8+1); | |
B[25] | 13 14 15 16 17 18 00 00 00 00 00 00 00 00 21 | (17个)以offset作为首地址,所对应的各个byte的值; |
摘要:程序设计的时候,其实只要关注两个信息:
1.校验B[3]~B[4]:校验长度正确;
2.B[26]~最后一个 :获取对应的值;
报文读取应用实例
读demo1:
西门子1200: 读取DB10, count=17 ,offset=19
PC发出报文
A[3]~A[4]=0x001F=31=读取报文总长度,
A[12]~A[13]=0x001C=序列号,
A[24]~A[25]=0x0011=17=读取请求count;
A[26]~A[27]=0x000A=10=DB10,
A[28]=0x84=读取的数据类型为DB块,
A[29]~A[31]=0x000098=152=19*8=读取偏移量offset(bit为单位) )
03 00|00 1F
|02 F0 80|32|01
|00 00|00 1C|00 0E
|00 00
|04|01|12|0A|10|02|00 11|00 0A|84|00 00 98
PLC回复报文:
B[3]~B[4]=0x002A=42=回复报文总长度,
B[12]~B[13]=0x001C=序列号,
B[16]~B[17]=0x0015=21=读取请求count(17)+4
B[24]~B[25]=0x0088=17*8=请求数据长度(bit为单位),
B[26]~最后=数据值
03|00|00 2A
|02 F0 80|32|03
|00 00|00 1C|00 02
|00 15
|00 00
|04|01|FF
|04|00 88
|13 14 15 16 17 00 00 00 00 00 00 00 00 00 00 00 00
读demo2:
西门子1200: 读取DB11, count=17 ,offset=19
PC发出报文:
A[3]~A[4]=0x001F=31=读取报文总长度,
A[12]~A[13]=0x008E=序列号,
A[24]~A[25]=0x0011=17=读取请求count;
A[26]~A[27]=0x000B=11=DB11,
A[28]=0x84=读取的数据类型为DB块,
A[29]~A[31]=0x000098=152=19*8=读取偏移量offset(bit为单位) )
03|00|00 1F|02 F0 80|32|01|00 00|00 8E|00 0E|00 00|04|01|12|0A|10|02|00 11|00 0B|84|00 00 98
PLC回复报文:
B[3]~B[4]=0x002A=42=回复报文总长度,
B[12]~B[13]=0x001C=序列号,
B[16]~B[17]=0x0015=21=读取请求count(17)+4
B[24]~B[25]=0x0088=17*8=请求数据长度(bit为单位),
B[26]~B[42]=数据值
03|00|00 2A|02 F0 80|32|03|00 00|00 8E|00 02|00 15 00 00|04|01|FF 04 00 88 13 14 15 16 17 18 00 00 00 00 00 00 00 00 21 22 23
读demo3:
西门子1200:读取DB11, count=16 ,offset=18
PC发出报文:
(A[3]~A[4]=0x001F=31=读取报文总长度,
A[12]~A[13]=0x0013=序列号,
A[24]~A[25]=0x0010=16=读取请求count;
A[26]~A[27]=0x000B=11=DB11,
A[28]=0x84=读取的数据类型为DB块,
A[29]~A[31]=0x000090=146=18*8=读取偏移量offset(bit为单位) )
03 00 00 1F 02 F0 80 32 01 00 00 00 13 00 0E 00
00 04 01 12 0A 10 02 00 10 00 0B 84 00 00 90
PLC回复报文:
(B[3]~B[4]=0x0029=41=回复报文总长度,
B[12]~B[13]=0x0013=序列号,
B[16]~B[17]=0x0014=20=读取请求count(16)+4
B[24]~B[25]=0x0080=16*8=请求数据长度(bit为单位),
B[26]~B[41]=数据值)
03 00 00 29 02 F0 80 32 03 00 00 00 13 00 02 00
14 00 00 04 01 FF 04 00 80 00 13 14 15 16 17 18
00 00 00 00 00 00 00 00 21
读demo4:
西门子300 (314) 读取D50, count=20 ,offset=4000
PC发出报文:
(A[3]~A[4]=0x001F=31=读取报文总长度,
A[12]~A[13]=0x0028=序列号,
A[24]~A[25]=0x0014=20=读取请求count;
A[26]~A[27]=0x0032=50=DB50,
A[28]=0x84=读取的数据类型为DB块,
A[29]~A[31]=0x007D00=32000=4000*8=读取偏移量offset(bit为单位)
03 00 00 1F02 F0 80 32 01 00 00 00 28 00 0E 00
00 04 01 12 0A 10 02 00 14 00 32 8400 7D 00
PLC回复报文:
B[3]~B[4]=0x002D=45=回复报文总长度,
B[12]~B[13]=0x0028=序列号,
B[16]~B[17]=0x0018=24=读取请求count(20)+4
B[24]~B[25]=0x00A0=20*8=请求数据长度(bit为单位),
B[26]~B[45]=数据值
03 00 00 2D02 F0 80 32 03 00 00 00 28 00 02 00 18 00 00 04 01 FF 04 00 A0 00 04 0E AB 00 00 00 00 00 00 03 00 00 00 00 00 00 00 00 00
读demo5:
西门子300 (315) 读取D10, count=100 ,offset=2
PC发出报文:
A[3]~A[4]=0x001F=31=读取报文总长度,
A[12]~A[13]=0x0003=序列号,
A[24]~A[25]=0x0064=100=读取请求count;
A[26]~A[27]=0x000A=10=DB10,
A[28]=0x84=读取的数据类型为DB块,
A[29]~A[31]=0x000010=16=2*8=读取偏移量offset(bit为单位)
03 00 00 1F 02 F0 80 32 01 00 00 00 03 00 0E 00
00 04 01 12 0A 10 02 00 64 00 0A 84 00 00 10
PLC回复报文:
B[3]~B[4]=0x007D=125=回复报文总长度,
B[12]~B[13]=0x0003=序列号,
B[16]~B[17]=0x0068=104=读取请求count(100)+4
B[24]~B[25]=0x0320=100*8=请求数据长度(bit为单位),
B[26]~B[125]=数据值
03 00 00 7D 02 F0 80 32 03 00 00 00 03 00 02 00 68 00 00 04 01 FF 04 03 20 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
读demo6:
西门子1200 读取X输入(input)两个byte:
PC发出报文:
(A[3]~A[4]=0x001F=31=读取报文总长度, A[12]A[13]=0x0002=序列号,A[24]A[25]=0x0002=2=读取请求count;
A[26]~A[27]=0x000A=10=DB10[其实这里写什么都可以,因为input不属于DB块],
A[28]=0x81=读取的数据类型为Input,A[29]~A[31]=0x000000=0=0*8=读取偏移量offset(bit为单位) )
03 00 00 1F 02 F0 80 32 01 00 00 00 02 00 0E 00 00 04 01 12 0A 10 02 00 02 00 0A 81 00 00 00
PLC回复报文:
(B[3]~B[4]=0x001B=27=回复报文总长度, B[12]B[13]=0x0002=序列号,B[16]B[17]=0x0068=104=读取请求count(100)+4
B[24]~B[25]=0x0320=100*8=请求数据长度(bit为单位), B[26]~B[27]=数据值)
03 00 00 1B 02 F0 80 32 03 00 00 00 02 00 02 00
06 00 00 04 01 FF 04 00 10 08 00
读demo7:
西门子1200 读取Y输出(output)两个byte:
PC发出报文:
(A[3]~A[4]=0x001F=31=读取报文总长度, A[12]A[13]=0x0001=序列号,A[24]A[25]=0x0002=2=读取请求count;
A[26]~A[27]=0x000A=10=DB10[其实这里写什么都可以,因为input不属于DB块],
A[28]=0x82=读取的数据类型为Output,A[29]~A[31]=0x000000=0=0*8=读取偏移量offset(bit为单位) )
03 00 00 1F 02 F0 80 32 01 00 00 00 01 00 0E 00 00 04 01 12 0A 10 02 00 02 00 0A 82 00 00 00
PLC回复报文:
(B[3]~B[4]=0x001B=27=回复报文总长度, B[12]B[13]=0x0002=序列号,B[16]B[17]=0x0068=104=读取请求count(100)+4
B[24]~B[25]=0x0320=100*8=请求数据长度(bit为单位), B[26]~B[27]=数据值)
03 00 00 1B 02 F0 80 32 03 00 00 00 01 00 02 00 06 00 00 04 01 FF 04 00 10 05 00
读demo8:
西门子1200 读取flag两个byte:
PC发出报文:
03 00 00 1F 02 F0 80 32 01 00 00 05 65 00 0E 00 00 04 01 12 0A 10 02 00 02 00 09 83 00 00 00
PLC回复报文:
(B[3]~B[4]=0x001B=27=回复报文总长度, B[12]B[13]=0x0565=序列号,B[16]B[17]=0x0006=6=读取请求count(2)+4
B[24]~B[25]=0x0010=2*8=请求数据长度(bit为单位), B[26]~B[27]=数据值)
03 00 00 1B 02 F0 80 32 03 00 00 05 65 00 02 00 06 00 00 04 01 FF 04 00 10 FF 17
到这里读的处理就算结束了;几个小注意点:
- 对于不同信号的PLC,除了初始化的CPUSolt不同;正常读/写指令是一样的;
- 读的时候,都是以byte为单位的,如果程序只需要bit,那么还是以Byte为单位去读,将读出的部分按bit再去分解;
- flag类型到底是什么,不是很清楚,有点类似三菱里的M点;这个也不需要去深究,一般项目里主要就是用DB块;
- 读取的长度如果是N(以byte为单位),那么返回的长度就是N8(以bit为单位);怎么判断长度是否要8;主要看后面是不是紧挨着数据,如果是数据,就需要*8;offset都是以bit为单位的;
- 正常读的操作都是DB块,所以在A[26]~A[27]这个字节写入DB块的编号,但是对于input,output,flags这三个类型,是不需要数据块编号的,不过我们可以随便写一个DB编号;
写操作
PC发出报文
TPKT
序号 | 报文 | 描述 | 备注 |
---|---|---|---|
A[0] | 03 | ||
A[1] | 00 | 固定报文头; | |
A[2]~A[3] | 00 1F | 整个读取请求长度为0x1F= 31 ; |
COTP
序号 | 报文 | 描述 | 备注 |
---|---|---|---|
A[4] | 02 | length | |
A[5] | F0 | type 标识类型,常见的值有: 0x01: ED Expedited Data,加急数据; 0x02: EA Expedited Data Acknowledgement,加急数据确认; 0x04: UD,用户数据; 0x05: RJ Reject,拒绝; 0x06: AK Data Acknowledgement,数据确认; 0x07: ER TPDU Error,TPDU错误; 0x08: DR Disconnect Request,断开请求; 0x0C: DC Disconnect Confirm,断开确认; 0x0D: CC Connect Confirm,连接确认; 0x0E: CR Connect Request,连接请求; 0x0F: DT Data,数据传输; |
|
A[6] | 80 | opt |
S7 Communication
序号 | 报文 | 描述 | 备注 |
---|---|---|---|
A[7] | 32 | 协议ID 一般指定为0x32 | |
A[8] | 01 | PDU类型 0x01 - JOB(Request: job with acknowledgement):作业请求。由主设备发送的请求(例如,读/写存储器,读/写块,启动/停止设备,设置通信) 0x02 - ACK(acknowledgement without additional field):确认响应,没有数据的简单确认(未遇到过由S7 300/400设备发送得) 0x03 - ACK_DATA(Response: acknowledgement with additional field):确认数据响应,这个一般都是响应JOB的请求 0x07 - USERDATA:原始协议的扩展,参数字段包含请求/响应ID(用于编程/调试,读取SZL,安全功能,时间设置,循环读取...); |
|
A[9]~A[10] | 00 00 | 冗余数据, | |
A[11]~A[12] | 00 1C | Protocol Data Unit Reference,HSL中默认用的是00 01 , |
|
A[13]~A[14] | 00 0E | 参数的总长度 也就是parameter的长度 | |
A[15]~A[16] | 00 00 | 数据的长度、也就是data部分数据的长度 如果无即为0 | |
A[17] | 05 | 功能码 0x00 CPU services CPU服务 0xf0 Setup communication 建立通信; 0x04 Read Var 读取值; 0x05 Write Var 写入值; 0x1a Request download 请求下载; 0x1b Download block 下载块; 0x1c Download ended 下载结束; 0x1d Start upload 开始上传; 0x1e Upload 上传; 0x1f End upload 上传结束; 0x28 PI-Service 程序调用服务; 0x29 PLC Stop 关闭PLC; |
|
A[18] | 01 | 代表了Item的个数 为1 即为一个 | |
A[19] | 12 | 结构标识通常都为0x12,代表变量规范 | |
A[20] | 0A | 长度规范、自此往后的长度 | |
A[21] | 10 | IDS的地址规范的格式类型 0x10: S7ANY Address data S7-Any pointer-like DB1.DBX10.2; 0x13: PBC-R_ID R_ID for PBC; 0x15: ALARM_LOCKFREE Alarm lock/free dataset; 0x16: ALARM_IND Alarm indication dataset; 0x19: ALARM_ACK Alarm acknowledge message dataset; 0x1a: ALARM_QUERYREQ Alarm query request dataset; 0x1c: NOTIFY_IND Notify indication dataset; 0xa2: DRIVEESANY seen on Drive ES Starter with routing over S7; 0xb2: 1200SYM Symbolic address mode of S7-1200; 0xb0: DBREAD Kind of DB block read, seen only at an S7-400; 0x82: NCK Sinumerik NCK HMI access; |
|
A[22] | 02 | 数据传输的大小 01-bit; 02-byte |
|
A[23]~A[24] | 00 11 | 两个字节,访问数据的个数,以byte为单位; | |
A[25]~A[26] | 00 0A | DB块的编号,比如DB50, 就是0x32=50, 两个字节,范围是0~65535 | |
A[27] | 84 | 访问数据块的类型 0x81-input 0x82-output 0x83-flag 0x84-DB(这个最常见); |
|
A[28]~A[30] | 访问DB块的偏移量offset (地址+1以byte为单位); 3个字节,范围是0~16777216 | ||
A[31]~A[32] | 00 04 | 写入方式为Byte | |
A[33]~A[34] | 00 10 | =2 * 8= 写入byte的个数(bit为单位) | |
A[36]~A[37] | FF FE | 写入数据 |
写demo1:
西门子1200 写 db10.WORD18=0xFFFE=65534; 也就是: DB10.b18=0xFF; DB10.B19=0xFE;
PC发出报文:
A[3]~A[4]=0x0025=37=读取报文总长度,
A[12]~A[13]=0x0005=序列号,
A[16]~A[17]=0x06=写入byte个数(2)+4 ,
A[23]=0x02=写入方式为byte,
A[24]~A[25]=0x0002=2=写入个数count;
A[26]~A[27]=0x000A=10=DB10,
A[28]=0x84=写入的数据类型为DB块,
A[29]~A[31]=0x000090=144=188=读取偏移量offset(bit为单位),
A[32]~A[33]=0x0004=写入方式为Byte ,
A[34]~A[35]=0x0010=28=写入byte的个数(bit为单位) ,
A[36]~A[37]= 写入数据
03|00|00 25
|02 F0 80|32|01
|00 00|00 05|00 0E
|00 06
|05
|01
|12|0A|10|02
|00 11
|00 0A
|84|00 00 90|00 04|00 10|FF FE
PLC回复报文:
B[12]~B[13]=0x0565=序列号,最后一个B[14]=0xFF表示写入
03|00|00 16
|02 F0 80|32|03
|00 00|00 05|00 02
|00 01 00 00 05 01|FF
写demo2:
1200 写入 DB10. X2.6=1 (这里是按bit写入)
PC发出报文:
A[3]~A[4]=0x0024=36=读取报文总长度
A[12]~A[13]=0x0008=序列号
A[16]~A[17]=0x05=写入bit个数(1)+4
A[26]~A[27]=0x000A=10=DB10
A[28]=0x84=写入的数据类型为DB块,
A[29]~A[31]=0x000016=22=2*8+6=读取偏移量offset( bit为单位)
A[32]~A[33]=0x0003=写入方式为bit
A[34]~A[35]=0x0001=写入bit的个数(bit为单位)
A[36]= 写入数据[0或1]
03 00 00 24 02 F0 80 32 01 00 00 00 08 00 0E 00 05 05 01 12 0A 10 01 00 01 00 0A 84 00 00 16 00 03 00 01 01
PLC回复报文:
(B[12]~B[13]=0x0565=序列号,最后一个B[14]=0xFF表示写入)
03 00 00 16 02 F0 80 32 03 00 00 00 08 00 02 00 01 00 00 05 01 FF
写demo3:
1200 写 入:output0=4
PC发出报文:
A[3]~A[4]=0x0024=36=读取报文总长度,
A[12]~A[13]=0x0008=序列号,
A[16]~A[17]=0x05=写入byte个数(1)+4
A[23]=0x02=写入方式为byte,
A[24]~A[25]=0x0001=1=写入个数count;
A[26]~A[27]=0x0001=DB1(因为是output,所以DB块编号无所谓),
A[28]=0x82=写入的数据类型为output,
A[29]~A[31]=0x000000=读取偏移量offset( bit为单位)
A[32]~A[33]=0x0004=写入方式为byte ,
A[34]~A[35]=0x0008=1*8=写入byte的个数 ,A[36]= 写入数据)
03 0000 24 02 F0 80 32 01 00 00 00 08 00 0E 00 05 05 01 12 0A 10 02 00 01 00 01 82 00 00 00 00 04 00 08 04
PLC回复报文:
B[12]~B[13]=0x0565=序列号,最后一个B[14]=0xFF表示写入
03 00 00 16 02 F0 80 32 03 00 00 00 08 00 02 00 01 00 00 05 01 FF
写demo4:
1200 写 输入:output 0.3=1
PC发出报文:
A[3]~A[4]=0x0024=36=写入报文总长度,
A[12]~A[13]=0x0003=序列号,
A[16]~A[17]=0x05=写入bit个数(1)+4
A[23]=0x01=写入方式为bit,
A[24]~A[25]=0x0001=1=写入个数count;
A[26]~A[27]=0x000A=10=DB10(因为是output,所以DB块编号无所谓),
A[28]=0x82=写入的数据类型为output,
A[29]~A[31]=0x000003=读取偏移量offset( bit为单位)
A[32]~A[33]=0x0003=写入方式为bit ,
A[34]~A[35]=0x0001=写入bit的个数(bit为单位) ,
A[36]= 写入数据[0或1]
03 00 00 24 02 F0 80 32 01 00 00 00 03 00 0E 00 05 05 01 12 0A 10 01 00 01 00 01 82 00 00 03 00 03 00 01 01
PLC回复报文:
B[12]~B[13]=0x0565=序列号,最后一个B[14]=0xFF表示写入
03 00 00 16 02 F0 80 32 03 00 00 00 03 00 02 00 01 00 00 05 01 FF
根据以上4个报文的demo,摸索出大致规律如下(未必完全正确,但是应付项目可以了);
B[1]~B[2]: 03 00 固定报文头;
B[14]: FF 标识写入正常;
注意点:
1.写入可以按byte和bit两种方法去操作;
2.对于byte,可以一口气写连续多个byte, 理论上一条指令连续写bit也可以,但是实践下来,发现有问题