Zephyr does Ethernet Too!

When it comes to the Internet of Things, wireless tech like celluar and WiFi get all the flashy press coverage. But wired devices aren’t second class citizens, they’re the connectivity-of-choice for tons of industrial applications. Zephyr makes it really easy to add an Ethernet connection to any project.

Here at Golioth we’re getting ready for a couple of conferences: the Zephyr Developer’s Summit and the Embedded World Conference, both in June. We’ll have hardware demos on site, and the thought of competing for RF spectrum with tens of thousands of other radios gives me the demo blues. So we will include a wired-network demo at the Golioth kiosk to be on the safe side.

What did it take to get our hardware up and running with Ethernet? Not much. It’s a quick and easy process, so let’s dive in!

Wire up an Ethernet module

We’ve chosen the WIZnet W5500 Ethernet chip to handle the Ethernet PHY. It connects to a microcontroller using SPI and there is a handy ETH WIZ Click module available that includes the jack and magnetics for easy prototyping. This chip has great driver support in Zephyr, which we’ll get to in the next section.

Wiring it up will be familiar to anyone who has worked with Serial Peripheral Interface (SPI) devices. I’m using an nRF52840 microcontroller in this demo. The bindings page for nrf-spi lists sck-pin, mosi-pin, miso-pin, and cs-gpios which connect to the ETH WIZ Click’s SCK, SDI, SDO, and CS pins. The w5500 bindings page also details int-gpios and reset-gpios which connect to the INT and RST pins on the Ethernet module.

We need to tell Zephyr that this module is present by adding it to an overlay file.

&spi1 {
	compatible = "nordic,nrf-spi";
	status = "okay";
	cs-gpios = <&gpio0 3 GPIO_ACTIVE_LOW>;
	test_spi_w5500: w5500@0 {
		compatible = "wiznet,w5500";
		label = "w5500";
		reg = <0x0>;
		spi-max-frequency = <10000000>;
		int-gpios = <&gpio0 2 GPIO_ACTIVE_LOW>;
		reset-gpios = <&gpio0 30 GPIO_ACTIVE_LOW>;

This overlay file uses the SPI pin definitions already present in the board DTS file. The node for the W5500 chip correctly assigns CS (chip select), INT, and RST pins.

Configure the Ethernet library and IP handling

The node shown above needs to be added to a board overlay file in your project. For your convenience, we have hello-ethernet sample code that includes board files for several different microcontrollers.

In addition to telling Zephyr how the Ethernet chip is connected, we need to tell Zephyr to build in the proper libraries.


These KConfig symbols tell the build tools that we need the SPI peripherals, we’ll be using Ethernet–specifically the W5500 chip, and that we’re going to need some tools to manage the DHCP process for acquiring and using an IP address. I’ve added these settings to a board-specific conf file, but they could be added to the prj.conf if you prefer.

That last part requires just a bit of work in the main.c file. We need to tell Zephyr that if Ethernet is enabled, we want to make an API call to acquire and use an IP address assigned by the wired network’s DHCP server.

#include <net/net_if.h>

/* This next part goes in main() before the loop */
		LOG_INF("Connecting to Ethernet");
		struct net_if *iface;
		iface = net_if_get_default();

Don’t forget to plug it in

This sounds silly, but I have spent fives-of-minutes wondering why I wasn’t able to get an IP address. I’m so used to working with WiFi and cellular, sometimes I forget to plug in the Ethernet cable. Don’t be me.

*** Booting Zephyr OS build zephyr-v3.0.0-3806-g05cc2e1ac388  ***

[00:00:00.259,429]  golioth_system: Initializing
[00:00:00.259,796]  golioth_hello: main: Start Hello sample
[00:00:00.259,826]  golioth_hello: Connecting to Ethernet
[00:00:00.259,887]  golioth_hello: Sending hello! 0
[00:00:00.259,948]  golioth_system: Starting connect
[00:00:00.260,131]  golioth_hello: Failed to send hello!
[00:00:00.260,467]  golioth: Fail to get address (coap.golioth.io 5684) -11
[00:00:00.260,467]  golioth_system: Failed to connect: -11
[00:00:00.260,498]  golioth_system: Failed to connect: -11
[00:00:05.260,223]  golioth_hello: Sending hello! 1
[00:00:05.260,437]  golioth_hello: Failed to send hello!
[00:00:05.260,589]  golioth_system: Starting connect
[00:00:05.260,925]  golioth: Fail to get address (coap.golioth.io 5684) -11
[00:00:05.260,955]  golioth_system: Failed to connect: -11
[00:00:05.260,955]  golioth_system: Failed to connect: -11
[00:00:05.300,750]  net_dhcpv4: Received:
[00:00:10.260,498]  golioth_hello: Sending hello! 2
[00:00:10.260,711]  golioth_hello: Failed to send hello!
[00:00:10.261,047]  golioth_system: Starting connect
[00:00:10.315,734]  golioth_system: Client connected!
[00:00:15.260,803]  golioth_hello: Sending hello! 3
[00:00:20.263,305]  golioth_hello: Sending hello! 4

That warning aside, the display above is a typical run of the hello-ethernet sample. You can see that the Ethernet connection is initialized shortly after power-on. The Golioth client immediately starts trying to send log messages to the cloud but fails until an IP address is secured about 5.3 seconds into runtime.

All Internet connections act the same in Zephyr

Zephyr abstracts the details of your Internet connection. Once it’s set up, your app has access to sockets for whatever operation it needs. For the most part your code doesn’t need to know how it’s actually getting to the network.

Of course there are exceptions. Here we needed to explicitly sort out an IP address. But considering the complexity that actually goes into operating a network stack, Zephyr sure has taken the degree of difficulty down to a minimum.

Talk with an Expert

Implementing an IoT project takes a team of people, and we want to help out as part of your team. If you want to troubleshoot a current problem or talk through a new project idea, we're here for you.