早年間在濟南鋼廠焦爐四大機車自動化項目中,我們已通過 OPC 技術實現遠程數據交互,借助 RSLink 服務器軟件,基于 OPC DA 規范完成對羅克韋爾 Logix 5500 系列 PLC 的間接控制,這也是 OPC 技術在工業場景的典型早期應用。
回溯OPC的發展歷程,1995 年Rockwell Automation、Siemens、Honeywell 等工業巨頭聯合成立 OPC 基金會,同年完成首個 OPC 數據訪問規范(OPC DA)草案,該規范基于微軟 COM/DCOM 技術構建,為工業數據傳輸確立了統一標準。2001 年,規范家族進一步擴展,新增歷史數據訪問(HDA)、報警與事件(AE)及安全規范,OPC 技術迅速普及,成為工業自動化領域的事實標準,會員企業規模突破百家。不過,以 OPC DA 為核心的 OPC Classic 存在明顯局限,因依賴 Windows COM/DCOM 技術,無法適配 Linux 或嵌入式系統,限制了其應用場景的拓展。
為突破這一技術瓶頸,2003 年 11 月,OPC UA 工作組在德國啟動開發工作,核心目標是擺脫 COM/DCOM 依賴,采用面向服務架構(SOA)重塑技術體系。同年 8 月,OPC UA 1.0 規范正式發布,標志著 OPC 技術進入跨平臺時代。2008 年,OPC UA 被國際電工委員會(IEC)采納為 IEC 62541 標準,完成國際標準化認證;2010 年,首個嵌入式 OPC UA 設備問世,實現了無操作系統 “裸奔” 運行,徹底打破了傳統 OPC 的部署限制。2017 年 11 月,OPC UA 1.04 版本發布,引入發布 - 訂閱(PubSub)機制,大幅提升了數據傳輸效率與實時性,更好地滿足了大規模工業數據交互需求。

2022 年,OPC UA 技術進一步深化,新增現場交換(UAFX)功能,專為控制器間(C2C)通信設計,支持離線配置與高實時性傳輸,完美適配工業機器人、CNC 等高精度控制場景。如今,OPC UA 已成為工業 4.0 與工業物聯網(IIoT)的核心通信標準,全球 90% 以上的工業自動化廠商均提供支持,真正實現了跨平臺、跨設備、跨行業的無縫數據互聯。
睿擎派是睿賽德公司推出的一款工業開發板,以瑞芯微 RK3506/RK3563 為主控芯片,底層搭載 RT-Thread 操作系統,基于專為工業場景打造的睿擎工業平臺進行開發,該平臺是全棧自主可控的軟硬件一體化解決方案,整合了數據采集、通信、控制、工業協議、AI、顯示六大核心功能,精準適配工業應用需求。所以常見的工業總線通信協議,比如CAN Open、EtherCAT和OPC UA都是支持的。
另外國內工控領域,相對于AB的PLC,西門子的PLC反而應用更為廣泛,我們早期就采用S7-200/300/400系列進行工業自動化系統開發。當下西門子主推S7-200Smart,S7-1200和S7-1500。其中S7-1200 固件V4.4版本以上開始支持OPC Server UA。
由此為了深度評測睿擎派的OPC UA能力,我們選用了S7-1200與之對接,實現遠程操控PLC的目的。
一、S7-1200開發環境搭建、編碼和OPC配置
S7-1200是德國西門子公司的PLC產品,采用TIA博途軟件進行開發。從2009年推出以來,基本上每一兩年就推出一個新版本,當前最新版本是V21,考慮到安裝包大小和常用功能,我選擇的是2022年推出的V18版本進行安裝。
安裝成功以后,在配置OPC Server的時候發現,一旦開啟OPC Server功能,部署的時候就會異常,如下網頁有相關的描述。
https://support.industry.siemens.com/cs/document/109971630/tia-portal-crash-when-compiling-the-plc?dti=0&lc=en-WW
如果是V18版本,安裝如下補丁,就可以解決相關問題。
https://pan.baidu.com/s/1NIjENUkc2GurhxkbKQQJDg提取碼:pfy6
為了便于測試,我們用梯形圖在PLC中編寫如下功能:
(1)開關量輸入I0.0和繼電器Q0.0聯動(為了便于中間可控制,通過上升沿和下降沿信號置位和復位的方式來操控繼電器)

(2)開啟一個定時器,以5秒為間隔,打開和關閉繼電器Q0.1

(3)實現計數器功能,便于OPC客戶端連續顯示一個不斷變化的數字

程序編寫完畢后,在PLC變量表里會顯示相關的變量,當然我們也可以自行增加PLC變量,便于和OPC客戶端交互。

這些工作完成后,我們就可以開始進行OPC UA相關的變量配置了。

右側窗口中的OPC UA元素可以拖動進入到中間的OPC UA服務器接口窗口,會自動添加一條變量。這個順序很重要,是后續OPC UA客戶端讀取數據的一個根據(不過如果中間有刪除,OPC中的索引順序并不連續)。
配置完畢后,連帶程序代碼就可以一起部署到PLC了。
(注意:S7-1200 V4.4以上才支持OPC Server UA。 我當下S7-1200的版本是V4.2.3,所以不支持OPC Server。需要在西門子官方網站下載最新的固件,然后升級PLC即可。當前最新的版本為V4.6.1)

部署成功后,轉至在線狀態,然后讓PLC進入運行狀態(RUN)。
二、OPC UA客戶端軟件UaExpert對接測試
由于OPC UA客戶端是通過命名空間索引和變量索引獲取數據的,所以我們先用OPC UA客戶端工具定位相關變量的索引,順便也測試一下OPC Server是否可以正常工作。
RTT官方示例中有相關工具的下載和使用說明。
https://www.rt-thread.com/ruiching/document/site/rc3506/qy1k2kok/#%E8%BF%90%E8%A1%8C-opc-ua-%E7%A4%BA%E4%BE%8B
OPC UA客戶端:
https://www.unified-automation.com/downloads/opc-ua-clients.html

我們PLC的OPC UA Server IP是192.168.1.200,端口是 4840,按上圖添加。

連接成功后,把右側的服務器接口_1(這個名字,其實是PLC中定義的)標簽可以拖動到中間的窗口,則所有的變量會呈現出來。其中如下紅框里面的部分,對后續的數據讀取格外重要。

三、睿擎派測試代碼開發
我們有睿擎派RK3506的開發板和一個4.3英寸的MIPI-DSV的LCD顯示觸摸屏,所以采lvgl和opc ua技術棧來實現對S7-1200遠程操控的功能。
所參考的官方示例分別為:
(1)03_network_opc_ua
示例有opc ua客戶端和服務端功能,我們只參考客戶端功能即可。
(2)05_gui_lvgl_ethercat_motor_control_7in_1024_600
雖然是7寸屏的功能示例,但是動態顯示電機狀態的代碼我們可以參考。
(3)05_gui_lvgl_mipi_ruiching_4_3in_480_800。
LVGL圖形界面完整的示例,可以參考各種控件的功能實現。

有了以上的代碼參考,我們要實現的功能其實也蠻簡單,就是顯示一個S7-1200的PLC圖片,IO狀態燈和真實的PLC同步顯示出來。然后增加四個Q繼電器按鈕,可以遠程開關PLC的四個繼電器。另外就是有一個標簽,實時顯示PLC里面的計數值。
1、LVGL界面實現
睿擎派RK3506 V1.7.2SDK集成的LVGL為V9.1.0版本,為2024年3月20日發布的。當前最新版本為V9.4.0于2025年10月16日發布。主要差異就是最新版本支持3D模型和GPU擴展支持。
關于圖片,網上可以搜索S7-1200的正面圖片,我實際搜索了一下,正面圖很少(和我當前型號契合的),并且分辨率不高和模糊,下載后,只好用PS工具加工了一下。圖片的尺寸需要和LCD顯示器適配(480800),所以圖片我們設定為450480。并保存為png格式。
打開LVGL官方圖片在線工具:
https://lvgl.io/tools/imageconverter
把圖片導入,會生成對應的C代碼文件。
同樣我們還需要顯示一些漢字內容,也需要用官方文字在線工具進行C代碼文件生成,LVGL官方文字在線生成工具:
https://lvgl.io/tools/fontconverter
以上的功能的主要實現代碼如下:
//默認開啟字體 14、18、22 font_large = &lv_font_montserrat_22; font_normal = &lv_font_montserrat_14; lv_obj_set_style_text_font(lv_screen_active(), font_normal,0); lv_obj_t*title_label =lv_label_create(lv_screen_active()); lv_obj_set_style_text_color(title_label,lv_color_hex(COLOR_DIALOG_TEXT), LV_PART_MAIN); lv_obj_set_style_text_font(title_label, &YF32HZ, LV_PART_MAIN); lv_label_set_text_fmt(title_label,"%s","睿擎派OPC-UA對接S7-1200演示"); lv_obj_set_pos(title_label,20,30); num_label =lv_label_create(lv_screen_active()); lv_obj_set_style_text_color(num_label,lv_color_hex(COLOR_BTN_PRESS), LV_PART_MAIN); lv_obj_set_style_text_font(num_label, font_large, LV_PART_MAIN); lv_label_set_text_fmt(num_label,"NUM: %06d",0); lv_obj_set_pos(num_label,160,100); lv_obj_t*plc_img =lv_img_create(lv_screen_active()); lv_img_set_src(plc_img, &S71200W ); lv_obj_set_pos(plc_img,15,160);
四個按鈕實現的代碼如下:
voidlv_create_do_button(void){ lv_color_tbtn_bg_color =lv_color_hex(COLOR_BTN_BG); lv_color_tbtn_press_color =lv_color_hex(COLOR_BTN_PRESS); for(uint8_ti =0; i < BTN_CNT; i++)? ? {? ? ? ? lv_obj_t?*btn =?lv_btn_create(lv_screen_active());? ? ? ? lv_obj_set_size(btn, BTN_WIDTH, BTN_HEIGHT);? ? ? ? lv_coord_t?curr_btn_x = BTN_START_X + i * (BTN_WIDTH + BTN_GAP);? ? ? ? lv_obj_set_pos(btn, curr_btn_x, BTN_START_Y);? ? ? ? lv_obj_set_style_bg_color(btn, btn_bg_color, LV_PART_MAIN); ? ? ? ?// 默認背景色? ? ? ? lv_obj_set_style_bg_color(btn, btn_press_color, LV_STATE_PRESSED);?// 按下背景色? ? ? ? lv_obj_set_style_radius(btn,?4, LV_PART_MAIN); ? ? ? ? ? ? ? ? ? ? // 圓角? ? ? ? lv_obj_set_style_pad_all(btn,?0, LV_PART_MAIN); ? ? ? ? ? ? ? ? ? ?// 無內邊距? ? ? ? lv_obj_set_style_border_width(btn,?0, LV_PART_MAIN); ? ? ? ? ? ? ? // 無邊框? ? ? ? lv_obj_set_style_bg_opa(btn, LV_OPA_COVER, LV_PART_MAIN); ? ? ? ? ?// 不透明? ? ? ? btn_label[i] =?lv_label_create(btn);? ? ? ? lv_label_set_text(btn_label[i], btn_text_arr[i]);? ? ? ? lv_obj_set_style_text_color(btn_label[i],?lv_color_hex(COLOR_BTN_TEXT), LV_PART_MAIN);? ? ? ? lv_obj_set_style_text_font(btn_label[i], font_large, LV_PART_MAIN);?// 正確字體調用? ? ? ? lv_obj_center(btn_label[i]);?// 文字水平+垂直絕對居中? ? ? ? lv_obj_add_event_cb(btn, btn_click_event_cb, LV_EVENT_CLICKED,?NULL);? ? }}
19個指示燈實現的代碼如下:
voidlv_draw_state_led(void){ // ===================== 全局固定參數定義 ===================== constlv_coord_trect_w_h =8; // 矩形尺寸:寬8px、高8px constlv_coord_trect_space =7; // 矩形之間的物理間隔:7像素(核心要求) // ===================== PLC 狀態 ===================== // 起始坐標:x=15+37 y=160+178 | 數量:3個 | 間隔7px | 8*8 | 橙色 lv_coord_tplc_state_x_start =15+37; lv_coord_tplc_state_y_start =160+178; for(uint8_ti =0; i 3; i++)? ? {? ? ? ? // 1. 創建矩形對象(LVGL9.1 屏幕對象API:lv_screen_active())? ? ? ? plc_state[i] =?lv_obj_create(lv_screen_active());? ? ? ? // 2. 設置矩形尺寸 8*8(固定)? ? ? ? lv_obj_set_size(plc_state[i], rect_w_h, rect_w_h);? ? ? ? // 3. 計算X坐標(核心:保證間距7px)、Y坐標固定? ? ? ? lv_coord_t?curr_x = plc_state_x_start + i * (rect_w_h + rect_space);? ? ? ? lv_obj_set_pos(plc_state[i], curr_x, plc_state_y_start);? ? ? ? // 4. 樣式配置:純色填充、直角、無邊框(小矩形標準樣式)? ? ? ? lv_obj_set_style_bg_color(plc_state[i],?lv_color_hex(COLOR_BLACK), LV_PART_MAIN);? ? ? ? lv_obj_set_style_radius(plc_state[i],?0, LV_PART_MAIN); ? ? ? ? ? ? ? ?// 圓角0 → 純矩形? ? ? ? lv_obj_set_style_border_width(plc_state[i],?0, LV_PART_MAIN); ? ? ? ? ?// 無邊框(無多余線條)? ? ? ? lv_obj_set_style_pad_all(plc_state[i],?0, LV_PART_MAIN); ? ? ? ? ? ? ? // 無內邊距? ? }? ? // ===================== PLC DI =====================? ? // 起始坐標:x=15+298 ?y=160+177 | 數量:6個 | 間隔7px | 8*8 | 綠色? ? lv_coord_t?plc_di_x_start =?15?+?298;? ? lv_coord_t?plc_di_y_start =?160?+?177;? ? for(uint8_t?i =?0; i 8; i++)? ? {? ? ? ? plc_di[i] =?lv_obj_create(lv_screen_active());? ? ? ? lv_obj_set_size(plc_di[i], rect_w_h, rect_w_h);? ? ? ? lv_coord_t?curr_x = plc_di_x_start + i * (rect_w_h + rect_space);? ? ? ? lv_obj_set_pos(plc_di[i], curr_x, plc_di_y_start);? ? ? ? lv_obj_set_style_bg_color(plc_di[i],?lv_color_hex(COLOR_BLACK), LV_PART_MAIN);? ? ? ? lv_obj_set_style_radius(plc_di[i],?0, LV_PART_MAIN);? ? ? ? lv_obj_set_style_border_width(plc_di[i],?0, LV_PART_MAIN);? ? ? ? lv_obj_set_style_pad_all(plc_di[i],?0, LV_PART_MAIN);? ? }? ? // ===================== PLC DQ =====================? ? // 起始坐標:x=15+298 ?y=160+299 | 數量:4個 | 間隔7px | 8*8 | 綠色? ? lv_coord_t?plc_do_x_start =?15?+?298;? ? lv_coord_t?plc_do_y_start =?160?+?299;? ? for(uint8_t?i =?0; i 8; i++)? ? {? ? ? ? plc_do[i] =?lv_obj_create(lv_screen_active());? ? ? ? lv_obj_set_size(plc_do[i], rect_w_h, rect_w_h);? ? ? ? lv_coord_t?curr_x = plc_do_x_start + i * (rect_w_h + rect_space);? ? ? ? lv_obj_set_pos(plc_do[i], curr_x, plc_do_y_start);? ? ? ? lv_obj_set_style_bg_color(plc_do[i],?lv_color_hex(COLOR_BLACK), LV_PART_MAIN);? ? ? ? lv_obj_set_style_radius(plc_do[i],?0, LV_PART_MAIN);? ? ? ? lv_obj_set_style_border_width(plc_do[i],?0, LV_PART_MAIN);? ? ? ? lv_obj_set_style_pad_all(plc_do[i],?0, LV_PART_MAIN);? ? }}
初始的時候,LED燈都顯示黑色。OPC Server連接成功后,PLC RUN燈位綠色,否則為橙色。開關量輸入和輸出燈根據實際狀態進行變化。
2 OPC UA客戶端功能實現
睿擎派RK3506 V1.7.2 SDK集成的open62541為V1.2.2版本,2019年9月18日發布的,屬于早期經典版,基礎功能完善,封裝相對簡單。最新工業級穩定版本為V1.5.1,2024年11月12日發布。核心區別如下,V1.2.2沒有批量讀取變量的函數,無PubSub(發布-訂閱)功能,V1.5.1原生適配RT-Thread,內存優化,但是需要C99編譯支持。
OPC UA客戶端有三個關鍵功能函數,我們專門創建open62541_client.c文件來實現,由于我們本代碼示例是基于05_gui_lvgl_mipi_ruiching_4_3in_480_800創建的,所以需要雙擊“RunChing Setings”項,開啟OPC UA功能,如下圖所示:

注:開啟OPC UA功能后,\opc_ua_lvgl_s7_1200\rt-thread\components\net_apps\open62541的目錄并沒有加入到Includes目錄,記得要添加上,否則對應的頭文件編譯時會提示找不到。
(1) opc ua server連接
intopen62541_connect(char*ip,intport){ charip_data[128] = {0}; rt_sprintf(ip_data,"opc.tcp://%s:%d", ip, port); if(client==NULL) { client =UA_Client_new(); UA_ClientConfig_setDefault(UA_Client_getConfig(client)); } UA_StatusCode retval =UA_Client_connect(client, ip_data); if(retval != UA_STATUSCODE_GOOD) { UA_Client_delete(client); client =NULL; return(int)retval; } return(int)retval;}
提供ip地址和端口即可,目前沒有開啟安全驗證,所以相對簡單。
(2) 讀取變量
目前我們只讀取了兩種類型的變量,就是布爾型和整型,代碼如下:
intopen62541_get_value(intns,inti,int*value){ if(client==NULL)return-1; UA_Variant read_value; UA_Variant_init(&read_value); UA_StatusCode retval = UA_Client_readValueAttribute(client,UA_NODEID_NUMERIC(ns,i), &read_value); if(retval == UA_STATUSCODE_GOOD) { UA_UInt32 type_num = -1; //提取類型編號 if(read_value.type != NULL) { type_num = read_value.type->typeIndex; } //Boolean if(type_num==0) { UA_Boolean *p = (UA_Boolean *)read_value.data; *value = (int)*p; } //UInt32 elseif(type_num==4) { UA_UInt32 *p = (UA_UInt32 *)read_value.data; *value = (int)*p; } else{ rt_kprintf("[err]type_num=%d\n", type_num); } //rt_kprintf(" - 類型:%s(編號:%u) arrayLength =%d\n", ua_typeid_to_name(type_num), type_num,read_value.arrayLength); } else { rt_kprintf("get [%d.%d] failed, code:%d\n", ns,i, retval); } UA_Variant_clear(&read_value); returnretval;}
(3)寫變量
我們目前是操作繼電器Q變量,該變量是布爾型,所以代碼僅支持該類型的寫操作。
intopen62541_set_value(intns,inti,UA_Boolean value){ if(client==NULL)return-1; UA_Variant write_value; UA_Variant_setScalar(&write_value, &value, &UA_TYPES[UA_TYPES_BOOLEAN]); UA_StatusCode retval = UA_Client_writeValueAttribute(client, UA_NODEID_NUMERIC(ns,i), &write_value); if(retval != UA_STATUSCODE_GOOD) { rt_kprintf("set [%d.%d] failed retval =%d\n",ns,i,retval); } return(int)retval;}
3 變量遠程實時讀寫
opc讀寫函數中的參數 int ns,int i 就對應了OPC客戶端工具畫紅框的部分,比如“NS4|Numeric|8”, 相應的ns=4,i=8;
LVGL不支持多線程操作,所以需要創建一個LVGL 定時器來定時刷新數據,另外由于定時器函數屬于UI線程回調,如果里面做長時間操作,會堵塞UI線程,界面操作會很卡。所以需要新創建一個線程來實現OPC UA變量的實時讀取。
static void opc_thread_entry(void *parameter){ intstate[10]; interr_count =0; intidx[10]={4,5,6,7,8,10,11,12,13,14}; while(1) { if(plc_connect_state!=0) { if(open62541_connect("192.168.1.200",4840)==0) { plc_connect_state =0; } } else { for(inti=0;i<10;i++){? ? ? ? ? ? ? ? ?if(open62541_get_value(4,idx[i],&state[i])==0){? ? ? ? ? ? ? ? ? ? io_state[i] =?state[i];? ? ? ? ? ? ? ? ? ? err_count =?0;? ? ? ? ? ? ? ? ?}? ? ? ? ? ? ? ? ?else?{? ? ? ? ? ? ? ? ? ? ?rt_kprintf("open62541_get_value?%d?err!!\n",i);? ? ? ? ? ? ? ? ? ? ?if(err_count++>10) { plc_connect_state = -1; } } } rt_kprintf("I%d%d%d%d%d%d\n",io_state[4],io_state[5],io_state[6],io_state[7],io_state[8],io_state[9]); rt_kprintf("Q%d%d%d%d\n\n",io_state[0],io_state[1],io_state[2],io_state[3]); inttemp_num =0; if(open62541_get_value(4,15,&temp_num)==0){ num = temp_num; } } rt_thread_mdelay(100); }}
創建一個定時器,300毫秒執行一次,來進行界面刷新。
lv_timer_create(data_timer_cb,300,NULL);staticvoiddata_timer_cb(lv_timer_t*timer){ //PLC狀態 if(plc_connect_state!=old_connect_state) { lv_obj_set_style_bg_color(plc_state[0],lv_color_hex(plc_connect_state==0?COLOR_GREEN:COLOR_ORANGE), LV_PART_MAIN); old_connect_state = plc_connect_state; } //計數 lv_label_set_text_fmt(num_label,"NUM: %06d",num); //IO狀態 for(inti=0;i<10;i++){? ? ? ? if(i 4)? ? ? ? {? ? ? ? ? ? lv_obj_set_style_bg_color(plc_do[i],lv_color_hex( io_state[i]!=1?COLOR_BLACK:COLOR_GREEN), LV_PART_MAIN);? ? ? ? ? ? lv_obj_set_style_text_color(btn_label[i],?lv_color_hex(io_state[i]!=1?COLOR_BTN_TEXT:COLOR_GREEN), LV_PART_MAIN);? ? ? ? }? ? ? ? else? ? ? ? {? ? ? ? ? ? lv_obj_set_style_bg_color(plc_di[i-4],lv_color_hex( io_state[i]!=1?COLOR_BLACK:COLOR_GREEN), LV_PART_MAIN);? ? ? ? }? ? }}由于寫變量操作,執行實現不長,所以直接在按鈕回調事件里實現了。static?void?btn_click_event_cb(lv_event_t?*e){? ? lv_event_code_t?code =?lv_event_get_code(e);? ? lv_obj_t?*btn =?lv_event_get_target(e);? ? if(code == LV_EVENT_CLICKED)? ? {? ? ? ? const?char?*btn_name =?lv_label_get_text(lv_obj_get_child(btn,?0));? ? ? ? int?index = btn_name[3]-'0';? ? ? ? rt_kprintf("DO: %s %d\n", btn_name,index);? ? ? ? if(open62541_set_value(4,4+index,1-io_state[index])==0)? ? ? ? {? ? ? ? ? ? io_state[index] =?1-io_state[index];? ? ? ? }? ? }}
四、操作演示視頻
(1)部署運行
部署成功后,程序會自動運行,連接成功后,會不斷讀取PLC的IO狀態及計數器的值。

(2)操作演示

視頻鏈接:
https://www.bilibili.com/video/BV1dQqMBgEKY/?spm_id_from=333.1387.homepage.video_card.click&vd_source=9d246b49a8f4b0a5dce8f3f5eba833cb
————————————————
版權聲明:本文為RT-Thread論壇用戶「劉洪峰AIoT」的原創文章,遵循CC 4.0 BY-SA版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://club.rt-thread.org/ask/article/d81bd8cab7c57c10.html
-
控制器
+關注
關注
114文章
17829瀏覽量
194507 -
OPC
+關注
關注
7文章
372瀏覽量
49290 -
遠程操控
+關注
關注
0文章
21瀏覽量
8800
發布評論請先 登錄
NI OPC與S7-1200連接問題
NI OPC與S7-1200鏈接問題
基于S7-1200的PLC的編程及擴展
WinCC V7.2與S7-1200的PLC通訊
S7-1200和S7-1200進行S7通信的詳細資料說明
SIMATIC NET與S7-1200基于以太網的S7通信
基于以太網的SIMATIC NET服務器與S7-1200通信
S7-1200與S7-300的選型區別
【睿擎派】OPC-UA遠程操控S7-1200
評論