Wiznet makers

viktor

Published May 04, 2026 ©

164 UCC

20 WCC

48 VAR

0 Contests

0 Followers

0 Following

Original Link

How to Register an ESP32-S3 as a Kubernetes Node with W5500 Ethernet?

picokubelet is a Rust firmware project that makes an ESP32-S3 board appear as a Kubernetes worker node to a real k3s cluster.

COMPONENTS Hardware components

WIZnet - W5500

x 1


PROJECT DESCRIPTION

Summary

picokubelet is a Rust firmware project that makes an ESP32-S3 board appear as a Kubernetes worker node to a real k3s cluster. The platform is Waveshare ESP32-S3-ETH, which includes an ESP32-S3R8 and a WIZnet W5500 Ethernet controller. W5500 provides the intended wired network transport for DHCP, TLS, and Kubernetes API communication between the microcontroller and the control plane.

What the Project Does

Kubernetes normally runs applications by placing containers into Pods on Nodes. A Node is usually a physical server, virtual machine, or single-board computer, and the kubelet is the local agent that registers the machine, reports health, and keeps the control plane updated.

AI created image

picokubelet recreates only the node-registration and heartbeat part of that behavior on a microcontroller. It does not run Linux containers. It does not execute Pods. Instead, the ESP32-S3 connects to a k3s API server, registers a Kubernetes Node, creates a Lease, periodically renews that lease, and patches node status so kubectl get nodes shows the ESP32-S3 as a worker. The repository states that Pods, volumes, exec, logs, and a real container runtime are faked or absent by design.

For a beginner, the project can be read this way: Kubernetes is the manager, the kubelet is the worker’s “I am alive” agent, and picokubelet is a tiny embedded version of that agent. The ESP32-S3 does not become a useful server; it becomes a deliberately constrained Kubernetes participant that proves the control-plane protocol can be spoken from bare-metal embedded Rust.

The data flow is:

ESP32-S3 boots → network interface comes up → DHCP assigns an IPv4 address → firmware opens TLS/HTTP traffic to the k3s API server → firmware registers a Node → firmware renews a Kubernetes Lease → firmware updates Node status and heap-memory annotations.

Where WIZnet Fits

Source: https://www.waveshare.com/esp32-s3-eth.htm

The WIZnet product is the W5500 Ethernet controller. The target hardware, Waveshare ESP32-S3-ETH, is described by the project as an ESP32-S3R8 board with a W5500 Ethernet controller on SPI and optional PoE. The Waveshare product page also states that the board includes an onboard W5500 Ethernet chip for extending a 10/100 Mbps network port through SPI.

In the current source, W5500 is used as the preferred network path. src/main.rs initializes SPI2 at 25 MHz, maps the W5500 SPI pins, probes the chip, and uses W5500 Ethernet when the probe and PHY link check succeed. If W5500 is not present or the cable link is down, the firmware falls back to Wi-Fi.

W5500 matters here because this project depends on repeated, long-lived communication with the Kubernetes API server. A normal kubelet sends heartbeats so the control plane can decide whether a node is healthy; Kubernetes tracks those heartbeats through Node status updates and Lease objects. Wired Ethernet is a better fit than Wi-Fi for this experiment when the target form factor is a small rack of always-on embedded nodes.

Technically, W5500 provides a hardwired TCP/IP Ethernet controller with SPI host access, 10/100 Ethernet MAC/PHY, internal Tx/Rx buffer memory, and multiple hardware sockets. WIZnet documents W5500 as a hardwired TCP/IP stack controller with SPI up to 80 MHz, 32 KB internal memory, and eight independent sockets.

Implementation Notes

The repository contains real W5500 code. The README still describes Wi-Fi as the early development path and W5500 Ethernet as the intended production form factor, but the current src/main.rs and src/net/ethernet.rs already include a W5500-first path with Wi-Fi fallback.

src/main.rs — W5500 SPI and pin configuration:

 
let spi = Spi::new(
    peripherals.SPI2,
    SpiConfig::default().with_frequency(Rate::from_mhz(25)),
)
.expect("W5500 SPI init failed")
.with_sck(peripherals.GPIO13)
.with_mosi(peripherals.GPIO11)
.with_miso(peripherals.GPIO12)
.into_async();

let eth_cs = Output::new(peripherals.GPIO14, Level::High, OutputConfig::default());
let mut eth_spi_dev = SpiDevice::new(eth_spi_bus, eth_cs);
 

This configures the physical W5500 host interface. The ESP32-S3 talks to W5500 over SPI2 using GPIO13 for SCK, GPIO11 for MOSI, GPIO12 for MISO, and GPIO14 for chip select. This exists so the firmware can test Ethernet before deciding whether to use wired networking or fall back to Wi-Fi.

src/main.rs — W5500 network-device creation:

 
let (eth_device, eth_runner) = embassy_net_wiznet::new::<
    2,
    2,
    embassy_net_wiznet::chip::W5500,
    _,
    _,
    _,
>(mac_addr, wiznet_state, eth_spi_dev, eth_int, eth_reset)
.await
.expect("W5500 init failed after successful probe");
 

This creates the W5500-backed embassy-net device. The code uses embassy_net_wiznet::chip::W5500, then starts the Ethernet runner and network runner so the rest of the kubelet logic can use the same network stack interface. The kubelet code above it does not need to know whether the packets are moving through W5500 Ethernet or Wi-Fi.

src/net/ethernet.rs — W5500 presence and link check:

 
const W5500_VERSIONR: u16 = 0x0039;
const W5500_PHYCFGR: u16 = 0x002e;
const W5500_VERSION: u8 = 0x04;
const W5500_PHY_LINK_UP: u8 = 0x01;
 

The Ethernet module reads W5500 common registers, checks that the version register returns the expected W5500 value, and checks the PHY link bit. That code exists to avoid silently selecting Ethernet when the chip is missing, miswired, or present without a valid cable link.

The kubelet side then performs the Kubernetes sequence. src/kubelet.rs builds the Node identity, anchors wall-clock time from the API server /version response, posts /api/v1/nodes, creates the initial Lease, and patches the initial status. src/reconcilers/lease.rs renews the Lease repeatedly, updates a renewal counter, and drives the LED state after successful renewals.

Practical Tips / Pitfalls

  • Treat this as a Kubernetes protocol experiment, not a way to run containers on ESP32-S3. The project intentionally does not implement Pods, volumes, logs, or a real container runtime.
  • Use Ethernet first when testing a long-running node heartbeat loop. Wi-Fi reconnection behavior can hide Kubernetes logic bugs behind RF instability.
  • Verify W5500 SPI pins against the Waveshare ESP32-S3-ETH schematic before porting. The code uses GPIO13 SCK, GPIO11 MOSI, GPIO12 MISO, GPIO14 CS, GPIO10 INT, and GPIO9 reset.
  • DHCP must complete before Kubernetes registration. The firmware waits for IPv4 configuration before building the API client.
  • TLS buffers are large for a microcontroller. The code allocates static TLS read/write buffers of roughly 16 KB each and an 8 KB response buffer, so heap and static memory pressure are real design constraints.
  • The code currently disables full certificate verification for the home-lab loop. That is acceptable for an experiment, but not for untrusted networks.
  • Watch the Lease interval and status interval separately. Lease renewal keeps the node alive; status PATCH updates the visible health fields that tools such as kubectl describe node inspect.

FAQ

Q: Why use W5500 for picokubelet?
A: W5500 gives the ESP32-S3 a wired Ethernet path for DHCP, TLS, and repeated Kubernetes API calls. For a node that must renew a Lease continuously, wired Ethernet is more predictable than Wi-Fi and avoids treating RF reconnection as part of the kubelet experiment.

Q: How does W5500 connect to the ESP32-S3?
A: In this repository, W5500 is connected over SPI. The code configures SPI2 at 25 MHz and uses GPIO13 for SCK, GPIO11 for MOSI, GPIO12 for MISO, GPIO14 for chip select, GPIO10 for interrupt, and GPIO9 for reset.

Q: What role does W5500 play in this project?
A: W5500 is the Ethernet transport between the embedded kubelet and the k3s API server. It carries the traffic used to register the Node, create and renew the Lease, patch status, and keep the ESP32-S3 visible to the Kubernetes control plane.

Q: Can beginners follow this project?
A: Beginners can understand the concept, but building it requires embedded Rust, ESP32-S3 flashing, networking, and basic Kubernetes knowledge. The simplest mental model is: the ESP32-S3 does not run apps; it only convinces Kubernetes that a tiny worker node exists and is alive.

Q: How does W5500 compare with Wi-Fi, LwIP, or ENC28J60 here?
A: Wi-Fi is useful for early development, but wired W5500 Ethernet is the better match for a stable node heartbeat. Compared with ENC28J60-style designs that depend more heavily on a host-side TCP/IP stack, W5500 provides a hardwired TCP/IP controller with internal buffers and hardware socket support. Compared with a normal Linux kubelet, picokubelet is much smaller and only implements the registration, Lease, and status portions needed to look alive.

Documents
Comments Write