1. 简介
Google 发布的 OpenThread 是 Thread® 网络协议的开源实现。Google Nest 发布了 OpenThread,以便将 Nest 产品中使用的技术广泛提供给开发者,从而加快开发面向智能互联家居的产品。
Thread 规范定义了一种基于 IPv6 的可靠、安全且低功耗的无线设备到设备通信协议,适用于住宅应用。OpenThread 实现了所有 Thread 网络层,包括 IPv6、6LoWPAN、IEEE 802.15.4(具有 MAC 安全性)、网状链路建立和网状路由。
在此 Codelab 中,您将在真实硬件上对 OpenThread 进行编程、创建和管理 Thread 网络,以及在节点之间传递消息。
学习内容
- 构建 OpenThread CLI 二进制文件并将其刷写到开发板
- 构建由 Linux 机器和开发板组成的 RCP
- 使用 OpenThread 守护程序和
ot-ctl
与 RCP 通信 - 使用 GNU Screen 和 OpenThread CLI 手动管理线程节点
- 将设备安全地调试到 Thread 网络
- IPv6 多播的工作原理
- 使用 UDP 在线程节点之间传递消息
所需条件
硬件:
- 3 个 Nordic Semiconductor nRF52840 开发板
- 3 条 USB 转 Micro USB 线,用于连接开发板
- 一台至少有 3 个 USB 端口的 Linux 机器
软件:
- GNU 工具链
- Nordic nRF5x 命令行工具
- Segger J-Link 软件
- OpenThread
- Git
2. 使用入门
OpenThread 模拟
在开始之前,建议您先完成 OpenThread 模拟 Codelab,熟悉基本的线程概念和 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 开发板。
安装 SEGGER J-Link
我们使用 SEGGER J-Link 对具有板载 JTAG 模块的 nRF52840 开发板进行编程。在 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 守护程序:
$ 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 会亮起。
如果这是连接到 Linux 计算机的第一个开发板,则会显示为串行端口 /dev/ttyACM0
(所有 nRF52840 开发板都使用 ttyACM
作为串行端口标识符)。
$ ls /dev/ttyACM* /dev/ttyACM0
记下用于 RCP 的 nRF52840 开发板的序列号:
前往 nRFx 命令行工具的位置,然后使用开发板的序列号将 OpenThread RCP 十六进制文件刷写到 nRF52840 开发板上。请注意,如果您省略 --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 机器)进行通信。
将 USB 线的 Micro-USB 端从已刷写 nRF52840 开发板的调试端口上拆下,然后重新连接到 RESET 按钮旁边的 Micro-USB nRF USB 端口。将 nRF 电源开关设置为 USB。
启动 OpenThread 守护程序
在 RCP 设计中,使用 OpenThread 守护程序与 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 中使用的另外两个线程节点是标准系统级芯片 (SoC) 设计上的完整线程设备 (FTD)。在生产环境中,您可以使用 wpantund
(一种生产级网络接口驱动程序)来控制 OpenThread NCP 实例,但在此 Codelab 中,我们将使用 ot-ctl
(OpenThread CLI)。
其中一个设备充当委托人,用于安全地对设备进行身份验证并将其委托到该网络。另一部设备用作加入者,委托人可以通过它对 Thread 网络进行身份验证。
构建和刷写
为 nRF52840 平台构建 OpenThread FTD 示例,并启用 Commissioner 和 Joiner 角色:
$ cd ~/src/ot-nrf528xx $ rm -rf build $ script/build nrf52840 USB_trans -DOT_JOINER=ON -DOT_COMMISSIONER=ON
前往包含 OpenThread 完整线程设备 (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 开发板的序列号:
前往 nRFx 命令行工具的位置,然后使用开发板的序列号将 OpenThread CLI FTD 十六进制文件刷写到 nRF52840 开发板上:
$ cd ~/nrfjprog/ $ ./nrfjprog -f nrf52 -s 683704924 --verify --chiperase --program \ ~/src/ot-nrf528xx/build/bin/ot-cli-ftd.hex --reset
将该董事会标记为“Commissioner”。
连接到原生 USB
由于 OpenThread FTD build 支持使用原生 USB CDC ACM 作为串行传输,因此您必须使用 nRF52840 开发板上的 nRF USB 端口与 RCP 主机(Linux 机器)进行通信。
将 USB 线的 Micro-USB 端从已刷写 nRF52840 开发板的调试端口上拆下,然后重新连接到 RESET 按钮旁边的 Micro-USB nRF USB 端口。将 nRF 电源开关设置为 USB。
验证 build
通过从终端窗口使用 GNU Screen 访问 OpenThread CLI 来验证构建是否成功。
$ screen /dev/ttyACM1
在新窗口中,按键盘上的“Enter”键几次,以调出 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”。
使用 Screen 进行验证时,请勿从命令行创建新的 Screen 实例,而是重新附加到现有实例,并在其中创建一个新窗口(您之前用于 FTD 专员的窗口):
$ screen -r
在 Screen 中创建新窗口:按 Ctrl+a → c
。
系统随即会显示一个新的命令行提示。访问 FTD Joiner 的 OpenThread CLI:
$ screen /dev/ttyACM2
在新窗口中,按键盘上的“Enter”键几次,以调出 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,此工具还支持在同一终端窗口中分屏。您可以使用此命令来查看一个节点对另一个节点发出的命令的响应方式。
理想情况下,您应该准备好四个窗口:
ot-daemon
服务 / 日志- 通过
ot-ctl
的 RCP Joiner - 通过 OpenThread CLI 使用 FTD Commissioner
- 通过 OpenThread CLI 的 FTD Joiner
如果您想使用自己的终端 / 串行端口配置或工具,可以直接跳到下一步。以最适合您的方式为所有设备配置终端窗口。
使用屏幕
为方便起见,请仅启动一个 Screen 会话。您应该在设置两个 FTD 时就已经创建了一个。
Screen 中的所有命令都以 Ctrl+a 开头。
基本屏幕命令:
重新附加到 Screen 会话(通过命令行) |
|
退出 Screen 会话 | Ctrl+a → |
在屏幕会话中创建新窗口 | Ctrl+a → |
在同一屏幕会话中切换窗口 | Ctrl+a → |
终止 Screen 会话中的当前窗口 | Ctrl+a → |
分屏
借助 Screen,您可以将终端拆分为多个窗口:
您可以使用 Ctrl+a 访问 screen
中的命令。每个命令都应以此快捷键组合开头。
如果您严格按照 Codelab 中的说明操作,则应该在同一 Screen 实例上看到两个窗口(FTD 委托方、FTD 加入方)。如需在两者之间分屏,请先进入现有的 Screen 会话:
$ screen -r
您应该使用 FTD 设备之一。在屏幕上,请按以下步骤操作:
- Ctrl+a →
S
水平拆分窗口 - Ctrl+a →
Tab
将光标移至新的空白窗口 - Ctrl+a →
n
可将该新窗口切换到下一个窗口 - 如果与顶部窗口相同,请再次按 Ctrl+a →
n
查看其他 FTD 设备
现在,这两个选项都显示出来了。使用 Ctrl+a → Tab
在两者之间切换。建议您使用 Ctrl+a → A
为每个窗口重新命名,以免混淆。
高级用途
如需进一步将屏幕拆分为四个象限并查看 ot-daemon
日志和 RCP Joiner ot-ctl
,必须在此同一 Screen 实例中启动这些服务。为此,请停止 ot-daemon
并退出 ot-ctl
,然后在新 Screen 窗口中重新启动它们(Ctrl+a → c
)。
此设置不是必需的,留作用户练习。
使用以下命令拆分窗口并在窗口之间导航:
创建新窗口 | Ctrl+a → |
垂直分屏 | Ctrl+a → |
水平分屏 | Ctrl+a → |
跳转到下一个显示的窗口 | Ctrl+a → |
向前或向后切换显示的窗口 | Ctrl+a → |
重命名当前窗口 | Ctrl+a → |
您可以随时通过按 Ctrl+a → d
退出 Screen,然后通过命令行中的 screen -r
重新附加。
如需详细了解 Screen,请参阅 GNU Screen 快速参考。
7. 创建 Thread 网络
现在,您已经配置好所有终端窗口和屏幕,接下来我们来创建线程网络。在 FTD Commissioner 中,创建一个新的运营数据集并将其提交为有效数据集。运营数据集是您要创建的 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 Commissioner 中,我们记下了影音平台键,例如 1234c0de7ab51234c0de7ab51234c0de
。如果您需要再次查找网络密钥,请在 FTD Commissioner 上运行以下命令:
## 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
调出线程,以便 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 Commissioner,检查路由器和子表,确认这两部设备是否属于同一网络。使用 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 网络,如下图所示的拓扑图所示:
拓扑图
在您完成本 Codelab 的其余部分时,每当网络状态发生变化时,我们都会显示新的线程拓扑图。节点角色的表示方式如下:
路由器始终是五边形,而端点始终是圆形。每个节点上的数字表示 CLI 输出中显示的路由器 ID 或子网 ID,具体取决于每个节点当时的角色和状态。
9. 为 FTD Joiner 分配佣金
现在,我们将第三个 Thread 设备添加到“Codelab”网络。这次,我们将使用更安全的带内调试流程,并且仅允许 FTD Joiner 加入。
在 FTD Joiner 上,获取 eui64
,以便 FTD 专员能够识别它:
## 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
启动线程,以便 FTD Joiner 加入“codelab”网络,并立即检查状态和 RLOC16:
## FTD Joiner ## ---------------- > thread start Done > state child Done > rloc16 0c02 Done
检查设备的 IPv6 地址。请注意,没有 ALOC。这是因为此设备不是主设备,也不具有需要 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 管理员,然后检查路由器和子表,确认“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 已作为端设备(子设备)连接到网络。更新后的拓扑如下所示:
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 也会随之变化。
确认 FTD Joiner 上的新状态和 RLOC16:
## FTD Joiner ## ---------------- > state router Done > rloc16 b800 Done
降级 FTD Joiner
您可以通过手动将 FTD Joiner 从路由器降级回端点设备来测试此行为。将状态更改为子网,然后检查 RLOC16:
## FTD Joiner ## ---------------- > state child Done > rloc16 0c03 Done
返回 FTD 管理员界面,FTD 联接器现在应显示在子表格中(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
的路由器。
移除主管
主线程由所有线程路由器自行选举产生。这意味着,如果当前主路由器从 Thread 网络中移除,其他路由器之一将成为新的主路由器。
在 FTD Commissioner 上,关闭 Thread 以将其从 Thread 网络中移除:
## FTD Commissioner ## ---------------------- > thread stop Done > ifconfig down Done
两分钟内,FTD Joiner 会成为新的线程主导。检查 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
检查子表。请注意,这里有一个新的 RLOC16。这是 RCP 汇聚器,其 ID 和扩展 MAC 地址表明了这一点。为了让 Thread 网络保持连通,它已将父级路由器从 FTD 委员更改为 FTD 汇聚器。这会为 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 Commissioner 上,重启 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
我们的线程网络再次由三个节点组成。
11. 问题排查
在不同的终端或屏幕窗口中管理包含多个设备的 Thread 网络可能很复杂。如果您遇到问题,可以按照以下提示“重置”网络或工作区的状态。
屏幕
如果您在配置中迷失了(Screen 窗口过多,或 Screen 中包含多个 Screen),请继续按 Ctrl+a → k 终止 Screen 窗口,直到没有任何窗口,并且命令行上的 screen -ls
输出 No Sockets found
。然后,为每部设备重新创建 Screen 窗口。即使屏幕被终止,设备状态也会保留。
线程节点
如果 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 地址 | 范围 | 送达 |
| Link-Local | 所有 FTD 和 MED |
| Link-Local | 所有 FTD 和边界路由器 |
| Mesh-Local | 所有 FTD 和 MED |
| Mesh-Local | 所有 FTD 和边界路由器 |
由于我们在此 Codelab 中不使用边界路由器,因此我们将重点关注两个 FTD 和 MED 多播地址。
Link-Local
链路本地范围包含单次无线传输(或单次“跳转”)可到达的所有 Thread 接口。网络拓扑决定了哪些设备会响应对 ff02::1
多播地址的 ping。
从 FTD Commissioner 对 ff02::1
执行 ping 操作:
## 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 委托者只需单跳即可到达的唯一设备。
现在,从 FTD Joiner 对 ff02::1
执行 ping 操作:
## 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。
这意味着 FTD Joiner 直接连接到 FTD Commissioner 和 RCP Joiner,这也证实了我们的拓扑结构。
Mesh-Local
Mesh-Local 作用域包含同一 Thread 网络中可访问的所有 Thread 接口。我们来看看对 ff03::1
多播地址执行 ping 操作的响应。
从 FTD Commissioner 对 ff03::1
执行 ping 操作:
## 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
地址。
从 FTD Joiner 对 ff03::1
执行 ping 操作,以确认行为是否相同:
## 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
记下这两个 ping 输出中的 RCP Joiner 响应时间。RCP 联接器到达 FTD 调度程序的时间(68 毫秒)比到达 FTD 联接器的时间(23 毫秒)要长得多。这是因为它必须跳转两次才能到达 FTD 委员,而 FTD 加入者只需跳转一次。
您可能还注意到,只有两个 FTD(而非 RCP Joiner)对网状本地多播 ping 做出了 RLOC 响应。这是因为 FTD 是网络中的路由器,而 RCP 是端点设备。
检查 RCP Joiner 的状态以确认:
## RCP Joiner ## ---------------- > state child
13. 使用 UDP 发送消息
OpenThread 提供的应用服务之一是用户数据报协议 (UDP),这是一种传输层协议。基于 OpenThread 构建的应用可以使用 UDP API 在 Thread 网络中的节点之间传递消息,也可以将消息传递到外部网络中的其他设备(如果 Thread 网络具有边界路由器,则可以将消息传递到互联网)。
UDP 套接字通过 OpenThread CLI 公开。我们将使用它在两个 FTD 之间传递消息。
获取 FTD Joiner 的 Mesh-Local 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 网络!
您现在了解了:
- 线程设备类型、角色和作用域之间的区别
- Thread 设备如何在网络中管理其状态
- 如何使用 UDP 在节点之间传递简单的消息
后续步骤
在此 Codelab 的基础上,尝试做以下练习:
- 使用
ot-cli-mtd
二进制文件将 FTD Joiner 板块重新刷写为 MTD,并观察到它从不将自己升级为路由器或尝试成为主副本 - 向网络添加更多设备(尝试使用其他平台!),并使用路由器和子表以及对多播地址的 ping 命令,勾勒出拓扑结构
- 使用 pyspinel 控制 NCP
- 使用 OpenThread 边界路由器将 NCP 转换为边界路由器,并将 Thread 网络连接到互联网
深入阅读
请访问 openthread.io 和 GitHub,获取各种 OpenThread 资源,包括:
- 支持的平台 - 了解支持 OpenThread 的所有平台
- 构建 OpenThread - 详细了解如何构建和配置 OpenThread
- 线程入门 - 介绍此 Codelab 中介绍的所有线程概念
参考: