Today I’m taking a deep dive into the Thread protocol! Based on IEEE 802.15.4, Thread is a mesh networking protocol designed to balance the needs of small, battery powered Internet-of-Things devices with the ability to communicate directly on The Internet. By leveraging IPv6 and 6LoWPAN, Thread is able to finally bring these automation networks into the land of the Internet Protocol, where interoperability thrives.

I walk through the setup of an OpenThread Border Router, OpenThread daemon for end devices, and compile the OpenThread Radio Co-Processor for a Nordic Semiconductor NRF52840 dongle, one of the cheapest ways to get started with Thread.

Contents

Video

Thumbnail

Dongle Firmware

First, we need firmware for our Radio Co-Processor. I’m using the Nordic Semi NRF52840 Dongle, using the firmware image provided by Nordic. Here’s the guide directly from them. I setup a small Debian VM on Proxmox using my cloud-init tutorial so I could easily pass-through USB devices and run the builds. Since the cloud-init VMs have no hardware support, I also had to install the normal kernel (apt install linux-image-generic -y) and reboot for it to detect my USB hardware. Nordic’s stuff seems to be very picky about Linux versions, it * really * wants Ubuntu 22.04 or 20.04 and not other versions, which is why the steps don’t exactly match theirs.

Anyway, here are the steps to follow:

# Install dependencies
sudo apt update
sudo apt install -y python3-pip git cmake ninja-build
# Add ourselves to dialout group so we can use the serial port
# NOTE: You may have to log out and back in after this or flashing will fail later
sudo usermod -a -G dialout $USER
# It's bad form to break system packages, but nordic doesn't provide us a better option
pip3 install --user -U west --break-system-packages
# Download nrfutil
curl https://files.nordicsemi.com/artifactory/swtools/external/nrfutil/executables/x86_64-unknown-linux-gnu/nrfutil -o nrfutil
# make executable
chmod +x nrfutil
# Move to location on PATH
sudo mv nrfutil /usr/local/bin/
# Install device so we can flash the device
nrfutil install device
# Install for pkg
nrfutil install nrf5sdk-tools
# Install toolchain-manager so we can manage toolchains
nrfutil install toolchain-manager
# Show toolchain options
nrfutil toolchain-manager search
# Install the latest version (as of this writing, 2.9.1)
nrfutil toolchain-manager install --ncs-version v2.9.1
# Make a subdir for us to work in
mkdir -p nrfbuild
cd nrfbuild
# Clone the repo at main (possibly dangerous, but you could choose a release tag instead)
python3 -m west init -m https://github.com/nrfconnect/sdk-nrf --mr main
# such as this tag
#west init -m https://github.com/nrfconnect/sdk-nrf --mr v2.9.1
# Make west download dependencies (this is very slow)
python3 -m west update
python3 -m west zephyr-export
# Go to the RCP folder and compile it
cd nrf/samples/openthread/coprocessor
# Launch into nrfutil toolchain environment (this sets up env vars)
nrfutil toolchain-manager launch --shell

At this point, you are in the coprocessor folder in the nrfutil shell. I stopped you here so you realize that you need to copy/paste again, in this new shell, and not in the other shell.

# -p means 'pristine' so it will start from scratch, in case you ran it before with errors
west build -b nrf52840dongle/nrf52840 -p

# Technically these don't need to go in the nrfutil shell, but they work either way

# Package the build for the bootloader
nrfutil pkg generate --hw-version 52 --sd-req=0x00 --application build/merged.hex --application-version 1 otbr-rcp.zip
# Flash the built onto the device
nrfutil dfu usb-serial -pkg otbr-rcp.zip -p /dev/ttyACM0

If you want a premade orbr-rcp.zip file, here’s the file I compiled (warning, random internet binaries, YMMV, no warranty, …)

In that case, you just need the nrfutil tool with device installed. You can also use their GUI flashing tool for Windows. I included the zip and the hex, some of the flashing tools want the hex instead.

Open Thread Border Router

followed https://openthread.io/codelabs/openthread-border-router#1 need to add WEB_GUI=1 before bootstrap and setup (it’s not the default on Debian)

I did this on Debian, this time on a single-board computer. I also tried a Raspberry Pi 4 and 3, and a Debian VM on Proxmox (but not using the cloud kernel!)

Here are the commands. You need sudo installed, even if you run as root, since OTBR’s scripts will use it.

# Instlll dependencies
sudo apt update
sudo apt install -y git
# Clone Open Thread Border Router
git clone https://github.com/openthread/ot-br-posix.git --depth 1
cd ot-br-posix
# Many of these exports have defaults already
# However, since they vary wildly for different OSes, I've set them here

# Enable Routing + Border Routing
export BACKBONE_ROUTER=1
export BORDER_ROUTING=1
# Disable NAT64 and DHCPv6-PD
export NAT64=0
export DNS64=0
export DHCPV6_PD=0
export DHCPV6_PD_REF=0
# Enable the web GUI and REST API
export WEB_GUI=1
export REST_API=1
# Set this to the name of your Ethernet interface to route to
export INFRA_IF_NAME=eth0

# Bootstrap environment
./script/bootstrap
# Actually do the build
./script/setup

Next we need to edit the UART location (only if you have multiple USB UART devices). You can find your serial devices in /dev/serial/by-id/, and pointing to those will be more reliable than using /dev/ttyACM0 if you have multiple dongles (i.e. Zigbee, Thread, ..). OpenThread Border Router loads an environment file called /etc/default/otbr-agent which contains some environment variables, so here’s what I started with and ended with:

# Default settings for otbr-agent. This file is sourced by systemd

# Options to pass to otbr-agent
OTBR_AGENT_OPTS="-I wpan0 -B ens18 spinel+hdlc+uart:///dev/ttyACM0 trel://ens18"
OTBR_NO_AUTO_ATTACH=0

and now it’s:

# Default settings for otbr-agent. This file is sourced by systemd

# Options to pass to otbr-agent
OTBR_AGENT_OPTS="-I wpan0 -B ens18 spinel+hdlc+uart:///dev/serial/by-id/usb-Nordic_Semiconductor_ASA_Thread_Co-Processor_FB7FFA86E3D48E34-if00 trel://ens18"
OTBR_NO_AUTO_ATTACH=0

Once that’s done, you can (re)start the services:

# Start the services
sudo systemctl start otbr-agent
sudo systemctl start otbr-web

And since I enabled the web UI, you can login on regular http port 80.

Thread Client

If you want to use Thread instead of Wifi, here’s the instructions to build Openthread as an end device (router eligible, but not a border router):

# Instlll dependencies
sudo apt update
sudo apt install -y git
# Clone Open Thread (not the border router)
git clone https://github.com/openthread/openthread.git --depth 1
cd openthread
# Bootstrap environment
./script/bootstrap
# Actually do the build
./script/cmake-build posix -DOT_DAEMON=ON

Next, we need to start up the daemon:

./build/posix/src/posix/ot-daemon -I wpan0 -v -d 4 'spinel+hdlc+uart:///dev/ttyACM0?uart-baudrate=115200'

(That will take the terminal, so login to another SSH session for ot-ctl)

Next, we can execute ./build/posix/src/posix/ot-ctl which should get us into an OpenThread terminal:

state
# Now we need to copy/paste the dataset from the border router
# On the BR, run:
# dataset active -x
# then copy the big blob of data,
# on the client, run:
dataset set active <big data blob>
# Now bring up the interface
ifconfig up
thread start
#And some useful commands for you
help
ipaddr
netdata show
extaddr

I wrote a systemd unit for this guy, so we can install it into /etc/systemd/system/ot-daemon.service:

[Unit]
Description=OpenThread DAemon
After=network-online.target

[Service]
ExecStart=/usr/local/bin/ot-daemon -I wpan0 -d 4 'spinel+hdlc+uart:///dev/ttyACM0?uart-baudrate=115200'
Restart=always

[Install]
WantedBy=multi-user.target

Also, copy ot-daemon and ot-ctl to /usr/local/bin so they are in a normal binary place to be happy