B4R Library rOpen62541 (OPC UA Server)

B4R Library rOpen62541



Brief
rOpen62541
is an open-source library wrapper for the industrial open62541 OPC UA protocol stack, specifically optimized for the ESP32-S3 Dual-Core architecture.
It provides thread-safe cross-core communication, dynamic string-node creation, and type-agnostic runtime write diagnostics.

Important Notice: This project is actively hosted and maintained on GitHub. Not all minor version bumps, patch adjustments, or documentation extensions will be individually announced in this forum thread. For the absolute latest code baseline and active development branches, please bookmark and monitor the repository directly.

👉 Access the main repository and assets here: GitHub



Author's Note & Personal Context
This library was developed purely for personal educational use, born out of a desire to dive deep into industrial connectivity and tackle the challenging feat of wrapping the open62541 stack for B4R. It wasn't easy to build, but exploring cross-platform client integration — such as B4J with the PyBridge or Node-RED — and seeing the dual-core hardware spring to life made it an incredibly rewarding project. Moving forward, this proof-of-concept server framework will serve as a foundational wireless gateway component for the author's open-source several MAKE projects.



OPC UA (Open Platform Communications Unified Architecture) is a robust, platform-independent, and highly secure industrial machine-to-machine (M2M) communication protocol framework widely deployed in Industry 4.0 / Industrial IoT (IIoT) environments.

Unlike standard message-based IoT protocols (like MQTT), OPC UA provides a unified Address Space allowing devices to structurally expose complex object folders, variable data nodes, and custom tracking methods with rich data metadata. It is extensively used to interconnect hardware sensors, embedded controllers, PLCs, industrial SCADA systems, and high-level enterprise MES/ERP software architectures seamlessly over modern Ethernet/Wi-Fi networks.



Purpose & Scope
  • Server-Only Architecture: This library is dedicated exclusively to acting as an OPC UA Server.
    It turns your microcontroller into a data provider but does not include client connection parsing capabilities.
  • Proof of Concept & Learning Project: This framework was explicitly developed as a personal educational project to learn the foundational basics of OPC UA by creating a custom standalone hardware device from scratch.
  • No Professional Intent: There is absolutely no intention for this codebase to be deployed in mission-critical environments, production facilities, or professional commercial installations.
  • Provides a high-level B4R abstraction layer for the native C-based open62541 library engine.
  • Offloads heavy TCP/IP layers and subscription socket polling entirely to ESP32 Core 0 (Network Core) using FreeRTOS tasks to guarantee zero timing jitter on your hardware loops.
  • Keeps ESP32 Core 1 (B4R Core) completely fluid and responsive for low-level critical hardware execution, physical interrupts, and timing loops.
  • Implements a strict FreeRTOS binary semaphore mutex (open62541Mutex) preventing data collisions or memory corruption during concurrent memory read/write cycles.
  • Exposes a universal, type-agnostic string node interceptor payload framework capable of catching incoming String, Int, or Float writes natively over a robust B4R Byte() array block.


Development Info
This B4R library is:
  • An open62541 protocol stack wrapper using Git-Revision: v1.2-rc1-20-g78a6721b-dirty.
  • Written in C++ using Arduino IDE 2.3.10+, Espressif ESP32 Arduino Core V3.x, and the standard B4Rh2xml / XMLTool parsing pipeline.
  • Mandatory Hardware Constraint:
    This library was developed and strictly tested with an ESP32-S3-N16R8 developer kit (32-bit Xtensa lx7 dual-core chip with 16MB Flash and 8MB PSRAM). Due to memory allocation sizes and dual-core constraints, utilizing this specific hardware class is highly recommended or mandatory.
  • Tested with B4R 4.00 (64-bit).
  • Not supported over WAN directly: Meant for local subnet networks (LAN/WLAN) where no external internet router firewall ports need to be exposed.


Compatibility
  • Supports Espressif ESP32-S3 high-memory microcontrollers (N16R8 format). Must ensure standard network lwIP socket frameworks are initialized.

Verified Client Compatibility
The rOpen62541 server layer has been tested with the following OPC UA clients across different ecosystems:
  • Node-RED - Handled smoothly using the node-red-contrib-opcua palette node framework.
  • opcua-commander - Fully responsive via the visual Node.js Terminal-based Explorer.
  • B4J with OPC UA Client - Highly compatible with the B4J SS_OPCUAClient industrial asset wrapper library.
  • B4J with PyBridge - Verified handling data loops passing through Python-bridged socket integrations.


This library explicitly uses the open62541 v1.2 legacy branch (v1.2-rc1-20-g78a6721b-dirty) instead of v1.3+ or v1.5+ release lines.
While modern versions introduce advanced enterprise desktop configurations, version 1.2 is carefully selected for the following critical engineering reasons:
  • Embedded-First Resource footprint: Version 1.2 compiles into a highly lightweight binary footprint. Newer versions contain massive auto-generated internal structures (such as updated Namespace 0 trees) that routinely hit compiler variable-tracking limits, causing the Xtensa compiler toolchain to freeze, link-crash, or hang the B4R IDE.
  • Native lwIP Connection Abstraction: The network socket management layer in v1.2 seamlessly adapts to the ESP32’s native embedded FreeRTOS/lwIP stack out of the box. Newer versions introduce rigid desktop POSIX dependencies (such as <poll.h> and complex desktop mutex types) that create structural friction on microcontrollers.
  • Streamlined Property Configuration: Version 1.2 exposes clean, low-level configuration functions like UA_ServerConfig_setCustomHostname(). Later versions completely refactor these into complex, deeply nested configuration allocation macros that are difficult to manage within an object-oriented B4R C++ wrapper interface.
  • Perfect Functional Match: The v1.2 branch provides 100% of the industrial protocol features required for this proof of concept (including dynamic float, integer, string, and raw binary ByteString node arrays) without any unnecessary software bloat.



Screenshot

1789464894535.png




Installation

To install the library:
  • Download the codebase package directly from the link above.
  • Copy the source sub-folder rOpen62541 straight into your B4R Additional Libraries directory, keeping the folder layout fully intact.
  • Copy the companion rOpen62541.xml definition file into your B4R Additional Libraries directory.


Code Example (Snippet)

rOpen62541 B4R Example:
Sub Process_Globals
	Private VERSION 			As String = "rOpen62541 EnvSim v20260913"
	Public Serial1 				As Serial
	Private WiFi 				As ESP8266WiFi						' Lib rESP8266WiFi
	Private SSID				As String = "***"
	Private PW 					As String = "***"
	Private OpcServer			As Open62541						' Lib rOpen62541
	Private PORT 				As Int = 4840
	Private NAMESPACE_INDEX 	As Int = 1
	Private AppTimer As Timer
	Private APPTIMER_INTERVAL As ULong = 2000
	Private bc As ByteConverter	'ignore
End Sub

Sub AppStart
	Serial1.Initialize(115200)
	AppTimer.Initialize("AppTimer_Tick", APPTIMER_INTERVAL)
	AppTimer.Enabled = False
	If WiFi.Connect2(SSID, PW) Then
		Log("[AppStart] WiFi connected > local ip=", WiFi.LocalIP)
		RunNative("DisableWiFiSleep", Null)
		RunNative("InitSystemClock", Null)
		If InitOpcServer Then
			AppTimer.Enabled = True
		End If
	Else
	End If
End Sub

Private Sub InitOpcServer As Boolean
	Dim TimeoutCounter As Int = 0
	Dim ServerBootFailed As Boolean = False
	OpcServer.Initialize(WiFi.LocalIp, PORT, "", "", "OnDataWrite")
	Do While OpcServer.IsReady = False
		Delay(100) ' 100ms yield ticks for the cooperative scheduler
		TimeoutCounter = TimeoutCounter + 1
		If TimeoutCounter >= 50 Then ' 50 ticks * 100ms = 5000ms (5 Seconds Timeout)
			ServerBootFailed = True
			Exit ' Break out of the endless loop safely!
		End If
	Loop
	If ServerBootFailed Then
		Log("[InitOpcServer][E] OPC UA Server initialization TIMEOUT! Core 0 failed.")
	Else
		OpcServer.AddFloatNode("Temperature", "Room Temperature", 20.0)
		OpcServer.AddFloatNode("Humidity", "Room Humidity", 68.0)
		OpcServer.AddIntNode("Counter", "Total Shift Cycle Count", 0)
		OpcServer.AddByteStringNode("RawTelemetry", "Atomic Hex Package", RawBuffer)
		OpcServer.AddStringNode("Trigger", "Remote Action Trigger", "0")
	End If
	Return Not(ServerBootFailed)
End Sub

Sub AppTimer_Tick
    If OpcServer.IsReady Then
		Dim CurrentTemp As Float = 24.5 + Rnd(-2.0, 3.0)
		Dim CurrentHum As Float = 68 + Rnd(-10.0, 11.0)
		OpcServer.WriteNumeric(NAMESPACE_INDEX, "Temperature", CurrentTemp)
		OpcServer.WriteNumeric(NAMESPACE_INDEX, "Humidity", CurrentHum)
    End If
End Sub

Private Sub OnDataWrite(buffer() As Byte)
	Log("[OnDataWrite] Client invoked the trigger method. value=", bc.StringFromBytes(buffer))
	'[OnDataWrite] Client invoked the trigger method. value=68
	'[OnDataWrite] Client invoked the trigger method. value=START
End Sub

#if C
#include "esp_wifi.h"
#include "time.h"
void DisableWiFiSleep(B4R::Object* o) {
    esp_wifi_set_ps(WIFI_PS_NONE);
    ::Serial.println("[Hardware Engine] Wi-Fi Modem-Sleep forcefully disabled! Radio set to high-performance mode.");
}

void InitSystemClock(B4R::Object* o) {
    configTime(3600, 0, "pool.ntp.org", "time.nist.gov");
    ::Serial.println("[Hardware Engine] SNTP Client started. Synchronizing baseline...");
}
#End If



License
  • rOpen62541 Library - MIT License as stated in the LICENSE file provided with rOpen62541.
  • Open62541 Library - Mozilla Public License v2.0 as stated in the LICENSE file provided with open62541.


Credits
  • Developers, maintainers, and open-source contributors of the official open62541 architecture framework, providing an industrial-grade embedded C implementation of OPC UA.
  • Anywhere software for the B4X suite of development tools.
  • @Peter Simpson for the B4J library SS_OPCUAClient.
  • AI for engineering collaboration.


Disclaimer
  • All product names, logos, protocols, and brands are property of their respective owners.
  • This B4R library is an independent open-source wrapper tracking standard open62541 architectures.
    It is neither officially endorsed nor maintained by the primary open62541 project core maintainers.
  • This codebase represents a strict standalone proof-of-concept / learning exercise and is explicitly not intended for professional, commercial, or critical industrial application.
  • This wrapper, its cross-core FreeRTOS mutex mappings, and its data type interceptor routines were designed and polished with the specialized interactive assistance of an AI engineering collaborator, achieving optimal compatibility with the B4R pre-compiler stack.

 
Last edited:

Peter Simpson

Expert
Licensed User
Longtime User
Good morning @rwblinn,
I missed this release yesterday, really nice work 👍

My original intention was to release this exact same B4R library at the same time as the B4J Server/Client libraries. In fact, my attempt at creating the B4R library actually delayed the B4J releases. Wrapping this B4R library was extremely difficult, so I deleted the whole attempt and decided I’d try again at a later date. But you have saved me the trouble.

I know how challenging this B4R wrapper must have been for you to create. You probably spent countless hours pulling your hair out just to get it wrapped and working correctly.

I’ll play around with your B4R library over the weekend.

Thank you...
 

rwblinn

Well-Known Member
Licensed User
Longtime User
Update 0.70 (Build 20260919)

Major architectural update to the rOpen62541 repository.
This release resolves a critical cross-core connection stability issue encountered on ESP32 boards equipped with external PSRAM.
The full source code, examples, and newly expanded step-by-step guides have been updated on GitHub.

👉 Get the latest release here on GitHub.
  • FIX: Optimized OPC UA server task stack size from 64KB to 16KB to force internal SRAM allocation, preventing silent cross-core PSRAM memory corruption and network lockouts during client disconnections.
  • NEW: ReadNumeric - Reads a node value using a numeric identifier.
  • NEW: ReadString - Reads a node value using a string identifier.
  • NEW: WriteNumeric - Writes a node numeric value using a string identifier.
  • NEW: WriteString - Writes a node string value using a string identifier.
  • NEW: Status Code Constants - Official OPC UA severity status codes (OPC 10000-4 clause 7.38).
  • NEW: GitHub Documention - Additional guides in docs DEV-NOTES, FUNCTIONS-REFERENCE, README, TROUBLESHOOTING, TUTORIAL-CALLBACKS, TUTORIAL-NODEID-LIST.
  • NEW: Example NodeIDs - Low-level standard namespace lookup implementations. Demonstrates querying standard Namespace 0 system variables and parsing complex structure payloads.
  • UPD: Example MethodCall - Revised methods names for the callbacks.
  • UPD: All examples to apply SRAM fix.
  • DEL: UpdateNodeValue - Replaced by WriteNumeric and WriteString for consistency with the Read functions.
 

Similar Threads

Top