A real-time industrial IoT pipeline that streams live sensor data from an ESP32 device directly into an OPC UA Server — with zero-config UDP auto-discovery, multi-gateway TCP streaming, and a fully structured plant address space.
┌──────────────────────────────────────┐ ┌─────────────────────────────────────────────┐
│ ESP32 Device │ │ WMind Gateway (Windows / Linux) │
│ │ │ │
│ 🌡 DS18B20 Temp Sensor (GPIO 14) │ │ ┌─────────────────────────────────────┐ │
│ 🔄 Rotary Encoder (GPIO 18 / 19) │ │ │ UDP Discovery Service :8888 │ │
│ │ UDP │ │ "WHO_IS_GATEWAY?" ←→ Reply │ │
│ 1. Broadcasts WHO_IS_GATEWAY? │────────▶│ └─────────────────────────────────────┘ │
│ 2. Receives gateway IP + Port │◀────────│ │
│ │ │ ┌─────────────────────────────────────┐ │
│ 3. Opens TCP connection │ TCP │ │ TCP Server :9000 │ │
│ 4. Streams JSON every 1 second │────────▶│ │ JSON → Parse → OPC UA Nodes │ │
│ │ │ └─────────────────────────────────────┘ │
└──────────────────────────────────────┘ │ │
│ ┌─────────────────────────────────────┐ │
│ │ OPC UA Server :4840 │ │
│ │ opc.tcp://localhost:4840/ │ │
│ │ WMindGateway │ │
│ └─────────────────────────────────────┘ │
└─────────────────────────────────────────────┘
opc_ua/
│
├── src/ # ESP32 PlatformIO firmware
│ └── main.cpp # WiFi, UDP discovery, TCP client, sensors
│
├── include/ # PlatformIO headers
├── lib/ # PlatformIO local libraries
├── test/ # PlatformIO tests
├── platformio.ini # Board + library config for ESP32
├── .gitignore
│
└── server/ # .NET OPC UA Gateway Server
├── Program.cs # Entry point — bootstraps OPC UA + discovery
├── SimulatorServer.cs # Subclasses StandardServer, wires NodeManager
├── SimulatorNodeManager.cs # Builds OPC UA address space + TCP ingestion
├── GatewayDiscoveryService.cs # UDP responder — tells ESP32 "I'm here"
├── OpcUaSimulator.csproj # .NET project file
├── simulator.sln # Solution file
├── appsettings.json
└── pki/ # Auto-generated OPC UA certificates
On boot, the ESP32:
- Connects to WiFi
- Broadcasts
WHO_IS_GATEWAY?over UDP port8888(subnet-wide) - Receives
I_AM_GATEWAY:<ip>:<port>replies and connects to all discovered gateways via TCP (supports up to 5 simultaneous gateways) - Reads real temperature every second from a DS18B20 one-wire sensor on GPIO 14
- Simulates voltage (15.0V – 30.0V) via a rotary encoder — each detent click = ±0.1V
- Serializes both signals as JSON and streams to all gateways every second
JSON payload streamed by ESP32:
{
"deviceId": "ESP32_01",
"timestamp": 12453,
"ns=2;s=Plant=MUMBAI_PLANT/Line=ASSEMBLY_01/Machine=CNC_02/Signal=VOLTAGE": 24.3,
"ns=2;s=Plant=MUMBAI_PLANT/Line=ASSEMBLY_01/Machine=CNC_02/Signal=TEMP": 31.7
}| File | Role |
|---|---|
GatewayDiscoveryService.cs |
Listens on UDP 8888. Replies with its own WiFi IP + TCP port so ESP32 auto-discovers it — no hardcoded IPs needed |
SimulatorNodeManager.cs |
TCP server on port 9000. Accepts multiple ESP32 clients, parses JSON, writes values into live OPC UA nodes |
SimulatorServer.cs |
Wires the custom node manager into the standard OPC UA server stack |
Program.cs |
Validates certificates, starts UDP + TCP + OPC UA, prints the endpoint |
Once running, the server exposes this live node hierarchy:
Objects/
└── 📁 MUMBAI_PLANT
└── 📁 ASSEMBLY_01
└── 📁 CNC_02
├── 📊 VOLTAGE (Double — updated live every second)
└── 🌡 TEMP (Double — updated live every second)
OPC UA Endpoint:
opc.tcp://localhost:4840/WMindGateway
Node IDs for direct browsing or subscription:
ns=2;s=Plant=MUMBAI_PLANT/Line=ASSEMBLY_01/Machine=CNC_02/Signal=VOLTAGE
ns=2;s=Plant=MUMBAI_PLANT/Line=ASSEMBLY_01/Machine=CNC_02/Signal=TEMP
| Component | ESP32 Pin | Notes |
|---|---|---|
| DS18B20 Data | GPIO 14 | Add 4.7kΩ pull-up to 3.3V |
| DS18B20 VCC | 3.3V | |
| DS18B20 GND | GND | |
| Rotary CLK | GPIO 18 | Internal pull-up enabled in code |
| Rotary DT | GPIO 19 | Internal pull-up enabled in code |
| Rotary GND | GND |
For ESP32 Firmware:
For Gateway Server:
- .NET 8 SDK or later
⚠️ Both the PC running the gateway and the ESP32 must be connected to the same WiFi network.
git clone https://github.com/your-username/opc-ua-esp32.git
cd opc-ua-esp32Open src/main.cpp and update these two lines with your network details:
const char* ssid = "YOUR_WIFI_SSID";
const char* password = "YOUR_WIFI_PASSWORD";Make sure the PC running the gateway server is on the same WiFi network as the ESP32.
Open a terminal, navigate to the server folder, and run:
cd server
dotnet runYou should see:
🔎 UDP Discovery Service running on port 8888
📡 TCP Gateway Listening on port 9000
✅ WMind OPC UA Gateway Running
📡 OPC UA Endpoint: opc.tcp://localhost:4840/WMindGateway
📡 TCP Server Port: 9000
Press ENTER to stop...
Start the server before powering the ESP32 so it can discover the gateway on boot.
Open the opc_ua/ root folder in VS Code with PlatformIO, then upload using either:
PlatformIO toolbar — click the → Upload (right arrow) button
or terminal:
pio run --target uploadAfter uploading, open the serial monitor at 115200 baud:
pio device monitor --baud 115200Expected output on the ESP32 serial monitor:
Connecting to WiFi....
WiFi connected
ESP32 IP: 192.168.1.105
Searching for Gateways...
Reply: I_AM_GATEWAY:192.168.1.10:9000
Gateway Found: 192.168.1.10:9000
Connected → 192.168.1.10
Sent → 24.30 V | 31.70 °C → 1 gateways
Sent → 24.30 V | 31.72 °C → 1 gateways
And back in the server terminal you will see:
🔌 ESP32 Connected: 192.168.1.105:54321
⚡ OPC UA Updated → V=24.30V | T=31.70°C
⚡ OPC UA Updated → V=24.30V | T=31.72°C
Use any OPC UA client — UaExpert, Prosys OPC UA Browser, Node-RED, or your own application — and connect to:
opc.tcp://localhost:4840/WMindGateway
Then browse or subscribe to these node IDs for live data:
| Signal | Node ID |
|---|---|
| Voltage | ns=2;s=Plant=MUMBAI_PLANT/Line=ASSEMBLY_01/Machine=CNC_02/Signal=VOLTAGE |
| Temperature | ns=2;s=Plant=MUMBAI_PLANT/Line=ASSEMBLY_01/Machine=CNC_02/Signal=TEMP |
Values update every 1 second as data arrives from the ESP32.
| Parameter | Default Value | Location |
|---|---|---|
| WiFi SSID | Edit in firmware | src/main.cpp |
| UDP Discovery Port | 8888 |
ESP32 + Server |
| TCP Data Port | 9000 |
ESP32 + Server |
| OPC UA Port | 4840 |
Program.cs |
| Max Gateways | 5 |
src/main.cpp |
| Voltage Range | 15.0V – 30.0V |
src/main.cpp |
| Encoder Step | 0.1V per click |
src/main.cpp |
| Temp Sensor Pin | GPIO 14 |
src/main.cpp |
| Rotary CLK Pin | GPIO 18 |
src/main.cpp |
| Rotary DT Pin | GPIO 19 |
src/main.cpp |
Defined in platformio.ini and auto-installed by PlatformIO:
lib_deps =
bblanchon/ArduinoJson
milesburton/DallasTemperature
paulstoffregen/OneWireRestored automatically on dotnet run:
<PackageReference Include="OPCFoundation.NetStandard.Opc.Ua" Version="1.5.374.118" />
<PackageReference Include="OPCFoundation.NetStandard.Opc.Ua.Server" Version="1.5.374.118" />- Certificates are auto-generated on first run inside
server/pki/ AutoAcceptUntrustedCertificates = trueis set for easy development- For production use: disable auto-accept and configure proper certificate trust
- To expose the server on your local network, replace
localhostin the endpoint with your machine's actual IP address
| Problem | Solution |
|---|---|
No gateways found on ESP32 |
Start the server before the ESP32 boots, or press the ESP32 reset button after the server is up |
| Server can't find WiFi IP | GatewayDiscoveryService only reads Wireless interfaces — connect your PC to WiFi, not Ethernet |
| OPC UA client can't connect | Allow port 4840 TCP inbound through Windows Firewall |
DS18B20 reads -127°C |
Check wiring and confirm 4.7kΩ pull-up resistor between GPIO 14 and 3.3V |
| Certificate error on startup | Delete the server/pki/ folder and restart — it will regenerate |
| Multiple gateways not connecting | Ensure each gateway server is running and on the same subnet |