1. Introduction
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.
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.
Install SEGGER J-Link
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.
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:
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.
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:
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.
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:
ot-daemon
service / logs- RCP Joiner via
ot-ctl
- FTD Commissioner via OpenThread CLI
- 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) |
|
Leave the Screen session | Ctrl+a → |
Create new window within the Screen session | Ctrl+a → |
Switch between windows in the same Screen session | Ctrl+a → |
Kill the current window in the Screen session | Ctrl+a → |
Split Screen
With Screen, you can split the terminal into multiple windows:
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:
- Ctrl+a →
S
to split the window horizontally - Ctrl+a →
Tab
to move the cursor to the new blank window - Ctrl+a →
n
to switch that new window to the next one - 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 → |
Split window vertically | Ctrl+a → |
Split window horizontally | Ctrl+a → |
Jump to the next displayed window | Ctrl+a → |
Switch the displayed window forward or back | Ctrl+a → |
Rename the current window | Ctrl+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:
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:
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:
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.
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
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
.
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
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
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 |
| Link-Local | All FTDs and MEDs |
| Link-Local | All FTDs and Border Routers |
| Mesh-Local | All FTDs and MEDs |
| 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
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.
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.
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.
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
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!
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:
- Supported Platforms — discover all the platforms that support OpenThread
- Build OpenThread — further details on building and configuring OpenThread
- Thread Primer — covers all the Thread concepts featured in this Codelab
Reference: