---
title: "How to Build Modbus TCP Slaves with W5500 on STM32 (Rust) and Bridge Them to OPC-UA?"
url: "https://maker.wiznet.io/viktor/projects/how-to-build-modbus-tcp-slaves-with-w5500-on-stm32-rust-and-bridge-them-to-opc-ua/"
markdown_url: "https://maker.wiznet.io/viktor/projects/how-to-build-modbus-tcp-slaves-with-w5500-on-stm32-rust-and-bridge-them-to-opc-ua/md"
type: "UCC: User Created Content"
author: "Antony Mapfumo"
author_url: "https://github.com/mapfumo/wk9-opcua-modbus"
editor: "WIZnet"
editor_url: "https://maker.wiznet.io/"
original_author: "Antony Mapfumo"
original_url: "https://github.com/mapfumo/wk9-opcua-modbus"
published: "2026-01-09"
language: "en"
hardware: ["WIZnet W5500"]
likes: 0
views: 884
comments: 0
source: "WIZnet Makers (https://maker.wiznet.io/)"
---

# How to Build Modbus TCP Slaves with W5500 on STM32 (Rust) and Bridge Them to OPC-UA?

> Antony Mapfumo’s Week 9 project builds two STM32F446RE Modbus TCP slave nodes using a WIZnet W5500 Ethernet controller

Original author: Antony Mapfumo (source: https://github.com/mapfumo/wk9-opcua-modbus)

## Components

- **WIZnet W5500** x 1 ([docs](https://docs.wiznet.io/Product/Chip/Ethernet/W5500))

WIZnet parts: W5500 ([Datasheet](https://docs.wiznet.io/Product/Chip/Ethernet/W5500/datasheet?utm_source=maker&utm_medium=project&utm_campaign=w5500), [product hub](https://maker.wiznet.io/products/w5500/))

## Article

### Summary

Antony Mapfumo’s Week 9 project builds two STM32F446RE Modbus TCP slave nodes using a WIZnet W5500 Ethernet controller, then bridges their register data into an OPC-UA server written in Python. It shows static-IP setup, socket state machines, IEEE-754 register encoding, and a SCADA-friendly data model.

[![](https://maker.wiznet.io/upload/ckeditor5/1118367%5F1767973079%2Epng)](https://www.mapfumo.net/posts/modbus-opcua-week-9/)

## Author introduction

Antony “Tony” Mapfumo is a network engineer currently focused on Industrial IoT (IIoT) and embedded systems. On his blog he documents hands-on experiments that connect communications engineering (networks/protocols) with embedded development on STM32, using tools like Embedded Rust/C++, LoRa/LoRaWAN, and industrial protocols—aiming for reliable, practical, “real-world engineering” explanations rather than abstract tutorials.

## About this blog series (and why Rust keeps showing up)

![](https://maker.wiznet.io/upload/ckeditor5/1118367%5F1767973307%2Epng)

This GitHub repository is labeled **“Week 9”** in a structured series that progresses from early embedded foundations into full “device → gateway → industrial integration.” The repo itself links the journey: RTIC/LoRa basics, gateway firmware, async gateway work, MQTT + InfluxDB + Grafana (Weeks 7–8), and then **Modbus TCP + OPC-UA** for Week 9.

Rust appears here because Tony is explicitly building “reliable, efficient” embedded systems and repeatedly chooses Rust-based embedded tooling (e.g., Embassy async on STM32 in this project). More generally, embedded Rust is popular for memory safety without GC and strong tooling—useful when you’re implementing low-level networking and protocol parsing on constrained targets.

## What the Week 9 project actually does (main points)

[![](https://maker.wiznet.io/upload/ckeditor5/1118367%5F1767973111%2Epng)](https://github.com/mapfumo/wk9-opcua-modbus)

This repository implements a small but very “industrial-shaped” automation stack:

- **Two embedded devices (STM32F446RE)** act as **Modbus TCP slaves/servers** on port **502**, each with a **static IP** (10.10.10.100 and 10.10.10.200)

- Each device reads **temperature/humidity** from an **SHT3x** sensor and exposes values through **holding registers (FC03 / 0x03)** using **IEEE-754 float32** encoding.

- A **desktop Python OPC-UA gateway** polls both Modbus devices every **2 seconds**, decodes registers, and publishes the values as OPC-UA variables for clients (e.g., UaExpert / SCADA-style consumers)

- Each device also drives an **SSD1306 128×64 OLED** over I²C to display IP, sensor values, and connection state (“LISTENING/CONNECTED”).

- The author reports practical engineering metrics: ~**70 ms** end-to-end (sensor → Modbus → OPC-UA) and **6+ hours** uptime testing with **0% packet loss** in the test scenario.

> Note on code review: GitHub’s HTML “file view” didn’t fully render source files in my browsing tool session, so I’m basing implementation details on the repository README’s documented structure, snippets, and behavior (e.g., `common.rs`, `modbus_1.rs`, `modbus_2.rs`, and the included Python gateway script).

## Why this blog/project is useful to other developers

![](https://maker.wiznet.io/upload/ckeditor5/1118367%5F1767973367%2Epng)

If you’re building real systems (not demos), this repo is valuable because it’s packed with the “sharp edges” people usually skip:

- **Industrial protocol thinking**: you must commit to strict register maps, endian rules, and deterministic polling—not “just publish JSON.” The README shows concrete register layouts, float encoding examples, and Modbus addressing gotchas.

- **Gateway pattern you can reuse**: the Modbus→OPC-UA bridge turns “legacy-ish device registers” into a richer, interoperable data model (namespaces + typed variables) that tools understand.

- **Real embedded networking concerns**: TCP socket state handling matters. The project explicitly documents a socket state machine and recovery approach—exactly what prevents field devices from “locking up” after imperfect connections.

- **Practical bus-sharing lessons**: sharing I²C between an async DMA sensor driver and a blocking OLED driver is the sort of integration problem that appears constantly in embedded work

## MQTT → Modbus, Grafana → SCADA (what that shift really means)

[![](https://maker.wiznet.io/upload/ckeditor5/1118367%5F1767973151%2Epng)](https://www.mapfumo.net/posts/modbus-opcua-week-9/)

Tony summarizes Week 9 as a move from “IoT protocols (MQTT, InfluxDB)” toward “industrial automation protocols (Modbus TCP, OPC-UA)” and SCADA integration. Here’s the practical meaning:

#### MQTT vs Modbus

- **MQTT** is a lightweight **publish/subscribe** messaging protocol: devices publish telemetry to a broker, subscribers receive updates. It’s excellent for IoT pipelines and constrained networks.

- **Modbus TCP** is a **request/response, register-based** protocol: a client reads/writes fixed addresses (registers/coils) on a server/slave. That rigidity is a feature in industrial environments—PLCs, HMIs, and SCADA systems expect stable “addresses.” The Modbus spec ecosystem formalizes this, including MBAP headers over TCP.

**In short:** MQTT is “stream messages to whoever’s listening,” Modbus is “read these registers at these addresses on this device.”

### Grafana vs SCADA

- **Grafana** is a general dashboarding/visualization platform for metrics and time-series data (great for “observability”-style monitoring).

- **SCADA** (Supervisory Control and Data Acquisition) is an industrial monitoring **and control** architecture: it gathers process data and can support operational control, alarm concepts, multi-site supervision, and integration with PLCs/industrial protocols.

**In short:** Grafana is “visualize your data,” SCADA is “operate the plant.”

### Why OPC-UA shows up here

OPC-UA is widely used as a vendor-neutral interoperability layer for industrial systems (richer data modeling and security than “raw registers”). In this repo, OPC-UA is the “northbound” interface: SCADA/HMI/test clients connect to the OPC-UA server instead of speaking Modbus directly.

## Where W5500 fits, and why it’s a sensible choice

This project uses **WIZnet W5500** Ethernet on both STM32 boards and even documents a **custom SPI driver** approach due to the lack of an existing Embassy driver at the time.

Why W5500 fits Modbus TCP devices especially well:

- W5500 is a **hardwired TCP/IP** Ethernet controller with integrated stack + MAC/PHY, letting MCUs talk TCP/UDP/IP via a socket-like interface over **SPI (up to 80 MHz supported)**.

- It includes **32 KB internal buffer memory** and supports **8 hardware sockets**, which is a good match for small industrial endpoints that need stable TCP behavior without a heavy software network stack.

- The repo’s “static IP only” decision aligns with common industrial LAN deployments, where fixed addressing is often preferred for PLC/SCADA mapping.

(That last point is an engineering inference, but it’s consistent with the repo’s documented static IP configuration and the typical way Modbus devices are deployed.)

## Architecture and implementation notes (from the repo’s documented design)

The README provides a clear end-to-end diagram and timing:

- **Polling interval:** every **2 seconds** for sensor read, Modbus poll, and OLED refresh.

- **Data path:** Sensor → registers in RAM → Modbus TCP read → float decode → OPC-UA variables → clients subscribe/read.

- **Latency estimate:** ~50 ms sensor read + ~20 ms Modbus TCP RTT ≈ ~70 ms total.

- The README also highlights the “hard parts” explicitly:

- **Custom W5500 register access** (SPI control byte + address bytes) to achieve async-friendly behavior.

- **Socket state machine** handling CLOSED/INIT/LISTEN/ESTABLISHED/CLOSE_WAIT with auto-recovery.

- **Modbus correctness details**: MBAP header fields, FC03 response shape, and a concrete register map including float32 big-endian packing.

## FAQ

#### 1) Why use W5500 for Modbus TCP devices instead of “just running a software TCP/IP stack”?

W5500 is built around a **hardwired TCP/IP stack** and exposes networking through a socket-like model over SPI, which reduces the integration burden on the MCU compared to maintaining a full software network stack. It also provides **32 KB internal buffers** and **8 hardware sockets**, which helps when you need stable TCP behavior for industrial polling patterns like Modbus TCP.

#### 2) How do you connect W5500 to an STM32 platform in a project like this?

In this repo, the STM32F446RE connects to a W5500 module over **SPI** and runs a static-IP configuration, then implements Modbus TCP server behavior on port 502. The author documents a custom, register-level SPI approach (because an Embassy-ready driver wasn’t available), and highlights socket-state handling as a core reliability technique.

#### 3) What role does W5500 play in the OPC-UA + Modbus architecture?

W5500 is the embedded Ethernet “front end” that makes each STM32 board appear as a **Modbus TCP slave** on the LAN (10.10.10.100:502 and 10.10.10.200:502). The Python gateway then polls those endpoints and republishes the values as OPC-UA variables for SCADA-style clients. Without W5500-class Ethernet, you don’t get a standard Modbus TCP device on the wire.

#### 4) Can beginners follow this project, or is it “industrial-only”?

Beginners can follow the **system concept** easily because the README includes diagrams, testing commands (ping, mbpoll), and a fully explained register map with float decoding examples. The advanced part is embedded networking: SPI-register Ethernet control, TCP socket states, and strict endian/encoding rules. If you’re new, treat it as a guided path into “real protocol work,” not a quick weekend sketch.

#### 5) How does W5500 compare to Wi-Fi modules or ENC28J60 for a similar gateway?

W5500 integrates MAC/PHY and a hardwired TCP/IP stack, and communicates via SPI (up to 80 MHz supported), giving a straightforward wired-LAN endpoint that fits industrial networks well. Wi-Fi can be excellent but often adds RF variability, provisioning, and security surface area. ENC28J60 typically requires a software TCP/IP stack on the MCU, which increases firmware complexity and resource pressure

---

Source: https://maker.wiznet.io/viktor/projects/how-to-build-modbus-tcp-slaves-with-w5500-on-stm32-rust-and-bridge-them-to-opc-ua/
