OpenThread released by Nest is an open-source implementation of the Thread® networking protocol. 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

What you'll need

Hardware:

Software:

Except as otherwise noted, the content of this Codelab is licensed under the Creative Commons Attribution 3.0 License, and code samples are licensed under the Apache 2.0 License.

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 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 Network Co-Processor (NCP) 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 J-Link Software and Documentation Pack

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.

Download nRF5x Command Line Tools

Place the extracted package in the root folder ~/

Install required packages

The following packages are needed to build OpenThread and wpantund on your Linux machine.

Install dependencies:

$ sudo apt-get install -y software-properties-common

Install packages needed for building and runtime:

$ sudo apt-get install -y gcc gawk g++ libdbus-1-dev libboost-dev libreadline-dev
$ sudo apt-get install -y gcc-arm-none-eabi
$ sudo apt-get install -y libtool autoconf autoconf-archive
$ sudo apt-get install -y lib32z1 lib32ncurses5 lib32bz2-1.0 ia32-libs

Install ARM GNU Toolchain

The ARM GNU Toolchain is used for building.

Download ARM GNU Toolchain portable archive

Follow the instructions at https://gnu-mcu-eclipse.github.io/toolchain/arm/install/#gnulinux to install the portable archive.

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

The OpenThread NCP design uses wpantund and the Spinel protocol on the Linux machine to communicate with OpenThread.

wpantund

Clone and install wpantund:

$ cd ~
$ git clone --recursive https://github.com/openthread/wpantund.git
$ cd wpantund
$ ./bootstrap.sh
$ ./configure --sysconfdir=/etc
$ make
$ sudo make install

Note that the make commands may take several minutes to complete.

Reboot your system:

$ sudo reboot

OpenThread

Clone and install OpenThread:

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

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

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 make clean.

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

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

$ cd ~/openthread/output/nrf52840/bin
$ arm-none-eabi-objcopy -O ihex ot-ncp-ftd ot-ncp-ftd.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 NCP:

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

$ cd ~/nrfjprog/
$ ./nrfjprog -f nrf52 -s 683704924 --chiperase --program \
       ~/openthread/output/nrf52840/bin/ot-ncp-ftd.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 "NCP" so that later you don't confuse the board roles.

Connect to native USB

Because the OpenThread NCP 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 NCP 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.

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/ttyACM0  \
        -o Config:TUN:InterfaceName utun7 \
        -o Daemon:SyslogMask " -info"

Output similar to the following is generated upon success:

wpantund[14590]: Starting wpantund 0.08.00d (Oct  3 2017 17:55:05) . . .
wpantund[14590]:         SOURCE_VERSION = 0.07.01-124-g038e8b0
wpantund[14590]:         BUILD_VERSION = 0.07.01-153-g6752c40
wpantund[14590]: Configuration file "/etc/wpantund.conf" read.
wpantund[14590]: Ready. Using DBUS bus ":1.15"
wpantund[14590]: Running as root without dropping privileges!
wpantund[14590]: State change: "uninitialized" -> "offline"
wpantund[14590]: NCP is running "OPENTHREAD/g4ccc23a5-dirty; NRF52840; Sep 22 2017 10:44:07"
wpantund[14590]: Driver is running "0.08.00d (0.07.01-124-g038e8b0/0.07.01-153-g6752c40; Oct  3 2017 17:55:05)"
wpantund[14590]: Network is not joinable
wpantund[14590]: Resetting interface(s). . .
wpantund[14590]: 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:

wpanctl:utun7> status
utun7 => [
        "NCP:State" => "offline"
        "Daemon:Enabled" => true
        "NCP:Version" => "OPENTHREAD/g4ccc23a5-dirty; NRF52840; Sep 22 2017 10:44:07"
        "Daemon:Version" => "0.08.00d (0.07.01-124-g038e8b0/0.07.01-153-g6752c40; Oct  3 2017 17:55:05)"
        "Config:NCP:DriverName" => "spinel"
        "NCP:HardwareAddress" => [F872DF9CD5AE4DCE]
]

The other two Thread nodes used in this Codelab 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 platform, 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

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 NCP 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 --chiperase --program \
       ~/openthread/output/nrf52840/bin/ot-cli-ftd.hex --reset

Label the board "Commissioner."

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. Check for IPv6 addresses:

> ipaddr
fdde:ad00:beef:0:6394:5a75:a1ad:e5a
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.

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. Check for IPv6 addresses:

> ipaddr
fdde:ad00:beef:0:3e2e:66e:9d41:ebcd
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.

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. wpantund service / logs
  2. NCP Joiner via wpanctl
  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:

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 wpantund logs and the NCP Joiner wpanctl, those services must be started within this same Screen instance. To do so, stop wpantund and exit wpanctl, 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.

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 codelab
Done
> extpanid C0DE7AB5C0DE7AB5
Done
> panid 0xC0DE
Done
> masterkey 1234C0DE7AB51234C0DE7AB51234C0DE
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
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)
fe80:0:0:0:1cd6:87a9:cb9d:4b1d         # Link-Local Address (LLA)
fdc0:de7a:b5c0:0:6394:5a75:a1ad:e5a    # Mesh-Local EID (ML-EID)

The "codelab" 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 | "codelab"          | 0xC0DE | 11 | C0DE7AB5C0DE7AB5 | 1ED687A9CB9D4B1D |  -20

From the OpenThread CLI on the FTD Joiner:

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

> scan
| J | Network Name     | Extended PAN     | PAN  | MAC Address      | Ch | dBm | LQI |
+---+------------------+------------------+------+------------------+----+-----+-----+
| 0 | codelab          | c0de7ab5c0de7ab5 | c0de | 1ed687a9cb9d4b1d | 11 | -38 |  56 |

If the "codelab" 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.

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 | "codelab"          | 0xC0DE | 11 | C0DE7AB5C0DE7AB5 | F65AE2853FF0C4E4 |  -32

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 1234C0DE7AB51234C0DE7AB51234C0DE
wpanctl:utun7> join 1
Joining "codelab" C0DE7AB5C0DE7AB5 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/g4ccc23a5-dirty; NRF52840; Sep 22 2017 10:44:07"
        "Daemon:Version" => "0.08.00d (0.07.01-124-g038e8b0/0.07.01-153-g6752c40; Oct  3 2017 17:55:05)"
        "Config:NCP:DriverName" => "spinel"
        "NCP:HardwareAddress" => [F872DF9CD5AE4DCE]
        "NCP:Channel" => 11
        "Network:NodeType" => "end-device"
        "Network:Name" => "codelab"
        "Network:XPANID" => 0xC0DE7AB5C0DE7AB5
        "Network:PANID" => 0xC0DE
        "IPv6:LinkLocalAddress" => "fe80::18e5:29b3:a638:943b"
        "IPv6:MeshLocalAddress" => "fdc0:de7a:b5c0:0:66bf:99b9:24c0:d55f"
        "IPv6:MeshLocalPrefix" => "fdc0:de7a:b5c0::/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 = 0x0C01

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     |
+----+--------+----------+-----------+-------+--------+-----+------------------+
|  3 | 0x0c00 |        3 |         0 |     0 |      0 |  35 | 1ed687a9cb9d4b1d |

Done
> 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 |
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 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.

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. On the FTD Joiner, scan for the network:

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

> scan
| J | Network Name     | Extended PAN     | PAN  | MAC Address      | Ch | dBm | LQI |
+---+------------------+------------------+------+------------------+----+-----+-----+
| 0 | codelab          | c0de7ab5c0de7ab5 | c0de | f65ae2853ff0c4e4 | 11 | -36 |  57 |

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
2f57d222545271f1
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 2f57d222545271f1 J01NME
Done

Switch to the FTD Joiner, and rescan:

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

> scan
| J | Network Name     | Extended PAN     | PAN  | MAC Address      | Ch | dBm | LQI |
+---+------------------+------------------+------+------------------+----+-----+-----+
| 1 | codelab          | c0de7ab5c0de7ab5 | c0de | 1ed687a9cb9d4b1d | 11 | -45 |  48 |

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 "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)
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)

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:

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
fe80:0:0:0:e4cd:d2d9:3249:a243
fdc0:de7a:b5c0:0:3e2e:66e:9d41:ebcd
Done

Check the child table. Notice that there's a new RLOC16. This is the NCP 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 NCP 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 NCP Joiner to attach to the FTD Joiner as a child. If you have the wpantund window visible, there should be new output as it associates with a new parent:

## wpantund ##
--------------

wpantund[2128]: Node type change: "end-device" -> "unknown"       
wpantund[2128]: State change: "associated" -> "associated:no-parent" 
wpantund[2128]: Taking interface(s) down. . .
wpantund[2128]: State change: "associated:no-parent" -> "associated"
wpantund[2128]: Adding address "fe80::dad4:de8a:a0f9:dc0a/64" to NCP
wpantund[2128]: Node type change: "unknown" -> "end-device"

Switch to the NCP Joiner and check the RLOC16 to confirm that it has changed:

## NCP Joiner ##
----------------

wpanctl:utun7> getprop Thread:RLOC16
Thread:RLOC16 = 0xB801

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.

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

To reset the NCP:

## NCP Joiner ##
----------------

wpanctl:utun7> leave
Leaving current WPAN. . .
wpanctl:utun7> reset
Resetting NCP. . .

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

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 NCP 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 NCP Joiner's LLA.

This means the FTD Joiner is directly connected to both the FTD Commissioner and the NCP 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 NCP 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 NCP Joiner in both ping outputs. The NCP 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 NCP Joiner. This is because the FTDs are Routers within the network, while the NCP is an End Device.

Check the NodeType of the NCP Joiner to confirm:

## NCP Joiner ##
----------------

wpanctl:utun7> getprop Network:NodeType
Network:NodeType = "end-device"

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

You've created a physical Thread network!

You now know:

Next steps

Building off of this Codelab, try the following exercises:

Further reading

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

Reference: