????????1.Open62541 概述 ?
????????2.Open62541 的功能 ?
????????3.Open62541 的开发环境 ?
????????4.Open62541 编译工具??
? ? ? ? 5.Open62451源码下载
? ? ? ? 6.Open62541源码编译
? ? ? ? 7.Open62541 Server服务器端测试
? ? ? ? 8.Open62541Client客户端测试
????????Open62541 是一款开源的嵌入式系统开发手册,旨在帮助开发者快速掌握 Open62541 的开发技能。本文将从 Open62541 的概述、功能、开发环境、编程接口和应用案例五个方面进行介绍。
一、Open62541 概述 ?
????????Open62541 是一款面向物联网、智能家居和工业控制等领域的嵌入式系统开发手册。它基于 Linux 内核,提供了丰富的硬件驱动和软件接口,方便开发者进行定制化开发。
二、Open62541 的功能 ?
????????Open62541 具有以下主要功能: ?
????????1.实时操作系统:基于 Linux 内核,支持多任务、多线程,提供实时性能。 ?
????????2.丰富的硬件驱动:支持多种常见硬件设备,如显示器、触摸屏、传感器等。 ?
????????3.强大的网络支持:支持以太网、Wi-Fi、蓝牙等多种网络协议,方便接入互联网。 ?
????????4.安全性:提供安全启动、安全传输等机制,保障系统安全。
三、Open62541 的开发环境 ?
????????开发 Open62541 系统需要以下工具和环境: ?
????????1.开发板:需要一块支持 Open62541 的开发板,如 Raspberry Pi、树莓派等。 ?
????????2.编辑器:可以使用任何文本编辑器,如 Notepad++、Visual Studio Code 等。 ?
????????3.编译器:需要安装 GCC(GNU Compiler Collection)等编译工具。 ?
????????4.调试器:可以使用 GDB(GNU Debugger)等调试工具。
四、Open62541编译工具
? ? ? ? 1、Cmake gui。
? ? ? ? 2、Visual Stu'di'o 2019。
? ? ? ? 3、Python3。
五、Open62541源码下载
? ? ? ? 1、官方网站
https://www.open62541.org/https://www.open62541.org/? ? ? ? 2、GitHub
https://github.com/open62541/open62541https://github.com/open62541/open62541?
? ? ? ? 3 、无法连接Open62541官网或者Github官网的解决办法。
? ? ? ? 存在无法连接Open62541官网或者Github官网的解决办法主要是修改系统C:\Windows\System32\drivers\etc目录下的Hosts文件,在Hosts文件末尾加入如下代码
????????#github
????????140.82.112.4 github.com
????????199.232.69.194 github.global.ssl.fastly.net
六、Open62541源码编译
? ? ? ? 1、所需编译工具Cmake Gui,Visual Studio2019,Python3。
? ? ? ? 2、编译过程
? ? ? ? (1)首先将下载后的源码解压到当前目录,并建立一个存放编译后目标文件的文件夹这里我们建立的文件夹名称为Open62541Build,如下图所示。
???????? (2)打开Cmake Gui配置文件路径如下图所示。
? ? ? ? (3)选择UA_ENABLE_AMALGAMATION选项为单独生产Open62541.h文件和Open62541.c文件,Cmakelist文件中有提到这一点。
? ? ? ? (4)编译目标环境配置为win32,Visual Studio 2019版本后进行编译,如下图所示。
? ? ? ? (5)编译完成后使用Visual Studio2019打开open62541.sln项目文件进行编译。目标环境选择Release Win32。
? ? ? ? (6)选择生产生成-->生成解决方案进行生成,如下所示。
? ? ? ? (7)编译完成后在目标文件夹下面可以看到Bin文件夹和Open62541.h文件和Open62541.c文件,如下图所示。
七、Open62541 Server服务器端测试
? ? ? ? 1、打开Visual Studio 2019新建名为Open62541ServerDemo的控制台程序。增加Open62541.h和Open62541.c文件。
? ? ? ? 2、项目属性连接器->输入->附加依赖项种增加WS2_32.Lib文件,不然编译会报错。如下图所示。
? ? ? ? 3、Open62541ServerDemo.cpp文件中增加如下代码。
#include <signal.h>
#include <stdlib.h>
#include "open62541.h"
UA_Boolean running = true;
static void stopHandler(int sign) {
UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "received ctrl-c");
running = false;
}
static void addLocalizedTextVariable(UA_Server* server, char* name)
{
/* Define the attribute of the myInteger variable node */
UA_VariableAttributes attr = UA_VariableAttributes_default;
UA_LocalizedText orig = UA_LOCALIZEDTEXT((char*)"en-US", "hello");
UA_Variant_setScalar(&attr.value, &orig, &UA_TYPES[UA_TYPES_LOCALIZEDTEXT]);
attr.description = UA_LOCALIZEDTEXT("en-US", name);
attr.displayName = UA_LOCALIZEDTEXT("en-US", name);
attr.dataType = UA_TYPES[UA_TYPES_LOCALIZEDTEXT].typeId;
attr.accessLevel = UA_ACCESSLEVELMASK_READ | UA_ACCESSLEVELMASK_WRITE;
/* Add the variable node to the information model */
UA_NodeId myNodeId = UA_NODEID_STRING(1,name);
UA_QualifiedName myName = UA_QUALIFIEDNAME(1, name);
UA_NodeId parentNodeId = UA_NODEID_NUMERIC(0,UA_NS0ID_OBJECTSFOLDER);
UA_NodeId parentReferenceNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES);
UA_Server_addVariableNode(server, myNodeId, parentNodeId, parentReferenceNodeId, myName,
UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE), attr, NULL, NULL);
}
static void addUInt32Variable(UA_Server* server, char* name)
{
/* Define the attribute of the myInteger variable node */
UA_VariableAttributes attr = UA_VariableAttributes_default;
UA_UInt32 orig = 100;
UA_Variant_setScalar(&attr.value, &orig, &UA_TYPES[UA_TYPES_UINT32]);
attr.description = UA_LOCALIZEDTEXT("en-US", name);
attr.displayName = UA_LOCALIZEDTEXT("en-US", name);
attr.dataType = UA_TYPES[UA_TYPES_UINT32].typeId;
attr.accessLevel = UA_ACCESSLEVELMASK_READ | UA_ACCESSLEVELMASK_WRITE;
/* Add the variable node to the information model */
UA_NodeId myNodeId = UA_NODEID_STRING(1, name);
UA_QualifiedName myName = UA_QUALIFIEDNAME(1, name);
UA_NodeId parentNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER);
UA_NodeId parentReferenceNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES);
UA_Server_addVariableNode(server, myNodeId, parentNodeId, parentReferenceNodeId, myName,
UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE), attr, NULL, NULL);
}
int main(void)
{
signal(SIGINT, stopHandler);
signal(SIGTERM, stopHandler);
UA_Server* server = UA_Server_new();
UA_ServerConfig_setDefault(UA_Server_getConfig(server));
addLocalizedTextVariable(server, "info1");
addLocalizedTextVariable(server, "info2");
addLocalizedTextVariable(server, "info3");
addUInt32Variable(server, "uint1");
addUInt32Variable(server, "uint2");
addUInt32Variable(server, "uint3");
UA_StatusCode retval = UA_Server_run(server, &running);
UA_Server_delete(server);
return retval == UA_STATUSCODE_GOOD ? EXIT_SUCCESS : EXIT_FAILURE;
}
? ? ? ? 4、运行Server项目。可以看到服务器建立成功地址为opc.tcp://DESKTOP-14CFB38:4840
? ? ? ? ?5、UaExpert连接服务器,在UaExpert中添加服务器如下图所示。
? ? ? ? 6、连接成功后将变量增加到数据视图中进行监控。
8、Open62541Client客户端测试
? ? ? ? 1、客户端和服务器端一样都是建立控制台项目,增加Open62541.h和Open62541.c文件项目属性附加依赖库中增加WS2_32.Lib文件,如下图所示。
? ? ? ? 2、Open62541ClientDemo.cpp文件中增加如下代码。
#include <stdlib.h>
#include <stdio.h>
#include "open62541.h"
static UA_StatusCode multiRead(UA_Client* client)
{
const int arraySize = 6;
UA_ReadValueId itemArray[arraySize];
for (int i = 0; i < arraySize; ++i)
{
UA_ReadValueId_init(&itemArray[i]);
itemArray[i].attributeId = UA_ATTRIBUTEID_VALUE;
}
itemArray[0].nodeId = UA_NODEID_STRING(1, "info1");
itemArray[1].nodeId = UA_NODEID_STRING(1, "info2");
itemArray[2].nodeId = UA_NODEID_STRING(1, "info3");
itemArray[3].nodeId = UA_NODEID_STRING(1, "uint1");
itemArray[4].nodeId = UA_NODEID_STRING(1, "uint2");
itemArray[5].nodeId = UA_NODEID_STRING(1, "uint3");
UA_ReadRequest request;
UA_ReadRequest_init(&request);
request.nodesToRead = &itemArray[0];
request.nodesToReadSize = arraySize;
UA_ReadResponse response = UA_Client_Service_read(client, request);
UA_StatusCode retStatusArray[arraySize];
UA_StatusCode retval = response.responseHeader.serviceResult;
if (retval == UA_STATUSCODE_GOOD)
{
if (response.resultsSize == arraySize)
{
for (int i = 0; i < arraySize; ++i)
{
retStatusArray[i] = response.results[i].status;
}
}
else
{
UA_ReadResponse_clear(&response);
return UA_STATUSCODE_BADUNEXPECTEDERROR;
}
}
for (int i = 0; i < arraySize; ++i)
{
if (retStatusArray[i] == UA_STATUSCODE_GOOD)
{
UA_DataValue res = response.results[i];
if (!res.hasValue) // no value
{
UA_ReadResponse_clear(&response);
return UA_STATUSCODE_BADUNEXPECTEDERROR;
}
UA_Variant out;
memcpy(&out, &res.value, sizeof(UA_Variant));
UA_Variant_init(&res.value);
if (out.type == &UA_TYPES[UA_TYPES_LOCALIZEDTEXT])
{
UA_LocalizedText* ptr = (UA_LocalizedText*)out.data;
printf("Text: %.*s\n", ptr->text.length, ptr->text.data);
}
else if (out.type == &UA_TYPES[UA_TYPES_UINT32])
{
UA_UInt32* ptr = (UA_UInt32*)out.data;
printf("UInt32 Value: %d\n", *ptr);
}
}
}
UA_ReadResponse_clear(&response);
return UA_STATUSCODE_GOOD;
}
UA_StatusCode multiWrite(UA_Client* client, UA_UInt32 Index)
{
const int arraySize = 6;
UA_WriteValue wValueArray[arraySize];
for (int i = 0; i < arraySize; ++i)
{
UA_WriteValue_init(&wValueArray[i]);
wValueArray[i].attributeId = UA_ATTRIBUTEID_VALUE;
}
wValueArray[0].nodeId = UA_NODEID_STRING(1, "info1");
wValueArray[1].nodeId = UA_NODEID_STRING(1, "info2");
wValueArray[2].nodeId = UA_NODEID_STRING(1, "info3");
wValueArray[3].nodeId = UA_NODEID_STRING(1, "uint1");
wValueArray[4].nodeId = UA_NODEID_STRING(1, "uint2");
wValueArray[5].nodeId = UA_NODEID_STRING(1, "uint3");
UA_Variant infoVar;
UA_LocalizedText info1Value = UA_LOCALIZEDTEXT("en-US", "world1");
UA_Variant_init(&infoVar);
UA_Variant_setScalar(&infoVar, &info1Value, &UA_TYPES[UA_TYPES_LOCALIZEDTEXT]);
wValueArray[0].value.value = infoVar;
wValueArray[0].value.hasValue = true;
UA_LocalizedText info2Value = UA_LOCALIZEDTEXT("en-US", "world2");
UA_Variant_init(&infoVar);
UA_Variant_setScalar(&infoVar, &info2Value, &UA_TYPES[UA_TYPES_LOCALIZEDTEXT]);
wValueArray[1].value.value = infoVar;
wValueArray[1].value.hasValue = true;
UA_LocalizedText info3Value = UA_LOCALIZEDTEXT("en-US", "world3");
UA_Variant_init(&infoVar);
UA_Variant_setScalar(&infoVar, &info3Value, &UA_TYPES[UA_TYPES_LOCALIZEDTEXT]);
wValueArray[2].value.value = infoVar;
wValueArray[2].value.hasValue = true;
UA_UInt32 uint1Value = 101+Index;
UA_Variant_init(&infoVar);
UA_Variant_setScalar(&infoVar, &uint1Value, &UA_TYPES[UA_TYPES_UINT32]);
wValueArray[3].value.value = infoVar;
wValueArray[3].value.hasValue = true;
UA_UInt32 uint2Value = 102+Index;
UA_Variant_init(&infoVar);
UA_Variant_setScalar(&infoVar, &uint2Value, &UA_TYPES[UA_TYPES_UINT32]);
wValueArray[4].value.value = infoVar;
wValueArray[4].value.hasValue = true;
UA_UInt32 uint3Value = 103+Index;
UA_Variant_init(&infoVar);
UA_Variant_setScalar(&infoVar, &uint3Value, &UA_TYPES[UA_TYPES_UINT32]);
wValueArray[5].value.value = infoVar;
wValueArray[5].value.hasValue = true;
UA_WriteRequest wReq;
UA_WriteRequest_init(&wReq);
wReq.nodesToWrite = &wValueArray[0];
wReq.nodesToWriteSize = arraySize;
UA_WriteResponse wResp = UA_Client_Service_write(client, wReq);
UA_StatusCode retval = wResp.responseHeader.serviceResult;
if (retval == UA_STATUSCODE_GOOD) {
if (wResp.resultsSize == 1)
retval = wResp.results[0];
else
retval = UA_STATUSCODE_BADUNEXPECTEDERROR;
}
UA_WriteResponse_clear(&wResp);
return retval;
}
int main(void)
{
UA_UInt32 mIndex;
mIndex = 0;
UA_Client* client = UA_Client_new();
UA_ClientConfig_setDefault(UA_Client_getConfig(client));
UA_StatusCode retval = UA_Client_connect(client, "opc.tcp://localhost:4840");
if (retval != UA_STATUSCODE_GOOD) {
UA_Client_delete(client);
return (int)retval;
}
while (1)
{
printf("---- Before write ---- \n");
multiRead(client);
printf("\n\n");
multiWrite(client, mIndex);
printf("---- After write ---- \n");
multiRead(client);
mIndex++;
}
UA_Client_delete(client); /* Disconnects the client internally */
return EXIT_SUCCESS;
}
? ? ? ? ?3、运行客户端程序进行测试。使用UaExpert进行监控如下图所示。