CODESYS與Python“牽手”
CODESYS PLC與Python的Socket“牽手”:開啟工業(yè)控制無(wú)限可能
關(guān)鍵詞:CODESYS、Python、Socket 通信、Raspberry Pi、工業(yè)物聯(lián)網(wǎng)、實(shí)時(shí)數(shù)據(jù)采集、跨語(yǔ)言集成
微信公眾號(hào)原文連接:
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 擁抱開放的編程世界
傳統(tǒng)工業(yè)里,PLC 穩(wěn)守固定邏輯,精準(zhǔn)執(zhí)行控制任務(wù)。在工業(yè)物聯(lián)網(wǎng)與智能制造浪潮下,工程師有了新期待:讓 PLC 與 Python 等現(xiàn)代高級(jí)編程語(yǔ)言聯(lián)手,將 PLC 的穩(wěn)定可靠與 Python 的開放多元完美融合,從而釋放出無(wú)限潛能。
本文將以一個(gè)精彩案例,展示 PLC 程序通過(guò) Socket 請(qǐng)求,讓樹莓派上 Python 服務(wù)器抓取實(shí)時(shí)天氣數(shù)據(jù)并回傳解析存儲(chǔ)的奇妙過(guò)程,一起探索!
二、系統(tǒng)總體架構(gòu)與數(shù)據(jù)流
系統(tǒng)由兩個(gè)主要部分構(gòu)成:
模塊 | 平臺(tái) | 功能描述 |
CODESYS PLC 程序 | 樹莓派上的 CODESYS Runtime | 客戶端。檢測(cè)觸發(fā)信號(hào)、建立TCP連接、發(fā)送命令、接收天氣 JSON 數(shù)據(jù)、解析并輸出到變量。 |
Python 服務(wù)端程序 | 樹莓派 / 其他主機(jī) | 服務(wù)器。監(jiān)聽TCP端口,接收PLC命令,通過(guò) HTTP 從 weather.com.cn 獲取實(shí)時(shí)天氣數(shù)據(jù),解析后返回 JSON 格式響應(yīng)。 |
三、CODESYS 端:實(shí)現(xiàn) PLC 調(diào)用外部服務(wù)的關(guān)鍵邏輯
Codesys中新建名為Socket_FB的功能塊(Function Block),在PLC主循環(huán)中調(diào)用。
3.1 功能塊的引腳設(shè)計(jì)
Socket_FB功能塊引腳示意圖
3.2 上升沿觸發(fā)與一次通信周期
代碼示例:
bRisingEdge := bSendTrigger AND NOT bTrigOld;
bTrigOld := bSendTrigger;
PLC 程序通過(guò)檢測(cè)輸入 bSendTrigger 的上升沿,觸發(fā)一次完整的通信任務(wù)。這樣可確保每次請(qǐng)求都是用戶或外部事件驅(qū)動(dòng),不會(huì)連續(xù)觸發(fā)導(dǎo)致網(wǎng)絡(luò)阻塞。一旦觸發(fā)在后續(xù)的程序中會(huì)依次執(zhí)行以下命令:
周期初始化,清空所有狀態(tài)變量;
創(chuàng)建 TCP socket;
連接到服務(wù)器;
發(fā)送命令;
等待并接收應(yīng)答;
解析結(jié)果;
關(guān)閉連接。
這是一個(gè)典型的“事務(wù)式通信模式”,類似工業(yè)現(xiàn)場(chǎng)中“一次握手、一問(wèn)一答”的數(shù)據(jù)采集流程。
3.3 周期初始化
代碼示例:
IF bRisingEdge THEN
bConnectOK := FALSE;
bSendOK := FALSE;
bRecvOK := FALSE;
bDone := FALSE;
sRecvBuffer := '';
iErrorCode := Errors.ERR_OK;
每次通訊開始前重置所有狀態(tài)標(biāo)志,清空接收緩沖區(qū)。
3.4 Socket 通信核心流程
CODESYS 的 SysSocket 庫(kù)提供了底層網(wǎng)絡(luò)訪問(wèn)能力:
函數(shù) | 作用 |
SysSockCreate() | 創(chuàng)建 socket,返回句柄 |
SysSockConnect() | 與服務(wù)器建立 TCP 連接 |
SysSockSend() | 發(fā)送數(shù)據(jù) |
SysSockRecv() | 接收數(shù)據(jù) |
SysSockClose() | 關(guān)閉連接 |
(1) 創(chuàng)建socket:
代碼示例:
hSocket := SysSockCreate(SOCKET_AF_INET, SOCKET_STREAM, SOCKET_IPPROTO_TCP, ADR(iResult));
Codesys庫(kù)SysSockCreate文檔
創(chuàng)建一個(gè)新的 socket,并返回該 socket 的句柄(handle)。這個(gè)句柄以后會(huì)作為參數(shù)傳給其他套接字相關(guān)函數(shù),例如 SysSockBind、SysSockConnect、SysSockListen、SysSockAccept、SysSockSend、SysSockRecv、SysSockClose 等。
參數(shù):SOCKET_AF_INET, SOCKET_STREAM, SOCKET_IPPROTO_TCP是 CODESYS 系統(tǒng)庫(kù)中定義的常量,初始值如下表所示。
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)設(shè)置socket服務(wù)器地址
代碼示例:
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" 轉(zhuǎn)換為可用于網(wǎng)絡(luò)通信的數(shù)值格式,并在轉(zhuǎn)換成功后,設(shè)置服務(wù)器地址結(jié)構(gòu) addrServer 的基本參數(shù):指定使用 IPv4 協(xié)議、端口號(hào)為 5678,并將目標(biāo) IP 地址設(shè)為 127.0.0.1,為后續(xù)建立 socket 連接做準(zhǔn)備。
Codesys庫(kù)SysSockInetAddr文檔
在使用SysSockConnect 前,需要把目標(biāo) IP(字符串)轉(zhuǎn)換為可寫入地址結(jié)構(gòu)的二進(jìn)制值。所以,SysSockInetAddr 通常是網(wǎng)絡(luò)通信初始化步驟中的一環(huán)。SysSockInetAddr 的作用就是:把 "點(diǎn)分十進(jìn)制" 的 IP 地址(例如 '127.0.0.1')轉(zhuǎn)成一個(gè) 32 位無(wú)符號(hào)整數(shù)(UDINT)形式。
在實(shí)際測(cè)試中使用的‘127.0.0.1’通過(guò)SysSockInetAddr轉(zhuǎn)換結(jié)果是16777343。一個(gè) IPv4 地址本質(zhì)上是 4 個(gè)字節(jié)(共 32 位),把它轉(zhuǎn)換為16進(jìn)制按字節(jié)拼起來(lái)是0x7F000001。網(wǎng)絡(luò)中數(shù)據(jù)是 Big Endian(高位在前),但大多數(shù) PLC/CPU(x86、ARM)是 Little Endian(低位在前)。也就是說(shuō)在內(nèi)存中這 4 個(gè)字節(jié)的排列是反的:
網(wǎng)絡(luò)字節(jié)序(標(biāo)準(zhǔn)): 7F 00 00 01
PLC內(nèi)存(小端表示): 01 00 00 7F
0x0100007F = (1 × 256^3) + (0 × 256^2) + (0 × 256) + 127= 16777343
Codesys庫(kù)SOCKADDRESS文檔
SOCKADDRESS 結(jié)構(gòu)用于在 CODESYS 中描述一個(gè)完整的網(wǎng)絡(luò)通信地址,它包含了建立或識(shí)別網(wǎng)絡(luò)連接所需的全部信息——包括地址族(如 IPv4)、端口號(hào)以及目標(biāo)或本地的 IP 地址。該結(jié)構(gòu)在調(diào)用SysSockConnect函數(shù)時(shí)作為參數(shù)使用,用來(lái)告訴系統(tǒng)“我要與哪個(gè) IP、哪個(gè)端口進(jìn)行通信”。其中端口號(hào)需要通過(guò) SysSockHtons() 轉(zhuǎn)換為網(wǎng)絡(luò)字節(jié)序,IP 地址通常由 SysSockInetAddr() 生成。簡(jiǎn)單來(lái)說(shuō),SOCKADDRESS 就是 CODESYS 中 socket 通信的“地址卡片”。
(3)建立socket連接
代碼示例:
SockConnect_Result := SysSockConnect(hSocket, ADR(addrServer), SIZEOF(addrServer));
這段代碼的作用是:通過(guò)已創(chuàng)建的 socket (hSocket),調(diào)用 SysSockConnect() 函數(shù),將其連接到由 addrServer 定義的遠(yuǎn)程服務(wù)器地址,并返回連接結(jié)果。
Codesys庫(kù)SysSockConnect文檔
SysSockConnect是一個(gè)用于實(shí)現(xiàn)客戶端連接socket服務(wù)器功能的功能塊。使用時(shí),需將傳入socket句柄和包含服務(wù)器IP地址和端口號(hào)等信息的SOCKADDRESS結(jié)構(gòu)等。函數(shù)執(zhí)行后會(huì)返回一個(gè)RTS_IEC_RESULT類型的值,用于指示連接操作是否成功,若返回0表示連接成功,可進(jìn)行后續(xù)數(shù)據(jù)傳輸?shù)炔僮?,否則需根據(jù)返回值進(jìn)行相應(yīng)的錯(cuò)誤處理。
(4) 發(fā)送命令
代碼示例:
sSendBuffer := 'fun1';
IF SysSockSend(hSocket, ADR(sSendBuffer), LEN(sSendBuffer), 0, ADR(SockSend_Result)) > 0 AND SockSend_Result = Errors.ERR_OK THEN
bSendOK := TRUE;
這段代碼的作用是:PLC通過(guò)已連接的socket發(fā)送字符串 “fun1”,并在確認(rèn)發(fā)送成功后設(shè)置發(fā)送成功標(biāo)志。
Codesys庫(kù)SysSockSend文檔
SysSockSend 函數(shù)用于向已建立的 socket發(fā)送數(shù)據(jù)。hSocket 是先前創(chuàng)建并連接成功的 socket 句柄;ADR(sSendBuffer) 提供發(fā)送緩沖區(qū)的內(nèi)存地址;LEN(sSendBuffer) 指定要發(fā)送的數(shù)據(jù)長(zhǎng)度;0 表示不使用額外的發(fā)送標(biāo)志;ADR(SockSend_Result) 用于接收運(yùn)行時(shí)系統(tǒng)返回的錯(cuò)誤碼。函數(shù)返回成功發(fā)送的字節(jié)數(shù)。如果發(fā)送的字節(jié)數(shù)大于 0 且系統(tǒng)返回碼 SockSend_Result 等于 Errors.ERR_OK,則說(shuō)明數(shù)據(jù)成功發(fā)出,于是程序?qū)?bSendOK 置為 TRUE,表示發(fā)送完成。
(5) 接收數(shù)據(jù)
代碼示例:
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;
這段代碼的主要作用是:從一個(gè)已建立的 TCP Socket (hSocket) 中接收數(shù)據(jù)并保存到接收緩沖區(qū)中 (sRecvBuffer),并在成功接收后標(biāo)記 bRecvOK := TRUE。
Codesys庫(kù)SysSockRecv文檔
SysSockRecv用于從 socket中接收數(shù)據(jù)。它通過(guò)指定的socket句柄 hSocket 從端口讀取數(shù)據(jù),并將接收到的字節(jié)寫入由 pbyBuffer 指向的接收緩沖區(qū)中,最大接收長(zhǎng)度由 diBufferSize 限制。
Codesys庫(kù)SysMemCpy文檔
SysMemCpy用于內(nèi)存數(shù)據(jù)復(fù)制,其作用是將指定源地址 pSrc 中的內(nèi)容復(fù)制到目標(biāo)地址 pDest,復(fù)制的字節(jié)數(shù)由參數(shù) udiCount 決定。
實(shí)際運(yùn)行狀態(tài)監(jiān)控
網(wǎng)絡(luò)傳輸?shù)讓硬徽J(rèn)識(shí)“字符串”,所有內(nèi)容(包括文字、數(shù)字、圖片)都要被轉(zhuǎn)為字節(jié)流(byte stream)。byRecvBuffer 收到的就是這些 ASCII/UTF-8 字節(jié),diRecvBytes是接收到的字節(jié)數(shù)量。
Codesys監(jiān)控byRecvBuffer
byRecvBuffer接收到的字節(jié)數(shù)據(jù)前9個(gè)依次為:123,34,110,97,109,101,101,110,34。根據(jù)字符與字節(jié)(ASCII / UTF-8 編碼)之間的關(guān)系,以上字節(jié)可轉(zhuǎn)譯為:{、"、n、a、m、e、e、n、"。
字符 | 十進(jìn)制字節(jié)值 | 十六進(jìn)制 | 含義 |
{ | 123 | 0x7B | 左花括號(hào) |
} | 125 | 0x7D | 右花括號(hào) |
" | 34 | 0x22 | 雙引號(hào) |
: | 58 | 0x3A | 冒號(hào) |
, | 44 | 0x2C | 逗號(hào) |
空格 | 32 | 0x20 | 空格 |
0–9 | 48–57 | 0x30–0x39 | 數(shù)字字符 |
a–z | 97–122 | 0x61–0x7A | 小寫字母 |
標(biāo)準(zhǔn) 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字節(jié),與監(jiān)控的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 為字段名的鍵值對(duì),并提取該鍵對(duì)應(yīng)的字符串值。類似從 ... "name": "Alice", ... 中提取 Alice 的功能。
雖然 PLC 沒(méi)有內(nèi)置完整 JSON 解析器,但通過(guò)字符串查找函數(shù)即可實(shí)現(xiàn)簡(jiǎn)化版字段提取。這說(shuō)明即便在嵌入式 PLC 環(huán)境中,也可以通過(guò)基礎(chǔ)字符串操作解析網(wǎng)絡(luò)數(shù)據(jù)。解析完成后,PLC 將天氣各項(xiàng)指標(biāo)寫入輸出變量,如:
s_Temp := GetFieldValue(sRecvBuffer, 'temp');
s_WindSpeed := GetFieldValue(sRecvBuffer, 'wse');
s_Humidity := GetFieldValue(sRecvBuffer, 'SD');
s_Weather := GetFieldValue(sRecvBuffer, 'weathere');
這些變量可用于顯示在 HMI、記錄數(shù)據(jù)庫(kù)、或驅(qū)動(dòng)后續(xù)控制邏輯。
(7) 關(guān)閉socket
SysSockClose(hSocket);
關(guān)閉已創(chuàng)建的套接字 hSocket,釋放與該套接字相關(guān)的系統(tǒng)資源,結(jié)束該網(wǎng)絡(luò)連接。
四、Python 端:CODESYS 的“外部智能助手”
4.1 設(shè)計(jì)思路
Python 在此系統(tǒng)中扮演“中間件服務(wù)層”角色。PLC 不直接訪問(wèn)互聯(lián)網(wǎng),而是請(qǐng)求 Python 服務(wù)端,由 Python 完成網(wǎng)絡(luò)請(qǐng)求與數(shù)據(jù)解析任務(wù),再將結(jié)果以簡(jiǎn)潔 JSON 返回。
這既保證了:
PLC 穩(wěn)定、安全(不直接暴露外網(wǎng)請(qǐng)求);
Python 靈活、強(qiáng)大(可訪問(wèn)任意 API 或算法)。
這種設(shè)計(jì)模式是“PLC + 外部語(yǔ)言”協(xié)同的典型結(jié)構(gòu)。
本文案例以python監(jiān)聽socket端口,接收來(lái)自PLC的命令來(lái)執(zhí)行獲取當(dāng)前天氣數(shù)據(jù)的功能,并且將天氣數(shù)據(jù)返還給PLC進(jìn)行解析。
4.2 主要功能模塊
(1) 天氣數(shù)據(jù)抓取
代碼示例:
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 庫(kù)訪問(wèn)氣象網(wǎng)站,提取返回?cái)?shù)據(jù)包中的 JSON 數(shù)據(jù)段。
解析后得到標(biāo)準(zhǔn)字典對(duì)象,例如:
{
"nameen": "Pudong",
"temp": "26",
"wde": "East",
"wse": "3.4",
"SD": "65%",
"qy": "1012"
}
(2) 端口監(jiān)聽
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 服務(wù)監(jiān)聽端口 5678,一旦接收到 "fun1",便執(zhí)行天氣抓取并回傳 JSON。采用多線程模式,保證可以同時(shí)服務(wù)多個(gè) PLC 連接。
4.3 CODESYS 與 Python 的契約:數(shù)據(jù)格式 + 通信協(xié)議
項(xiàng)目 | 內(nèi)容 |
連接方式 | TCP |
端口號(hào) | 5678 |
請(qǐng)求命令 | fun1 |
返回格式 | UTF-8 編碼的 JSON 文本 |
通信周期 | 按 PLC 觸發(fā)(例如每 30 秒或人工觸發(fā)) |
通過(guò)這種契約,PLC不需要理解Python,只需發(fā)命令并解析字符串即可。這正是 “CODESYS 調(diào)用 Python” 的精髓:PLC 不直接運(yùn)行 Python 代碼,但通過(guò) Socket 請(qǐng)求 → Python 執(zhí)行 → 結(jié)果返回,實(shí)現(xiàn)了間接調(diào)用。
五、實(shí)驗(yàn)結(jié)果與運(yùn)行驗(yàn)證
實(shí)驗(yàn)環(huán)境:
硬件:Raspberry Pi 4B + 2GB RAM
操作系統(tǒng):Raspberry Pi OS (64-bit)
CODESYS 版本:3.5 SP21
Python 版本:3.11
網(wǎng)絡(luò):同機(jī)運(yùn)行(127.0.0.1)
運(yùn)行效果:
1. 啟動(dòng) Python 服務(wù)器:
[服務(wù)器啟動(dòng)] 正在監(jiān)聽 127.0.0.1:5678
樹莓派Python
2. 在 CODESYS 中觸發(fā) bSendTrigger 上升沿:
bConnectOK = TRUE
bSendOK = TRUE
bRecvOK = TRUE
s_Temp = "26"
s_WindSpeed = "3.4"
s_Humidity = "65%"
bDone = TRUE
Codesys在線監(jiān)控
3. Codesys可視化界面顯示:
Codesys可視化界面
驗(yàn)證:通信成功,數(shù)據(jù)解析正確。
六、CODESYS 與 Python 協(xié)同的技術(shù)意義
6.1 打破 PLC 封閉邊界
傳統(tǒng) PLC 依賴專有協(xié)議和有限的函數(shù)庫(kù),難以直接與云端 API 或第三方系統(tǒng)交互。通過(guò) SysSocket,CODESYS 實(shí)現(xiàn)了跨語(yǔ)言通信的“開放接口”,使 PLC 能夠訪問(wèn):
Web 服務(wù)(RESTful API)
本地算法服務(wù)(Python、C/C++)
數(shù)據(jù)庫(kù)代理(通過(guò) Python、Node.js 等)
6.2 各取所長(zhǎng)的架構(gòu)優(yōu)勢(shì)
模塊 | 優(yōu)勢(shì) | 角色定位 |
CODESYS PLC | 實(shí)時(shí)性強(qiáng)、控制邏輯穩(wěn)定 | 數(shù)據(jù)消費(fèi)者、執(zhí)行層 |
Python 程序 | 網(wǎng)絡(luò)與算法能力強(qiáng) | 數(shù)據(jù)提供者、智能層 |
這是一種工業(yè)界常見(jiàn)的分層架構(gòu):PLC 負(fù)責(zé)“控制”,Python 負(fù)責(zé)“智能”。
七、擴(kuò)展應(yīng)用方向
(1)工業(yè) IoT 數(shù)據(jù)融合
可將 Python 改為接入 MQTT、Modbus、OPC UA 等接口,PLC 作為訂閱者。
(2)AI 輔助控制
Python 端可運(yùn)行機(jī)器學(xué)習(xí)模型,根據(jù)實(shí)時(shí)天氣預(yù)測(cè)能耗或生產(chǎn)計(jì)劃,再通過(guò) Socket 返回控制參數(shù)。
(3)邊緣計(jì)算節(jié)點(diǎn)
樹莓派同時(shí)運(yùn)行 PLC 與 Python,形成“混合智能邊緣設(shè)備”,既具實(shí)時(shí)性又具云連接能力。
(4)云服務(wù)接口
可替換天氣 API 為任意 Web 服務(wù)(設(shè)備管理、能源監(jiān)控、物流狀態(tài)等),實(shí)現(xiàn) CODESYS 與云端系統(tǒng)的數(shù)據(jù)橋接。
八、結(jié)語(yǔ):CODESYS 與 Python 的融合之路
本文以“CODESYS 獲取實(shí)時(shí)天氣數(shù)據(jù)”為案例,完整展示了:
l 如何在樹莓派上運(yùn)行 CODESYS Runtime;
l 如何用 IEC 61131-3 語(yǔ)言建立Socket通信;
l 如何通過(guò) Python 服務(wù)器實(shí)現(xiàn)外部數(shù)據(jù)訪問(wèn);
l 以及兩者協(xié)作實(shí)現(xiàn)“PLC 調(diào)用 Python”的機(jī)制。
這不是簡(jiǎn)單的網(wǎng)絡(luò)實(shí)驗(yàn),而是一個(gè)工業(yè)控制新時(shí)代的縮影。PLC 不再局限于封閉的邏輯循環(huán),而是可以與 AI、云端、Web 世界自由交互。Python 也不只是桌面腳本,而能成為工業(yè)現(xiàn)場(chǎng)的“智慧補(bǔ)腦”。
這種模式預(yù)示著未來(lái)工業(yè)控制系統(tǒng)的方向:控制邏輯與數(shù)據(jù)智能的融合,實(shí)時(shí)性與開放性的統(tǒng)一。
提交

投訴建議