西門子S7報文解析


1.報文的基本格式

1.1 第1和第2個字節是:固定報文頭03 00,這里我們就用到三種報文: a.初始化 b. 讀 c.寫,都是這種格式;

1.2 第3和第4個字節是:整個報文的長度;

       其它部分就是各種報文的個性化處理了;

       下面分析大量報文的案例進行規律分析,為了便於對照,每種都用1200 和300 兩種對照demo顯示:

 

2.初始化報文

初始化報文分兩個交互:

2.1 交互一
西門子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 不用配置的不同點,就一個地方:前者 CPUSlot = 1 ,后者CPUSlot = 2;
所以可以摸索規律是:
a.pc發起第一個初始化報文的時候,第18個字節標識了CPUSlot ;
b.plc回復報文和讀取報文長度一樣都是22個字節長度;
c.plc回復報文的最后一個字節也是CPUSlot ,這個可以用來校驗;
d. plc回復的第10個字節一個是06,一個是04,這個好像是小型號的區別;
細節摸索下來:1200該字節是06,314是04,315是03;咱寫程序的時候,就不要考慮這個來校驗了;

 

2.2交互二
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的報文后,就通過一個布爾變量通知整個系統可以正常讀寫;

 

3.讀操作

 

讀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

根據以上8個報文的demo,摸索出大致規律如下(未必完全正確,但是應付項目可以了);
A[1]~A[2]: 03 00 固定報文頭;
A[3]~A[4]: 00 1F 整個讀取請求長度為0x1F= 31 ;
A[5]~A[11]: 02 F0 80 32 01 00 00 固定6個字節;
A[12]~A[13]: 兩個字節,標識序列號,回復報文相同位置和這個完全一樣;范圍是0~65535;
A[14]~A[23]:00 0E 00 00 04 01 12 0A 10 02 固定10個字節
A[24]~A[25]:兩個字節,訪問數據的個數,以byte為單位;
A[26]~A[27]: DB塊的編號,比如DB50, 就是0x32=50, 兩個字節,范圍是0~65535(也許是一個1個字節,因為沒有設置估DB255以上的數據塊,所以不知道到底是幾個字節,姑且認為是2個字節);
A[28] : 訪問數據塊的類型:0x81-input ,0x82-output ,0x83-flag , 0x84-DB(這個最常見);
A[29]~A[31]: 訪問DB塊的偏移量offset (地址+1以byte為單位); 3個字節,范圍是0~16777216(一般 用不到這么大)


程序設計的時候,其實主要關注最后4個信息,即:
1. A[24]~A[25]: 訪問byte個數
2. A[26]~A[27]: DB塊編號
3. A[28] : 數據塊類型
4.A[29]~A[31] :訪問地址偏移量;相當於首地址編號
B[1]~B[2]: 03 00 固定報文頭
B[3]~B[4]: 整個讀取回復報文長度:25+讀取長度;
B[5]~B[11]: 02 F0 80 32 03 00 00 固定6個字節,和讀取請求相同的位置幾乎一樣,就 B[9]=0x03 ;A[9]=0x01;
B[12]~B[13]: 兩個字節,標識序列號,回復報文相同位置和這個完全一樣;范圍是0~65535;

B[14]~B[15]: 兩個字節,固定為00 02;對應讀取位置是 00 0E;正好 02+0E=10 ;有點補碼的感覺,其實不需要關注規律,反正是固定的;
B[16]~B[17]:兩個字節,=請求讀取的字節數+4;
B[18]~B[23]:6個字節,固定為:00 00 04 01 FF 04 ;
B[24]~B[25]:兩個字節, 請求訪問的byte個數*8 ;其實就是以二進制為單位的個數;由此可以看出,一口氣最多訪問的地址個數是8192;
B[26]~ 最后一個 :以offset作為首地址,所對應的各個byte的值;


程序設計的時候,其實只要關注兩個信息
1.校驗B[3]~B[4]:校驗長度正確;
2.B[26]~最后一個 :獲取對應的值;
到這里讀的處理就算結束了;幾個小注意點:
1. 對於不同信號的PLC,除了初始化的CPUSolt不同;正常讀/寫指令是一樣的;
2.讀的時候,都是以byte為單位的,如果程序只需要bit,那么還是以Byte為單位去讀,將讀出的部分按bit再去分解;
3.flag類型到底是什么,不是很清楚,有點類似三菱里的M點;這個也不需要去深究,一般項目里主要就是用DB塊;
4.讀取的長度如果是N(以byte為單位),那么返回的長度就是N*8(以bit為單位);怎么判斷長度是否要*8;主要看后面是不是緊挨着數據,
如果是數據,就需要*8;offset都是以bit為單位的;
5.正常讀的操作都是DB塊,所以在A[26]~A[27]這個字節寫入DB塊的編號,但是對於input,output,flags這三個類型,是不需要數據塊編號的,
不過我們可以隨便寫一個DB編號;


4.寫操作

寫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=18*8=讀取偏移量offset(bit為單位),A[32]~A[33]=0x0004=寫入方式為Byte , A[34]~A[35]=0x0010=2*8=寫入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 02 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)+4A[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,摸索出大致規律如下(未必完全正確,但是應付項目可以了);
A[1]~A[2]: 03 00 固定報文頭;
A[3]~A[4]: 整個報文長度:35+寫入長度;
A[5]~A[11]: 02 F0 80 32 01 00 00 固定6個字節(和讀取的完全一樣)
A[12]~A[13]: 兩個字節,標識序列號,回復報文相同位置和這個完全一樣;范圍是0~65535;
A[14]~A[15]:00 0E 固定2個字節;
A[16]~A[17]:寫入長度+4;
A[18]~A[22]: 05 01 12 0A 10 固定5個自己
A[23] : 寫入方式: 01-按bit寫入; 02-按byte寫入;
A[24]~A[25]:兩個字節,寫入數據的個數(可能是byte或bit, 按A[23]來區分)
A[26]~A[27]: DB塊的編號
A[28] : 寫入數據塊的類型:0x81-input ,0x82-output ,0x83-flag , 0x84-DB(這個最常見);
A[29]~A[31]: 寫入DB塊的偏移量offset (地址+1以byte為單位); 3個字節,范圍是0~16777216(一般 用不到這么大)A[32]~A[33]:寫入方式為: 03-按bit寫入; 04-按byte寫入;
A[34]~A[35]:寫入bit的個數(bit為單位)
A[36]~最后 : 連續的寫入值;

 

B[1]~B[2]: 03 00 固定報文頭;

B[14]: FF 標識寫入正常;

到這里,初始化,讀,寫 這3種方式都摸索完了,未必都正確,應付開發應該綽綽有余;
在接下來的時間里,就需要把這些規律變成相應的程序;


注意點:
1.寫入可以按byte和bit兩種方法去操作;
2.對於byte,可以一口氣寫連續多個byte, 理論上一條指令連續寫bit也可以,但是實踐下來,發現有問題,所以對於bit操作,我們就一個一個寫吧;


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM