OpenThread with Zephyr – building network

As a continuation of the last post, today we are going to build our OpenThread network with the help of Zephyr OS.


When preparing resources for the needs of this article, we wanted to build as big OpenThread network as possible. That’s why we tried to grab as many OpenThread capable (with 802.15.4 support) platforms as possible. We came up with the following:

  • FRDM-KW41Z,
  • nRF52840 PCA10056,
  • CC2650 SensorTag,
  • CC3200 LaunchXL + EM Adapter Booster Pack + CC2500EM,
  • MSP430F5529 LaunchPad + EM Adapter Booster Pack + CC2500EM.

Unfortunately, we were only able to run OpenThread + Zephyr on FRDM-KW41Z and nRF52840_PCA10056, because:

  • CC2650 SensorTag‘s RAM memory size turned out to be too small to hold both Zephyr OS and OpenThread. Even turning off components such as CLI and logging didn’t help:
Memory region         Used Size  Region Size  %age Used        
FLASH:                74596 B    130984 B     56.95%   
FLASH_CCFG:           88 B       88 B         100.00%         
SRAM:                 56544 B    20 KB        276.09%
  • Even though CC3200 LaunchXL board is supported in Zephyr, using it together with CC2500EM (802.15.4 transceiver) would require a specially designed device tree. And as every developer knows – implementing new things often gives birth to bugs which then need to be fixed. Since we’ve aimed only to do a fast evaluation, we’ve decided to put away this board.
  • It turned out that MSP430F5529 LaunchPad is currently not supported in Zephyr.

Setting up OpenThread Border Router

Before setting up the Border Router, we have to flash an OpenThread NCP (Network Co-Processor) firmware image to some OpenThread-capable board. Our Border Router needs it to be able to connect to the OpenThread network. It’s really simple, in our case we’ve used one of the nRF52840_PCA10056. You need to:

# clone OpenThread repository
git clone
cd openthread
# set up the environment using provided script
# build the OpenThread firmware using Makefile for your board,
# and proper flags 
make -f examples/Makefile-nrf52840 BORDER_AGENT=1 BORDER_ROUTER=1 COMMISSIONER=1 UDP_FORWARD=1
# build .bin file from .hex file
arm-none-eabi-objcopy -O binary output/nrf52840/bin/ot-ncp-ftd ot-ncp-ftd.bin

Afterward, we can flash the binary as we would normally flash the firmware for our chosen board. In the case of nRF52840_PCA10056, we simply drag&dropped the binary to the board, utilizing the board’s SEGGER J-Link interface (which makes it possible for the board to turn up as a normal storage device in the host PC filesystem).

To quickly set up the Border Router itself, we have several options to choose from:

  • build and configure the OpenThread Border Router on our own, using the tutorial available here. Supported platforms are Raspberry Pi 3B and BeagleBone Black.
  • run OpenThread Border Router Docker image, as described here. The tutorial says that it can be run on the Raspberry Pi 3B (RPi3B) or any Linux-based machine but we’ve only tested it on Raspberry.
  • use the ready-to-go Raspberry Pi system image – RaspPIoT Border Router Demo from Nordic Semiconductor’s nRF5 SDK for Thread.

We advise you to use the third option because it involves the smallest workload. Simply download the image, flash it on a microSD card (using e.g. Etcher), plug it into your Raspberry and power it up.

The used Raspberry should:

  • be connected to the Internet through the Ethernet cable
  • have an ability to set up the WiFi hotspot (so WiFi dongle might be necessary).
  • have the previously prepared NCP plugged into one of Raspberry’s USB ports.

The Border Router can either join one of the existing OpenThread networks or form the new one. Here, we will form a new network, leaving the default configuration as it is. Later, we will join this network from the end nodes.

Creating network

To form a network, we can:

  • use the Border Router’s web GUI,
  • log in to the Border Router and set up the network through the command line, using wpanctl service.

To use the first option, log in to the Border Router’s WiFi Access Point. The login credentials are:

SSID: BorderRouter-AP
Password: 12345678

After logging in, you can access Web GUI by entering address in your web browser.

To form a network, click on the Form button on the right. Leave the default network parameters and click the Form button. After several seconds, you should see a prompt message informing you that the network was successfully formed.

If you prefer the command line, log in to the Raspberry using ssh or a Serial-USB converter. You can configure and start the network using the following commands:

# First, leave the current network (the Border Router could form it 
# automatically on power-up) to be able to change the network's configuration
sudo wpanctl leave
# set the channel used by the network to 15
sudo wpanctl setprop NCP:Channel 15
# Set the network's PANID
sudo wpanctl setprop Network:PANID 0x1234
# Set the network's Extended PANID
sudo wpanctl setprop Network:XPANID 1111111122222222
# Set the network's Master KEy
sudo wpanctl setprop Network:Key 00112233445566778899aabbccddeeff
# Set the on-mesh prefix
sudo wpanctl config-gateway -d "fd11:22::"
# Form the network using "OpenThreadDemo" name
sudo wpanctl form "OpenThreadDemo"

We can verify that the operation was successful by using the sudo wpanctl status command. The output should look similar to this:

wpan0 => [
    "NCP:State" => "associated"
    "Daemon:Enabled" => true
    "NCP:Version" => "OPENTHREAD/20180926-00639-gfb11d96e; NRF52840; May 27 2019 15:22:52"
    "Daemon:Version" => "0.08.00d (; Apr  5 2019 13:05:28)"
    "Config:NCP:DriverName" => "spinel"
    "NCP:HardwareAddress" => [F4CE368B2528BBCF]
    "NCP:Channel" => 15
    "Network:NodeType" => "leader"
    "Network:Name" => "OpenThreadDemo"
    "Network:XPANID" => 0x1111111122222222
    "Network:PANID" => 0x1234
    "IPv6:LinkLocalAddress" => "fe80::2cdf:7e62:a7c1:8138"
    "IPv6:MeshLocalAddress" => "fd11:1111:1122:0:e7a6:b7bd:63e4:d87f"
    "IPv6:MeshLocalPrefix" => "fd11:1111:1122::/64"
    "com.nestlabs.internal:Network:AllowingJoin" => false

Setting up OpenThread End Node

In order to set up OpenThread end nodes with Zephyr OS, we will use the sample code provided by Zephyr in the form of echo_server example. It’s a simple application that waits for incoming TCP/UDP packets on some pre-defined port and simply sends it back.

Thread protocol has been designed with low-power devices in mind, so TCP is heavily discouraged if we want to achieve long battery life (it introduces a lot of communication overhead and 802.15.4 radio is very power-hungry). That’s why by default, TCP is disabled in this sample’s code
when using OpenThread overlay.

After downloading the Zephyr repository and setting the environment up, simply go to samples/net/sockets/echo_server directory and build the application as described in the README, using OpenThread overlay configuration file. Don’t forget to use the proper -DBOARD parameter.

Before that, let’s do one little modification to the overlay-ot.conf file. We’ll add an additional configuration parameter:


This line makes it possible to join the OpenThread network using CLI of the end nodes. After that, you can use the following commands to build the application:

For nRF52840_PCA10056:

$ cd $ZEPHYR_BASE/samples/net/sockets/echo_server 
$ mkdir build && cd build 
$ cmake -GNinja -DBOARD=nrf52840pca10056 -DCONF_FILE="prj.conf overlay-ot.conf" .. 
$ ninja run

For FRDM-KW41Z :

$ cd $ZEPHYR_BASE/samples/net/sockets/echo_server 
$ mkdir build && cd build 
$ cmake -GNinja -DBOARD=frdm_kw41z -DCONF_FILE="prj.conf overlay-ot.conf" .. 
$ ninja run

Joining the network

Normally, when building Zephyr application with OpenThread enabled, the interface will get initialized, before calling our application’s main() function, using network configuration provided in the used .conf file. For example, if we would like to connect to the network that we’ve configured on the Border Router, we’d have to add the following lines to our overlay-ot.conf file:

CONFIG_OPENTHREAD_PANID=4660  # 0x1234 in decimal form

But hardcoding the network’s credentials is not always convenient. We will try to join the Border Router’s network using the Thread Commissioning, and that’s also why we’ve set CONFIG_OPENTHREAD_JOINER=y in the overlay-ot.conf file in the previous chapter. More information about the Thread Commissioning can be found here and here

To join the network, first, we have to use the following commands in the end node’s CLI:

uart:~$ ot thread stop
uart:~$ ot panid 0xffff
uart:~$ ot eui64

Before we start joining the network, we have to turn on the commissioner service on the Border Router and tell it that the new device with the eui64 obtained above and the PSKd=JOINER1 will try to join the network:

pi@raspberrypi:~ $ sudo wpanctl commissioner start
Commissioner started
pi@raspberrypi:~ $ sudo wpanctl commissioner joiner-add 257073449c617a0d 300 JOINER1
Added Joiner 25:70:73:44:9C:61:7A:0D, timeout:300, PSKd:"JOINER1"

Then, we can use the following commands back in the end node’s CLI to join the network:

uart:~$ ot joiner start JOINER1
Join success
uart:~$ ot thread start

Remember that every device that joins the network has a unique eui64 and should use a unique PSKd. We could always tell the commissioner to ignore the device’s eui64 when it tries to join the network, by using * wildcard character:

pi@raspberrypi:~ $ sudo wpanctl commissioner joiner-add * 300 JOINER1

But I think we can agree that it brings a big security hole to our network, doesn’t it?


After establishing the OpenThread connection from our two end nodes to our Border Router, we have a great sea of possibilities. Our MCUs have access to the Internet, which we can demonstrate by pinging the public Google DNS server.

OpenThread nodes use the IPv6 addresses, but the Border Router runs a NAT64 service which can translate proper IPv6 addresses to IPv4 when the IPv6 address use a 64:ff9b::/96 prefix. The Google DNS server as we know has an IPv4 address of, so we should translate that to IPv6 using for example this webpage. Then we can use the obtained IPv6 adress and combine it with the 64:ff9b::/96 prefix. So, the full IPv6 address that we will use is 64:ff9b::808:808. We can use it in the end node’s CLI to ping it:

uart:~$ ot ping 64:ff9b::808:808
16 bytes from 64:ff9b:0:0:0:0:808:808: icmp_seq=1 hlim=48 time=70ms

Obviously we can also ping from one node to the other, but we first need to obtain the address of the node we want to ping, so in the first one’s CLI. The OpenThread interface uses a lot of addresses but we have to look for the one with the network’s on-mesh prefix of fd11:22:::

uart:~$ ot ipaddr
fd11:22:0:0:189b:7a27:cdb9:f589  # this is the one

After that, we can ping this node from the other one by executing:

uart:~$ ot ping fd11:22:0:0:189b:7a27:cdb9:f589
16 bytes from fd11:22:0:0:189b:7a27:cdb9:f589: icmp_seq=2 hlim=64 time=90ms

As we remember, the basic purpose of the echo_server sample is to echo UDP packets received by the end node. We can find the port the application uses for UDP packet reception by inspecting src/common.h file:

#define MY_PORT 4242

This can be verified by executing the following command on the Border Router:

pi@raspberrypi:~ $ echo -n "hello" | nc -6u -w1 fd11:22:0:0:189b:7a27:cdb9:f589 4242

We can see that the end node in fact responded with hello message. We should also see the following message in the end node’s CLI:

[01:51:46.646,362] <dbg> net_echo_server_sample.process_udp: (0x20007c24): UDP (IPv6): Received and replied with 5 bytes


As we can see, OpenThread can be very easily integrated with Zephyr OS allowing us to achieve the Internet connectivity on small, low-power IoT devices. That opens a lot of opportunities – we can send MQTT messages or query SNTP servers among other things.

In LPN Plant we connect consulting, technical expertise and financial effectiveness to design and implement low power wireless solutions for industry. If you are looking for product developers or just need support in a piece of your system feel free to contact us. If you enjoying this type of content feel free to share content on social media.

Leave a Reply

Your email address will not be published. Required fields are marked *