在使用TwinCAT測試EtherCAT EOE功能時,我們會發現正常是無法使用Wireshark去進行網絡抓包抓取EtherCAT報文的,今天這篇文章就帶大家來上手EtherCAT抓包方式。
準備環境
硬件環境:
EtherKit開發板
網線一根
軟件環境
TwinCAT3
RT-Thread studio
wireshark
EtherCAT EOE工程下載
首先 RT-Thread studio 安裝 EtherKit SDK 包,然后新建一個示例工程:etherkit_ethercat_eoe,編譯并下載程序;

將電腦一端網線連接至 EtherKit 的 ETH0,同時修改以太網適配器IP保持與從站IP在同一網段下:

觀察開發板串口狀態,此時可以看到 eoe app 已經正常運行了:

TwinCAT3 模擬EtherCAT主站啟動
在啟動 TwinCAT3過程中還需要配置ESI文件及安裝Twin CAT驅動,可參考EtherKit用戶手冊,此處不再詳述;
接下來我們主站掃描從站設備,并激活該從站設備:

激活從站設備后可以看到 EtherKit 開發板板載以太網指示燈正常工作,同時默認從站DC模式為 SM-Synchron :

TwinCAT3配置DC-Synchron
首先我們需要開啟 wireshark 抓包支持,右鍵點擊 Device,在 Adapter 中勾選 Promiscuous Mode(use with Wireshark only)來開啟混雜模式;
接著我們需要先切換DC模式,由默認的 SM-Synchron 模式切換為 DC-Synchron;
接著我們點擊上方導航欄 TwinCAT->Restart TwinCAT(config Mode);
提示 Restart TwinCAT system in config Mode,點擊確定;
shell再次提示 Load IO設備,點擊確定;
然后點擊激活;
接著我們再切換回 SM-Synchron,并打開wireshark,選擇對應的網卡設備,此時就可以看到ECAT報文了;
EtherCAT數據幀結構
EtherCAT數據直接使用以太網數據幀進行傳輸,幀類型為0x88A4。一個EtherCAT數據包括2個字節的數據頭和44~1498字節的數據,其中數據區由一個或多個EtherCAT子報文組成,每個子報文對應獨立的設備或從站存儲區域;
EtherCAT幀結構定義
EtherCAT幀結構定義
每個EtherCAT子報文包括子報文頭、數據域和相應的工作計數器(WKC,Working Counter);WKC記錄了子報文被從站操作的次數,主站為每個通信服務子報文設置預期的WKC,發送子報文中的工作計數器初值為0,子報文被從站正確處理后,工作計數器將增加一個增量,主站比較返回子報文中的WKC和預期WKC來判斷子報文是否被正確處理;WKC由ESC在處理數據幀的同時進行處理,不同的通信服務對WKC的增加方式不同;
EtherCAT子報文結構定義
EtherCAT報文分析
1.請求報文
首先我們設置過濾規則:
ecat.cmd==BRD && ecat.ado==0x130
我們隨機抓取一條BRD報文,主站向從站發送的偏移地址為 0x130 的報文,表示讀取從站特定寄存器(如設備標識符、狀態字等)的值。此類報文在系統初始化或狀態監測中常被使用。
2.應答報文
從站啟動過程:主站依次向偏移地址 0x120 發送 1、2、4、8 命令,控制從站依次進入初始化(INIT)、預操作(PRE-OP)、安全模式(SAFE-OP)和操作模式(OP):
ecat.ado==0x120 && (ecat.adp==0x03e9 ||ecat.adp==0xffff)
其中 ecat.adp == 0xffff 表示廣播方式,主站向所有從站發送命令;而 ecat.adp==0x03e9(例如)表示特定從站地址(可根據實際地址修改)發送控制命令。
3.控制命令與EOE報文過濾
我們使用如下過濾規則來抓取EtherCAT控制命令與以太網封裝(EOE, Ethernet over EtherCAT)相關的報文:
(ecat.ado==0x120 || ecat_mailbox.eoe) && (ecat.adp==0x03e9 || ecat.adp==0xffff)
解析說明
ecat.ado==0x120:表示抓取訪問地址偏移 0x120 的寄存器命令,此為 從站狀態控制寄存器,主站通過它向從站發送模式切換指令(如INIT、PRE-OP、SAFE-OP、OP);
ecat_mailbox.eoe:表示抓取所有 EOE協議封裝的以太網數據,EOE允許通過EtherCAT傳輸標準以太網幀(如TCP/IP、UDP);
ecat.adp==0x03e9:指定從站地址為 0x03e9(十進制1001),用于單個從站點對點通信;
ecat.adp==0xffff:表示廣播命令,主站向所有從站同時發起操作。
應用場景
此過濾規則可用于同時監控:
主站對某個從站(或全部從站)的工作模式控制行為;
通過EOE傳輸的數據幀(常見于使用TCP/IP通信的EtherCAT從站,如帶IP接口的遠程IO模塊或工業攝像頭);
下面是使用Wireshark實際抓包情況:
4. EtherCAT EOE抓包TCP報文
首先我們修改工程目錄下的 src/hal_entry.c 文件,將該文件全部替換為如下代碼:
/** Copyright (c) 2006-2024, RT-Thread Development Team** SPDX-License-Identifier: Apache-2.0** Change Logs:* Date Author Notes* 2024-03-11 Wangyuqiang first version*/#include#include"hal_data.h"#include#include#include#include#include#include#defineBUFSZ (1024)staticconstcharsend_data[] ="This is TCP Server from RT-Thread.";voidhal_entry(void){ rt_kprintf("\nHello RT-Thread!\n"); rt_kprintf("==================================================\n"); rt_kprintf("This example project is an ethercat eoe routine!\n"); rt_kprintf("==================================================\n");}staticvoidtcpserv(void*parameter){ char*recv_data;/* 用于接收的指針,后面會做一次動態分配以請求可用內存 */ socklen_tsin_size; intsock, connected, bytes_received; structsockaddr_inserver_addr, client_addr; rt_bool_tstop = RT_FALSE;/* 停止標志 */ intret; recv_data =rt_malloc(BUFSZ +1);/* 分配接收用的數據緩沖 */ if(recv_data == RT_NULL) { rt_kprintf("No memory\n"); return; } /* 一個socket在使用前,需要預先創建出來,指定SOCK_STREAM為TCP的socket */ if((sock =socket(AF_INET, SOCK_STREAM,0)) ==-1) { /* 創建失敗的錯誤處理 */ rt_kprintf("Socket error\n"); /* 釋放已分配的接收緩沖 */ rt_free(recv_data); return; } /* 初始化服務端地址 */ server_addr.sin_family = AF_INET; server_addr.sin_port =htons(5000);/* 服務端工作的端口 */ server_addr.sin_addr.s_addr = INADDR_ANY; rt_memset(&(server_addr.sin_zero),0,sizeof(server_addr.sin_zero)); /* 綁定socket到服務端地址 */ if(bind(sock, (structsockaddr *)&server_addr,sizeof(structsockaddr)) ==-1) { /* 綁定失敗 */ rt_kprintf("Unable to bind\n"); /* 釋放已分配的接收緩沖 */ rt_free(recv_data); return; } /* 在socket上進行監聽 */ if(listen(sock,5) ==-1) { rt_kprintf("Listen error\n"); /* release recv buffer */ rt_free(recv_data); return; } rt_kprintf("\nTCPServer Waiting for client on port 5000...\n"); while(stop != RT_TRUE) { sin_size =sizeof(structsockaddr_in); /* 接受一個客戶端連接socket的請求,這個函數調用是阻塞式的 */ connected =accept(sock, (structsockaddr *)&client_addr, &sin_size); /* 返回的是連接成功的socket */ if(connected 0)? ? ? ? {? ? ? ? ? ? rt_kprintf("accept connection failed! errno = %d\n", errno);? ? ? ? ? ? continue;? ? ? ? }? ? ? ? /* 接受返回的client_addr指向了客戶端的地址信息 */? ? ? ? rt_kprintf("I got a connection from (%s , %d)\n",? ? ? ? ? ? ? ? ? ?inet_ntoa(client_addr.sin_addr),?ntohs(client_addr.sin_port));? ? ? ? /* 客戶端連接的處理 */? ? ? ? while?(1)? ? ? ? {? ? ? ? ? ? /* 發送數據到connected socket */? ? ? ? ? ? ret =?send(connected, send_data,?strlen(send_data),?0);? ? ? ? ? ? if?(ret 0)? ? ? ? ? ? {? ? ? ? ? ? ? ? /* 發送失敗,關閉這個連接 */? ? ? ? ? ? ? ? closesocket(connected);? ? ? ? ? ? ? ? rt_kprintf("\nsend error,close the socket.\r\n");? ? ? ? ? ? ? ? break;? ? ? ? ? ? }? ? ? ? ? ? else?if?(ret ==?0)? ? ? ? ? ? {? ? ? ? ? ? ? ? /* 打印send函數返回值為0的警告信息 */? ? ? ? ? ? ? ? rt_kprintf("\n Send warning,send function return 0.\r\n");? ? ? ? ? ? }? ? ? ? ? ? /* 從connected socket中接收數據,接收buffer是1024大小,但并不一定能夠收到1024大小的數據 */? ? ? ? ? ? bytes_received =?recv(connected, recv_data, BUFSZ,?0);? ? ? ? ? ? if?(bytes_received 0)? ? ? ? ? ? {? ? ? ? ? ? ? ? /* 接收失敗,關閉這個connected socket */? ? ? ? ? ? ? ? closesocket(connected);? ? ? ? ? ? ? ? break;? ? ? ? ? ? }? ? ? ? ? ? else?if?(bytes_received ==?0)? ? ? ? ? ? {? ? ? ? ? ? ? ? /* 打印recv函數返回值為0的警告信息 */? ? ? ? ? ? ? ? rt_kprintf("\nReceived warning,recv function return 0.\r\n");? ? ? ? ? ? ? ? closesocket(connected);? ? ? ? ? ? ? ? break;? ? ? ? ? ? }? ? ? ? ? ? /* 有接收到數據,把末端清零 */? ? ? ? ? ? recv_data[bytes_received] =?'\0';? ? ? ? ? ? if?(strcmp(recv_data,?"q") ==?0?||?strcmp(recv_data,?"Q") ==?0)? ? ? ? ? ? {? ? ? ? ? ? ? ? /* 如果是首字母是q或Q,關閉這個連接 */? ? ? ? ? ? ? ? closesocket(connected);? ? ? ? ? ? ? ? break;? ? ? ? ? ? }? ? ? ? ? ? else?if?(strcmp(recv_data,?"exit") ==?0)? ? ? ? ? ? {? ? ? ? ? ? ? ? /* 如果接收的是exit,則關閉整個服務端 */? ? ? ? ? ? ? ? closesocket(connected);? ? ? ? ? ? ? ? stop = RT_TRUE;? ? ? ? ? ? ? ? break;? ? ? ? ? ? }? ? ? ? ? ? else? ? ? ? ? ? {? ? ? ? ? ? ? ? /* 在控制終端顯示收到的數據 */? ? ? ? ? ? ? ? rt_kprintf("RECEIVED DATA = %s \n", recv_data);? ? ? ? ? ? }? ? ? ? }? ? }? ? /* 退出服務 */? ? closesocket(sock);? ? /* 釋放接收緩沖 */? ? rt_free(recv_data);? ? return?;}static?int?tcpserv_app(void){? ? rt_thread_t?tcps =?rt_thread_create("tcpserv", tcpserv, RT_NULL,?2048,?18,?10);? ? if(tcps != RT_NULL)? ? {? ? ? ? rt_thread_startup(tcps);? ? }? ? return?0;}
編譯下載后重新燒錄程序并啟動,在從站成功連接到主站后,執行 tcpserv_app 指令來啟動一個 TCP Server:

同時我們打開 TCP 測試軟件,配置 TCP 客戶端,配置信息如下:
目標IP:192.168.10.100(從站IP)
目標端口:5000

同時打開 Wireshark ,查看 EtherCAT EOE網絡下的 TCP報文能夠被成功捕獲:

附錄1 EtherCAT通信服務命令
EtherCAT 子報文所有的服務都是以主站操作描述的。數據鏈路層去規定了從站內部物理存儲、讀寫和交換(讀取并馬上寫入)數據的服務。讀寫操作和尋址方式共同決定了子報文的通信服務類型, 由子報文頭中的命令字節表示:

主站接收到返回數據幀后,檢查子報文中的WKC,如果不等于預期值,則表示此子報文沒有被正確處理。子報文的WKC預期值與通信服務類型和尋址地址相關。子報文經過某一個從站時,如果是單獨地讀或寫操作, WKC 加1 。如果是讀寫操作, 讀成功時WKC加1,寫成功時WKC 加2 ,讀寫全部完成時WKC加3,子報文由多個從站處理時, WKC是各個從站處理結果的累加。
附錄2 EtherCAT狀態碼
-
網絡
+關注
關注
14文章
7734瀏覽量
90248 -
開發板
+關注
關注
25文章
5448瀏覽量
101444 -
ethercat
+關注
關注
19文章
956瀏覽量
39550
發布評論請先 登錄
如何使用TwinCAT3內部的函數來獲取系統時間?
如何去使用TwinCAT3中SCOPE功能?
一種倍福TwinCAT3中讀取臺達伺服扭矩
倍福TwinCAT(貝福Beckhoff)基礎教程 TwinCAT安裝配置
使用TwinCAT3實現高級測量數據處理
TwinCAT3的入門教程詳細說明
TwinCAT 3運動控制教程Version 1.0
RX72M單芯片EtherCAT伺服方案(下)操作說明與規范
如何基于TwinCAT3實現伺服電機控制

評論