:open62541 客戶端連接 PLC 全流程)
1. 目標與原理IoT2050(OPC UA 客戶端,open62541) ──opc.tcp://PLC_IP:4840──? S7-1200(OPC UA 服務器)IoT2050 作為邊緣網關,通過 OPC UA 采集 PLC 數(shù)據(jù)塊中的變量。2. 前置條件(缺一不可,先核對)條件要求說明CPU 固件經典款 ≥ V4.4 / G2 ≥ V4.1S7-1200 經典款從 FW V4.4 起內置 OPC UA 服務器;S7-1200 G2 從 V4.1 起組態(tài)軟件TIA Portal ≥ V16(G2 需 V21)V16 起才能組態(tài) V4.4 的 S7-1200運行許可證SIMATIC OPC UA S7-1200 Basic(訂貨號6ES7823-0BA00-2BA0紙質 /6ES7823-0BE00-2BA0下載版)一個 PLC 一個授權。沒有授權 OPC UA 服務器不工作(或使用與 CPU 關聯(lián)的 Basic 許可證)網絡IoT2050 與 PLC 同網段、可互相 ping 通客戶端地址固定格式opc.tcp://PLC_IP:48403. TIA Portal 側配置(關鍵步驟)3.1 激活 OPC UA 服務器設備視圖 → 選中 CPU → 屬性 →OPC UA → 服務器→ 勾選"激活 OPC UA 服務器"。激活后服務器地址固定為:opc.tcp://PLC的IP:4840。??如果項目樹里看不到"OPC UA 通信"目錄:先確認是否激活了 OPC UA 服務器(有些版本激活并編譯后才出現(xiàn)),再確認固件版本達標。3.2 建模"服務器接口"(S7-1200 特有,必做?。㏒7-1200 與 S7-1500 的關鍵區(qū)別:S7-1500S7-1200(本文場景)勾選 DB"允許 OPC UA 訪問"? 下載后變量自動出現(xiàn)?不夠!服務器接口(Server Interface)可選必須手動建模!S7-1200不支持標準 SIMATIC 服務器接口,光勾選 DB 的"允許 OPC UA 訪問"屬性沒用,必須手動建模接口并把變量拖進去,編譯下載后才對客戶端可見——這是新手最常踩的坑。創(chuàng)建服務器接口(TIA 不同版本入口不同):方式 A(TIA V16~V20 通用):項目樹 → 展開 CPU →OPC UA 通信 服務器接口→ 雙擊"添加新服務器接口"方式 B(TIA V21,可能):項目樹 → CPU →"程序塊" → 雙擊"添加新塊"→ 對象類型選"OPC UA 服務器接口"命名并確認后,在接口表格的"新增"行,從左側拖入要暴露的DB 或單個變量。??數(shù)組注意:S7-1200 OPC UA 每個數(shù)組元素算一個變量,上限 2000 個。如果 DB 里有兩個Array[0..999]共 2000 個元素會頂?shù)缴舷?,建議新建一個小的優(yōu)化訪問 DB放幾個變量用于測試,或只拖入數(shù)組的前幾個元素。??非優(yōu)化 DB 兼容性:OPC UA 僅支持符號訪問。非優(yōu)化尋址的 DB(如 snap7 用的)只要有符號名,同樣可以拖入服務器接口;但穩(wěn)妥起見,驗證階段建議用優(yōu)化訪問的小 DB。3.3 安全策略 + 運行許可證 + 下載安全策略:調試期 CPU 屬性 →OPC UA → 安全,勾選"無安全設置"(None)最省事;生產環(huán)境務必用Basic256Sha256+ 證書,并禁用其它策略。運行許可證:CPU 屬性 →運行系統(tǒng)許可證 → OPC UA→ 設置購買的許可證類型(SIMATIC OPC UA S7-1200 Basic)。可信客戶端(可選):調試期可勾選"運行時自動接受客戶端證書",免去手動配置可信列表。編譯項目 →下載到 PLC(硬件和軟件都下載)。4. IoT2050 側驗證(open62541 客戶端)4.1 第一步:瀏覽地址空間,確認變量已暴露用 open62541 寫一個 C 客戶端,遞歸瀏覽地址空間(跳過 Server 系統(tǒng)子樹,只看 DeviceSet 下的 PLC 數(shù)據(jù)):cat /tmp/ua_client_test.c 'EOF' #include open62541/client.h #include open62541/client_highlevel.h #include open62541/client_config_default.h #include stdio.h ? static UA_Boolean isInstance(UA_NodeClass nc) { return nc == UA_NODECLASS_OBJECT || nc == UA_NODECLASS_VARIABLE; } ? static void browseRecursive(UA_Client *client, UA_NodeId node, int depth) { if (depth 6) return; UA_BrowseRequest bReq; UA_BrowseRequest_init(bReq); bReq.requestedMaxReferencesPerNode = 100; bReq.nodesToBrowse = UA_BrowseDescription_new(); bReq.nodesToBr