天天PLC培训学校欢迎您!

|在线报名
天天PLC培训学校
课程导航

CODESYS与Python“牵手”

logo
来源:天天PLC

更新:2026/3/7|关注35

资讯详情

关键词: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 也不只是桌面脚本,而能成为工业现场的“智慧补脑”。

这种模式预示着未来工业控制系统的方向:控制逻辑与数据智能的融合,实时性与开放性的统一。

原文:https://www.gongkong.com/article/202510/111101.html

  • 培训课程

在线咨询
咨询电话
18501512500
联系人:王老师
工作时间:7*24
联系微信
天天PLC培训