工控網首頁
>

應用設計

>

CODESYS與Python“牽手”

CODESYS與Python“牽手”

2025/10/14 10:06:57

CODESYS PLC與Python的Socket“牽手”:開啟工業控制無限可能

 

關鍵詞:CODESYS、Python、Socket 通信、Raspberry Pi、工業物聯網、實時數據采集、跨語言集成

 

微信公眾號原文連接:

https://mp.weixin.qq.com/s/0jwwYbOWUb5lJ0G26GVjsA

CSDN:

https://blog.csdn.net/qq_36063437/article/details/153248117?fromshare=blogdetail&sharetype=blogdetail&sharerId=153248117&sharerefer=PC&sharesource=qq_36063437&sharefrom=from_link

一、引言:讓 PLC 擁抱開放的編程世界

傳統工業里,PLC 穩守固定邏輯,精準執行控制任務。在工業物聯網與智能制造浪潮下,工程師有了新期待:讓 PLC 與 Python 等現代高級編程語言聯手,將 PLC 的穩定可靠與 Python 的開放多元完美融合,從而釋放出無限潛能。

本文將以一個精彩案例,展示 PLC 程序通過 Socket 請求,讓樹莓派上 Python 服務器抓取實時天氣數據并回傳解析存儲的奇妙過程,一起探索!

 

二、系統總體架構與數據流

系統由兩個主要部分構成:

模塊

平臺

功能描述

CODESYS PLC 程序

樹莓派上的 CODESYS   Runtime

客戶端。檢測觸發信號、建立TCP連接、發送命令、接收天氣 JSON 數據、解析并輸出到變量。

Python 服務端程序

樹莓派 / 其他主機

服務器。監聽TCP端口,接收PLC命令,通過 HTTP 從   weather.com.cn 獲取實時天氣數據,解析后返回 JSON 格式響應。

 

三、CODESYS 端:實現 PLC 調用外部服務的關鍵邏輯

Codesys中新建名為Socket_FB的功能塊(Function Block),在PLC主循環中調用。

3.1 功能塊的引腳設計

Socket_FB功能塊引腳示意圖

 

3.2 上升沿觸發與一次通信周期

代碼示例:

bRisingEdge := bSendTrigger AND NOT bTrigOld;

bTrigOld := bSendTrigger;

PLC 程序通過檢測輸入 bSendTrigger 的上升沿,觸發一次完整的通信任務。這樣可確保每次請求都是用戶或外部事件驅動,不會連續觸發導致網絡阻塞。一旦觸發在后續的程序中會依次執行以下命令:

  • 周期初始化,清空所有狀態變量;

  • 創建 TCP socket;

  • 連接到服務器;

  • 發送命令;

  • 等待并接收應答;

  • 解析結果;

  • 關閉連接。

這是一個典型的“事務式通信模式”,類似工業現場中“一次握手、一問一答”的數據采集流程。

 

3.3 周期初始化

代碼示例:

IF bRisingEdge THEN

    bConnectOK := FALSE;

    bSendOK := FALSE;

    bRecvOK := FALSE;

    bDone := FALSE;

    sRecvBuffer := '';

    iErrorCode := Errors.ERR_OK;

每次通訊開始前重置所有狀態標志,清空接收緩沖區。

 

3.4 Socket 通信核心流程

CODESYS 的 SysSocket 庫提供了底層網絡訪問能力:

函數

作用

SysSockCreate()

創建 socket,返回句柄

SysSockConnect()

與服務器建立 TCP 連接

SysSockSend()

發送數據

SysSockRecv()

接收數據

SysSockClose()

關閉連接

 

(1)    創建socket:

代碼示例:

hSocket := SysSockCreate(SOCKET_AF_INET, SOCKET_STREAM, SOCKET_IPPROTO_TCP, ADR(iResult));

Codesys庫SysSockCreate文檔

創建一個新的 socket,并返回該 socket 的句柄(handle)。這個句柄以后會作為參數傳給其他套接字相關函數,例如 SysSockBind、SysSockConnect、SysSockListen、SysSockAccept、SysSockSend、SysSockRecv、SysSockClose 等。

 

參數:SOCKET_AF_INET, SOCKET_STREAM, SOCKET_IPPROTO_TCP是 CODESYS 系統庫中定義的常量,初始值如下表所示。

Name

Type

Initial

Comment

SOCKET_AF_INET

INT

2

AddressFamily: DINTernetwork: UDP, TCP,   etc.

SOCKET_STREAM

DINT

1

Socket types: stream socket

SOCKET_IPPROTO_TCP

DINT

6

Protocols: tcp

 

(2)設置socket服務器地址

代碼示例:

SockInetAddr_Result := SysSockInetAddr('127.0.0.1', ADR(ipAddr));

IF SockInetAddr_Result = Errors.ERR_OK THEN

addrServer.sin_family := SOCKET_AF_INET;

    addrServer.sin_port := SysSockHtons(5678);

    addrServer.sin_addr.ulAddr := ipAddr;

這段代碼的作用是:將字符串形式的 IP 地址 "127.0.0.1" 轉換為可用于網絡通信的數值格式,并在轉換成功后,設置服務器地址結構 addrServer 的基本參數:指定使用 IPv4 協議、端口號為 5678,并將目標 IP 地址設為 127.0.0.1,為后續建立 socket 連接做準備。

 

Codesys庫SysSockInetAddr文檔

在使用SysSockConnect 前,需要把目標 IP(字符串)轉換為可寫入地址結構的二進制值。所以,SysSockInetAddr 通常是網絡通信初始化步驟中的一環。SysSockInetAddr 的作用就是:把 "點分十進制" 的 IP 地址(例如 '127.0.0.1')轉成一個 32 位無符號整數(UDINT)形式。

在實際測試中使用的‘127.0.0.1’通過SysSockInetAddr轉換結果是16777343。一個 IPv4 地址本質上是 4 個字節(共 32 位),把它轉換為16進制按字節拼起來是0x7F000001。網絡中數據是 Big Endian(高位在前),但大多數 PLC/CPU(x86、ARM)是 Little Endian(低位在前)。也就是說在內存中這 4 個字節的排列是反的:

網絡字節序(標準): 7F 00 00 01

PLC內存(小端表示): 01 00 00 7F

0x0100007F = (1 × 256^3) + (0 × 256^2) + (0 × 256) + 127= 16777343

 

Codesys庫SOCKADDRESS文檔

SOCKADDRESS 結構用于在 CODESYS 中描述一個完整的網絡通信地址,它包含了建立或識別網絡連接所需的全部信息——包括地址族(如 IPv4)、端口號以及目標或本地的 IP 地址。該結構在調用SysSockConnect函數時作為參數使用,用來告訴系統“我要與哪個 IP、哪個端口進行通信”。其中端口號需要通過 SysSockHtons() 轉換為網絡字節序,IP 地址通常由 SysSockInetAddr() 生成。簡單來說,SOCKADDRESS 就是 CODESYS 中 socket 通信的“地址卡片”。

 

(3)建立socket連接

代碼示例:

SockConnect_Result := SysSockConnect(hSocket, ADR(addrServer), SIZEOF(addrServer));

這段代碼的作用是:通過已創建的 socket (hSocket),調用 SysSockConnect() 函數,將其連接到由 addrServer 定義的遠程服務器地址,并返回連接結果。

Codesys庫SysSockConnect文檔

SysSockConnect是一個用于實現客戶端連接socket服務器功能的功能塊。使用時,需將傳入socket句柄和包含服務器IP地址和端口號等信息的SOCKADDRESS結構等。函數執行后會返回一個RTS_IEC_RESULT類型的值,用于指示連接操作是否成功,若返回0表示連接成功,可進行后續數據傳輸等操作,否則需根據返回值進行相應的錯誤處理。

 

(4) 發送命令

代碼示例:

sSendBuffer := 'fun1';

IF SysSockSend(hSocket, ADR(sSendBuffer), LEN(sSendBuffer), 0, ADR(SockSend_Result)) > 0 AND SockSend_Result = Errors.ERR_OK THEN

bSendOK := TRUE;

這段代碼的作用是:PLC通過已連接的socket發送字符串 “fun1”,并在確認發送成功后設置發送成功標志。

 

Codesys庫SysSockSend文檔

SysSockSend 函數用于向已建立的 socket發送數據。hSocket 是先前創建并連接成功的 socket 句柄;ADR(sSendBuffer) 提供發送緩沖區的內存地址;LEN(sSendBuffer) 指定要發送的數據長度;0 表示不使用額外的發送標志;ADR(SockSend_Result) 用于接收運行時系統返回的錯誤碼。函數返回成功發送的字節數。如果發送的字節數大于 0 且系統返回碼 SockSend_Result 等于 Errors.ERR_OK,則說明數據成功發出,于是程序將 bSendOK 置為 TRUE,表示發送完成。

 

(5) 接收數據

代碼示例:

diRecvBytes := SysSockRecv(hSocket, ADR(byRecvBuffer), SIZEOF(byRecvBuffer), 0, ADR(SockRecv_Result));

IF diRecvBytes > 0 AND SockRecv_Result = Errors.ERR_OK THEN

IF diRecvBytes > SIZEOF(sRecvBuffer) - 1 THEN

    diRecvBytes := SIZEOF(sRecvBuffer) - 1;

    END_IF

    SysMemCpy(ADR(sRecvBuffer), ADR(byRecvBuffer), diRecvBytes);

    sRecvBuffer[diRecvBytes] := BYTE#0;

    bRecvOK := TRUE;

這段代碼的主要作用是:從一個已建立的 TCP Socket (hSocket) 中接收數據并保存到接收緩沖區中 (sRecvBuffer),并在成功接收后標記 bRecvOK := TRUE。

 

Codesys庫SysSockRecv文檔

SysSockRecv用于從 socket中接收數據。它通過指定的socket句柄 hSocket 從端口讀取數據,并將接收到的字節寫入由 pbyBuffer 指向的接收緩沖區中,最大接收長度由 diBufferSize 限制。

 

Codesys庫SysMemCpy文檔

SysMemCpy用于內存數據復制,其作用是將指定源地址 pSrc 中的內容復制到目標地址 pDest,復制的字節數由參數 udiCount 決定。

 

實際運行狀態監控

網絡傳輸底層不認識“字符串”,所有內容(包括文字、數字、圖片)都要被轉為字節流(byte stream)。byRecvBuffer 收到的就是這些 ASCII/UTF-8 字節,diRecvBytes是接收到的字節數量。

Codesys監控byRecvBuffer

byRecvBuffer接收到的字節數據前9個依次為:123,34,110,97,109,101,101,110,34。根據字符與字節(ASCII / UTF-8 編碼)之間的關系,以上字節可轉譯為:{、"、n、a、m、e、e、n、"。

字符

十進制字節值

十六進制

含義

{

123

0x7B

左花括號

}

125

0x7D

右花括號

"

34

0x22

雙引號

:

58

0x3A

冒號

,

44

0x2C

逗號

空格

32

0x20

空格

0–9

48–57

0x30–0x39

數字字符

a–z

97–122

0x61–0x7A

小寫字母

標準 ASCII 或 UTF-8 編碼

文本的案例中接收到的完整字符串為:{"nameen": "baoshan", "temp": "23.9", "wde": "NW", "wse": "11km/h", "SD": "84%", "qy": "1015", "njd": "4km", "updatetime": "20:40", "rain": "0", "rain24h": "0", "aqi": "88", "aqi_pm25": "88", "weathere": "haze"},共211字節,與監控的diRecvBytes值一致。

 

(6) JSON 解析

代碼示例:

s_Nameen := GetFieldValue(sRecvBuffer, 'nameen');

FUNCTION GetFieldValue : STRING

...

sPattern := CONCAT(sKey, '": "');

iStart := FIND(sSrc, sPattern);

...

GetFieldValue := LEFT(sTemp, iEnd - 1);

這段代碼的主要作用是:從 sSrc 字符串中查找以 sKey 為字段名的鍵值對,并提取該鍵對應的字符串值。類似從 ... "name": "Alice", ... 中提取 Alice 的功能。

雖然 PLC 沒有內置完整 JSON 解析器,但通過字符串查找函數即可實現簡化版字段提取。這說明即便在嵌入式 PLC 環境中,也可以通過基礎字符串操作解析網絡數據。解析完成后,PLC 將天氣各項指標寫入輸出變量,如:

s_Temp := GetFieldValue(sRecvBuffer, 'temp');

s_WindSpeed := GetFieldValue(sRecvBuffer, 'wse');

s_Humidity := GetFieldValue(sRecvBuffer, 'SD');

s_Weather := GetFieldValue(sRecvBuffer, 'weathere');

這些變量可用于顯示在 HMI、記錄數據庫、或驅動后續控制邏輯。

 

(7) 關閉socket

SysSockClose(hSocket);

關閉已創建的套接字 hSocket,釋放與該套接字相關的系統資源,結束該網絡連接。

 

四、Python 端:CODESYS 的“外部智能助手”

4.1 設計思路

Python 在此系統中扮演“中間件服務層”角色。PLC 不直接訪問互聯網,而是請求 Python 服務端,由 Python 完成網絡請求與數據解析任務,再將結果以簡潔 JSON 返回。

這既保證了:

  • PLC 穩定、安全(不直接暴露外網請求);

  • Python 靈活、強大(可訪問任意 API 或算法)。

這種設計模式是“PLC + 外部語言”協同的典型結構。

本文案例以python監聽socket端口,接收來自PLC的命令來執行獲取當前天氣數據的功能,并且將天氣數據返還給PLC進行解析。

 

4.2 主要功能模塊

(1) 天氣數據抓取

代碼示例:

def get_weather_data():

    url = f"https://d1.weather.com.cn/sk_2d/101020300.html?_{int(time.time() * 1000)}"

    headers = {

        'Referer': 'https://e.weather.com.cn/',

        'User-Agent': 'Mozilla/5.0'

    }

    response = requests.get(url, headers=headers, timeout=10)

return parse_weather_data(response.text)

這段代碼的作用是:Python 使用 requests 庫訪問氣象網站,提取返回數據包中的 JSON 數據段。

解析后得到標準字典對象,例如:

{

  "nameen": "Pudong",

  "temp": "26",

  "wde": "East",

  "wse": "3.4",

  "SD": "65%",

  "qy": "1012"

}

(2) 端口監聽

def handle_client(conn, addr):

    data = conn.recv(2048).decode('utf-8').strip()

    if data == "fun1":

        weather_data = format_weather_data(get_weather_data())

        reply = json.dumps(weather_data, ensure_ascii=False)

        conn.sendall(reply.encode('utf-8'))

Python 服務監聽端口 5678,一旦接收到 "fun1",便執行天氣抓取并回傳 JSON。采用多線程模式,保證可以同時服務多個 PLC 連接。

 

4.3 CODESYS 與 Python 的契約:數據格式 + 通信協議

項目

內容

連接方式

TCP

端口號

5678

請求命令

fun1

返回格式

UTF-8 編碼的 JSON 文本

通信周期

按 PLC 觸發(例如每 30 秒或人工觸發)

通過這種契約,PLC不需要理解Python,只需發命令并解析字符串即可。這正是 “CODESYS 調用 Python” 的精髓:PLC 不直接運行 Python 代碼,但通過 Socket 請求 → Python 執行 → 結果返回,實現了間接調用。

 

五、實驗結果與運行驗證

實驗環境:

  • 硬件:Raspberry Pi 4B + 2GB RAM

  • 操作系統:Raspberry Pi OS (64-bit)

  • CODESYS 版本:3.5 SP21

  • Python 版本:3.11

  • 網絡:同機運行(127.0.0.1)

 

運行效果:

1.       啟動 Python 服務器:

[服務器啟動] 正在監聽 127.0.0.1:5678

樹莓派Python

2.       在 CODESYS 中觸發 bSendTrigger 上升沿:

bConnectOK = TRUE 

bSendOK = TRUE 

bRecvOK = TRUE 

s_Temp = "26" 

s_WindSpeed = "3.4" 

s_Humidity = "65%" 

bDone = TRUE

Codesys在線監控

3.       Codesys可視化界面顯示:

Codesys可視化界面

驗證:通信成功,數據解析正確。

 

六、CODESYS 與 Python 協同的技術意義

6.1 打破 PLC 封閉邊界

傳統 PLC 依賴專有協議和有限的函數庫,難以直接與云端 API 或第三方系統交互。通過 SysSocket,CODESYS 實現了跨語言通信的“開放接口”,使 PLC 能夠訪問:

  • Web 服務(RESTful API)

  • 本地算法服務(Python、C/C++)

  • 數據庫代理(通過 Python、Node.js 等)

6.2 各取所長的架構優勢

模塊

優勢

角色定位

CODESYS PLC

實時性強、控制邏輯穩定

數據消費者、執行層

Python 程序

網絡與算法能力強

數據提供者、智能層

這是一種工業界常見的分層架構:PLC 負責“控制”,Python 負責“智能”。

 

七、擴展應用方向

(1)工業 IoT 數據融合

可將 Python 改為接入 MQTT、Modbus、OPC UA 等接口,PLC 作為訂閱者。

(2)AI 輔助控制

Python 端可運行機器學習模型,根據實時天氣預測能耗或生產計劃,再通過 Socket 返回控制參數。

(3)邊緣計算節點

樹莓派同時運行 PLC 與 Python,形成“混合智能邊緣設備”,既具實時性又具云連接能力。

(4)云服務接口

可替換天氣 API 為任意 Web 服務(設備管理、能源監控、物流狀態等),實現 CODESYS 與云端系統的數據橋接。

 

八、結語:CODESYS 與 Python 的融合之路

本文以“CODESYS 獲取實時天氣數據”為案例,完整展示了:

l   如何在樹莓派上運行 CODESYS Runtime;

l   如何用 IEC 61131-3 語言建立Socket通信;

l   如何通過 Python 服務器實現外部數據訪問;

l   以及兩者協作實現“PLC 調用 Python”的機制。

這不是簡單的網絡實驗,而是一個工業控制新時代的縮影。PLC 不再局限于封閉的邏輯循環,而是可以與 AI、云端、Web 世界自由交互。Python 也不只是桌面腳本,而能成為工業現場的“智慧補腦”。

這種模式預示著未來工業控制系統的方向:控制邏輯與數據智能的融合,實時性與開放性的統一。

審核編輯(
王靜
)
投訴建議

提交

查看更多評論