Build a Thread network with nRF52840-MDK

Build a Thread network with nRF52840-MDK

Table of Contents

Introduction 

OpenThread is an open-source implementation of the Thread® networking protocol. Nest has released OpenThread to make the networking technology used in Nest products more broadly available to developers, in order to accelerate the development of products for the connected home.

The Thread specification defines an IPv6-based reliable, secure and low-power wireless device-to-device communication protocol for home applications. OpenThread implements all Thread networking layers (IPv6, 6LoWPAN, IEEE 802.15.4 with MAC security, Mesh Link Establishment, Mesh Routing) and device roles, as well as Border Router support.Following this post, you'll program OpenThread on nRF52840-MDK, create and manage a Thread network, and pass messages between nodes.

In the following sections, you'll program OpenThread on nRF52840-MDK, create and manage a Thread network, and pass messages between nodes. 

What'll you need

  • 3 nRF52840-MDK boards
  • 3 USB 3.1 Type-C Cables
  • A Linux/macOS machine with at least 3 USB ports

 Getting Started

The following sections provide detailed information about the required software toolchains.

Install ARM GNU Toolchain

Download and install the 6-2017-q2-update version. Then make sure to add the path to your toolchain to your OS PATH environment variable:

# in ~/.bash_profile, add the following script
export PATH="<path to install directory>/gcc-arm-none-eabi-6-2017-q2-update/bin:${PATH}"

Adding the path makes it possible to run the toolchain executables from any directory using the terminal. To verify that the path is set correctly, type the following in your terminal:

$ arm-none-eabi-gcc --version

Install pyOCD

pyOCD is an Open Source python 2.7 based library for programming and debugging ARM Cortex-M microcontrollers using CMSIS-DAP. Linux, OSX and Windows are supported.

The latest stable version of pyOCD may be installed via pip as follows:

$ pip install --pre -U pyocd

Install wpantund

wpantund is a user-space network interface driver/daemon that provides a native IPv6 network interface to a low-power wireless Network Co-Processor (or NCP). It was written and developed by Nest Labs to make supporting Thread connectivity on Unix-like operating systems more straightforward.

wpantund is designed to marshall all access to the NCP, ensuring that it always remains in a consistent and well-defined state.

Follow the wpantund Installation Guide from openthread/wpantund to install wpantund.

Clone OpenThread

Clone and install OpenThread:

$ cd ~
$ git clone --recursive https://github.com/openthread/openthread.git
$ cd openthread
$ ./bootstrap

Specify platform configuration for nRF52840-MDK by adapting the openthread/examples/platforms/nrf52840/platform-config.h file to:

/**
 * @def UART_HWFC
 * UART Hardware Flow Control.
 * @brief Possible values:
 *         \ref NRF_UART_HWFC_ENABLED - HW Flow control enabled.
 *         \ref NRF_UART_HWFC_DISABLED - HW Flow control disabled.
 */
#ifndef UART_HWFC
#define UART_HWFC NRF_UART_HWFC_DISABLED
#endif
/**
 * @def UART_PIN_TX
 * UART TX Pin.
 */
#ifndef UART_PIN_TX
#define UART_PIN_TX 20
#endif
/**
 * @def UART_PIN_RX
 * UART RX Pin.
 */
#ifndef UART_PIN_RX
#define UART_PIN_RX 19
#endif

Now you're ready to build and flash OpenThread to the nRF52840-MDK.

Set up the NCP Joiner

Build and flash

Build the OpenThread nRF52840 example with Joiner functionality. A device uses the Joiner role to be securely authenticated and commissioned onto a Thread network. Always clean the repo of previous builds first by running make clean.

$ cd ~/openthread
$ make -f examples/Makefile-nrf52840 clean
$ make -f examples/Makefile-nrf52840 JOINER=1

After a successful build, the elf files can be found in openthread/output/nrf52840/bin. Convert the OpenThread FTD NCP binary hex file:

$ cd ~/openthread/output/nrf52840/bin
$ arm-none-eabi-objcopy -O ihex ot-ncp-ftd ot-ncp-ftd.hex

Connect nRF52840-MDK to your computer with a USB cable. Use pyocd-flashtool -l to get the boardId:

$ pyocd-flashtool -l
WARNING:root:Unsupported board found 1026
0 => Unknown Board [cortex_m] boardId => 10260000083ab20300000000000000000000000097969902

Flash the OpenThread NCP FTD hex file onto the nRF52840-MDK, using the boardId:

$ pyocd-flashtool -t nrf52 -b 10260000083ab20300000000000000000000000097969902 -ce ot-ncp-ftd.hex

Label the board NCP so that later you don't confuse the board roles.

Configure wpantund

In the NCP design, use wpantund to communicate with and manage the Thread device.

In a terminal window, start wpantund on the serial port with the NCP, creating the interface utun7 and enabling info logs:

$ sudo /usr/local/sbin/wpantund -o Config:NCP:SocketPath /dev/cu.usbmodem14112  \
        -o Config:TUN:InterfaceName utun7 \
        -o Daemon:SyslogMask " -info"

Output similar to the following is generated upon success:

Jun  9 01:29:49  wpantund[12257] : Starting wpantund 0.08.00d (Jun  9 2018 00:31:51) . . .
Jun  9 01:29:49  wpantund[12257] :  SOURCE_VERSION = 0.07.01-217-g86d29d6
Jun  9 01:29:49  wpantund[12257] :  BUILD_VERSION = 0.07.01-217-g86d29d6
Jun  9 01:29:49  wpantund[12257] : Configuration file "/etc/wpantund.conf" read.
Jun  9 01:29:49  wpantund[12257] : Ready. Using DBUS bus ":1.2"
Jun  9 01:29:49  wpantund[12257] : Running as root without dropping privileges!
Jun  9 01:29:49  wpantund[12257] : State change: "uninitialized" -> "offline"
Jun  9 01:29:49  wpantund[12257] : NCP is running "OPENTHREAD/20170716-00584-ge4f5f240-dirty; NRF52840; Jun  8 2018 23:36:19"
Jun  9 01:29:49  wpantund[12257] : Driver is running "0.08.00d (0.07.01-217-g86d29d6; Jun  9 2018 00:31:51)"
1:29:49  wpantund[12257] : Network is not joinable
Jun  9 01:29:49  wpantund[12257] : Resetting interface(s). . .
Jun  9 01:29:49  wpantund[12257] : Finished initializing NCP

Leave this terminal window open so that logs from wpantund can be viewed.

A user-defined interface is required to communicate with the NCP using wpanctl. Open a new terminal window and using wpanctl, connect to the interface you just set up:

$ sudo /usr/local/bin/wpanctl -I utun7
wpanctl:utun7>

Verify build

Verify that the NCP is successfully running OpenThread:

Set up the FTDs

The other two Thread nodes used in this section are Full Thread Devices (FTDs) on the standard System-on-Chip (SoC) design. They do not use wpantund, and the user manually manages them with the OpenThread CLI.

One device functions as the Commissioner, to securely authenticate and commission devices onto that network. The other device functions as a Joiner that the Commissioner can authenticate to the Thread network.

Build and flash

Build the OpenThread FTD example for the nRF52840-MDK, with the Commissioner and Joiner roles enabled:

$ cd ~/openthread
$ make -f examples/Makefile-nrf52840 clean
$ make -f examples/Makefile-nrf52840 COMMISSIONER=1 JOINER=1

Navigate to the directory with the OpenThread Full Thread Device (FTD) CLI binary, and convert it to hex format:

$ cd ~/openthread/output/nrf52840/bin
$ arm-none-eabi-objcopy -O ihex ot-cli-ftd ot-cli-ftd.hex

Connect another nRF52840-MDK to your computer with a USB cable. Use pyocd-flashtool -l to get the boardId:

$ pyocd-flashtool -l
WARNING:root:Unsupported board found 1026
WARNING:root:Unsupported board found 1026
0 => Unknown Board [cortex_m] boardId => 10260000083ac27f00000000000000000000000097969902
1 => Unknown Board [cortex_m] boardId => 10260000083ab20300000000000000000000000097969902

Flash the OpenThread CLI FTD hex file onto the nRF52840-MDK, using the boardId:

$ pyocd-flashtool -t nrf52 -b 10260000083ac27f00000000000000000000000097969902 -ce ot-cli-ftd.hex

Label the board Commissioner.

Verify build

Verify a successful build by accessing the OpenThread CLI using GNU Screen from a terminal window. The nRF52840-MDK boards use a baud rate of 115200.

$ screen /dev/cu.usbmodem142112 115200

In the new window, press Return on the keyboard a few times to bring up the OpenThread CLI > prompt. Check for IPv6 addresses:

> ipaddr
fd11:2233:4455:0:99ea:1fe9:acd6:d384
fe80:0:0:0:2003:a240:810f:1598
Done

Set up the FTD Joiner

Repeat the above process to flash the third nRF52840-MDK board, using the existing ot-cli-ftd.hex build. Then label the board Joiner.

Open a new terminal window, and then access the OpenThread CLI for the FTD Joiner:

$ screen /dev/cu.usbmodem142412 115200

In this new window, press Return on the keyboard a few times to bring up the OpenThread CLI > prompt. Check for IPv6 addresses:

> ipaddr
fe80:0:0:0:d079:7d86:6413:4f4e
fd11:2233:4455:0:e839:eb52:f7ec:74e3
Done

Create the Thread network

Now that you have all your terminal windows and screens configured, let's create our Thread network. On the FTD Commissioner, configure the network and bring up Thread:

## FTD Commissioner ##
----------------------
> networkname makerdiary
Done
> extpanid 1122334455667788
Done
> panid 0x1122
Done
> masterkey 11223344556677881122334455667788
Done
> ifconfig up
Done
> thread start
Done

After a moment, check the device state. It should be the Leader. Also get the RLOC16 for future reference.

## FTD Commissioner ##
----------------------
> state
leader
Done
> rloc16
3400
Done

Check the device's IPv6 addresses:

## FTD Commissioner ##
----------------------
> ipaddr
fd11:2233:4455:0:0:ff:fe00:fc00        # Leader Anycast Locator (ALOC)
fd11:2233:4455:0:0:ff:fe00:3400        # Routing Locator (RLOC)
fd11:2233:4455:0:99ea:1fe9:acd6:d384   # Mesh-Local EID (ML-EID)
fe80:0:0:0:2003:a240:810f:1598         # Link-Local Address (LLA)
Done

The "makerdiary" network is now visible when scanned from other Thread devices.

From wpanctl on the NCP Joiner:

## NCP Joiner ##
----------------
wpanctl:utun7> scan
   | Joinable | NetworkName        | PAN ID | Ch | XPanID           | HWAddr           | RSSI
---+----------+--------------------+--------+----+------------------+------------------+------
 1 |       NO | "makerdiary"       | 0x1122 | 11 | 1122334455667788 | 2203A240810F1598 |  -59

From the OpenThread CLI on the FTD Joiner:

## FTD Joiner ##
----------------
> scan
| J | Network Name     | Extended PAN     | PAN  | MAC Address      | Ch | dBm | LQI |
+---+------------------+------------------+------+------------------+----+-----+-----+
> | 0 | makerdiary       | 1122334455667788 | 1122 | 2203a240810f1598 | 11 | -32 | 244 |
Done

If the makerdiary network doesn't appear in the list, try scanning again.

You may note that in both scans, the network seems to be not joinable (Joinable column on the NCP Joiner, J column on the FTD Joiner). This only means that Thread Commissioning is not active on the network. It can still be joined out-of-band, by entering the network master key in the joiner device manually.

Add the NCP Joiner

Let's add the NCP Joiner to the Thread network we just created, using an out-of-band process. Scan for networks on the NCP Joiner:

## NCP Joiner ##
----------------
wpanctl:utun7> scan
   | Joinable | NetworkName        | PAN ID | Ch | XPanID           | HWAddr           | RSSI
---+----------+--------------------+--------+----+------------------+------------------+------
 1 |       NO | "makerdiary"       | 0x1122 | 11 | 1122334455667788 | EE7A6D71FB28C60D |  -68

To join, set the network master key on the NCP Joiner and join network 1 (the ID in the first column of the scan output):

## NCP Joiner ##
----------------
wpanctl:utun7> setprop Network:Key 11223344556677881122334455667788
wpanctl:utun7> join 1
Joining "makerdiary" 1122334455667788 as node type "end-device"
Successfully Joined!

Check the status of the NCP Joiner to verify. It might take a few seconds for all IPv6 addresses to appear in the output.

## NCP Joiner ##
----------------
wpanctl:utun7> status
utun7 => [
    "NCP:State" => "associated"
    "Daemon:Enabled" => true
    "NCP:Version" => "OPENTHREAD/20170716-00650-g631557e8-dirty; NRF52840; Jun  9 2018 15:45:03"
    "Daemon:Version" => "0.08.00d (0.07.01-217-g86d29d6; Jun  9 2018 00:31:51)"
    "Config:NCP:DriverName" => "spinel"
    "NCP:HardwareAddress" => [9019EC5D617D7AAB]
    "NCP:Channel" => 11
    "Network:NodeType" => "end-device"
    "Network:Name" => "makerdiary"
    "Network:XPANID" => 0x1122334455667788
    "Network:PANID" => 0x1122
    "IPv6:LinkLocalAddress" => "fe80::60bd:ff84:2121:344d"
    "IPv6:MeshLocalAddress" => "fd11:2233:4455::f:b5e:169b:b875"
    "IPv6:MeshLocalPrefix" => "fd11:2233:4455::/64"
    "com.nestlabs.internal:Network:AllowingJoin" => false
]

Make note of the IPv6:MeshLocalAddress, you'll use it later.

Get the NCP Joiner's RLOC16:

## NCP Joiner ## 
----------------
wpanctl:utun7> getprop Thread:RLOC16 Thread:RLOC16 = 0x3403

Back on the FTD Commissioner, check the router and child tables to confirm both devices are part of the same network. Use the RLOC16 to identify the NCP Joiner.

## FTD Commissioner ##
----------------------
> router table
| ID | RLOC16 | Next Hop | Path Cost | LQ In | LQ Out | Age | Extended MAC     |
+----+--------+----------+-----------+-------+--------+-----+------------------+
| 13 | 0x3400 |       63 |         0 |     0 |      0 |   0 | 2203a240810f1598 |

Done
> child table
| ID  | RLOC16 | Timeout    | Age        | LQ In | C_VN |R|S|D|N| Extended MAC     |
+-----+--------+------------+------------+-------+------+-+-+-+-+------------------+
|   3 | 0x3403 |        240 |         66 |     3 |   86 |1|1|1|1| 62bdff842121344d |

Done

Ping the mesh-local address of the NCP Joiner (the IPv6:MeshLocalAddress attribute from the NCP Joiner's status output) to verify connectivity:

## FTD Commissioner ##
----------------------
> ping fd11:2233:4455::f:b5e:169b:b875
> 8 bytes from fd11:2233:4455:0:f:b5e:169b:b875: icmp_seq=2 hlim=64 time=35ms

We now have a Thread network consisting of two nodes, illustrated by this topology diagram:

Commission the FTD Joiner

Now let's add the third Thread device to the "makerdiary" network. This time we're going to use the more secure in-band commissioning process. On the FTD Joiner, scan for the network:

## FTD Joiner ##
----------------
> scan
| J | Network Name     | Extended PAN     | PAN  | MAC Address      | Ch | dBm | LQI |
+---+------------------+------------------+------+------------------+----+-----+-----+
> | 0 | makerdiary       | 1122334455667788 | 1122 | 2203a240810f1598 | 11 | -32 | 244 |
Done

A 0 in the J column indicates that Thread Commissioning is not active on the device.

Let's be specific when commissioning on this next device, and only allow the FTD Joiner to join. Still on the FTD Joiner, get the eui64, so the FTD Commissioner can identify it:

## FTD Joiner ##
----------------
> eui64
31ae3e8e7b87cfd6
Done

On the FTD Commissioner, start the commissioner and specify the eui64 of the device that can join, along with the Joiner Credential. The Joiner Credential is a device-specific passphrase.

## FTD Commissioner ##
----------------------
> commissioner start
Done
> commissioner joiner add 31ae3e8e7b87cfd6 J01NME
Done

Switch to the FTD Joiner, and rescan:

## FTD Joiner ##
----------------
> scan
| J | Network Name     | Extended PAN     | PAN  | MAC Address      | Ch | dBm | LQI |
+---+------------------+------------------+------+------------------+----+-----+-----+
> | 1 | makerdiary       | 1122334455667788 | 1122 | 2203a240810f1598 | 11 | -32 | 244 |
Done

As indicated by the 1 in the J column, Thread Commissioning is now active on the network. Start the joiner role with the Joiner Credential you just set up on the FTD Commissioner:

## FTD Joiner ##
----------------
> ifconfig up
Done
> joiner start J01NME
Done

Within a minute or so, you get a confirmation of a successful authentication:

## FTD Joiner ##
----------------
>Join success

Bring up Thread so the FTD Joiner joins the makerdiary network, and immediately check the state and RLOC16:

## FTD Joiner ##
----------------
> thread start
Done
> state
child
Done
> rloc16
3404
Done

Check the device's IPv6 addresses. Notice that there is no ALOC. That's because this device is not the Leader, nor does it hold an Anycast-specific role that requires an ALOC.

## FTD Joiner ##
----------------
> ipaddr
fd11:2233:4455:0:0:ff:fe00:3404
fe80:0:0:0:7827:55cf:47e4:5907
fd11:2233:4455:0:e839:eb52:f7ec:74e3
Done

Immediately switch to the FTD Commissioner and check the router and child tables to confirm that three devices exist in the makerdiary network:

## FTD Commissioner ##
----------------------
> router table
| ID | RLOC16 | Next Hop | Path Cost | LQ In | LQ Out | Age | Extended MAC     |
+----+--------+----------+-----------+-------+--------+-----+------------------+
| 13 | 0x3400 |       63 |         0 |     0 |      0 |   0 | 2203a240810f1598 |

Done
> child table
| ID  | RLOC16 | Timeout    | Age        | LQ In | C_VN |R|S|D|N| Extended MAC     |
+-----+--------+------------+------------+-------+------+-+-+-+-+------------------+
|   3 | 0x3403 |        240 |        231 |     3 |   86 |1|1|1|1| 62bdff842121344d |
|   4 | 0x3404 |        240 |         49 |     3 |   88 |1|1|1|1| 7a2755cf47e45907 |

Done

Based on the RLOC16, the FTD Joiner has attached to the network as an End Device (child). Here is our updated topology:

Send messages with UDP

One of the application services that OpenThread provides is User Datagram Protocol (UDP), a Transport Layer protocol. An application built on OpenThread could use the UDP API to pass messages between nodes in a Thread network, or to other devices in an external network (like the internet, if the Thread network features a Border Router).

UDP sockets are exposed through the OpenThread CLI. Let's use it to pass messages between the two FTDs.

Get the Mesh-Local EID address for the FTD Joiner. We're using this address because it's reachable from anywhere within the Thread network.

## FTD Joiner ##
----------------
> ipaddr
fd11:2233:4455:0:0:ff:fe00:fc00        # Leader Anycast Locator (ALOC)
fd11:2233:4455:0:0:ff:fe00:f400        # Routing Locator (RLOC)
fe80:0:0:0:7827:55cf:47e4:5907         # Link-Local Address (LLA)
fd11:2233:4455:0:e839:eb52:f7ec:74e3   # Mesh-Local EID (ML-EID)
Done

Start UDP and bind it to a socket for any IPv6 address:

## FTD Joiner ##
----------------
> udp open
Done
> udp bind :: 1212

Switch to the FTD Commissioner, start UDP, and connect to the socket you set up on the FTD Joiner, using its ML-EID:

## FTD Commissioner ##
----------------------
> udp open
Done
> udp connect fd11:2233:4455:0:e839:eb52:f7ec:74e3 1212
Done

The UDP connection should be live between the two nodes. Send a message from the FTD Commissioner:

## FTD Commissioner ##
----------------------
> udp send hello-openthread
Done

On the FTD Joiner, the UDP message has been received!

## FTD Joiner ##
----------------
> 16 bytes from fd11:2233:4455:0:0:ff:fe00:3400 49153 hello-openthread

Congratulations!

You've created a physical Thread network with nRF52840-MDK!

Reference

License

Content on this pages is licensed under the Creative Commons Attribution 3.0 License. Origin works by Jeff Bumgardner at Google.

Back to blog