How Does ZoneTherm Use W5500 to Connect Seven OpenTherm Heating Zones?
ZoneTherm combines seven OpenTherm thermostats, configurable valve groups and W5500 Ethernet for local zone control and Home Assistant integration.

WIZnet - W5500
ZoneTherm PCB U3. ESP32-S3 uses ESP-IDF Ethernet over SPI2 at 20 MHz; CS GPIO10, IRQ GPIO14, SCK GPIO12, MOSI GPIO11, MISO GPIO13.
Seven Room Requests, One Shared Manifold
ZoneTherm is an open-source controller design that connects seven OpenTherm wall thermostats to seven configurable valve outputs and Home Assistant. An ESP32-S3 handles thermostat messages and room decisions, an MCP23017 expander drives the valve bank, and a W5500 provides wired Ethernet for MQTT and the web interface. A room can control several valves when its floor-heating circuit contains multiple loops.
Max Brants publishes the firmware, KiCad hardware and Svelte web UI under the MIT License. Valve-set control arrived in August 2026, with release v1.1.0 on 10 August. A September source update removed per-zone outside-temperature data. The project offers a useful implementation to study and build, with heat-source control handled separately from its room-valve logic.
Generated technical diagram: thermostat messages, valve outputs and Ethernet each have a distinct role.
The underlying problem is the mismatch between rooms and plumbing. Seven thermostats represent seven independent requests, but a manifold may serve several loops in one room. The water circuit also has a single heating or cooling direction. ZoneTherm separates those concerns into room demand, valve ownership and one global season.
Why Answer as a Boiler?
OpenTherm is a digital conversation between a room thermostat and a heating appliance. The thermostat initiates requests; the appliance responds. ZoneTherm implements the appliance side of seven separate links, allowing each wall unit to report its room temperature and requested setpoint while retaining its local controls and schedule.
The main firmware terminates those conversations itself. There is no upstream OpenTherm boiler connection. ZoneTherm uses the received room information to operate valves, while another part of the installation commands the heat source. That distinction determines where the project fits: it is a zone controller, rather than a transparent gateway forwarding commands to a boiler.
The firmware gives OpenTherm communication its own task on ESP32-S3 core 1. Responses are scheduled 40 milliseconds after a request using deadlines, so waiting for one response does not put the other channels to sleep. A separate once-per-second control cycle handles the valve decisions alongside networking and MQTT work.
One Thermostat Can Own Several Valves
The August valve-set change lets one thermostat own a group such as V1, V2 and V3. Each room computes a single demand state; ValvePlan::solve combines the valve groups for rooms requesting heating or cooling into one output mask. The MCP23017 receives the resulting valve-bank command over I²C, the two-wire peripheral bus connected to GPIO4 and GPIO5.
Source UI rendered locally with example assignments. These are demonstration settings, not an installed system.
A valve can belong to at most one thermostat. The Svelte configuration page disables checkboxes already owned by another zone, and the REST API rejects overlapping assignments rather than saving a conflicting plan. Unassigned valves remain closed. A thermostat with no assigned valves can still report demand, but that demand opens no output.
The firmware also preserves the older one-to-one mapping during an upgrade. If stored settings predate valve sets, each thermostat initially retains its corresponding valve. This matters because adding flexible software assignments should not silently change which physical circuit a room controls.
A Small Temperature Band Prevents Chatter
ZoneTherm uses hysteresis, a small band around the setpoint that prevents repeated switching when temperature hovers near the target. In heating mode, a room requests heat below the lower threshold and stops above the upper threshold. Inside the band, the existing demand state stays unchanged. Cooling reverses the comparison.
Generated technical diagram: a symmetric band retains the previous demand between its two thresholds.
The decision lives in one pure C++ function, ClimateLogic::valveWanted, used by the valve plan. The source comments explain that earlier code had three diverging copies of this comparison. Centralizing the rule makes control and reported action easier to keep consistent, while the hardware-independent function can be tested on a desktop compiler.
One season applies to the whole plant. Home Assistant can disable a room, but enabling another room does not give it an independent cooling season while the remaining rooms heat. The shared water circuit therefore remains consistent with the room-level controls.
What the W5500 Adds
The W5500 is ZoneTherm's wired LAN interface. EthDriver.cpp configures SPI at 20 MHz, using GPIO12 for clock, GPIO11 for data out, GPIO13 for data in, GPIO10 for chip select and GPIO14 for interrupts. ESP-IDF creates the W5500 Ethernet driver and attaches it to the processor's network interface; the host software runs the network services.
Generated technical diagram: the ESP32 network stack carries MQTT and HTTP through the W5500 Ethernet interface.
The driver contains an instructive porting fix. Its comments describe an Arduino-to-ESP-IDF migration that left the Ethernet MAC address unset. The physical link came up, but DHCP address assignment failed. The current code derives a stable Ethernet MAC address from the ESP32 and programs it through ETH_CMD_S_MAC_ADDR before starting the interface.
Ethernet has priority once it obtains an IP address. The network state machine then stops Wi-Fi; without usable Ethernet it tries the configured wireless network, followed by a provisioning access point if needed. A working physical link and a usable network address are separate milestones, a distinction reflected in both the driver and connection policy.
Bringing Room State into Home Assistant
MQTT exchanges named messages through a broker. ZoneTherm publishes discovery information so Home Assistant can create a climate entity and a modulation sensor for each zone. Room state is sent on changes and every 30 seconds, while retained availability and a last-will message let Home Assistant mark a disconnected controller unavailable.
Original Svelte UI rendered with demonstration data and zero request counters. No hardware measurements are shown.
Setpoints can travel back toward the wall. A Home Assistant or web-UI command updates the thermostat through OpenTherm's TrOverride mechanism, allowing the local unit to show the new target. The September 5 commit, credited to Claude in the repository history, removed the unused outside-temperature field from the responder, JSON, discovery and tests. The current interface concentrates on room state, setpoints and modulation.
Build Status and Practical Boundaries
The repository's 115 host tests passed during this review, covering message handling, room state, valve planning and related helpers. The Svelte source also passed its checker and production build. The latest firmware CI stopped on a missing SCons build-tool module; the published v1.1.0 release remains the available August firmware and filesystem pair.
A fresh board needs both firmware and filesystem images because the web UI lives in LittleFS. The prototype's stale-data policy also deserves attention before deployment: a thermostat that stops reporting can leave its last temperature and valve demand in use. The public sources provide design and software evidence, with installation and hardware validation still required for a specific system.
The Maker article on OpenAmber's W5500-equipped heat-pump controller explores the appliance side through Modbus. ZoneTherm offers a complementary study of room-side thermostat conversations and valve assignment, with wired networking connecting local control to Home Assistant.
FAQ
Q. Does ZoneTherm control the boiler over OpenTherm? No. It answers seven wall thermostats and drives zone valves; the heat source is controlled elsewhere.
Q. Can one room operate several floor-heating loops? Yes. A thermostat can own several of the seven valves, which follow that room's demand together.
Q. Can two thermostats share one valve? The configuration requires exclusive ownership. The UI disables valves owned by another zone, and the API rejects overlapping assignments.
Q. What is the W5500 responsible for? It supplies the Ethernet frame interface for the ESP32-S3 network stack, carrying MQTT, HTTP and update traffic.
Q. Does each zone choose heating or cooling independently? No. Zone enablement is individual, while the heating or cooling season is global.
-
ZoneTherm source repository
MIT firmware, KiCad hardware and Svelte UI for seven thermostat channels.
-
Firmware architecture and host tests
Task ownership, build commands and domain-test coverage. Current discovery sensor count is defined by HaDiscovery.cpp.
-
W5500 Ethernet driver
SPI pins, ESP-IDF MAC/PHY initialization and Ethernet MAC-address setup.
-
Ethernet and Wi-Fi selection
Ethernet IP availability takes priority over fallback Wi-Fi.
-
Valve-set implementation
Demand aggregation and exclusive valve ownership.
-
Climate logic and stale-data tests
Heating/cooling hysteresis, disabled zones and current stale-reading behavior.
-
Current Home Assistant discovery
Climate entity and modulation sensor per zone; stable legacy entity identifiers.
-
Release v1.1.0
10 August 2026 firmware, filesystem image and SHA256 sums.
-
September source change and CI
Host tests passed; firmware build stopped on a missing SCons module.
-
Original Svelte configuration UI
Zone names, enablement, valve-set exclusivity and runtime settings.
-
KiCad hardware sources
PCB, schematic, fabrication outputs and board models.
-
MIT license
Project terms; vendored dependencies retain their own notices.
