Wiznet makers

Benjamin

Published August 21, 2026 © Apache License 2.0 (Apache-2.0)

153 UCC

11 WCC

18 VAR

0 Contests

0 Followers

2 Following

Original Link

Polymath's Protective Stop: Why Does the W5500 PoE Remote Go Silent on Any Fault?

Polymath Robotics' open protective stop runs on a W5500 PoE board. Two ESP32-S3 cores must agree byte for byte, or it sends nothing and the robot stops.

COMPONENTS Hardware components

WIZnet - W5500

x 1

Ethernet controller on the Waveshare ESP32-S3-ETH carrier board the remote is built around. Driven over SPI2 at 20 MHz (MOSI GPIO11, MISO GPIO12, SCLK GPIO13, CS GPIO14, INT GPIO10, RST GPIO9) through


PROJECT DESCRIPTION

📌 A Stop Button That Has to Prove Itself Every 100 Milliseconds

Polymath Robotics is a San Francisco company that, in its own words on polymathrobotics.com, "builds core autonomy and safety systems for off-highway vehicles." The company opened its protective-stop repository in April 2024 and has kept working on it: nine contributors, a last push on 2026-08-20, and 74 design and test documents under docs/ as of 2026-08-21. The device is a stop button an operator uses to halt an autonomous machine from outside it, and its wired path is a WIZnet W5500 on a Waveshare ESP32-S3-ETH carrier board.

Photograph of the assembled Protective Stop remote: a yellow and white 3D printed enclosure with a large red twist-release mushroom button on top, and an RJ45 Ethernet jack visible in the side pod The finished unit. The red twist-release button is an NKK FF0126BBCAEA01, and the Ethernet jack in the side pod is the only cable it needs. Photo: Polymath Robotics, CC-BY-4.0.

The design puts the burden of proof on the remote instead of on the stop command. The remote transmits continuously while everything is healthy, and any fault makes it stop transmitting. As the README puts it, "Every failure mode degrades to 'no message,' and no message is a stop."

Protective Stop, Not Emergency Stop

The repository draws its own boundary in the opening section of the README, and it is worth repeating exactly. "Safety scope: this is a protective stop (a controlled, preventative shutdown), not an emergency stop. It is not a substitute for an E-stop where power is immediately physically removed from a machine."

A protective stop asks the machine controller to bring the vehicle to a controlled halt; an emergency stop cuts power at the contactor. This project builds the first and says plainly it is not the second.

Two Cores, Two Loops, One memcmp

The README describes the sensing arrangement precisely: "The remote (Waveshare ESP32-S3-ETH, W5500 PoE Ethernet) senses a physical DPST normally-closed E-stop switch through two independent loopback channels, one per CPU core." A DPST normally-closed switch has two separate contact pairs, both closed until someone presses it, and each ESP32-S3 core owns exactly one of them.

Diagram of the lockstep path: a DPST normally closed switch feeds two GPIO loops, core 0 reads loop A and core 1 reads loop B using different code expressions, and a comparator does a 40 byte memcmp that either transmits or sends nothing Generated technical diagram of the lockstep path in firmware/main/main.c and estop_verdict.c.

Every 100 ms tick, each core drives its output pin high, reads the echo, drives it low, and reads again. A healthy loop conducts as driven, returning 1 on the high phase and 0 on the low phase, so sampling both phases catches a pin stuck high as surely as a wire that has fallen off.

The unusual part is what the cores do with that reading. Core 0 picks the codeword by arithmetic, indexing a two-entry table with ((rb_hi ^ 1) | rb_lo) & 1. Core 1 reaches the same result with a plain boolean, (rb_hi == 1) && (rb_lo == 0). The comment in estop_verdict.c explains why: the two expressions "are logically identical when correct (so no lockstep-divergence in normal operation)", yet a systematic bug in either one makes only that core wrong. Redundant code with an identical bug fails identically; diverse code does not.

Each core builds its own 40-byte pstop message, PSTOP_MESSAGE_SIZE in pstop_c/pstop/include/pstop/config.h, and a comparator task runs memcmp across all 40 bytes, CRC-16 included. Only a byte-for-byte match is transmitted. The repository calls this 2oo2 to run and 1oo2 to stop. The arrangement turns six different situations into a stop at the machine:

  • The operator presses the button, both poles open, both cores agree on STOP, and a STOP message goes out.
  • One loop wire breaks, the cores disagree, and the comparator sends nothing.
  • A core task stalls past its 80 ms publish deadline, and the tick counts as a mismatch.
  • A sense pin loses its pull-down, and a pad-configuration read-back running about once a second latches a sticky fault.
  • The cores stop advancing their liveness signals relative to each other for 500 ms, and a cross-check latches a fault.
  • The VPN drops, and because the socket is bound to the tunnel the send fails instead of leaking plaintext.

Inside the Side Pod: The W5500 Carrier and Four Loop Wires

The bill of materials in hardware/README.md names the board without hedging: "Waveshare ESP32-S3-ETH | The W5500 Ethernet carrier board this design is built around. For PoE-powered units order the ESP32-S3-POE-ETH variant on the same page; it includes the PoE Module (B) that mounts on the board." Waveshare's product page describes the same part as an "Onboard W5500 Ethernet chip for extending 10/100Mbps network port through SPI interface".

Photograph looking down into the printed base: the ESP32-S3-ETH board sits in a rectangular side pod with its RJ45 jack facing out, steel ballast weights around it, and white, yellow, green, red and black wires soldered to the header Assembly step 3. The white and yellow pairs are the two E-stop loops, and the RJ45 faces out of the pod. Photo: Polymath Robotics, CC-BY-4.0.

Seven connections leave the board, fixed in firmware rather than in configuration.

Board pinGoes toWire color
VBUSLED ring 5Vred
GNDLED ring GNDblack
IO17LED ring data ingreen
IO39Button terminal 11, pole Awhite
IO40Button terminal 12, pole Awhite
IO41Button terminal 21, pole Byellow
IO42Button terminal 22, pole Byellow

The sense pins carry internal pull-downs, which is what makes a broken wire safe. In the words of hardware/README.md, "the firmware drives one GPIO and reads the echo on another, once per pole, so a broken wire reads the same as a pressed button (STOP)."

⚙️ Why Ethernet Ranks First in the Firmware

The W5500 sits on SPI2 at 20 MHz, with MOSI on GPIO11, MISO on GPIO12, SCLK on GPIO13, chip select on GPIO14, interrupt on GPIO10 and reset on GPIO9. The firmware enables it with CONFIG_ETH_SPI_ETHERNET_W5500=y in firmware/sdkconfig.defaults and brings it up in dcs_eth.c before any other interface.

Diagram comparing three uplinks: Ethernet at route priority 128 with an RJ45 and 802.3af PoE, USB-NCM at 110 for bench work, and Wi-Fi at 100 as a documented fallback Generated technical diagram of the route priorities set in dcs_eth.c, ml_dev_tether.c and dcs_net_supervisor.c.

Preference is a number rather than an opinion. Ethernet is created with route_prio = 128, the USB-CDC-NCM tether takes 110, and Wi-Fi STA keeps the ESP-IDF default of 100. A supervisor task re-pins the default route once a second, so a cable plugged in later promotes itself without a reboot. All three interfaces come up together at runtime rather than being selected when the firmware is built.

The README's open-items list states that "WiFi is the weakest transport, at roughly 98 % soak reply rate with a fail-safe stop and self-heal burst every 10 to 15 minutes", and sets the policy as "Ethernet primary, WiFi fallback, USB-NCM for bench and service." A robustness note dated 2026-08-03 records that a transmit-path drop seen on the USB tether "never happens" on W5500 Ethernet because "the SPI driver queues the frame and returns OK."

One Cable for Power and the Safety Link

Power over Ethernet is what keeps a finished unit down to one cable, and the build treats it as standard rather than optional. The assembly guide instructs the builder to "Set the PoE Module (B) onto the board and fasten it with the longer Phillips screws that come with the ESP32-S3-ETH; every unit gets the module." Waveshare's product page for the carrier board lists that module as IEEE 802.3af compliant.

Photograph of the open enclosure with the lid folded back: a black Waveshare PoE Module (B) is screwed onto the ESP32-S3-ETH board in the side pod, and red, black, green, white and yellow wires run from the board up to the LED ring and E-stop button in the lid Assembly step 4. The PoE Module (B) mounts on top of the carrier board, after which the RJ45 carries both power and the safety link. Photo: Polymath Robotics, CC-BY-4.0.

Assembling one unit takes about 15 minutes, and the enclosure prints as a two-unit plate in roughly 4.5 hours using 144 g of filament. The FreeCAD source is included, so the mechanical design is reproducible rather than merely visible.

When the W5500 Itself Wedges

The most transferable engineering in the repository, for anyone else building on this chip, is a watchdog that assumes the Ethernet controller can fail quietly. The comment in dcs_eth.c names the failure it was written for: "the W5500 can black-hole with NO ETHERNET_EVENT_DISCONNECTED and the netif still reporting UP with a valid lease", which leaves the route supervisor pinning a dead path.

Diagram of the W5500 health watchdog: two detection signals, an SPI probe of the PHYCFGR register and a black-hole check with a gateway cross-check, feeding a three rung recovery ladder that ends in a hardware reset pulse on GPIO9 Generated technical diagram of the detection and recovery logic in firmware/components/dcs_support/src/dcs_eth.c.

Detection uses two signals, both polled at 1 Hz and both required to persist three consecutive ticks. One is a read-only PHY register read of PHYCFGR at 0x002E, which forces a real SPI transaction and so surfaces a wedged bus as a failed ioctl. The other is a black-hole test: link up and lease valid, yet no peer has replied for 8 seconds and the uplink gateway is silent too. That cross-check separates a local W5500 problem from a peer that is simply unreachable.

Recovery then climbs three rungs, each given 8 seconds to restore the link. The first stops and starts the driver. The second rebuilds driver and netif, re-running the W5500 software reset and the VERSIONR check. The third asserts a hardware reset on GPIO9 for 1 ms against the datasheet minimum of 500 us, then waits 5 ms for the PLL against a 1 ms maximum, citing W5500 datasheet v1.1.0 section 5.5.1 in the source comment. Throughout, the machine sees silence and holds STOP, so recovery only ever restores availability.

What the Project Publishes and What It Does Not

The integrity target is SIL 3 under IEC 61508 with an equivalent PL e track under ISO 13849, and the README describes both as targets the project is pursuing rather than ratings it holds. The safety case under docs/safety/ includes a system definition, a HARA, an FMEA and FMEDA, a traceability matrix, and an open-items file tracking the gaps toward a quantified claim. A MISRA C:2012 review dated 2026-07-21, run with the free cppcheck misra addon, cut findings in firmware/components/dcs_support from 513 to 273 and in main.c from 30 to 16, with the residue recorded as a deviation register.

Test evidence is dated and specific, and also entirely self-reported. A 6 hour 11 minute fleet soak on 2026-08-03 recorded zero reboots, zero lockstep mismatches and zero spurious stops across 2864 samples. No independent party has verified any of it. The single tagged release, v1.2 of 2026-08-20, carries a detailed changelog but ships only the automatic source archives, so there is no firmware binary to flash. Three gaps matter before anyone reproduces the build: there is no custom PCB, since "The design contains no custom electronics"; the check-in and OTA tooling expects a centralized management system the project does not include; and a deprecated set of ROS 2 packages still sits under archive/, kept for reference only.

Related WIZnet Maker Projects

Crane Emergency Stop is the closest neighbour on this site, an ESP32-S3 with a W5500 module built to stop a bridge crane remotely. The problem is the same, a person outside a heavy machine who needs it to halt, but the direction of trust is opposite: that design sends an explicit emergency_stop command into a CANopen gateway, while the Polymath remote treats the absence of a message as the stop. Read together they contrast command-driven with liveness-driven safety.

OneTouch: W5100S-Based Ethernet Motor Control and Robotics Messaging Stack shares the robotics context and the watchdog instinct, linking Jetson control to ODrive motors through a W5100S bridge. It places the wired link inside the robot rather than between robot and operator, so the two cover opposite halves of a wired control path.

ESP32-S3-ETH OPC UA Gateway runs on the same Waveshare carrier board but in Rust, serving industrial data instead of a safety signal. It shows how differently the same W5500 SPI path is used once the requirement shifts from throughput to guaranteed silence.

Smart Home Energy Management Controller with the Waveshare ESP32-S3-POE-ETH uses the PoE variant of the same board through ESPHome, and answers the same one-cable powering question in a building rather than on a machine.

❓ FAQ

Q. What does this project use the W5500 for? The W5500 is the Ethernet controller on the Waveshare ESP32-S3-ETH board, driven over SPI2 at 20 MHz through the ESP-IDF esp_eth driver. It carries the pstop heartbeat as UDP inside a WireGuard tunnel, at the highest route priority of the three interfaces.

Q. Is this an emergency stop? No, and the repository says so itself. The README defines the scope as a protective stop, a controlled preventative shutdown, and states that it is not a substitute for an E-stop where power is immediately physically removed from a machine.

Q. What happens if one of the two E-stop loop wires breaks? The core owning the broken loop reads it as open and produces a STOP codeword while the other core still produces OK, so the encodings differ and the comparator transmits nothing. The machine stops on heartbeat silence, about 2.0 seconds with the shipped defaults of a 400 ms heartbeat and five missed windows.

Q. Do I need the PoE version of the board? The bill of materials tells builders to order the ESP32-S3-POE-ETH variant for PoE-powered units, and the assembly guide says every unit gets the PoE Module (B). A unit can also run on USB power, in which case the RJ45 carries the network only.

Q. Is this certified to SIL 3? Not yet. SIL 3 under IEC 61508 and PL e under ISO 13849 are stated as design targets, with the safety case and its remaining gaps published under docs/safety/. Every test result in the repository is the developers' own, and that was still the case when the repository was checked on 2026-08-21.

Documents
  • Protective Stop repository

    Polymath Robotics' open protective-stop remote. Apache-2.0 firmware, CERN-OHL-P-2.0 hardware, CC-BY-4.0 documentation. Last push 2026-08-20.

  • Hardware README: BOM, pinout and licence manifest

    Names the Waveshare ESP32-S3-ETH as the W5500 carrier board, lists the IO39 to IO42 loop wiring, and links the W5500 datasheet as a component reference.

  • Assembly guide with build photos

    Six-step photo build guide, including fitting the Waveshare PoE Module (B) onto the carrier board. Source of the two build photographs in this post.

  • dcs_eth.c: W5500 bring-up and health watchdog

    SPI pin map, route priority 128, and the three-rung recovery ladder for a wedged W5500 including the datasheet-timed RST pulse.

  • estop_verdict.c: the diverse dual-core decision core

    The pure verdict function both cores run, with the arithmetic-image and boolean expressions that make the lockstep comparison diverse rather than merely redundant.

  • Safety case documents

    System definition, HARA, safety requirements, FMEA and FMEDA, coverage and traceability, plus the open-items file tracking gaps toward the SIL 3 and PL e targets.

  • Release v1.2 (2026-08-20)

    The only tagged release. Changelog for the stability campaign, including the console-output budget that removed the lockstep-mismatch stops. Source archives only, no firmware binary.

  • Waveshare ESP32-S3-ETH product page

    The carrier board. Describes the onboard W5500 over SPI, the 802.3af PoE module option, and the ESP32-S3-POE-ETH variant the BOM specifies.

  • W5500 datasheet v1.1.0

    The datasheet the hardware README links as a component reference and the firmware cites for its RSTn pulse and PLL lock timings.

  • WIZnet W5500

    WIZnet product documentation for the Ethernet controller used on the carrier board.

  • microlink embedded Tailscale client

    The WireGuard and DERP transport the pstop heartbeat rides on, credited in the README as funded in part by Polymath Robotics.

Comments Write