Minimal Open62541 OPC-UA server encapsulated in a C++ class
The following code snippet demonstrates how to create a minimal OPC-UA server using the open62541 library, encapsulated in a C++ class. This example includes basic server functionality such as starting the server, handling requests, and stopping the server.
OPCUAServer.hpp
#pragma once
#include <open62541/server.h>
#include <thread>
#include <atomic>
#include <memory>
class OPCUAServer {
public:
OPCUAServer();
~OPCUAServer();
bool start();
void stop();
bool isRunning() const;
private:
void serverLoop();
void setupVariableNode();
UA_Server* server_;
std::unique_ptr<std::thread> serverThread_;
std::atomic<bool> running_;
std::atomic<bool> shouldStop_;
};
OPCUAServer.cpp
#include "Configuration/OPCUAServer.hpp"
#include <cstdlib>
OPCUAServer::OPCUAServer()
: server_(nullptr), running_(false), shouldStop_(false) {
}
OPCUAServer::~OPCUAServer() {
stop();
}
bool OPCUAServer::start() {
if (running_) {
return false; // Already running
}
// Create a server listening on port 4840 (default)
server_ = UA_Server_new();
if (!server_) {
return false;
}
// Setup the variable node
setupVariableNode();
// Start the server thread
shouldStop_ = false;
serverThread_ = std::make_unique<std::thread>(&OPCUAServer::serverLoop, this);
return true;
}
void OPCUAServer::stop() {
if (running_) {
shouldStop_ = true;
if (serverThread_ && serverThread_->joinable()) {
serverThread_->join();
}
if (server_) {
UA_Server_delete(server_);
server_ = nullptr;
}
running_ = false;
}
}
bool OPCUAServer::isRunning() const {
return running_;
}
void OPCUAServer::serverLoop() {
running_ = true;
// Start the server
UA_StatusCode retval = UA_Server_run_startup(server_);
if (retval != UA_STATUSCODE_GOOD) {
running_ = false;
return;
}
// Run the server until stop is requested
while (!shouldStop_) {
UA_Server_run_iterate(server_, true);
}
// Shutdown the server
UA_Server_run_shutdown(server_);
running_ = false;
}
void OPCUAServer::setupVariableNode() {
// Add a variable node to the server
// 1) Define the variable attributes
UA_VariableAttributes attr = UA_VariableAttributes_default;
attr.displayName = UA_LOCALIZEDTEXT("en-US", "the answer");
UA_Int32 myInteger = 42;
UA_Variant_setScalar(&attr.value, &myInteger, &UA_TYPES[UA_TYPES_INT32]);
// 2) Define where the node shall be added with which browsename
UA_NodeId newNodeId = UA_NODEID_STRING(1, "the.answer");
UA_NodeId parentNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER);
UA_NodeId parentReferenceNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES);
UA_NodeId variableType = UA_NODEID_NULL; // take the default variable type
UA_QualifiedName browseName = UA_QUALIFIEDNAME(1, "the answer");
// 3) Add the node
UA_Server_addVariableNode(server_, newNodeId, parentNodeId,
parentReferenceNodeId, browseName,
variableType, attr, NULL, NULL);
}
Usage Example
#include <iostream>
#include "OPCUAServer.hpp"
int main() {
OPCUAServer opcuaServer;
if (opcuaServer.start()) {
std::cout << "OPC UA Server started successfully." << std::endl;
} else {
std::cerr << "Failed to start OPC UA Server." << std::endl;
return EXIT_FAILURE;
}
// Run the server for a while
while(true) {
std::this_thread::sleep_for(std::chrono::seconds(10));
}
opcuaServer.stop();
std::cout << "OPC UA Server stopped." << std::endl;
return EXIT_SUCCESS;
}
How to compile
g++ -std=gnu++17 -o opcua_server main.cpp OPCUAServer.cpp -lopen62541
Example output
[2025-05-25 23:24:10.345 (UTC+0200)] info/eventloop Starting the EventLoop
[2025-05-25 23:24:10.345 (UTC+0200)] warn/server AccessControl: Unconfigured AccessControl. Users have all permissions.
[2025-05-25 23:24:10.345 (UTC+0200)] info/server AccessControl: Anonymous login is enabled
[2025-05-25 23:24:10.345 (UTC+0200)] warn/server x509 Certificate Authentication configured, but no encrypting SecurityPolicy. This can leak credentials on the network.
[2025-05-25 23:24:10.346 (UTC+0200)] info/session TCP 0 | SC 0 | Session "Administrator" | AddNode (i=15303): No TypeDefinition. Use the default TypeDefinition for the Variable/Object
[2025-05-25 23:24:10.347 (UTC+0200)] info/session TCP 0 | SC 0 | Session "Administrator" | AddNode (i=25451): No TypeDefinition. Use the default TypeDefinition for the Variable/Object
[2025-05-25 23:24:10.352 (UTC+0200)] info/session TCP 0 | SC 0 | Session "Administrator" | AddNode (ns=1;s=the.answer): No TypeDefinition. Use the default TypeDefinition for the Variable/Object
OPC UA Server started successfully.
[2025-05-25 23:24:10.352 (UTC+0200)] info/network TCP | Listening on all interfaces
[2025-05-25 23:24:10.353 (UTC+0200)] info/network TCP 4 | Creating listen socket for "0.0.0.0" (with local hostname "mycomputer") on port 4840
[2025-05-25 23:24:10.353 (UTC+0200)] info/server New DiscoveryUrl added: opc.tcp://mycomputer:4840
[2025-05-25 23:24:10.353 (UTC+0200)] info/network TCP 5 | Creating listen socket for "::" (with local hostname "mycomputer") on port 4840
How it looks like in opcua-client
If this post helped you, please consider buying me a coffee or donating via PayPal to support research & publishing of new posts on TechOverflow