使用 nRF52840 开发板和 OpenThread 构建 Thread 网络

关于此 Codelab
schedule88 分钟
subject上次更新时间:2025年8月15日
account_circleJeff Bumgardner 编写

1. 简介

26b7f4f6b3ea0700.png

Google 发布的 OpenThreadThread® 网络协议的开源实现。Google Nest 发布了 OpenThread,以便开发者广泛使用 Nest 产品中使用的技术,从而加快智能互联家居产品的开发速度。

Thread 规范定义了一种基于 IPv6 的可靠、安全且低功耗的无线设备到设备通信协议,适用于家庭应用。OpenThread 可实现所有 Thread 网络层,包括 IPv6、6LoWPAN、具有 MAC 安全性的 IEEE 802.15.4、网状链路建立和网状路由。

在此 Codelab 中,您将在真实硬件上对 OpenThread 进行编程,创建和管理 Thread 网络,并在节点之间传递消息。

4806d16a8c137c6d.jpeg

学习内容

  • 将 OpenThread CLI 二进制文件构建并刷写到开发板
  • 构建包含 Linux 机器和开发板的 RCP
  • 使用 OpenThread 守护程序和 ot-ctl 与 RCP 通信
  • 使用 GNU Screen 和 OpenThread CLI 手动管理 Thread 节点
  • 将设备安全地调试到 Thread 网络
  • IPv6 多播的运作方式
  • 使用 UDP 在 Thread 节点之间传递消息

所需条件

硬件:

  • 3 个 Nordic Semiconductor nRF52840 开发板
  • 3 根 USB 转 Micro-USB 线缆,用于连接板
  • 一台具有至少 3 个 USB 端口的 Linux 机器

软件:

  • GNU 工具链
  • Nordic nRF5x 命令行工具
  • Segger J-Link 软件
  • OpenThread
  • Git

2. 使用入门

OpenThread 模拟

在开始之前,您可能需要先完成 OpenThread 模拟 Codelab,以熟悉基本的 Thread 概念和 OpenThread CLI。

串行端口终端

你应熟悉如何通过终端连接到串行端口。此 Codelab 使用 GNU Screen 并提供使用情况概览,但你也可以使用其他终端软件。

Linux 机器

此 Codelab 旨在让您使用基于 i386 或 x86 的 Linux 机器作为无线电协处理器 (RCP) Thread 设备的宿主,并刷写所有 Thread 开发板。所有步骤已在 Ubuntu 14.04.5 LTS (Trusty Tahr) 上进行了测试。

Nordic Semiconductor nRF52840 板

此 Codelab 使用三个 nRF52840 PDK 板

a6693da3ce213856.png

我们使用 SEGGER J-Link 对 nRF52840 板进行编程,这些板具有板载 JTAG 模块。在 Linux 机器上安装此工具。

为您的设备下载相应的软件包,并将其安装在正确的位置。在 Linux 上,文件位置为 /opt/SEGGER/JLink

安装 nRF5x 命令行工具

借助 nRF5x 命令行工具,您可以将 OpenThread 二进制文件刷写到 nRF52840 板上。在 Linux 机器上安装相应的 nRF5x-Command-Line-Tools-<OS> build。

将解压缩的软件包放在根文件夹 ~/

安装 ARM GNU 工具链

ARM GNU 工具链用于构建。

建议将解压缩的归档文件放在 Linux 机器上的 /opt/gnu-mcu-eclipse/arm-none-eabi-gcc/ 中。如需了解安装说明,请按照归档文件中的 readme.txt 文件中的说明操作。

安装界面(可选)

Screen 是一款用于访问通过串行端口连接的设备的简单工具。此 Codelab 使用 Screen,但您可以使用自己喜欢的任何串行端口终端应用。

$ sudo apt-get install screen

3. 克隆代码库

OpenThread

克隆并安装 OpenThread。script/bootstrap 命令可确保已安装工具链并正确配置了环境:

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

构建 OpenThread Daemon:

$ script/cmake-build posix -DOT_DAEMON=ON

现在,您可以构建 OpenThread 并将其刷写到 nRF52840 板上。

4. 设置 RCP Joiner

构建和刷写

构建具有 Joiner 和原生 USB 功能的 OpenThread nRF52840 示例。设备使用 Joiner 角色在 Thread 网络上安全地进行身份验证和调试。原生 USB 支持将 USB CDC ACM 用作 nRF52840 与主机之间的串行传输。

始终先运行 rm -rf build 清理之前 build 的代码库。

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

前往包含 OpenThread RCP 二进制文件的目录,并将其转换为十六进制格式:

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

将 USB 线缆连接到 nRF52840 板上外部电源引脚旁边的 Micro-USB 调试端口,然后将其插入 Linux 机器。将 nRF52840 板上的 nRF 电源开关设置为 VDD。正确连接后,LED5 会亮起。

20a3b4b480356447.png

如果这是连接到 Linux 机器的第一个开发板,它会显示为串行端口 /dev/ttyACM0(所有 nRF52840 开发板都使用 ttyACM 作为串行端口标识符)。

$ ls /dev/ttyACM*
/dev/ttyACM0

记下用于 RCP 的 nRF52840 板的序列号:

c00d519ebec7e5f0.jpeg

前往 nRFx 命令行工具的所在位置,然后使用 nRF52840 板的序列号将 OpenThread RCP hex 文件刷写到该板上。请注意,如果您省略 --verify 标志,系统会显示一条警告消息,告知您刷写过程可能会在没有错误的情况下失败。

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

成功后,系统会生成以下输出:

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.

将董事会标记为“RCP”,以免日后混淆董事会角色。

连接到原生 USB

由于 OpenThread RCP build 允许使用原生 USB CDC ACM 作为串行传输,因此您必须使用 nRF52840 板上的 nRF USB 端口与 RCP 主机(Linux 机器)进行通信。

从已刷写的 nRF52840 板的调试端口分离 USB 线的 Micro-USB 端,然后将其重新连接到 RESET 按钮旁边的 Micro-USB nRF USB 端口。将 nRF 电源开关设置为 USB

46e7b670d2464842.png

启动 OpenThread 守护程序

在 RCP 设计中,使用 OpenThread Daemon 与 Thread 设备通信并管理该设备。使用 -v 详细标志启动 ot-daemon,以便查看日志输出并确认它正在运行:

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

成功时,处于详细模式的 ot-daemon 会生成类似于以下内容的输出:

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

请勿关闭此终端窗口,以便查看 ot-daemon 的日志。

使用 ot-ctl 与 RCP 节点通信。ot-ctl 使用与 OpenThread CLI 应用相同的 CLI。因此,您可以像控制其他模拟 Thread 设备一样控制 ot-daemon 节点。

在第二个终端窗口中,启动 ot-ctl

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

检查您使用 ot-daemon 启动的节点 2(RCP 节点)的 state

> state
disabled
Done

5. 设置 FTD

此 Codelab 中使用的另外两个 Thread 节点是基于标准片上系统 (SoC) 设计的全功能 Thread 设备 (FTD)。一个设备用作委托方,用于安全地验证设备并将其委托到相应网络。另一台设备充当 Commissioner 可以向 Thread 网络进行身份验证的 Joiner。

构建和刷写

为 nRF52840 平台构建 OpenThread FTD 示例,并启用 Commissioner 和 Joiner 角色:

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

前往包含 OpenThread Full Thread Device (FTD) CLI 二进制文件的目录,并将其转换为十六进制格式:

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

将 USB 线缆连接到 nRF52840 板上外部电源针脚旁边的 Micro-USB 端口,然后将其插入 Linux 机器。如果 RCP 仍连接到 Linux 机器,则新主板应显示为串行端口 /dev/ttyACM1(所有 nRF52840 主板都使用 ttyACM 作为串行端口标识符)。

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

与之前一样,请记下用于 FTD 的 nRF52840 板的序列号:

c00d519ebec7e5f0.jpeg

前往 nRFx 命令行工具的位置,然后使用 nRF52840 板的序列号将 OpenThread CLI FTD HEX 文件刷写到该板上:

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

将该板块标记为“专员”。

连接到原生 USB

由于 OpenThread FTD build 允许使用原生 USB CDC ACM 作为串行传输,因此您必须使用 nRF52840 板上的 nRF USB 端口与 RCP 主机(Linux 机器)进行通信。

从已刷写的 nRF52840 板的调试端口分离 USB 线的 Micro-USB 端,然后将其重新连接到 RESET 按钮旁边的 Micro-USB nRF USB 端口。将 nRF 电源开关设置为 USB

46e7b670d2464842.png

验证 build

通过从终端窗口使用 GNU Screen 访问 OpenThread CLI 来验证构建是否成功。

$ screen /dev/ttyACM1

在新窗口中,按几次键盘上的 Return 键,以显示 OpenThread CLI > 提示。启动 IPv6 接口并检查地址:

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

使用 Ctrl+a →

d 以从 FTD Commissioner CLI 界面分离并返回到 Linux 终端,以便刷写下一个板。如需随时重新进入 CLI,请从命令行使用 screen -r。如需查看可用屏幕的列表,请使用 screen -ls

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

设置 FTD Joiner

使用现有的 ot-cli-ftd.hex build 重复上述流程,以刷写第三个 nRF52840 板。完成后,请务必使用 nRF USB 端口将开发板重新连接到 PC,并将 nRF 电源开关设置为 VDD

如果其他两个节点在连接此第三个板时连接到 Linux 机器,则该节点应显示为串行端口 /dev/ttyACM2

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

将板子标记为“Joiner”。

使用屏幕进行验证时,请重新附加到现有屏幕,并在其中创建一个新窗口(用于 FTD 调试器),而不是从命令行创建新的屏幕实例:

$ screen -r

使用 Ctrl+a → c 在 Screen 中创建新窗口。

系统会显示新的命令行提示。访问 FTD Joiner 的 OpenThread CLI:

$ screen /dev/ttyACM2

在此新窗口中,按键盘上的 Return 键几次,以显示 OpenThread CLI > 提示。启动 IPv6 接口并检查地址:

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

现在,FTD Joiner CLI 与 FTD Commissioner 位于同一 Screen 实例中,您可以使用 Ctrl+a → n 在它们之间切换。

使用 Ctrl+a →

d 随时退出屏幕。

6. 终端窗口设置

接下来,您将频繁切换 Thread 设备,因此请确保所有设备都处于有效状态且易于访问。到目前为止,我们一直使用 Screen 来访问两个 FTD,此工具还允许在同一终端窗口中进行分屏。使用此功能可查看一个节点对在另一个节点上发出的命令的反应。

理想情况下,您应该有四个窗口可供随时使用:

  1. ot-daemon 服务 / 日志
  2. 通过 ot-ctl 成为 RCP Joiner
  3. 通过 OpenThread CLI 运行的 FTD Commissioner
  4. 通过 OpenThread CLI 实现 FTD Joiner

如果您希望使用自己的终端 / 串行端口配置或工具,可以跳到下一步。以最适合您的方式为所有设备配置终端窗口。

使用屏幕

为方便使用,请仅启动一个 Screen 会话。您在设置两个 FTD 时应该已经创建了一个。

Screen 中的所有命令都以 Ctrl+a 开头。

基本 Screen 命令:

重新附加到 Screen 会话(从命令行)

screen -r

离开 Screen 会话

Ctrl+a → d

在 Screen 会话中创建新窗口

Ctrl+a → c

在同一 Screen 会话中切换窗口

Ctrl+a → n(前进)Ctrl+a → p(后退)

在 Screen 会话中终止当前窗口

Ctrl+a → k

分屏

借助 Screen,您可以将终端拆分为多个窗口:

f1cbf1258cf0a5a.png

screen 中的命令可通过使用 Ctrl+a 来访问。每个命令都应以该快捷键组合开头。

如果您一直严格按照 Codelab 的说明进行操作,那么您应该在同一屏幕实例上看到两个窗口(FTD 委托方、FTD 加入方)。如需在两个屏幕之间分屏,请先进入现有的 Screen 会话:

$ screen -r

您应使用 FTD 设备。在“屏幕”中,按照以下步骤操作:

  1. Ctrl+a → S 可水平拆分窗口
  2. Ctrl+a → Tab 将光标移至新的空白窗口
  3. Ctrl+a → n 可将该新窗口切换到下一个窗口
  4. 如果与顶部窗口相同,请再次按 Ctrl+a → n 以查看其他 FTD 设备

现在,这两个凭据都可见。使用 Ctrl+a → Tab 在它们之间切换。建议您使用 Ctrl+a → A 为每个窗口重新命名,以免混淆。

高级用途

如需进一步将屏幕分成四个象限,并查看 ot-daemon 日志和 RCP Joiner ot-ctl,必须在此同一屏幕实例中启动这些服务。为此,请停止 ot-daemon 并退出 ot-ctl,然后在新的 Screen 窗口中重新启动它们 (Ctrl+a → c)。

此设置不是必需的,留给用户自行练习。

使用以下命令拆分窗口并在窗口之间导航:

创建新窗口

Ctrl+a → c

垂直拆分窗口

Ctrl+a →

水平拆分窗口

Ctrl+a → S

跳转到下一个显示的窗口

Ctrl+a → Tab

向前或向后切换显示的窗口

Ctrl+a → np

重命名当前窗口

Ctrl+a → A

您可以随时按 Ctrl+a → d 离开 Screen,然后通过命令行中的 screen -r 重新连接。

如需详细了解 Screen,请参阅 GNU Screen 快速参考

7. 创建 Thread 网络

现在,您已配置好所有终端窗口和屏幕,接下来我们来创建 Thread 网络。在 FTD 调试器上,创建一个新的运行数据集并将其提交为有效数据集。运行数据集是您要创建的 Thread 网络的配置。

## 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

记下稍后将使用的网络密钥 1234c0de7ab51234c0de7ab51234c0de

将此数据集提交为有效数据集:

> dataset commit active
Done

启动 IPv6 接口:

> ifconfig up
Done

启动 Thread 协议操作:

> thread start
Done

稍等片刻,然后检查设备状态。应该是领导者。同时获取 RLOC16 以供日后参考。

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

> state
leader
Done
> rloc16
0c00
Done

检查设备的 IPv6 地址:

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

现在,从其他 Thread 设备扫描时,可以看到“codelab”网络。

RCP Joiner 中,从 ot-ctl 开始:

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

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

FTD Joiner 上通过 OpenThread CLI 执行以下操作:

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

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

如果列表中未显示“codelab”网络,请尝试再次扫描。

8. 添加 RCP Joiner

Thread 调试未在网络上处于活动状态,这意味着我们需要使用带外调试流程将 RCP Joiner 添加到我们刚刚创建的 Thread 网络。

FTD 委托方上,我们记下了网络密钥,例如 1234c0de7ab51234c0de7ab51234c0de。如果您需要再次查找网络密钥,请在 FTD 委托方上运行以下命令:

## FTD Commissioner ##

> dataset networkkey
1234c0de7ab51234c0de7ab51234c0de
Done

接下来,在 RCP Joiner 上,将其有效数据集网络密钥设置为 FTD 委托方网络密钥:

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

> dataset networkkey 1234c0de7ab51234c0de7ab51234c0de
Done
> dataset commit active
Done

检查数据集,确保其设置正确无误。

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

> dataset
Network Key: 1234c0de7ab51234c0de7ab51234c0de

启动 Thread,以便 RCP Joiner 加入“codelab”网络。等待几秒钟,检查状态、RLOC16 及其 IPv6 地址:

## 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

记下网格本地 IPv6 地址(此处为 fdc0:de7a:b5c0:0:66bf:99b9:24c0:d55f),您稍后会用到它。

返回到 FTD 调试器,检查路由器和子表,确认这两个设备属于同一网络。使用 RLOC16 标识 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

对 RCP Joiner 的网格本地地址(从 RCP Joiner 的 ipaddr 输出中获得的网格本地地址)执行 ping 操作,以验证连接:

## 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

现在,我们有一个由两个节点组成的 Thread 网络,如下图所示:

otcodelab_top01C_2nodes.png

拓扑图

在完成本 Codelab 的剩余部分时,每当网络状态发生变化,我们都会显示新的 Thread 拓扑图。节点角色表示法如下:

b75a527be4563215.png

路由器始终为五边形,而终端设备始终为圆形。每个节点上的数字表示 CLI 输出中显示的路由器 ID 或子 ID,具体取决于每个节点当时的当前角色和状态。

9. 调试 FTD Joiner

现在,我们来向“codelab”网络添加第三个 Thread 设备。这次,我们将使用更安全的带内调试流程,并且仅允许 FTD Joiner 加入。

FTD Joiner 上,获取 eui64,以便 FTD Commissioner 识别它:

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

> eui64
2f57d222545271f1
Done

FTD 调试器上,启动调试器并指定可加入设备的 eui64 以及加入器凭据,例如 J01NME。加入者凭据是一个特定于设备的字符串,由全大写字母数字字符(0-9 和 A-Y,为便于阅读,不包括 I、O、Q 和 Z)组成,长度介于 6 到 32 个字符之间。

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

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

切换到 FTD Joiner。使用您刚刚在 FTD 调试器上设置的加入者凭据启动加入者角色:

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

> ifconfig up
Done
> joiner start J01NME
Done

在一分钟左右的时间内,您会收到身份验证成功的确认信息:

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

>
Join success

启动 Thread,以便 FTD Joiner 加入“codelab”网络,并立即检查状态和 RLOC16:

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

> thread start
Done
> state
child
Done
> rloc16
0c02
Done

检查设备的 IPv6 地址。请注意,没有 ALOC。这是因为该设备不是 Leader,也不持有需要 ALOC 的 Anycast 特定角色。

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

立即切换到 FTD Commissioner,然后检查路由器和子表,确认“codelab”网络中存在三个设备:

## 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

根据 RLOC16,FTD Joiner 已作为终端设备(子设备)连接到网络。以下是我们更新后的拓扑:

otcodelab_top01C_ed01.png

10. 线程在运行

此 Codelab 中的 Thread 设备是一种特定的完整 Thread 设备 (FTD),称为“符合路由器条件的端设备”(REED)。这意味着它们既可以充当路由器,也可以充当终端设备,并且可以从终端设备升级为路由器。

Thread 最多可支持 32 个路由器,但会尽量将路由器数量保持在 16 到 23 之间。如果 REED 作为终端设备(子设备)连接,且路由器的数量低于 16,则在两分钟内的随机时间段后,它会自动将自己提升为路由器。

如果您在添加 FTD Joiner 后在 Thread 网络中有两个子设备,请等待至少两分钟,然后重新检查 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

FTD Joiner(扩展 MAC = e6cdd2d93249a243)已将自己提升为路由器。请注意,RLOC16 不同(b800 而不是 0c02)。这是因为 RLOC16 基于设备的路由器 ID 和子设备 ID。当其从终端设备转换为路由器时,其路由器 ID 和子 ID 值会发生变化,RLOC16 也会发生变化。

otcodelab_top01C.png

FTD Joiner 上确认新状态和 RLOC16:

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

> state
router
Done
> rloc16
b800
Done

降级 FTD Joiner

您可以手动将 FTD Joiner 从路由器降级为端设备,以测试此行为。将状态更改为子状态,并检查 RLOC16:

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

> state child
Done
> rloc16
0c03
Done

otcodelab_top01C_ed02.png

返回到 FTD Commissioner,FTD Joiner 现在应显示在子表中(ID = 3)。在过渡期间,它甚至可能同时存在于这两个位置:

## 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

过一段时间后,它会切换回 RLOC 为 b800 的路由器。

otcodelab_top01C.png

移除主管

领导者在所有 Thread 路由器中自行选举产生。这意味着,如果当前领导者从 Thread 网络中移除,其他路由器之一将成为新的领导者。

FTD 委托方上,关闭 Thread 以将其从 Thread 网络中移除:

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

> thread stop
Done
> ifconfig down
Done

在两分钟内,FTD Joiner 成为新的 Thread 领导者。检查 FTD Joiner 的状态和 IPv6 地址,以验证:

## 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

检查子级表。请注意,现在有了一个新的 RLOC16。这是 RCP Joiner,如其 ID 和扩展 MAC 所示。为了保持 Thread 网络正常运行,它已将父路由器从 FTD Commissioner 切换到 FTD Joiner。这会导致 RCP Joiner 的 RLOC16 发生变化(因为其路由器 ID 从 3 变为 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

您可能需要等待几分钟,RCP Joiner 才能作为子级附加到 FTD Joiner。检查状态和 RLOC16,确认:

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

> state
child
> rloc16
b801

重新连接 FTD 调试器

只有两个节点的 Thread 网络没什么意思。让我们让 FTD 专员重新上线。

FTD 调试器上,重启 Thread:

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

> ifconfig up
Done
> thread start
Done

在两分钟内,它会自动重新连接到“codelab”网络,成为终端设备,然后将自己升级为路由器。

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

> state
router
Done

检查 FTD Joiner 上的路由器和子表,以验证:

## 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

我们的 Thread 网络再次由三个节点组成。

11. 问题排查

在不同的终端或屏幕窗口上管理具有多个设备的 Thread 网络可能很复杂。如果您遇到问题,可以使用以下提示“重置”网络或工作区的状态。

屏幕

如果您在配置中迷失了方向(屏幕窗口过多,或屏幕内有屏幕),请一直使用 Ctrl+a → k 杀死屏幕窗口,直到不存在任何屏幕窗口,并且命令行上的 screen -ls 输出 No Sockets found。然后,为每部设备重新创建屏幕窗口。即使屏幕被终止,设备状态也会保留。

Thread 节点

如果 Thread 网络拓扑与此 Codelab 中描述的拓扑不同,或者节点因某种原因(可能是因为为它们供电的 Linux 机器进入休眠状态)而断开连接,最好关闭 Thread,清除网络凭据,然后从创建 Thread 网络这一步重新开始。

重置 FTD:

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

> thread stop
Done
> ifconfig down
Done
> factoryreset
Done

可以通过 ot-ctl 以相同的方式重置 RCP:

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

> thread stop
Done
> ifconfig down
Done
> factoryreset
Done

12. 使用多播

多播用于一次向一组设备传递信息。在 Thread 网络中,系统会预留特定地址以用于多播,并根据范围将设备分为不同的群组。

IPv6 地址

范围

已送达

ff02::1

本地链接

所有 FTD 和 MED

ff02::2

本地链接

所有 FTD 和边界路由器

ff03::1

网状本地

所有 FTD 和 MED

ff03::2

网状本地

所有 FTD 和边界路由器

由于我们在此 Codelab 中未使用边界路由器,因此我们重点介绍两个 FTD 和 MED 多播地址。

链路本地范围包括通过单次无线电传输或单次“跳跃”可到达的所有 Thread 接口。网络拓扑决定了哪些设备会响应对 ff02::1 多播地址的 ping。

来自 FTD 专员的 Ping ff02::1

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

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

网络中还有另外两台设备(FTD Joiner 和 RCP Joiner),但 FTD Commissioner 只收到一个响应,来自 FTD Joiner 的链路本地地址 (LLA)。这意味着 FTD 联接器是 FTD 调试器可以通过单跳到达的唯一设备。

otcodelab_top02C_02_LL.png

现在,从 FTD Joiner 中 ping ff02::1

## 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

两个回答!检查其他设备的 IPv6 地址,我们可以看到第一个地址(以 4b1d 结尾)是 FTD 委托方的 LLA,第二个地址(以 943b 结尾)是 RCP 加入方的 LLA。

otcodelab_top02C_02_LL02.png

这意味着 FTD Joiner 直接连接到 FTD Commissioner 和 RCP Joiner,这证实了我们的拓扑。

网状本地

网状本地范围包含同一 Thread 网络中可访问的所有 Thread 接口。我们来看看对 ff03::1 多播地址的 ping 响应。

来自 FTD 专员的 Ping ff03::1

## 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

这次,FTD 委托人收到了两个响应,一个来自 FTD 加入者的路由定位符 (RLOC,以 b800 结尾),另一个来自 RCP 加入者的网状本地 EID (ML-EID,以 d55f 结尾)。这是因为网状本地范围涵盖了整个 Thread 网络。无论设备位于网络的哪个位置,它都会订阅 ff03::1 地址。

otcodelab_top02C_02_ML.png

FTD Joiner ping ff03::1,确认行为相同:

## 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

请注意两个 ping 输出中 RCP Joiner 的响应时间。RCP Joiner 到达 FTD Commissioner 的时间 (68ms) 比到达 FTD Joiner 的时间 (23ms) 长得多。这是因为 FTD 专员需要两跳才能到达,而 FTD 加入者只需要一跳。

您可能还注意到,网状本地多播 ping 仅针对两个 FTD(而非 RCP Joiner)返回了 RLOC。这是因为 FTD 是网络中的路由器,而 RCP 是终端设备。

检查 RCP Joiner 的状态以确认:

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

> state
child

13. 使用 UDP 发送消息

OpenThread 提供的一项应用服务是用户数据报协议 (UDP),这是一种传输层协议。基于 OpenThread 构建的应用可以使用 UDP API 在 Thread 网络中的节点之间传递消息,也可以在外部网络(例如互联网,如果 Thread 网络具有边界路由器)中的其他设备之间传递消息。

UDP 套接字通过 OpenThread CLI 公开。我们使用它在两个 FTD 之间传递消息。

获取 FTD Joiner 的网格本地 EID 地址。我们之所以使用此地址,是因为它可以从 Thread 网络中的任何位置访问。

## 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

启动 UDP 并将其绑定到任何 IPv6 地址的套接字:

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

> udp open
Done
> udp bind :: 1212

切换到 FTD 委托方,启动 UDP,并使用 FTD Joiner 上的 ML-EID 连接到您设置的套接字:

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

> udp open
Done
> udp connect fdc0:de7a:b5c0:0:3e2e:66e:9d41:ebcd 1212
Done

两个节点之间的 UDP 连接应处于活动状态。来自 FTD 专员的消息:

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

> udp send hellothere
Done

FTD Joiner 上,已收到 UDP 消息!

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

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

14. 恭喜!

您已创建了一个物理 Thread 网络!

b915c433e7027cc7.png

您现在已了解:

  • Thread 设备类型、角色和范围之间的区别
  • Thread 设备如何在网络中管理其状态
  • 如何使用 UDP 在节点之间传递简单消息

后续步骤

在此 Codelab 的基础上,尝试做以下练习:

  • 使用 ot-cli-mtd 二进制文件将 FTD Joiner 板重新刷写为 MTD,并观察到它永远不会自行升级为路由器或尝试成为 Leader
  • 向网络添加更多设备(尝试使用其他平台!),并使用路由器和子表以及对多播地址的 ping 来绘制拓扑
  • 使用 pyspinel 控制 NCP
  • 使用 OpenThread 边界路由器将 NCP 转换为边界路由器,并将 Thread 网络连接到互联网

深入阅读

如需各种 OpenThread 资源,请访问 openthread.ioGitHub,其中包括:

参考: