muc-opcua
QuackPLC uses W5500 hardware TCP to serve OPC UA while keeping its 1 kHz PLC scan timing stable without lwIP or an RTOS socket layer.
How Does a 1 kHz Soft PLC Serve OPC UA over W55RP20 Hardware TCP/IP Without lwIP?
The WIZnet code lives in muc-plc, not in muc-opcua: the library is transport-agnostic and
ships no chip driver, while muc-plc supplies the W5500 adapter that connects it to hardware.
muc-plc is a PLC runtime project that brings QuackPLC functionality to embedded MCUs. It targets MCUs including the RP2040 and was developed with an architecture that uses the W5500 hardware TCP/IP engine inside the W55RP20 as the network interface for the RP2040.
The key idea is to run the PLC control logic and industrial protocols on the MCU, while offloading TCP/IP processing to the W5500.
A PLC continuously repeats a scan cycle: read inputs → execute logic → update outputs.
muc-plc is designed as a 1 kHz software PLC, meaning it executes this PLC scan cycle every 1 ms — 1,000 control cycles per second.
Industrial protocols such as Modbus TCP, OPC UA, and S7comm are also implemented as Protocol Satellites, separated from the core PLC runtime.
In other words, a key design goal is to separate the PLC control loop from network protocol processing, minimizing its impact on control timing.
To let monitoring software read the controller's data, it adds muc-opcua — a tiny server library for OPC UA, the standard protocol that SCADA and factory-monitoring systems use to read data from industrial devices. The bridge between that server and the Ethernet chip is a single ~100-line file: w5500_opcua_tcp.cpp.
Why This Is Worth a Look
Networking and precise timing usually fight each other on a small MCU. A software TCP/IP stack (like lwIP, the one most embedded projects use) spends CPU time on retransmissions, timers, and checksums — and that time comes out of your control loop.
QuackPLC's answer: don't run a TCP stack on the CPU at all. The W5500 die inside the W55RP20 is a "hardware TCP/IP" chip — it manages TCP connections in silicon, and the firmware only reads and writes finished payload bytes. The project's log records the decision plainly: the original lwIP plan was dropped in favor of "hardware TCP/IP — no lwIP dependency."
Key takeaway — the design treats Ethernet like an I/O module: a peripheral with a predictable access cost, not a software subsystem competing with your control loop for CPU time.
How It Works
muc-opcua never opens sockets itself. It asks the application for a transport, and the adapter file answers with six functions — listen, accept, read, write, close, shutdown — each mapped directly onto a W5500 hardware socket using WIZnet's ioLibrary API.
All six return immediately without blocking. That matches how muc-opcua runs: one mu_server_poll() call in the main loop drives everything, no RTOS and no threads required.
Where You Could Use This Pattern
- Machine controllers — a small motion or process controller that a SCADA system reads over standard OPC UA, without you writing a custom protocol.
- Remote I/O and sensor nodes — field devices that publish their values to factory software in a format it already understands.
- Timing-critical devices that need networking — anything where adding Ethernet must not disturb a precise control or measurement loop.
- Simple firmware without an RTOS — the whole stack runs from a bare-metal main loop; no threads, no lwIP configuration, no RTOS tuning.
What the W5500 Actually Does — and Does Not Do
Inside the W55RP20 package sit two dies: an RP2040 MCU and a W5500 Ethernet controller with its own MAC and PHY, joined by an internal bus. The W5500 owns TCP connection state, retransmission, and checksums for up to 8 sockets. In this project it simultaneously handles DHCP at boot, a small web status page, and the OPC UA endpoint.
Architecture boundary — the W5500 is not the OPC UA server. Message encoding, sessions, and security run in the muc-opcua library on the RP2040 core (its Basic256Sha256 encryption has been validated against the OPC Foundation's reference client). The W5500 is not the PLC either: scan timing and control logic never touch it. Its contribution is exactly the layer the design wanted off the CPU — transport.
The Technical Details
For readers who want the numbers behind the story:
- muc-opcua is written in plain C11 and never calls
mallocduring execution, so memory use is fully predictable (zero dynamic allocation). Its smallest profile compiles to just 23.9 KiB of flash on a Cortex-M0+. - It is not tied to any particular TCP/IP stack or chip (transport-agnostic) — hardware services are injected through small adapter structs, which is why a 100-line file is enough to port it onto W5500 hardware sockets.
- QuackPLC runs its scan from a hardware timer at exactly 1 kHz and measures every cycle's deviation from the schedule (jitter), publishing p50/p90/p99 statistics on a built-in web page.
- Its interpreter executes 158 instruction types modeled on Siemens S7-300/400 STL, the instruction style used by real industrial PLCs.
- The Ethernet side builds on WIZnet's official WIZnet-PICO-C ioLibrary port, including the PIO-driven QSPI link to the W5500 die.
Check Before You Use It
Note on the screenshot: the measurement page shown above is the repository's soak build, which does run. What "not wired yet" below refers to is the OPC UA endpoint — a separate path from that jitter page.
- Not wired end-to-end yet. The adapter file exists but is not compiled into the firmware target on this branch, and the demo
main.cppallocates the server's memory without starting its poll loop. The design is complete; the final wiring is yours. - Socket clash. The demo web page and the OPC UA adapter both use hardware socket 1 — a combined build must assign separate sockets (the W5500 has 8).
- Hardcoded path. ioLibrary include paths point to the author's local
/tmp/WIZnet-PICO-C/checkout; adjust for your machine.
None of these diminish the published architecture — they mark where curation ends and your build begins.
muc-opcua vs. WIZnet OPC UA USB stdio Example
Both projects demonstrate OPC UA on resource-constrained microcontrollers, but they focus on different aspects of the system.
muc-opcua
muc-opcua focuses on providing a lightweight OPC UA Server implementation for microcontrollers.
Its main goal is to make OPC UA practical on small embedded systems such as RP2040-class MCUs. The project separates platform-dependent components such as networking, timing, random number generation, and cryptography through adapter interfaces.
In short:
muc-opcua focuses on building the OPC UA engine itself for small MCUs.
It is suitable as a foundation for developers who want to integrate OPC UA into their own embedded hardware and network environment.
WIZnet OPC UA USB stdio Example
The WIZnet opcua_usb_stdio project takes a more application-oriented approach.
It demonstrates how an RP2350 and W6300 Ethernet controller can expose real application data through an OPC UA Server. The W6300 is particularly suited to embedded Ethernet applications because it provides hardware TCP/IP offload; WIZnet's Pico ecosystem supports the W6300 through QSPI/PIO.
Sensor-like data is received through USB serial and mapped to OPC UA Nodes:
USB Serial
↓
$DATA:23.50,101.32,65.20
↓
RP2350
↓
OPC UA Nodes
├─ Channel_1
├─ Channel_2
└─ Channel_3
↓
W6300 Ethernet
↓
OPC UA ClientThis makes the example useful for understanding the complete path from MCU data acquisition to an industrial OPC UA client.
Quick Comparison
| muc-opcua | WIZnet OPC UA USB stdio | |
|---|---|---|
| Main Goal | Lightweight OPC UA implementation | Practical OPC UA application example |
| Focus | OPC UA protocol stack | Data acquisition and OPC UA data exposure |
| MCU | Small MCUs such as RP2040 | RP2350 |
| Ethernet | Platform-dependent adapter | W6300 hardware TCP/IP |
| Application Data | User-defined | USB serial sensor data |
| OPC UA Role | Server | Server |
| Best For | Building custom OPC UA products | Learning and prototyping embedded OPC UA devices |
Bottom Line
A hobby-priced board becomes a PLC that factory software can read over standard OPC UA — and its control loop stays on time because TCP lives in the W5500's silicon, not in the CPU. The whole integration between the OPC UA library and the Ethernet chip is six small callbacks. If you build timing-sensitive devices that need real networking, this is the pattern worth copying.
❓ FAQ
What is OPC UA, in one line?
The standard protocol factory-monitoring software (SCADA/MES) uses to read data from industrial devices — supporting it means off-the-shelf tools can browse your device without custom code.
Is muc-opcua a WIZnet library?
No. It is an independent OPC UA server that works with any transport. The WIZnet connection is the small adapter that maps its TCP interface onto W5500 hardware sockets.
Does the W5500 run any OPC UA logic?
No. It only handles TCP. All OPC UA encoding, sessions, and security run in the library on the RP2040 core.
Can I flash this branch and browse the PLC from an OPC UA client today?
Not as-is — the adapter is not yet compiled in and the demo doesn't start the server loop. See "Check Before You Use It."
Why not lwIP on the same chip?
The project explicitly chose hardware TCP/IP to keep stack processing off the 1 kHz control core; its status log records the switch away from the original lwIP plan.
한국어 — 1 kHz 소프트 PLC에 W55RP20 하드웨어 TCP/IP를 연결하는 방법
WIZnet 코드는 muc-opcua가 아니라 muc-plc에 있습니다. 라이브러리는 전송 계층 독립이라 칩 드라이버를 포함하지 않고, 이를 하드웨어에 이어주는 W5500 어댑터는 muc-plc가 제공합니다.
이 프로젝트는 RP2040 + W5500이라는 작은 MCU 플랫폼이 단순 Ethernet 통신 보드를 넘어, 1 kHz Software PLC와 OPC UA Server 같은 산업용 기능을 구현할 수 있는 플랫폼으로 확장될 가능성을 보여주는 사례입니다.
muc-plc는 QuackPLC의 기능을 임베디드 MCU에서 구현하기 위한 PLC Runtime 프로젝트입니다. RP2040을 포함한 MCU를 대상으로 하며, RP2040 네트워크 타깃으로 W55RP20의 W5500 Hardware TCP/IP를 활용하는 구조로 개발하였습니다.
핵심은 PLC 제어와 산업용 프로토콜은 MCU에서 실행하면서, TCP/IP 처리는 W5500으로 Offload하는 구조입니다.
PLC는 입력을 읽고 → 로직을 실행하고 → 출력을 갱신하는 Scan Cycle을 반복합니다.
muc-plc는 1 kHz 소프트 PLC이라는 이름을 지니고 있는데, 이 PLC Scan Cycle을 1 ms마다 수행합니다. 즉, 초당 1,000번 PLC 제어 사이클을 실행하는 구조입니다.
또한 Modbus TCP, OPC UA, S7comm 같은 산업용 프로토콜을 PLC Runtime과 분리된 Protocol Satellite 형태로 구성합니다.
즉, PLC 제어 루프와 네트워크 프로토콜 처리를 분리하여 제어 타이밍에 미치는 영향을 줄이는 것이 중요한 설계 방향입니다.
OPC UA는 어떻게 연결하는가
muc-plc에는 OPC UA를 위한 두 가지 구현 흐름이 있습니다.
초기에는 자체적으로 최소한의 OPC UA Binary Codec과 Browse/Read 기능을 구현했고, 이후에는 별도 프로젝트인 muc-opcua의 Embedded Profile을 OPC UA Satellite로 통합하는 방향으로 확장되었습니다.
W5500과 muc-opcua를 연결하기 위한 w5500_opcua_tcp.cpp도 존재합니다.
구조는 다음과 같습니다.
SCADA / OPC UA Client
↓
W5500 Hardware TCP/IP
↓
TCP Adapter
↓
muc-opcua
↓
muc-plc PLC Runtime즉, W5500이 OPC UA를 처리하는 것이 아니라 TCP/IP 전송을 담당하고, OPC UA 메시지와 Address Space 같은 상위 로직은 MCU에서 처리합니다.
기술적으로 보면
숫자가 궁금한 독자를 위한 상세:
- muc-opcua는 순수 C code로 작성됐고 실행 중
malloc을 호출하지 않아 메모리 사용량을 미리 예측할 수 있다(zero dynamic allocation). 최소 프로파일은 Cortex-M0+에서 플래시 23.9 KiB. - 특정 TCP/IP 스택이나 칩에 종속되지 않는 구조(transport-agnostic) — 하드웨어 서비스를 작은 어댑터 구조체로 주입받기 때문에, 100줄짜리 파일 하나로 W5500 하드웨어 소켓 포팅이 끝난다.
- muc-PLC는 하드웨어 타이머로 정확히 1 kHz 스캔을 돌리고, 매 사이클의 일정 이탈량(지터)을 측정해 p50/p90/p99 통계를 내장 웹 페이지로 공개한다.
- 인터프리터는 실제 산업 PLC의 명령 방식인 Siemens S7-300/400 STL을 본뜬 158개 명령을 실행한다.
- 이더넷 쪽은 WIZnet 공식 WIZnet-PICO-C ioLibrary 포트 기반이며, W5500 다이로 가는 PIO 구동 QSPI 링크를 포함한다.
muc-opcua와 WIZnet OPC UA USB stdio 예제 비교
두 프로젝트 모두 소형 MCU에서 OPC UA Server를 구현한다는 공통점이 있지만, 목적과 접근 방식은 꽤 다릅니다.
muc-opcua
muc-opcua는 RP2040급 소형 MCU에서도 동작할 수 있도록 설계된 경량 OPC UA Server 라이브러리입니다. C11 기반이며, 작은 메모리 환경을 고려해 heap 사용을 최소화하거나 제거하는 구조를 갖고 있습니다. 또한 TCP, 시간, 난수, 암호화 같은 하드웨어 의존 기능을 adapter 형태로 분리해 다양한 MCU와 네트워크 스택에 이식할 수 있도록 설계되어 있습니다.
주요 특징은 다음과 같습니다.
- 소형 MCU용 OPC UA Server 자체 구현
opc.tcp및 OPC UA Binary 지원- Nano, Micro, Embedded 등 기능별 profile 제공
- Read, Browse, Discovery, Subscription 등의 OPC UA 기능 지원
- 플랫폼과 네트워크 스택을 adapter 구조로 분리
- RP2040 등 Cortex-M0+급 MCU를 직접 타깃으로 함
즉, muc-opcua의 핵심은 “MCU에서 사용할 수 있는 범용 OPC UA protocol stack을 만드는 것”에 가깝습니다.
WIZnet OPC UA USB stdio 예제
opcua_usb_stdio는 RP2350 + W6300 환경에서 실제 데이터를 OPC UA Node로 노출하는 응용 예제입니다. USB CDC로 입력된 센서 데이터를 받아 OPC UA Server의 Node 값으로 연결하고, UaExpert에서 실시간으로 확인하는 구조입니다.
데이터 흐름은 다음처럼 볼 수 있습니다.
USB Serial
↓
$DATA:23.50,101.32,65.20
↓
RP2350
↓
Sensor_Node
├─ Channel_1
├─ Channel_2
└─ Channel_3
↓
W6300 Ethernet
↓
OPC UA TCP :4840
↓
UaExpert예제에서는 Sensor_Node 아래에 Device 정보와 Channel_1, Channel_2, Channel_3, RawFrame, FrameCount 등의 Node를 구성하고, USB로 입력된 값을 OPC UA Client에서 실시간으로 확인할 수 있습니다.
두 프로젝트의 차이
| 항목 | muc-opcua | WIZnet OPC UA USB stdio(open62541) |
|---|---|---|
| 목적 | 소형 MCU 전용 OPC UA Server stack 개발 | open62541 stack을 이용한 RP2xxx 시리즈 응용 예제 |
| 중심 관심사 | OPC UA 프로토콜 자체 | OPC UA를 이용한 데이터 노출 |
| 대상 MCU | RP2040 등 소형 MCU 전반 | RP2350 |
| Ethernet | 사용자가 adapter 구현 | W6300 사용 |
| OPC UA Client | UaExpert 등 표준 Client | UaExpert |
| 데이터 | 사용자 정의 Address Space | USB로 입력한 센서 값 |
| Port | 일반적으로 4840 | 4840 |
| 활용 방향 | 다른 MCU/제품에 이식 | WIZnet 기반 OPC UA 구현 참고 |
두 프로젝트를 한 문장으로 비교하면 다음과 같습니다.
muc-opcua가 “MCU에서 OPC UA를 구현하기 위한 엔진”이라면, WIZnet
opcua_usb_stdio는 “W6300과 RP2350을 이용해 그 OPC UA 개념을 실제 센서 데이터에 적용한 응용 사례”에 가깝습니다.
특히 WIZnet 예제는 단순히 OPC UA 연결 여부를 확인하는 데 그치지 않고, MCU 내부 데이터를 OPC UA Address Space의 Node로 구성하고 Ethernet을 통해 산업용 OPC UA Client에 전달하는 전체 흐름을 보여준다는 점에서 의미가 있습니다.
직접 사용하기 전에 확인할 점
위에서 소개한 1 kHz 측정 페이지는 실제로 동작하는 테스트 결과입니다. 다만 OPC UA 기능은 현재 브랜치에서 W5500과 완전히 연결되어 바로 실행할 수 있는 상태는 아닙니다.
- OPC UA 통합 작업이 일부 남아 있습니다. W5500과 OPC UA를 연결하는 어댑터 코드는 있지만, 현재 펌웨어에 완전히 포함되어 실행되는 상태는 아닙니다.
- W5500 소켓 번호를 조정해야 합니다. 웹 페이지와 OPC UA가 같은 하드웨어 소켓 번호를 사용하도록 되어 있어, 두 기능을 함께 사용할 경우 서로 다른 소켓으로 변경해야 합니다. W5500은 최대 8개의 하드웨어 소켓을 제공합니다.
- 라이브러리 경로를 수정해야 합니다. WIZnet ioLibrary 경로가 개발자의 PC 환경에 맞게 설정되어 있어, 실제 빌드 환경에 맞게 변경해야 합니다.
즉, PLC의 1 kHz 동작과 관련 기능들은 확인할 수 있지만, W55RP20에서 OPC UA까지 바로 실행되는 완성 예제는 아닙니다. 실제 사용하려면 몇 가지 통합 작업이 추가로 필요합니다.
핵심 정리
취미용으로도 쉽게 구할 수 있는 보드가, 공장 소프트웨어에서 표준 OPC UA로 데이터를 읽을 수 있는 PLC로 확장됩니다. TCP 처리를 CPU가 아닌 W5500 하드웨어가 담당하기 때문에 제어 루프의 타이밍 부담을 줄일 수 있습니다. 또한 OPC UA 라이브러리와 이더넷 칩 사이의 연동도 6개의 간단한 콜백 함수로 구성됩니다. 타이밍이 중요한 장비에 네트워크 기능을 추가하려는 경우 참고할 만한 설계 패턴입니다.
❓ FAQ
OPC UA가 뭔지 한 줄로?
공장 모니터링 소프트웨어(SCADA/MES)가 산업 장비에서 데이터를 읽을 때 쓰는 표준 프로토콜. 지원하면 기성 도구가 커스텀 코드 없이 장치를 브라우징한다.
muc-opcua는 WIZnet 라이브러리인가?
아니다. 어떤 전송 수단과도 동작하는 독립 OPC UA 서버다. WIZnet과의 접점은 TCP 인터페이스를 W5500 하드웨어 소켓에 매핑한 작은 어댑터다.
W5500이 OPC UA 로직을 실행하나?
아니다. TCP만 처리한다. 인코딩·세션·보안은 전부 RP2040 코어의 라이브러리에서 실행된다.
이 브랜치를 그대로 플래싱하면 OPC UA 클라이언트로 브라우징 가능한가?
지금은 불가 — 어댑터가 컴파일에 빠져 있고 데모가 서버 루프를 시작하지 않는다. "직접 사용하기 전에 확인할 점" 참조.
같은 칩에서 lwIP는 왜 안 썼나?
1 kHz 제어 코어에서 스택 처리를 배제하려고 하드웨어 TCP/IP를 명시적으로 선택했다. 상태 문서에 원래 lwIP 계획에서의 전환이 기록돼 있다.
Documents
- muc-plc branch 013: github.com/occamsshavingkit/muc-plc/tree/013-qplco-bytecode-loader
- W5500 adapter: src/w5500_opcua_tcp.cpp
- muc-opcua repository: github.com/occamsshavingkit/muc-opcua
- W55RP20-EVB-Pico product page: https://wiznet.io/products/evaluation-boards/w55rp20-evb-pico
- WIZnet-PICO-C (ioLibrary port): github.com/WIZnet-ioNIC/WIZnet-PICO-C

