Build a Thread network with nRF52840 boards and OpenThread

1. Introduction

26b7f4f6b3ea0700.png

OpenThread released by Google is an open-source implementation of the Thread® networking protocol. Google Nest has released OpenThread to make the technology used in Nest products broadly available to developers 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 including IPv6, 6LoWPAN, IEEE 802.15.4 with MAC security, Mesh Link Establishment, and Mesh Routing.

In this Codelab, you'll program OpenThread on real hardware, create and manage a Thread network, and pass messages between nodes.

4806d16a8c137c6d.jpeg

What you'll learn

  • Building and flashing OpenThread CLI binaries to dev boards
  • Building an RCP consisting of a Linux machine and a dev board
  • Communicating with an RCP using OpenThread Daemon and ot-ctl
  • Manually managing Thread nodes with GNU Screen and the OpenThread CLI
  • Secure commissioning of devices onto a Thread network
  • How IPv6 multicast works
  • Passing messages between Thread nodes with UDP

What you'll need

Hardware:

  • 3 Nordic Semiconductor nRF52840 dev boards
  • 3 USB to Micro-USB cables to connect the boards
  • A Linux machine with at least 3 USB ports

Software:

  • GNU Toolchain
  • Nordic nRF5x command line tools
  • Segger J-Link software
  • OpenThread
  • Git

2. Getting started

OpenThread Simulation

Before beginning, you might want to run through the OpenThread Simulation Codelab, to get familiar with basic Thread concepts and the OpenThread CLI.

Serial port terminals

You should be familiar with how to connect to a serial port through a terminal. This Codelab uses GNU Screen and provides a usage overview, but any other terminal software can be used.

Linux machine

This Codelab was designed to use an i386- or x86-based Linux machine to serve as the host to a Radio Co-Processor (RCP) Thread device, and to flash all Thread development boards. All steps were tested on Ubuntu 14.04.5 LTS (Trusty Tahr).

Nordic Semiconductor nRF52840 boards

This Codelab uses three nRF52840 PDK boards.

a6693da3ce213856.png

We use SEGGER J-Link to program the nRF52840 boards, which have onboard JTAG modules. Install this on your Linux machine.

Download the appropriate package for your machine, and install it in the proper location. On Linux this is /opt/SEGGER/JLink.

Install nRF5x Command Line Tools

The nRF5x Command Line Tools allow you to flash the OpenThread binaries to the nRF52840 boards. Install the appropriate nRF5x-Command-Line-Tools-<OS> build on your Linux machine.

Place the extracted package in the root folder ~/

Install ARM GNU Toolchain

The ARM GNU Toolchain is used for building.

We recommend placing the extracted archive in /opt/gnu-mcu-eclipse/arm-none-eabi-gcc/ on your Linux machine. Follow the instructions in the archive's readme.txt file for installation instructions.

Install Screen (optional)

Screen is a simple tool for accessing devices connected by a serial port. This Codelab uses Screen, but you may use any serial port terminal application you wish.

$ sudo apt-get install screen

3. Clone repositories

OpenThread

Clone and install OpenThread. The script/bootstrap commands make sure the toolchain is installed and the environment is properly configured:

$ mkdir -p ~/src
$ cd ~/src
$ git clone --recursive https://github.com/openthread/openthread.git
$ cd openthread
$ ./script/bootstrap

Build OpenThread Daemon:

$ script/cmake-build posix -DOT_DAEMON=ON

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

4. Set up the RCP Joiner

Build and flash

Build the OpenThread nRF52840 example with Joiner and native USB functionality. A device uses the Joiner role to be securely authenticated and commissioned onto a Thread network. Native USB enables the use of USB CDC ACM as a serial transport between the nRF52840 and the host.

Always clean the repo of previous builds first by running rm -rf build.

$ cd ~/src
$ git clone --recursive https://github.com/openthread/ot-nrf528xx.git
$ cd ot-nrf528xx
$ script/build nrf52840 USB_trans

Navigate to the directory with the OpenThread RCP binary, and convert it to hex format:

$ cd ~/src/ot-nrf528xx/build/bin
$ arm-none-eabi-objcopy -O ihex ot-rcp ot-rcp.hex

Attach the USB cable to the Micro-USB debug port next to the external power pin on the nRF52840 board, and then plug it into the Linux machine. Set the nRF power source switch on the nRF52840 board to VDD. When connected correctly, LED5 is on.

20a3b4b480356447.png

If this is the first board attached to the Linux machine, it appears as serial port /dev/ttyACM0 (all nRF52840 boards use ttyACM for the serial port identifier).

$ ls /dev/ttyACM*
/dev/ttyACM0

Note the serial number of the nRF52840 board being used for the RCP:

c00d519ebec7e5f0.jpeg

Navigate to the location of the nRFx Command Line Tools, and flash the OpenThread RCP hex file onto the nRF52840 board, using the board's serial number. Note that if you leave out the --verify flag, you'll see a warning message telling you that the flash process can fail without error.

$ cd ~/nrfjprog/
$ ./nrfjprog -f nrf52 -s 683704924  --verify --chiperase --program \
       ~/src/ot-nrf528xx/build/bin/ot-rcp.hex --reset

The following output is generated upon success:

Parsing hex file.
Erasing user available code and UICR flash areas.
Applying system reset.
Checking that the area to write is not protected.
Programing device.
Applying system reset.
Run.

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

Connect to native USB

Because the OpenThread RCP build enables use of native USB CDC ACM as a serial transport, you must use the nRF USB port on the nRF52840 board to communicate with the RCP host (Linux machine).

Detach the Micro-USB end of the USB cable from the debug port of the flashed nRF52840 board, then reattach it to the Micro-USB nRF USB port next to the RESET button. Set the nRF power source switch to USB.

46e7b670d2464842.png

Start OpenThread Daemon

In the RCP design, use OpenThread Daemon to communicate with and manage the Thread device. Start ot-daemon with the -v verbose flag so you can see log output and confirm that it is running:

$ cd ~/src/openthread
$ sudo ./build/posix/src/posix/ot-daemon -v \
    'spinel+hdlc+uart:///dev/ttyACM0?uart-baudrate=115200'

When successful, ot-daemon in verbose mode generates output similar to the following:

ot-daemon[12463]: Running OPENTHREAD/thread-reference-20200818-1938-g0f10480ed; POSIX; Aug 30 2022 10:55:05
ot-daemon[12463]: Thread version: 4
ot-daemon[12463]: Thread interface: wpan0
ot-daemon[12463]: RCP version: OPENTHREAD/thread-reference-20200818-1938-g0f10480ed; SIMULATION; Aug 30 2022 10:54:10

Leave this terminal window open so that logs from ot-daemon can be viewed.

Use ot-ctl to communicate with the RCP node. ot-ctl uses the same CLI as the OpenThread CLI app. Therefore, you can control ot-daemon nodes in the same manner as the other simulated Thread devices.

In a second terminal window, start ot-ctl:

$ sudo ./build/posix/src/posix/ot-ctl
>

Check the state of Node 2 (the RCP node) you started with ot-daemon:

> state
disabled
Done

5. Set up the FTDs

The other two Thread nodes used in this Codelab are Full Thread Devices (FTDs) on the standard System-on-Chip (SoC) design. In a Production setting, one might use wpantund, a production-grade network interface driver, to control OpenThread NCP instances, but in this codelab, we'll use ot-ctl, 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 platform, with the Commissioner and Joiner roles enabled:

$ cd ~/src/ot-nrf528xx
$ rm -rf build
$ script/build nrf52840 USB_trans -DOT_JOINER=ON -DOT_COMMISSIONER=ON

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

$ cd ~/src/ot-nrf528xx/build/bin
$ arm-none-eabi-objcopy -O ihex ot-cli-ftd ot-cli-ftd.hex

Attach the USB cable to the Micro-USB port next to the external power pin on the nRF52840 board, and then plug it into the Linux machine. If the RCP is still attached to the Linux machine, this new board should appear as serial port /dev/ttyACM1 (all nRF52840 boards use ttyACM for the serial port identifier).

$ ls /dev/ttyACM*
/dev/ttyACM0  /dev/ttyACM1

As before, note the serial number of the nRF52840 board being used for the FTD:

c00d519ebec7e5f0.jpeg

Navigate to the location of the nRFx Command Line Tools, and flash the OpenThread CLI FTD hex file onto the nRF52840 board, using the board's serial number:

$ cd ~/nrfjprog/
$ ./nrfjprog -f nrf52 -s 683704924 --verify --chiperase --program \
       ~/src/ot-nrf528xx/build/bin/ot-cli-ftd.hex --reset

Label the board "Commissioner."

Connect to native USB

Because the OpenThread FTD build enables use of native USB CDC ACM as a serial transport, you must use the nRF USB port on the nRF52840 board to communicate with the RCP host (Linux machine).

Detach the Micro-USB end of the USB cable from the debug port of the flashed nRF52840 board, then reattach it to the Micro-USB nRF USB port next to the RESET button. Set the nRF power source switch to USB.

46e7b670d2464842.png

Verify build

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

$ screen /dev/ttyACM1 115200

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

> ifconfig up
Done
> ipaddr
fe80:0:0:0:1cd6:87a9:cb9d:4b1d
Done

Use Ctrl+a →

d to detach from the FTD Commissioner CLI screen and return to the Linux terminal so that the next board can be flashed. To re-enter the CLI at any time, use screen -r from the command line. To see a list of available screens, use screen -ls:

$ screen -ls
There is a screen on:
        74182.ttys000.mylinuxmachine        (Detached)
1 Socket in /tmp/uscreens/S-username.

Set up the FTD Joiner

Repeat the above process to flash the third nRF52840 board, using the existing ot-cli-ftd.hex build. When done, be sure to reconnect the board to the PC using the nRF USB port and set the nRF power source switch to VDD.

If the other two nodes are attached to the Linux machine when this third board is attached, it should appear as serial port /dev/ttyACM2:

$ ls /dev/ttyACM*
/dev/ttyACM0  /dev/ttyACM1  /dev/ttyACM2

Label the board "Joiner."

When verifying using Screen, instead of creating a new instance of Screen from the command line, reattach to the existing one and make a new window within it (that you used for the FTD Commissioner):

$ screen -r

Create the new window within Screen with Ctrl+a → c.

A new command line prompt appears. Access the OpenThread CLI for the FTD Joiner:

$ screen /dev/ttyACM2 115200

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

> ifconfig up
Done
> ipaddr
fe80:0:0:0:6c1e:87a2:df05:c240
Done

Now that the FTD Joiner CLI is in the same instance of Screen as the FTD Commissioner, you can switch between them using Ctrl+a → n.

Use Ctrl+a →

d at any time to exit Screen.

6. Terminal window setup

Going forward, you will be switching between Thread devices frequently, so make sure all of them are live and easily accessible. So far, we have been using Screen to access the two FTDs, and this tool also allows split screen on the same terminal window. Use this to see how one node reacts to commands issued on another.

Ideally, you should have four windows readily available:

  1. ot-daemon service / logs
  2. RCP Joiner via ot-ctl
  3. FTD Commissioner via OpenThread CLI
  4. FTD Joiner via OpenThread CLI

If you wish to use your own terminal / serial port configuration or tool, feel free to skip to the next step. Configure the terminal windows for all devices in the way that works best for you.

Using Screen

For ease of use, only start one Screen session. You should already have one from when you set up both FTDs.

All commands within Screen start with Ctrl+a.

Basic Screen commands:

Reattach to the Screen session (from the command line)

screen -r

Leave the Screen session

Ctrl+a → d

Create new window within the Screen session

Ctrl+a → c

Switch between windows in the same Screen session

Ctrl+a → n (forward)Ctrl+a → p (back)

Kill the current window in the Screen session

Ctrl+a → k

Split Screen

With Screen, you can split the terminal into multiple windows:

f1cbf1258cf0a5a.png

Commands in screen are accessed by using Ctrl+a. Every command should start with this access key combo.

If you've been following the Codelab exactly, you should have two windows (FTD Commissioner, FTD Joiner) on the same Screen instance. To split the screen between the two, first enter your existing Screen session:

$ screen -r

You should be on one of the FTD devices. Follow these steps in Screen:

  1. Ctrl+a → S to split the window horizontally
  2. Ctrl+a → Tab to move the cursor to the new blank window
  3. Ctrl+a → n to switch that new window to the next one
  4. If it's the same as the top window, Ctrl+a → n again to view the other FTD device

They are now both visible. Switch between them using Ctrl+a → Tab. It's recommend you retitle each window with Ctrl+a → A to avoid confusion.

Advanced use

To further split the screen into quadrants and view the ot-daemon logs and the RCP Joiner ot-ctl, those services must be started within this same Screen instance. To do so, stop ot-daemon and exit ot-ctl, and restart them within new Screen windows (Ctrl+a → c).

This setup is not required and is left as an exercise for the user.

Split and navigate between windows with the following commands:

Create new window

Ctrl+a → c

Split window vertically

Ctrl+a →

Split window horizontally

Ctrl+a → S

Jump to the next displayed window

Ctrl+a → Tab

Switch the displayed window forward or back

Ctrl+a → n or p

Rename the current window

Ctrl+a → A

Leave Screen at any time with Ctrl+a → d and reattach with screen -r from the command line.

For more information on Screen, see the GNU Screen quick reference.

7. 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, create a new Operational Dataset and commit it as the active one. The Operational Dataset is the configuration for the Thread network you are creating.

## FTD Commissioner ##
----------------------

> dataset init new
Done
> dataset
Active Timestamp: 1
Channel: 11
Channel Mask: 07fff800
Ext PAN ID: c0de7ab5c0de7ab5
Mesh Local Prefix: fdc0:de7a:b5c0/64
Network Key: 1234c0de7ab51234c0de7ab51234c0de
Network Name: OpenThread-c0de
PAN ID: 0xc0de
PSKc: ebb4f2f8a68026fc55bcf3d7be3e6fe4
Security Policy: 0, onrcb
Done

Make note of the Network Key 1234c0de7ab51234c0de7ab51234c0de which will be used later.

Commit this dataset as the active one:

> dataset commit active
Done

Bring up the IPv6 interface:

> ifconfig up
Done

Start Thread protocol operation:

> 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
0c00
Done

Check the device's IPv6 addresses:

## FTD Commissioner ##
----------------------

> ipaddr
fdc0:de7a:b5c0:0:0:ff:fe00:fc00        # Leader Anycast Locator (ALOC)
fdc0:de7a:b5c0:0:0:ff:fe00:c00         # Routing Locator (RLOC)
fdc0:de7a:b5c0:0:6394:5a75:a1ad:e5a    # Mesh-Local EID (ML-EID)
fe80:0:0:0:1cd6:87a9:cb9d:4b1d         # Link-Local Address (LLA)

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

From ot-ctl on the RCP Joiner:

## RCP Joiner ##
----------------

> scan
| PAN  | MAC Address      | Ch | dBm | LQI |
+------+------------------+----+-----+-----+
| c0de | 1ed687a9cb9d4b1d | 11 | -36 | 232 |

From the OpenThread CLI on the FTD Joiner:

## FTD Joiner ##
----------------

> scan
| PAN  | MAC Address      | Ch | dBm | LQI |
+------+------------------+----+-----+-----+
| c0de | 1ed687a9cb9d4b1d | 11 | -38 | 229 |

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

8. Add the RCP Joiner

Thread Commissioning is not active on the network, which means that we'll need to add the RCP Joiner to the Thread network we just created using an out-of-band commissioning process.

On the FTD Commissioner, we made a note of the Network Key, for example 1234c0de7ab51234c0de7ab51234c0de. If you need to lookup the Network Key again, run the following command on the FTD Commissioner:

## FTD Commissioner ##

> dataset networkkey
1234c0de7ab51234c0de7ab51234c0de
Done

Next, on the RCP Joiner, set its active dataset Network Key to the FTD Commissioner Network Key:

## RCP Joiner ##
----------------

> dataset networkkey 1234c0de7ab51234c0de7ab51234c0de
Done
> dataset commit active
Done

Check the dataset to ensure that it's set correctly.

## RCP Joiner ##
----------------

> dataset
Network Key: 1234c0de7ab51234c0de7ab51234c0de

Bring up Thread so the RCP Joiner joins the "codelab" network. Wait a few seconds, check the state, RLOC16 and its IPv6 addresses:

## RCP Joiner ##
----------------

> ifconfig up
Done
> thread start
Done
> state
child
Done
> rloc16
0c01
Done
> ipaddr
fdc0:de7a:b5c0:0:0:ff:fe00:0c01         # Routing Locator (RLOC)
fdc0:de7a:b5c0:0:66bf:99b9:24c0:d55f    # Mesh-Local EID (ML-EID)
fe80:0:0:0:18e5:29b3:a638:943b          # Link-Local Address (LLA)
Done

Make note of the Mesh-Local IPv6 Address (fdc0:de7a:b5c0:0:66bf:99b9:24c0:d55f here), you'll use it later.

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 RCP Joiner.

## FTD Commissioner ##
----------------------

> router table
| ID | RLOC16 | Next Hop | Path Cost | LQ In | LQ Out | Age | Extended MAC     |
+----+--------+----------+-----------+-------+--------+-----+------------------+
|  3 | 0x0c00 |        3 |         0 |     0 |      0 |  35 | 1ed687a9cb9d4b1d |

Done
> child table
| ID  | RLOC16 | Timeout    | Age        | LQ In | C_VN |R|S|D|VER| Extended MAC     |
+-----+--------+------------+------------+-------+------+-+-+-+---+------------------+
|   1 | 0x0c01 |        240 |         25 |     3 |   89 |1|1|1|  2| 1ae529b3a638943b |
Done

Ping the mesh-local address of the RCP Joiner (the Mesh-Local address obtained from the RCP Joiner's ipaddr output) to verify connectivity:

## FTD Commissioner ##
----------------------

> ping fdc0:de7a:b5c0:0:66bf:99b9:24c0:d55f
> 8 bytes from fdc0:de7a:b5c0:0:66bf:99b9:24c0:d55f: icmp_seq=1 hlim=64 time=40ms

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

otcodelab_top01C_2nodes.png

Topology diagrams

As you work through the rest of the Codelab, we'll show a new Thread topology diagram whenever the state of the network changes. Node roles are denoted as follows:

b75a527be4563215.png

Routers are always pentagons, and End Devices are always circles. The numbers on each node represent the Router ID or Child ID shown in the CLI output, depending on each node's current role and state at that time.

9. Commission the FTD Joiner

Now let's add the third Thread device to the "codelab" network. This time we're going to use the more secure in-band commissioning process, and only allow the FTD Joiner to join.

On the FTD Joiner, get the eui64, so the FTD Commissioner can identify it:

## FTD Joiner ##
----------------

> eui64
2f57d222545271f1
Done

On the FTD Commissioner, start the commissioner and specify the eui64 of the device that can join, along with the Joiner Credential, for example J01NME. The Joiner Credential is a device-specific string of all uppercase alphanumeric characters (0-9 and A-Y, excluding I, O, Q and Z for readability), with a length between 6 and 32 characters.

## FTD Commissioner ##
----------------------

> commissioner start
Done
> commissioner joiner add 2f57d222545271f1 J01NME
Done

Switch to the FTD Joiner. Start the joiner role with the Joiner Credential that 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 "codelab" network, and immediately check the state and RLOC16:

## FTD Joiner ##
----------------

> thread start
Done
> state
child
Done
> rloc16
0c02
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
fdc0:de7a:b5c0:0:0:ff:fe00:c02         # Routing Locator (RLOC)
fdc0:de7a:b5c0:0:3e2e:66e:9d41:ebcd    # Mesh-Local EID (ML-EID)
fe80:0:0:0:e4cd:d2d9:3249:a243         # Link-Local Address (LLA)

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

## FTD Commissioner ##
----------------------

> router table
| ID | RLOC16 | Next Hop | Path Cost | LQ In | LQ Out | Age | Extended MAC     |
+----+--------+----------+-----------+-------+--------+-----+------------------+
|  3 | 0x0c00 |        3 |         0 |     0 |      0 |  50 | 1ed687a9cb9d4b1d |

> child table
| ID  | RLOC16 | Timeout    | Age        | LQ In | C_VN |R|S|D|N| Extended MAC     |
+-----+--------+------------+------------+-------+------+-+-+-+-+------------------+
|   1 | 0x0c01 |        240 |         25 |     3 |   89 |1|1|1|1| 1ae529b3a638943b |
|   2 | 0x0c02 |        240 |         15 |     3 |   44 |1|1|1|1| e6cdd2d93249a243 |
Done

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

otcodelab_top01C_ed01.png

10. Thread in action

The Thread devices in this Codelab are a specific kind of Full Thread Device (FTD) called a Router Eligible End Device (REED). This means they can function as either a Router or End Device, and can promote themselves from an End Device to a Router.

Thread can support up to 32 Routers, but tries to keep the number of Routers between 16 and 23. If a REED attaches as an End Device (child) and the number of Routers is below 16, after a random time period within two minutes it automatically promotes itself to a Router.

If you had two children in your Thread network after adding the FTD Joiner, wait at least two minutes, and then recheck the router and child tables on the FTD Commissioner:

## FTD Commissioner ##
----------------------

> router table
| ID | RLOC16 | Next Hop | Path Cost | LQ In | LQ Out | Age | Extended MAC     |
+----+--------+----------+-----------+-------+--------+-----+------------------+
|  3 | 0x0c00 |        3 |         0 |     0 |      0 |  50 | 1ed687a9cb9d4b1d |
| 46 | 0xb800 |       63 |         0 |     3 |      3 |   1 | e6cdd2d93249a243 |

> child table
| ID  | RLOC16 | Timeout    | Age        | LQ In | C_VN |R|S|D|N| Extended MAC     |
+-----+--------+------------+------------+-------+------+-+-+-+-+------------------+
|   1 | 0x0c01 |        240 |         61 |     3 |   89 |1|1|1|1| 1ae529b3a638943b |
Done

The FTD Joiner (Extended MAC = e6cdd2d93249a243) has promoted itself to a Router. Note that the RLOC16 is different (b800 instead of 0c02). That's because the RLOC16 is based on the Router ID and Child ID of a device. When it transitions from End Device to Router, its Router ID and Child ID values change, and so does the RLOC16.

otcodelab_top01C.png

Confirm the new state and RLOC16 on the FTD Joiner:

## FTD Joiner ##
----------------

> state
router
Done
> rloc16
b800
Done

Downgrade the FTD Joiner

You can test this behavior by manually downgrading the FTD Joiner from a Router back to an End Device. Change the state to child and check the RLOC16:

## FTD Joiner ##
----------------

> state child
Done
> rloc16
0c03
Done

otcodelab_top01C_ed02.png

Back on the FTD Commissioner, the FTD Joiner should now appear in the child table (ID = 3). It may even be in both while it transitions:

## FTD Commissioner ##
----------------------

> router table
| ID | RLOC16 | Next Hop | Path Cost | LQ In | LQ Out | Age | Extended MAC     |
+----+--------+----------+-----------+-------+--------+-----+------------------+
|  3 | 0x0c00 |        3 |         0 |     0 |      0 |  50 | 1ed687a9cb9d4b1d |
| 46 | 0xb800 |       63 |         0 |     3 |      3 |   1 | e6cdd2d93249a243 |

> child table
| ID  | RLOC16 | Timeout    | Age        | LQ In | C_VN |R|S|D|N| Extended MAC     |
+-----+--------+------------+------------+-------+------+-+-+-+-+------------------+
|   1 | 0x0c01 |        240 |         61 |     3 |   89 |1|1|1|1| 1ae529b3a638943b |
|   3 | 0x0c03 |        240 |         16 |     3 |   94 |1|1|1|1| e6cdd2d93249a243 |
Done

After some time, it will switch back to a Router with an RLOC of b800.

otcodelab_top01C.png

Remove the Leader

The Leader is self-elected among all Thread Routers. This means if the current Leader is removed from the Thread network, one of the other Routers will become the new Leader.

On the FTD Commissioner, shut down Thread to remove it from the Thread network:

## FTD Commissioner ##
----------------------

> thread stop
Done
> ifconfig down
Done

Within two minutes, the FTD Joiner becomes the new Thread leader. Check the state and IPv6 addresses of the FTD Joiner to verify:

## FTD Joiner ##
----------------

> state
leader
Done
> ipaddr
fdc0:de7a:b5c0:0:0:ff:fe00:fc00       # Now it has the Leader ALOC!
fdc0:de7a:b5c0:0:0:ff:fe00:b800
fdc0:de7a:b5c0:0:3e2e:66e:9d41:ebcd
fe80:0:0:0:e4cd:d2d9:3249:a243
Done

otcodelab_top02C_01.png

Check the child table. Notice that there's a new RLOC16. This is the RCP Joiner, as indicated by its ID and Extended MAC. In order to keep the Thread network together, it has switched parent Routers, from the FTD Commissioner to the FTD Joiner. This results in a new RLOC16 for the RCP Joiner (because its Router ID changed, from 3 to 46).

## FTD Joiner ##
----------------

> child table
| ID  | RLOC16 | Timeout    | Age        | LQ In | C_VN |R|S|D|N| Extended MAC     |
+-----+--------+------------+------------+-------+------+-+-+-+-+------------------+
|   1 | 0xb801 |        240 |         27 |     3 |  145 |1|1|1|1| 1ae529b3a638943b |
Done

You may have to wait a few minutes for the RCP Joiner to attach to the FTD Joiner as a child. Check the state and the RLOC16 to confirm that:

## RCP Joiner ##
--------------

> state
child
> rloc16
b801

Reattach the FTD Commissioner

A Thread network with two nodes isn't much fun. Let's bring the FTD Commissioner back online.

On the FTD Commissioner, restart Thread:

## FTD Commissioner ##
----------------------

> ifconfig up
Done
> thread start
Done

Within two minutes, it automatically reattaches to the "codelab" network as an End Device, and then promotes itself to a Router.

## FTD Commissioner ##
----------------------

> state
router
Done

Check the router and child tables on the FTD Joiner to verify:

## FTD Joiner ##
----------------

> router table
| ID | RLOC16 | Next Hop | Path Cost | LQ In | LQ Out | Age | Extended MAC     |
+----+--------+----------+-----------+-------+--------+-----+------------------+
|  3 | 0x0c00 |       63 |         0 |     3 |      3 |   0 | 1ed687a9cb9d4b1d |
| 46 | 0xb800 |       46 |         0 |     0 |      0 |  15 | e6cdd2d93249a243 |

> child table
| ID  | RLOC16 | Timeout    | Age        | LQ In | C_VN |R|S|D|N| Extended MAC     |
+-----+--------+------------+------------+-------+------+-+-+-+-+------------------+
|   1 | 0xb801 |        240 |        184 |     3 |  145 |1|1|1|1| 1ae529b3a638943b |
Done

otcodelab_top02C_02.png

Our Thread network consists of three nodes again.

11. Troubleshooting

Managing a Thread network with multiple devices on different terminal or Screen windows can be complicated. Use these tips to "reset" the state of the network or your workspace if you encounter issues.

Screen

If you ever get lost in your configuration (too many Screen windows, or Screens within Screen), keep killing Screen windows with Ctrl+a → k until none exist and screen -ls on the command line outputs No Sockets found. Then recreate Screen windows for each device. Device states are retained even when Screen is killed.

Thread nodes

If the Thread network topology is not as described in this Codelab, or nodes disconnect for some reason (perhaps because the Linux machine powering them went to sleep), it's best to bring down Thread, clear the network credentials, and begin again from the Create the Thread network step.

To reset the FTDs:

## FTD Commissioner or FTD Joiner ##
------------------------------------

> thread stop
Done
> ifconfig down
Done
> factoryreset
Done

The RCP can be reset in the same way via ot-ctl:

## RCP Joiner ##
----------------

> thread stop
Done
> ifconfig down
Done
> factoryreset
Done

12. Using multicast

Multicast is used to communicate information to a group of devices at once. In a Thread network, specific addresses are reserved for multicast use with different groups of devices, depending on the scope.

IPv6 Address

Scope

Delivered to

ff02::1

Link-Local

All FTDs and MEDs

ff02::2

Link-Local

All FTDs and Border Routers

ff03::1

Mesh-Local

All FTDs and MEDs

ff03::2

Mesh-Local

All FTDs and Border Routers

Since we're not using a Border Router in this Codelab, let's focus on the two FTD and MED multicast addresses.

Link-Local scope comprises all Thread interfaces reachable by a single radio transmission, or a single "hop." The network topology dictates which devices respond to a ping to the ff02::1 multicast address.

Ping ff02::1 from the FTD Commissioner:

## FTD Commissioner ##
----------------------

> ping ff02::1
> 8 bytes from fe80:0:0:0:e4cd:d2d9:3249:a243: icmp_seq=2 hlim=64 time=9ms

There are two other devices in the network (FTD Joiner and RCP Joiner), but the FTD Commissioner only received one response, from the FTD Joiner's Link-Local Address (LLA). This means that the FTD Joiner is the only device that the FTD Commissioner can reach with a single hop.

otcodelab_top02C_02_LL.png

Now ping ff02::1 from the FTD Joiner:

## FTD Joiner ##
----------------

> ping ff02::1
> 8 bytes from fe80:0:0:0:1cd6:87a9:cb9d:4b1d: icmp_seq=1 hlim=64 time=11ms
8 bytes from fe80:0:0:0:18e5:29b3:a638:943b: icmp_seq=1 hlim=64 time=24ms

Two responses! Checking the IPv6 addresses for the other devices, we can see the first one (ending in 4b1d) is the FTD Commissioner's LLA, and the second one (ending in 943b) is the RCP Joiner's LLA.

otcodelab_top02C_02_LL02.png

This means the FTD Joiner is directly connected to both the FTD Commissioner and the RCP Joiner, which confirms our topology.

Mesh-Local

Mesh-Local scope comprises all Thread interfaces reachable within the same Thread network. Let's see the responses to a ping to the ff03::1 multicast address.

Ping ff03::1 from the FTD Commissioner:

## FTD Commissioner ##
----------------------

> ping ff03::1
> 8 bytes from fdc0:de7a:b5c0:0:0:ff:fe00:b800: icmp_seq=3 hlim=64 time=9ms
8 bytes from fdc0:de7a:b5c0:0:66bf:99b9:24c0:d55f: icmp_seq=3 hlim=64 time=68ms

This time the FTD Commissioner received two responses, one from the FTD Joiner's Routing Locator (RLOC, ending in b800) and one from the RCP Joiner's Mesh-Local EID (ML-EID, ending in d55f). That's because mesh-local scope comprises the entire Thread network. No matter where in the network a device is, it will be subscribed to the ff03::1 address.

otcodelab_top02C_02_ML.png

Ping ff03::1 from the FTD Joiner to confirm the same behavior:

## FTD Joiner ##
----------------

> ping ff03::1
> 8 bytes from fdc0:de7a:b5c0:0:0:ff:fe00:c00: icmp_seq=2 hlim=64 time=11ms
8 bytes from fdc0:de7a:b5c0:0:66bf:99b9:24c0:d55f: icmp_seq=2 hlim=64 time=23ms

otcodelab_top02C_02_LL02.png

Note the response time for the RCP Joiner in both ping outputs. The RCP Joiner took much longer to reach the FTD Commissioner (68ms) than it did to reach the FTD Joiner (23ms). That's because it has to make two hops to reach the FTD Commissioner, compared to one hop for the FTD Joiner.

You may have also noticed that the mesh-local multicast ping responded with the RLOC only for the two FTDs—not the RCP Joiner. This is because the FTDs are Routers within the network, while the RCP is an End Device.

Check the state of the RCP Joiner to confirm:

## RCP Joiner ##
----------------

> state
child

13. 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
fdc0:de7a:b5c0:0:0:ff:fe00:fc00        # Leader Anycast Locator (ALOC)
fdc0:de7a:b5c0:0:0:ff:fe00:b800        # Routing Locator (RLOC)
fe80:0:0:0:e4cd:d2d9:3249:a243         # Link-Local Address (LLA)
fdc0:de7a:b5c0:0:3e2e:66e:9d41:ebcd    # 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 fdc0:de7a:b5c0:0:3e2e:66e:9d41:ebcd 1212
Done

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

## FTD Commissioner ##
----------------------

> udp send hellothere
Done

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

## FTD Joiner ##
----------------

> 10 bytes from fdc0:de7a:b5c0:0:0:ff:fe00:c00 49153 hellothere

14. Congratulations!

You've created a physical Thread network!

b915c433e7027cc7.png

You now know:

  • the difference between Thread device types, roles, and scopes
  • how Thread devices manage their states within the network
  • how to pass simple messages between nodes using UDP

Next steps

Building off of this Codelab, try the following exercises:

  • Reflash the FTD Joiner board as an MTD using the ot-cli-mtd binary, and observe that it never upgrades itself to a Router or tries to become the Leader
  • Add more devices (try a different platform!) to the network and sketch out the topology by using router and child tables, along with pings to the multicast addresses
  • Use pyspinel to control the NCP
  • Convert the NCP into a Border Router using OpenThread Border Router and connect your Thread network to the internet

Further reading

Check out openthread.io and GitHub for a variety of OpenThread resources, including:

Reference: