The Raspberry Pi Zero Ethernet Dilemma: USB OTG vs. SPI HATs
The Raspberry Pi Zero lacks a native RJ45 jack. To add hardwired raspberry pi zero ethernet connectivity, you must route data through either the Micro-USB OTG port or the 40-pin GPIO header via SPI. While a $12 Micro-USB to Ethernet adapter works for basic Linux networking, it monopolizes your only USB port and forces the Pi's CPU to handle the entire TCP/IP stack in software. For embedded IoT projects requiring simultaneous sensor reading and low-power network transmission, an SPI Ethernet HAT is the superior engineering choice.
However, not all SPI HATs are equal. The older ENC28J60 chips require the Pi's Linux kernel to process every TCP packet in software, often maxing out the CPU on single-core Zero W boards and causing dropped packets. The W5500 chip, conversely, features a hardware TCP/IP offload engine that handles routing, TCP, and UDP internally, passing only clean payload data over the SPI bus.
Decision Path: Choosing Your Ethernet Interface
| Requirement | Path | Resulting Hardware |
|---|---|---|
| Need >50 Mbps throughput or standard desktop Linux use? | USB 2.0 Bus | Micro-USB OTG to 10/100 Ethernet Adapter |
| Need GPIO access + low power IoT + hardware TCP offload? | SPI0 Bus | DEFAULT PICK: Waveshare W5500 Ethernet HAT |
| Need GPIO access but only doing basic UDP broadcast? | SPI0 Bus | ENC28J60 HAT (Not recommended for TCP) |
Decision Terminated: For the remainder of this guide, we are building a hardwired IoT environmental logger using the Waveshare W5500 Ethernet HAT.
Parts List and Hardware Specifications
This build targets the Raspberry Pi Zero 2 W. While the code will run on the original Zero W, the quad-core SC7000P processor on the Zero 2 W handles Python's requests library and I2C polling without introducing SPI bus latency.
Estimated Time: 45 minutes
Estimated Cost: $38 - $45 USD
| Component | Exact Variant / Model | Estimated Price |
|---|---|---|
| Microcontroller | Raspberry Pi Zero 2 W (with pre-soldered 40-pin header) | $15.00 |
| Ethernet HAT | Waveshare W5500 Ethernet HAT (SPI Interface) | $14.50 |
| Sensor | BME280 I2C Temperature/Humidity/Pressure Module (Adafruit 2652 or generic) | $9.00 |
| Networking | CAT6 Ethernet Patch Cable + 10/100/1000 Switch Port | $5.00 |
Wiring the W5500 SPI HAT and BME280 Sensor
The W5500 HAT plugs directly onto the Pi Zero's 40-pin header. It utilizes the primary SPI0 bus. The BME280 sensor will share the I2C1 bus. Because the W5500 HAT passes through the 40-pin header, you can stack the BME280 wiring on top of the HAT's male pins.
Pin Mapping Table
| Function | Pi Zero GPIO (BCM) | Physical Pin | Target Module |
|---|---|---|---|
| SPI0 MOSI | GPIO 10 | 19 | W5500 HAT |
| SPI0 MISO | GPIO 9 | 21 | W5500 HAT |
| SPI0 SCLK | GPIO 11 | 23 | W5500 HAT |
| SPI0 CE0 (CS) | GPIO 8 | 24 | W5500 HAT |
| W5500 INT | GPIO 25 | 22 | W5500 HAT |
| W5500 RST | GPIO 24 | 18 | W5500 HAT |
| I2C1 SDA | GPIO 2 | 3 | BME280 Sensor |
| I2C1 SCL | GPIO 3 | 5 | BME280 Sensor |
| 3.3V Power | N/A | 1 | BME280 Sensor (VIN) |
| Ground | N/A | 6 | BME280 Sensor (GND) |
Configuring Raspberry Pi OS (Bookworm/2026) for SPI Ethernet
Modern Raspberry Pi OS (Bookworm and later) moved the boot configuration directory. The config.txt file is now located at /boot/firmware/config.txt, not /boot/config.txt. You must load the specific device tree overlay for the W5500 chip.
- SSH into your Pi Zero or open a terminal.
- Open the configuration file:
sudo nano /boot/firmware/config.txt - Scroll to the bottom and add the W5500 overlay, explicitly defining the interrupt pin and speed:
dtparam=spi=on dtoverlay=w5500,int_pin=25,spd=10000000 - Enable the I2C bus for the sensor:
sudo raspi-config-> Interface Options -> I2C -> Enable. - Reboot the system:
sudo reboot. - Verify the kernel recognized the chip by checking the network interfaces:
ip addr show eth0. You should see an IP address assigned via DHCP.
For deeper technical details on device tree overlays, refer to the official Raspberry Pi Configuration Documentation.
Python Logging Script with Network Error Handling
The following script targets the Raspberry Pi Zero 2 W. It reads the BME280 sensor via I2C and POSTs the JSON payload to a local server via the W5500 Ethernet interface. It includes robust error handling for the specific network failures common to SPI Ethernet setups.
Prerequisites: sudo apt install python3-smbus python3-pip followed by pip3 install bme280 requests.
import time
import requests
from smbus2 import SMBus
import bme280
# Target Board: Raspberry Pi Zero 2 W
# I2C Bus 1 for BME280
I2C_BUS = 1
BME280_ADDR = 0x76
API_ENDPOINT = 'http://192.168.1.100:8080/api/sensor'
def init_sensor():
bus = SMBus(I2C_BUS)
calibration_params = bme280.load_calibration_params(bus, BME280_ADDR)
return bus, calibration_params
def read_and_post(bus, calibration_params):
try:
# Poll sensor
data = bme280.sample(bus, BME280_ADDR, calibration_params)
payload = {
'temp_c': round(data.temperature, 2),
'humidity': round(data.humidity, 2),
'pressure': round(data.pressure, 2)
}
# POST via W5500 hardware TCP stack
response = requests.post(API_ENDPOINT, json=payload, timeout=5)
response.raise_for_status()
print(f'Success: Posted {payload}')
except requests.exceptions.ConnectionError as e:
# Triggered when the remote server is down or blocking the port
print(f'Network Error: Connection refused or host down. Details: {e}')
except OSError as e:
# Triggered when the Pi's routing table drops the packet (SPI overlay failure)
if 'Errno 101' in str(e):
print(f'Fatal Network Error: [Errno 101] Network is unreachable. Check SPI overlay.')
else:
print(f'OS Error: {e}')
except FileNotFoundError as e:
# Triggered if I2C bus is missing
print(f'Hardware Error: {e}')
except Exception as e:
print(f'Unexpected error: {e}')
if __name__ == '__main__':
bus, params = init_sensor()
while True:
read_and_post(bus, params)
time.sleep(60)
Debugging: First Three Things to Check When It Fails
When hardwired SPI networking fails, the Linux kernel usually fails silently at the hardware layer, presenting as a generic Python network error. If your script fails, follow this ranked decision tree.
1. Exact Error: OSError: [Errno 101] Network is unreachable
- Cause: The Linux kernel did not load the W5500 driver, meaning
eth0does not exist. The SPI overlay failed to initialize the chip. - Fix: Run
dmesg | grep w5500. If you see SPI timeout errors, yourspd=10000000parameter inconfig.txtis too high for your specific cable length or HAT batch. Drop it tospd=4000000and reboot. Ensure you edited/boot/firmware/config.txtand not the legacy/boot/config.txtpath.
2. Exact Error: requests.exceptions.ConnectionError: HTTPConnectionPool... Max retries exceeded
- Cause: The W5500 has an IP address, but the TCP handshake is being rejected by the destination server or a managed switch.
- Fix: The W5500 supports 10/100 Half/Full duplex. Some modern Gigabit enterprise switches fail to auto-negotiate with the W5500's PHY layer. Log into your managed switch and hardcode the specific port to 100Mbps Full Duplex. Alternatively, verify your API endpoint is listening on
0.0.0.0and not just127.0.0.1.
3. Exact Error: FileNotFoundError: [Errno 2] No such file or directory: '/dev/i2c-1'
- Cause: The I2C bus is disabled, or the BME280 is wired to the wrong pins.
- Fix: Run
ls /dev/i2c*. Ifi2c-1is missing, runsudo raspi-configand enable I2C. If it is present but the sensor still fails, runsudo i2cdetect -y 1. If the grid is empty, check your SDA/SCL wiring and ensure the BME280 module has pull-up resistors (most Adafruit/SparkFun modules do; cheap generic clones often require external 4.7kΩ pull-ups to 3.3V).
For more on W5500 specific hardware quirks, consult the Waveshare W5500 Wiki.
Extending or Simplifying the Build
Depending on your deployment environment, you may need to alter this hardware configuration.
How to Simplify (Drop the HAT)
If you realize you do not need the 40-pin GPIO header for other sensors and just want a reliable headless Linux node, abandon the SPI HAT entirely. Purchase a Micro-USB OTG to 10/100 Ethernet Adapter (approx. $12). Plug it directly into the Pi Zero's micro-USB data port (not the power port). The Linux kernel includes the asix and cdc_ether drivers natively; it will appear as eth0 instantly with zero config.txt modifications. This is the fastest path for a simple Pi-hole or MQTT broker node.
How to Extend (Add Power over Ethernet)
Running a single CAT6 cable for both data and power is ideal for attic or outdoor deployments. The standard Raspberry Pi PoE HATs do not fit the Zero's footprint. To extend this build with PoE:
- Purchase the Waveshare PoE HAT for Pi Zero (specifically designed for the Zero footprint).
- Stack it above the W5500 HAT using the included stacking headers.
- The PoE HAT includes an isolated DC-DC converter that pulls 48V from the Ethernet line and steps it down to 5V, feeding the Pi's 5V rail directly through the GPIO header.
- Warning: Ensure your network switch supports 802.3af (PoE) or 802.3at (PoE+). Passive 24V PoE injectors (common in older Ubiquiti gear) will destroy the Pi's voltage regulator.
By offloading the TCP/IP stack to the W5500 silicon, your Pi Zero 2 W remains free to handle edge-compute tasks, local MQTT logging, or cryptographic TLS handshakes without the network interface choking the system bus.






