CODESYS + UaExpert + Qt OPC UA 入门指南
本文档记录从零开始使用 CODESYS 编写 PLC 程序、通过 OPC UA 协议与 Qt 客户端通信的完整过程包含环境配置、程序开发、连接测试以及实际踩坑经历。主要难点在于免证书进行匿名登录CODESYS关于这个功能藏得太深了搞了很久T^T环境信息PLC 软件CODESYS Control Win V3 x64软 PLC运行于 WindowsOPC UA 客户端工具UaExpertUnified Automation开发环境Qt 5.15.2 open62541CentOS 7 虚拟机 / Windows 均可OPC UA 端口4840目录一、整体架构二、环境准备三、CODESYS PLC 程序开发四、启用 OPC UA Server五、用 UaExpert 连接测试六、Qt 客户端开发七、踩坑经历八、附录一、整体架构┌─────────────────────┐ OPC UA/TCP ┌─────────────────────┐ │ CODESYS Control │ ◄──────────────► │ Qt OPC UA Client │ │ Win V3 x64 │ port 4840 │ (open62541) │ │ (PLC UA Server) │ │ │ └─────────────────────┘ └─────────────────────┘ ▲ ▲ │ OPC UA/TCP │ ▼ │ ┌─────────────────────┐ │ │ UaExpert │ ◄──────────────────────────┘ │ (调试/验证用) │ 可选用于前期验证 NodeId └─────────────────────┘CODESYS既是 PLC 运行时执行梯形图逻辑又内置 OPC UA Server对外暴露变量UaExpert通用 OPC UA 客户端用于浏览节点、验证变量 NodeId是开发 Qt 客户端前的必备调试工具Qt 客户端基于 open62541 库开发的 GUI 程序连接 CODESYS 读写变量二、环境准备2.1 安装 CODESYS访问 https://store.codesys.com/codesys-control/ 下载CODESYS Control Win V3免费版含 SL 许可安装时勾选CODESYS Control Win V3 x6464 位软 PLC 运行时同时会安装CODESYS Development SystemIDE用于编写 PLC 程序CODESYS Development System 既是编程 IDE也用于下载/在线调试 PLC 程序。2.2 安装 UaExpert访问 https://www.unified-automation.com/downloads/opc-ua-clients.html下载UaExpert免费需注册账号2.3 Qt open62541 环境Qt 客户端依赖 open62541 开源 OPC UA 协议栈。环境搭建可参考同目录下的OPCUA_Demo_完整指南.md关键步骤组件说明Qt 5.15.2GUI 框架open62541 v1.5.xOPC UA C 协议栈编译为libopen62541.so.pro文件需添加LIBS -lopen62541三、CODESYS PLC 程序开发3.1 新建工程打开 CODESYS Development SystemFile → New Project选择Standard project设备选择CODESYS Control Win V3 x64编程语言选择Ladder Diagram (LD)或其他熟悉的语言3.2 定义变量在main程序PRG中声明 6 个变量PROGRAM main VAR Counter : INT : 0; // 计数器每周期 1 Temperature : REAL : 25.0; // 温度°C Pressure : REAL : 100.0; // 压力kPa Humidity : REAL : 60.0; // 湿度% TargetTemp : REAL : 22.0; // 目标温度可写入 DeviceMode : INT : 0; // 设备模式可写入 END_VAR⚠️注意 IEC 61131-3 数据类型与 OPC UA 类型的对应关系这是后续踩坑的根源IEC 61131-3 类型位宽对应 OPC UA 类型INT16 位有符号Int16DINT32 位有符号Int32REAL32 位浮点FloatLREAL64 位浮点DoubleINT在 IEC 61131-3 中是 16 位不是 C 语言的 32 位这点极易搞混。3.3 编写梯形图逻辑用梯形图LD实现控制逻辑Counter 每个扫描周期递增Temperature/Pressure/Humidity 模拟波动根据 DeviceMode 和 TargetTemp 进行简单控制。3.4 编译并下载到 PLCBuild → Compile编译检查语法在线登录Online → Login点击StartF5让 PLC 进入运行状态确认 PLC 状态指示灯为绿色RUNNING四、启用 OPC UA Server停止并登出后进行配置修改按照如图所示操作打开Opc UA服务。允许匿名登录否则需要配置证书会比较麻烦。配置完成后重新编译并登录启动。五、用 UaExpert 连接测试UaExpert 是开发 Qt 客户端前的关键一步——先用它确认 NodeId 和数据类型再写代码。5.1 添加服务器打开 UaExpert点击工具栏Add Server➕参考下图所示进行配置5.2 连接并浏览地址空间在左侧 Server 列表中双击刚添加的服务器连接成功后展开Address Space面板沿路径Objects → DeviceSet → CODESYS Control Win V3 x64 → ...逐级展开找到main下的 6 个变量节点5.3 拖拽变量到 Data Access View打开下方Data Access View标签页将 6 个变量从 Address Space 拖拽到 Data Access View即可实时看到变量值变化5.4 写入变量测试在 Data Access View 中选中TargetTemp或DeviceMode右键 →Write…输入新值确认写入观察 CODESYS 中变量是否更新通过 UaExpert 验证 NodeId 和数据类型无误后即可开始编写 Qt 客户端。补充CODESYS 暴露的变量 NodeId 采用字符串格式命名规则为|var|设备名.Application.POU名.变量名例如本项目的变量 NodeId 为变量NodeIdCounterns4;s|var|CODESYS Control Win V3 x64.Application.main.CounterTemperaturens4;s|var|CODESYS Control Win V3 x64.Application.main.TemperaturePressurens4;s|var|CODESYS Control Win V3 x64.Application.main.PressureHumidityns4;s|var|CODESYS Control Win V3 x64.Application.main.HumidityTargetTempns4;s|var|CODESYS Control Win V3 x64.Application.main.TargetTempDeviceModens4;s|var|CODESYS Control Win V3 x64.Application.main.DeviceModens4是 CODESYS 变量命名空间公共前缀BasePath为|var|CODESYS Control Win V3 x64.Application.main.六、Qt 客户端开发6.1 项目配置.pro文件关键配置QT core gui widgets CONFIG c11 LIBS -lopen625416.2 连接服务器m_clientUA_Client_new();UA_ClientConfig_setDefault(UA_Client_getConfig(m_client));QByteArray urlBaurl.toUtf8();UA_StatusCode retUA_Client_connect(m_client,urlBa.constData());// ret UA_STATUSCODE_GOOD 表示连接成功6.3 读取变量字符串 NodeIdCODESYS 变量用字符串 NodeId需用UA_NODEID_STRINGQByteArray banodeIdStr.toUtf8();UA_NodeId nodeIdUA_NODEID_STRING(ns,ba.data());UA_Variant value;UA_Variant_init(value);UA_StatusCode retUA_Client_readValueAttribute(m_client,nodeId,value);if(retUA_STATUSCODE_GOODUA_Variant_hasScalarType(value,UA_TYPES[UA_TYPES_FLOAT])){UA_Float v*(UA_Float*)value.data;// 使用 v ...}UA_Variant_clear(value);6.4 写入变量写入前需确认目标数据类型避免类型不匹配// 先读取确定类型再按实际类型写入UA_Variant cur;UA_Client_readValueAttribute(m_client,nodeId,cur);// 根据 cur.type 选择对应的 setScalarCopy ...UA_Variant_clear(cur);6.5 界面与运行客户端界面包含连接配置区URL、命名空间、BasePath自动变量读取区Counter / Temperature / Pressure / Humidity手动变量读写区TargetTemp / DeviceModeBrowse Address Space 按钮浏览服务器节点结构日志区运行流程输入 URLopc.tcp://PLC所在IP:4840设置 NS 4设置 BasePath |var|CODESYS Control Win V3 x64.Application.main.点击 Connect点击各 Read 按钮读取或 Read All 批量读取在 Set 输入框填值点 Write 写入 TargetTemp / DeviceMode七、踩坑经历以下是实际开发中遇到的问题按出现顺序记录。坑 1数据类型不匹配 — 读取 ERR、写入 BadTypeMismatch现象Temperature、Pressure、Humidity、TargetTempREAL 类型读写正常但Counter、DeviceModeINT 类型读取显示 ERR写入报BadTypeMismatch。原因CODESYS 的INT是IEC 61131-3 标准的 16 位整数对应 OPC UAInt16而 Qt 客户端代码按Int32读写。REAL 是 32 位 Float恰好匹配所以没问题。解决读取写一个variantToInt64辅助函数兼容 SByte / Byte / Int16 / UInt16 / Int32 / UInt32 / Int64 / UInt64 所有整数类型统一转为qint64返回。写入写入前先读取目标节点的当前值获取其实际 OPC UA 数据类型再用UA_Variant_setScalarCopy按实际类型写入。// 读取时兼容所有整数类型staticboolvariantToInt64(constUA_Variant*value,qint64*out){constUA_DataType*tvalue-type;if(tUA_TYPES[UA_TYPES_INT16]){*out*(UA_Int16*)value-data;returntrue;}if(tUA_TYPES[UA_TYPES_UINT16]){*out*(UA_UInt16*)value-data;returntrue;}if(tUA_TYPES[UA_TYPES_INT32]){*out*(UA_Int32*)value-data;returntrue;}// ... 其他类型returnfalse;}教训永远不要假设 PLC 变量的数据类型。IEC 61131-3 的INT≠ C 的int。用 UaExpert 先确认每个变量的实际 OPC UA 类型。坑 2浏览地址空间崩溃 —free(): invalid pointer现象点击 Browse Address Space 后浏览到第一个字符串 NodeId 的节点时崩溃控制台报*** Error in ...: free(): invalid pointer: 0x0000000002359828 ***原因UA_NODEID_STRING宏是浅拷贝nodeId.identifier.string.data指向QByteArray的内部缓冲区Qt 管理的内存。当UA_BrowseRequest_clear清理请求时调用UA_free()试图释放这个指针——但它是 Qt 分配的内存不是 UA 分配器分配的导致free()崩溃。关键点前 6 个被浏览的节点都是数字 NodeId如ns0;i85不涉及字符串指针所以不崩溃。第 7 个节点ns4;s|plc|CODESYS Control Win V3 x64是第一个字符串 NodeId一清理就炸。解决字符串 NodeId 必须用UA_STRING_ALLOC分配 UA 堆内存让UA_BrowseRequest_clear能正确释放// ❌ 错误指向 Qt 内存clear 时崩溃QByteArray strBacur.strId.toUtf8();nodeIdUA_NODEID_STRING(cur.ns,strBa.data());// ✅ 正确用 UA 分配器复制字符串QByteArray strBacur.strId.toUtf8();UA_String uaStrUA_STRING_ALLOC(strBa.constData());nodeIdUA_NODEID_STRING(cur.ns,(char*)uaStr.data);// uaStr 所有权交给 nodeId由 UA_BrowseRequest_clear 统一释放教训open62541 中凡是涉及字符串/字节串的 NodeId要注意内存所有权。UA_NODEID_STRING是浅拷贝UA_*_clear会尝试free字符串指针——必须确保该指针指向 UA 分配器分配的内存。坑 3编译错误 —const char*到char*的转换现象编译报错error: invalid conversion from const char* to char* [-fpermissive]原因UA_NODEID_STRING宏的第二个参数类型是char*而QByteArray::constData()返回const char*类型不匹配。解决改用QByteArray::data()它返回非 const 的char*// ❌ constData() 返回 const char*nodeIdUA_NODEID_STRING(ns,strBa.constData());// ✅ data() 返回 char*nodeIdUA_NODEID_STRING(ns,strBa.data());教训Qt 的QByteArray提供constData()const和data()非 const两个版本传给要求char*的 C 接口时用data()。坑 4程序崩溃后 UI 日志丢失现象程序崩溃后界面上的日志区被清空无法分析崩溃原因。原因日志只写到了QTextEdit控件内存中崩溃时进程异常终止控件内容来不及保存。解决调试阶段用宏同时输出到 UI 日志和控制台qDebug()控制台输出在崩溃后依然保留#defineBROWSE_LOG(msg)do{log(msg);qDebug().noquote()msg;}while(0)正式调试完成后已移除 qDebug 双输出恢复为简洁的log()调用。教训调试崩溃问题时日志必须输出到进程外控制台/文件不能只依赖进程内的 UI 控件。坑 5递归浏览导致栈溢出风险现象早期版本用递归实现地址空间浏览节点层级深时可能栈溢出崩溃。解决改用BFS 迭代QQueue队列并设置最大深度MAX_DEPTH3和最大节点数MAX_NODES300双保险避免无限递归。教训地址空间浏览优先用迭代而非递归PLC 服务器的节点树可能很深。八、附录8.1 变量清单变量名IEC 类型OPC UA 类型NodeId 后缀初始值读/写CounterINTInt16Counter0只读TemperatureREALFloatTemperature25.0只读PressureREALFloatPressure100.0只读HumidityREALFloatHumidity60.0只读TargetTempREALFloatTargetTemp22.0读/写DeviceModeINTInt16DeviceMode0读/写8.2 连接配置速查参数值URLopc.tcp://PLC_IP:4840Namespace (NS)4BasePath|var|CODESYS Control Win V3 x64.Application.main.8.3 常用 OPC UA 状态码状态码含义常见原因Good成功—BadTypeMismatch类型不匹配写入的数据类型与节点定义不符BadNodeIdUnknown节点不存在NodeId 字符串拼写错误BadConnectionClosed连接已关闭服务器断开或网络中断BadTimeout超时网络不通或服务器无响应8.4 IEC 61131-3 与 OPC UA / C 类型对照IEC 61131-3位宽OPC UAC/C (Qt)BOOL1BooleanboolSINT8 有符号SByteint8_tUSINT8 无符号Byteuint8_tINT16 有符号Int16int16_tUINT16 无符号UInt16uint16_tDINT32 有符号Int32int32_tUDINT32 无符号UInt32uint32_tREAL32 浮点FloatfloatLREAL64 浮点Doubledouble易错PLC 的INT 16 位不是 C 的int通常 32 位

相关新闻

百度网盘提取码智能获取工具完整实战指南

百度网盘提取码智能获取工具完整实战指南

百度网盘提取码智能获取工具完整实战指南 【免费下载链接】baidupankey 在线查询网盘提取码(维护中 rm repo) 项目地址: https://gitcode.com/gh_mirrors/ba/baidupankey 还在为百度网盘资源下载时的提取码而烦恼吗?baidupankey作为一…

2026/10/16 15:59:20 阅读更多 →
从 PHP 到 AI + Golang,程序员自救转型手记(四十):基类改造

从 PHP 到 AI + Golang,程序员自救转型手记(四十):基类改造

这是一个系列 Blog,作者将以一个 PHP 全栈工程师的身份,利用 AI 工具(claude code、codex、deepseek、豆包等):从零开始学习 golang 语言,并最终完成 ai-go-admin(github | gitee)开…

2026/10/16 10:10:16 阅读更多 →
5个常用的网页制作软件和建站系统,省时又省力!

5个常用的网页制作软件和建站系统,省时又省力!

当你想建立一个网站和站群时,你会需要一款强大且便于使用的网页制作软件或建站工具。在这篇文章中,我们将为你推荐5款常用的网页制作软件或建站系统,帮助你在网页制作中省时又省力! 1. PageAdmin PageAdmin是一款国产企业级网站…

2026/10/16 16:27:18 阅读更多 →

最新新闻

STM32寄存器编程实战:GPIO、定时器与串口

STM32寄存器编程实战:GPIO、定时器与串口

每次看到论坛里有人说“寄存器难”,我都想拉住他聊五分钟。我入行那几年也被寄存器折磨过,直到有一次调一个功耗敏感的项目,手里的库函数版本和例程对不上,底层配置出来的引脚电平就是不对。从那以后我翻烂了参考手册,…

2026/10/17 16:01:21 阅读更多 →
OpenClaw从入门到应用——工具(Tools):apply-patch 实战配置与验证

OpenClaw从入门到应用——工具(Tools):apply-patch 实战配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/17 16:01:21 阅读更多 →
Oracle实战:开放式基金交易平台数据库设计与PL/SQL编程实现

Oracle实战:开放式基金交易平台数据库设计与PL/SQL编程实现

简介:这是一份Oracle项目实战文档,以开放式基金交易平台数据库设计为完整案例,适合Oracle初学者与数据库设计人员用于练习从需求分析到表结构落地的全过程。文档围绕招商银行某分行的实际业务场景,梳理基金管理、客户账户管理、交…

2026/10/17 16:01:21 阅读更多 →
Docker部署TailChat开源即时通讯平台:用TaoToken统一Key打通消息机器人与AI能力

Docker部署TailChat开源即时通讯平台:用TaoToken统一Key打通消息机器人与AI能力

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/17 16:01:21 阅读更多 →
DC-6靶机深度解析:WordPress渗透与Linux提权实战指南

DC-6靶机深度解析:WordPress渗透与Linux提权实战指南

1. 项目概述:为什么DC-6是WordPress渗透学习绕不开的“分水岭”如果你正在系统性地打磨Web渗透能力,尤其是想真正吃透WordPress生态下的攻防逻辑,DC-6绝不是一张可跳过的练习卷——它是一道明确的分水岭。我带过不少刚从基础靶机(…

2026/10/17 16:01:21 阅读更多 →
基于深度学习的疲劳驾驶检测:目标检测、图像分类与实时推理实战

基于深度学习的疲劳驾驶检测:目标检测、图像分类与实时推理实战

我还记得去年做毕设选题那会儿,导师给了我几个方向,我一眼就看中了"基于深度学习的疲劳驾驶检测"这个题目。原因很直接:疲劳驾驶导致的事故率一直居高不下,而市面上很多所谓"智能驾驶预警"要么靠红外打哈欠检…

2026/10/17 16:00:20 阅读更多 →

日新闻

DeepSeek选型与部署:V3/R1、Ollama、API全攻略

DeepSeek选型与部署:V3/R1、Ollama、API全攻略

简介:《DeepSeek使用方法.pdf》是一份面向DeepSeek入门及进阶用户的完整使用指南,重点围绕V3与R1两款模型的能力差异与适用场景展开,帮助读者根据任务类型与成本选择合适的模型。资源共1个PDF文件,压缩包大小约311KB,篇…

2026/10/17 0:00:25 阅读更多 →
200+Deepseek润色指令拆解:从底层原理到场景落地的完整指南

200+Deepseek润色指令拆解:从底层原理到场景落地的完整指南

简介:这是一份聚焦DeepSeek提示词优化的《200Deepseek润色指令》PDF文档,共收录两百余条可直接套用的润色指令,涵盖语法修正、风格统一、逻辑梳理、表达精炼等维度,并针对商务写作、学术论文、广告文案等场景给出差异化建议。文档…

2026/10/17 0:00:25 阅读更多 →
OpenCV轮廓法物体计数系统:轻量、可控、可落地的工业级实现

OpenCV轮廓法物体计数系统:轻量、可控、可落地的工业级实现

简介:本资源是一份基于OpenCV的本科课程设计完整报告,面向计算机视觉初学者、图像处理学习者及高校通信/信息类专业学生,解决物体智能计数与检测结果结构化记录的实际问题。项目覆盖图像预处理、轮廓检测、边界框绘制、轨迹栏交互式参数调节、…

2026/10/17 0:00:25 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/16 21:35:51 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/17 1:44:59 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/16 15:04:24 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/16 21:54:38 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/16 3:49:43 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/16 14:23:36 阅读更多 →