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

1. 简介

26b7f4f6b3ea0700

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

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

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

4806d16a8c137c6d.jpeg

学习内容

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

所需条件

硬件:

  • 3 块北欧半导体 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 对具有板载 JTAG 模块的 nRF52840 板进行编程。在您的 Linux 机器上安装此文件。

为您的计算机下载合适的软件包,并将其安装在正确的位置。在 Linux 上,此位置为 /opt/SEGGER/JLink

安装 nRF5x 命令行工具

您可以使用 nRF5x 命令行工具将 OpenThread 二进制文件刷写到 nRF52840 开发板上。安装相应的 nRF5x-Command-Line-Tools-<OS>如何在 Linux 机器上构建

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

安装 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 和原生 USB 功能构建 OpenThread nRF52840 示例。设备使用 Joiner 角色接受安全的身份验证,并调试到 Thread 网络。原生 USB 支持将 USB CDC ACM 用作 nRF52840 和主机之间的串行传输。

始终先运行 rm -rf 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

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

$ ls /dev/ttyACM*
/dev/ttyACM0

请注意用于 RCP 的 nRF52840 板的序列号:

c00d519ebec7e5f0.jpeg

找到 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.

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

连接到原生 USB

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

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

46e7b670d2464842

启动 OpenThread 守护程序

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

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

如果成功,详细模式下的 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) 设计上的全线程设备 (FTD)。在正式版设置中,可以使用生产级网络接口驱动程序 wpantund 来控制 OpenThread NCP 实例,但在此 Codelab 中,我们将使用 ot-ctl,即 OpenThread CLI。

一台设备充当“调试器”,用于安全地对设备进行身份验证并调试设备加入该网络。另一台设备充当联接器,调试器可以向 Thread 网络进行身份验证。

构建和刷写

在启用了 Commissioner 和 Joiner 角色的情况下,为 nRF52840 平台构建 OpenThread FTD 示例:

$ 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 命令行工具的位置,然后使用该板的序列号将 OpenThread CLI FTD 十六进制文件刷写到 nRF52840 板上:

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

将 Jamboard 标记为“佣金”。

连接到原生 USB

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

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

46e7b670d2464842

验证 build

通过在终端窗口中使用 GNU Screen 访问 OpenThread CLI,验证构建是否成功。nRF52840 板使用的波特率为 115200。

$ screen /dev/ttyACM1 115200

在新窗口中,按键盘上的回车键数次,调出 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 连接符

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

如果在连接第三个开发板时,另外两个节点也连接到了 Linux 机器,则它应显示为串行端口 /dev/ttyACM2

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

将面板标记为“连接者”。

使用 Screen 进行验证时,不要从命令行创建新的 Screen 实例,而是重新附加到现有实例,然后在该实例中打开一个新窗口(您用于 FTD 调试器):

$ screen -r

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

系统会显示一个新的命令行提示符。访问 FTD 联接器的 OpenThread CLI:

$ screen /dev/ttyACM2 115200

在此新窗口中,按键盘上的回车键数次即可调出 OpenThread CLI > 提示符。启动 IPv6 接口并检查地址:

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

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

使用 Ctrl+a →

d 随时退出屏幕。

6. 终端窗口设置

今后,您会经常在 Thread 设备之间切换,因此请确保所有这些设备都已发布且易于访问。到目前为止,我们一直使用 Screen 来访问两个 FTD,该工具还允许在同一终端窗口上进行分屏。使用此方法可查看一个节点对在另一个节点上发出的命令有何反应。

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

  1. ot-daemon 服务 / 日志
  2. RCP 联接器(通过 ot-ctl
  3. 通过 OpenThread CLI 使用 FTD 调试器
  4. 通过 OpenThread CLI 使用 FTD 联接器

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

使用屏幕

为了方便使用,请仅启动一个屏幕会话。设置两个 FTD 时应该已经有一个 FTD。

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

基本的屏幕命令:

重新连接到屏幕会话(从命令行)

screen -r

退出屏幕会话

Ctrl+a → d

在 Screen 会话中创建新窗口

Ctrl+a → c

在同一屏幕会话中在窗口之间切换

Ctrl+a → n(向前)Ctrl+a → p(向后)

在“屏幕”会话中关闭当前窗口

Ctrl+a → k

分屏

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

f1cbf1258cf0a5a.png

您可以使用 Ctrl+a 访问 screen 中的命令。每个命令都应以此访问键组合开头。

如果您严格遵循此 Codelab,则应该在同一 Screen 实例上有两个窗口(FTD Commissioner、FTD Joiner)。如需在两者之间分屏,请先进入现有的屏幕会话:

$ 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 联接器 ot-ctl,必须在同一 Screen 实例中启动这些服务。为此,请停止 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 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)

“Codelab”从其他 Thread 设备扫描时,网络现在可见。

RCP 联接器上的 ot-ctl 中:

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

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

通过 FTD 联接器上的 OpenThread CLI:

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

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

如果“Codelab”网络未显示在列表中,请尝试重新扫描。

8. 添加 RCP 连接符

在该网络中,线程调试尚未启用,这意味着我们需要将 RCP 联接器添加到我们刚刚通过带外调试流程创建的 Thread 网络。

FTD 调试器中,记下了网络密钥,例如 1234c0de7ab51234c0de7ab51234c0de。如果您需要再次查找网络密钥,请在 FTD 调试器上运行以下命令:

## FTD Commissioner ##

> dataset networkkey
1234c0de7ab51234c0de7ab51234c0de
Done

接下来,在 RCP 连接器上,将其活跃数据集网络密钥设置为 FTD 调试器网络密钥:

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

> dataset networkkey 1234c0de7ab51234c0de7ab51234c0de
Done
> dataset commit active
Done

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

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

> dataset
Network Key: 1234c0de7ab51234c0de7ab51234c0de

启动线程,以便 RCP 联接器加入“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

记下 Mesh-Local IPv6 地址(此处为 fdc0:de7a:b5c0:0:66bf:99b9:24c0:d55f),稍后您将用到它。

返回 FTD Commissioner,检查路由器和子表,确认两台设备都属于同一网络。使用 RLOC16 识别 RCP 连接符。

## 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 RCP 连接器的网格本地地址(从 RCP 连接器的 ipaddr 输出获得的网格本地地址)以验证连接:

## 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 的其余部分时,每当网络状态发生变化时,我们都会显示一个新的线程拓扑图。节点角色的表示方式如下:

b75a527be4563215.png

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

9. 调试 FTD 连接符

现在,我们将第三个 Thread 设备添加到“Codelab”。这次我们将使用更安全的带内调试流程,并仅允许 FTD 连接者加入。

FTD 连接符上,获取 eui64,以便 FTD 调试器识别它:

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

> eui64
2f57d222545271f1
Done

FTD Commissioner(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 联接器。使用您刚刚在 FTD Commissioner 设置的 Joiner 凭据来开始执行联接者角色:

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

> ifconfig up
Done
> joiner start J01NME
Done

在大约一分钟内,您会收到身份验证成功的确认消息:

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

>
Join success

启动线程,以便 FTD 联接器加入“Codelab”网络,并立即检查状态和 RLOC16:

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

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

检查设备的 IPv6 地址。请注意,不存在 ALOC。这是因为此设备不是主要设备,也不担任需要 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)

立即切换到 FTD Commissioner 并检查路由器和子表,确认“Codelab”中是否存在 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 |

> 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 联接器已作为最终设备(子设备)连接到网络。以下是更新后的拓扑:

otcodelab_top01C_ed01.png

10. 讨论帖运作方式

此 Codelab 中的 Thread 设备是一种特定的全线程设备 (FTD),称为适合路由器的终端设备 (REED)。这意味着它们可以充当路由器或终端设备,并且可以将自己从终端设备提升为路由器。

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

如果在添加 FTD 联接器后,您的 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 联接器 (Extended MAC = e6cdd2d93249a243) 已将自身提升为路由器。请注意,RLOC16 并不相同(使用 b800 而不是 0c02)。这是因为 RLOC16 基于设备的路由器 ID 和设备子 ID。当它从终端设备转换为路由器时,其路由器 ID 和子 ID 值也会发生变化,RLOC16 也会发生变化。

otcodelab_top01C.png

FTD 联接器上确认新状态和 RLOC16:

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

> state
router
Done
> rloc16
b800
Done

降级 FTD 连接符

您可以通过手动将 FTD 连接器从路由器降级回最终设备来测试此行为。将状态更改为“child”并检查 RLOC16:

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

> state child
Done
> rloc16
0c03
Done

otcodelab_top01C_ed02.png

回到 FTD Commissioner,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 的路由器。

otcodelab_top01C.png

移除主管

主要副本在所有 Thread Router 路由器中是自选的。这意味着,如果从 Thread 网络中移除当前主要路由器,则其他路由器之一将成为新的主要路由器。

FTD Commissioner 中,关闭 Thread 以将其从 Thread 网络中移除:

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

> thread stop
Done
> ifconfig down
Done

在两分钟内,FTD 联接器成为新的 Thread 主要副本。检查 FTD 联接器的状态和 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 联接器,由其 ID 和扩展 MAC 表示。为了将 Thread 网络保持在一起,它已将父级路由器从 FTD 调试器切换到 FTD 联接器。这会为 RCP 联接器生成新的 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 连接符作为子级附加到 FTD 连接者。请检查状态和 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 联接器上的路由器和子表,以验证:

## 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 网络拓扑与此 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 连接器和 RCP 连接器),但 FTD 调试器仅收到一个响应,即来自 FTD 连接器的链路本地地址 (LLA)。这意味着 FTD 连接器是 FTD 调试器可以通过单跳到达的唯一设备。

otcodelab_top02C_02_LL.png

现在,从 FTD 联接器 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 连接器直接连接到 FTD 调试器和 RCP 连接器,这可以确认我们的拓扑。

局部网状网

Mesh-Local 范围包含在同一 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 联接器的 Mesh-Local EID(ML-EID,以 d55f 结尾)。这是因为网格局部范围包含整个 Thread 网络。无论设备在网络中的什么位置,都会订阅 ff03::1 地址。

otcodelab_top02C_02_ML.png

FTD 联接器 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 连接符的响应时间。RCP 连接符到达 FTD 调试器所用的时间(68 毫秒)比到达 FTD 连接符(23 毫秒)的时间长得多。这是因为与 FTD 连接器的一次跃点不同,它必须进行两次跃点才能到达 FTD 调试器。

您可能还注意到,网格本地多播 ping 仅针对两个 FTD(而不是 RCP 联接器)响应了 RLOC。这是因为 FTD 是网络内的路由器,而 RCP 是终端设备。

请检查 RCP 联接器的状态以确认:

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

> state
child

13. 使用 UDP 发送消息

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

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

获取 FTD 联接器的网格本地 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 Commissioner,启动 UDP,并使用其 ML-EID 连接到您在 FTD 联接器上设置的套接字:

## 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 联接器上,已收到 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 连接器板重新刷写为 MTD,并观察到它绝不会将自己升级为路由器或试图成为主要副本
  • 向网络添加更多设备(尝试使用其他平台!),并使用路由器表和子表以及对多播地址的 ping 功能草拟拓扑
  • 使用 pyspinel 控制 NCP
  • 使用 OpenThread 边界路由器将 NCP 转换为边界路由器,并将您的 Thread 网络连接到互联网

深入阅读

您可以访问 openthread.ioGitHub,获取各种 OpenThread 资源,包括:

参考: