While typical beginner microcontrollers rely on external Wi-Fi shields or USB dongles, having a hardwired RJ45 Ethernet jack directly on the board changes the game for stability, deterministic latency, and reliability. In this post, I will walk through how the microcontroller interfaces with its internal Ethernet controller in MicroPython, how to bring up the network interface using both static addressing and DHCP, and the core application-layer protocols that make connected hardware tick.
Connecting the Dots: SPI Meets Ethernet
Before looking at the code, it is worth understanding the internal architecture. The onboard Ethernet controller operates as an SPI peripheral. Even though it resides on the same PCB, the main MCU communicates with it just like any external SPI chip: using a clock line, data lines, and dedicated control pins for Chip Select (CS), Reset (RST), and Power Down (PWN).
A common trap when configuring this board is peripheral selection. The board exposes multiple SPI buses, but internally, the WIZnet Ethernet hardware is wired to SPI 2, not SPI 1. Pinning it correctly makes the difference between a live link and a silent board.
Bringing Up the Interface: Code & Configuration
MicroPython provides the network module, which contains pre-built drivers for the WIZnet chip family (network.WIZNET5K). Connecting to the network follows a clean, sequential flow: configure the SPI bus and control pins, instantiate the Network Interface Controller (NIC), activate it, and assign IP configuration parameters.
Depending on your deployment model, you will configure the interface using either a Static IP or dynamic addressing via DHCP.
Approach 1: Static IP Configuration
A static IP is the standard approach for embedded servers, internal dashboards, or industrial field nodes where other systems need an unchanging, predictable address to reach the board.
import network
import time
from machine import Pin, SPI
# 1. Network static configuration parameters
NET_IP = "192.168.1.20"
NET_SN = "255.255.255.0"
NET_GW = "192.168.1.1"
NET_DNS = "8.8.8.8"
# 2. Initialize the internal SPI 2 bus at 8 MHz
spi = SPI(2, baudrate=8_000_000, polarity=0, phase=0)
# 3. Configure dedicated hardware control pins
cs = Pin("PB12", Pin.OUT)
rst = Pin("PD9", Pin.OUT)
pwn = Pin("PE15", Pin.OUT, value=0)
# 4. Instantiate the WIZnet controller object and activate
nic = network.WIZNET5K(spi, cs, rst)
nic.active(True)
# 5. Apply the static network configuration (IP, Subnet, Gateway, DNS)
nic.ifconfig((NET_IP, NET_SN, NET_GW, NET_DNS))
print("Ethernet initialized with Static IP!")
print("Board IP configuration:", nic.ifconfig())
Once the script runs, verify the link by opening a terminal on any computer connected to the same subnet:
Bash
ping 192.168.1.20
Seeing zero packet loss and instant ping responses confirms that the board is officially a reachable node on the local network.
Approach 2: Dynamic IP via DHCP
If you are deploying devices across unknown client networks or home routers where you don't control the subnet range, hardcoding IPs creates conflicts. DHCP allows the board to request an IP address, gateway, and DNS configuration automatically from the local router.
Because DHCP negotiation requires a multi-step handshake across the wire (DORA: Discover, Offer, Request, Acknowledge), we instruct the driver to use "dhcp" and poll until the router completes the assignment:
import time
import network
from machine import Pin, SPI
# 1. Initialize the internal SPI 2 bus at 8 MHz
spi = SPI(2, baudrate=8_000_000, polarity=0, phase=0)
# 2. Configure dedicated hardware control pins
cs = Pin("PB12", Pin.OUT)
rst = Pin("PD9", Pin.OUT)
pwn = Pin("PE15", Pin.OUT, value=0)
# 3. Instantiate the WIZnet controller object and activate
nic = network.WIZNET5K(spi, cs, rst)
nic.active(True)
# 4. Request network configuration dynamically via DHCP
print("Requesting IP address via DHCP...")
nic.ifconfig("dhcp")
# 5. Wait for the DHCP lease to resolve
timeout = 10
start_time = time.time()
while not nic.isconnected() or nic.ifconfig()[0] == "0.0.0.0":
if time.time() - start_time > timeout:
raise RuntimeError("DHCP assignment timed out! Check cable or router.")
time.sleep(0.5)
ip, subnet, gateway, dns = nic.ifconfig()
print("Ethernet initialized via DHCP!")
print(f"Assigned IP: {ip}")
print(f"Subnet Mask: {subnet}")
print(f"Gateway: {gateway}")
print(f"DNS Server: {dns}")The Headless Catch-22: Finding Your DHCP Address
While running the board tethered to your computer via USB, nic.ifconfig() prints the assigned address straight to the Thonny REPL. But once the board runs headless in an enclosure or remote cabinet, how do you locate it?
- Network Scanners: Run an ARP/subnet scan from your terminal (nmap -sn 192.168.1.0/24 or arp -a), or use GUI utilities like Angry IP Scanner to find devices showing WIZnet in their MAC vendor field.
- Router DHCP Reservations: Log into your router's admin portal and bind the board's physical MAC address (nic.config('mac')) to a static reservation, guaranteeing the board pulls the exact same IP every boot.
- Flip the Connection Model: In IoT production environments, nodes don't wait around to be contacted. Instead, have the microcontroller reach outbound upon boot to an MQTT broker or HTTP endpoint, reporting its assigned IP payload automatically.
Beyond Ping: Application-Layer Protocols
Getting an IP address is just the gateway. Microcontrollers rarely just sit on a network; they exchange structured data with servers, dashboards, and other machines. Here are four primary application protocols most embedded systems rely on:
1. HTTP (HyperText Transfer Protocol)
What it is: The foundation of the World Wide Web, operating on a client-server request-response architecture.
In Embedded: Microcontrollers use HTTP GET requests to fetch configuration data or API responses, and POST requests to send bulk sensor batches to a REST API. You can even run a tiny MicroPython web server directly on the board to host an interactive HTML dashboard.
Trade-off: HTTP headers are verbose text strings, which introduce high overhead for resource-constrained chips transmitting tiny numbers frequently.
2. MQTT (Message Queuing Telemetry Transport)
What it is: A lightweight, publish-subscribe (pub/sub) messaging protocol specifically engineered for IoT and constrained devices.
In Embedded: Instead of direct point-to-point connections, nodes connect to a central broker (like Mosquitto). A sensor node publishes temperature readings to a topic (e.g., lab/temperature), and any number of consumers (dashboards, databases, mobile apps) subscribe to that topic to receive updates in real time.
Trade-off: Extremely small packet headers (as small as 2 bytes) and built-in Quality of Service (QoS) levels make it far more power- and bandwidth-efficient than HTTP for telemetry.
3. FTP (File Transfer Protocol)
What it is: A legacy protocol built specifically for transferring files between a client and a remote server.
In Embedded: Useful for moving large data logs (such as an SD-card log of sensor history) or uploading over-the-air firmware updates and web assets directly onto the microcontroller’s flash storage without re-flashing via Thonny.
Trade-off: Uses dual channels (one for control, one for raw data) which adds complexity to firewall traversal and connection state management.
4. SNMP (Simple Network Management Protocol)
What it is: An application-layer protocol used by network administrators to monitor and manage network-attached devices.
In Embedded: In enterprise or industrial setups, network gear polls devices using SNMP to query health metrics: uptime, memory usage, link speed, packet errors, or temperature. If something faults, the board can proactively send an SNMP "Trap" alert.
Trade-off: Heavily structured around hierarchical Management Information Bases (MIBs) and binary ASN.1 encoding, making it great for network infrastructure monitoring but overkill for simple hobby projects.
Moving to Sockets
Bringing up the Ethernet interface with network.WIZNET5K directly populates MicroPython's standard socket module. Now that the physical network stack is active and addressed—whether fixed statically or leased via DHCP—the next step is using raw TCP and UDP sockets to send structured payloads, bridge our UART serial data onto the local network, and stream real telemetry to an MQTT broker.
Jonathan
Discussions
Become a Hackaday.io Member
Create an account to leave a comment. Already have an account? Log In.