Google 致力于为黑人社区推动种族平等。查看具体行动

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

1. 简介

26b7f4f6b3ea0700.png

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

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

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

4806d16a8c137c6d.jpeg

学习内容

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

所需条件

硬件:

  • 3 个北欧半导体 nRF52840 开发板
  • 3 条 USB 转 Micro USB 线,用于连接开发板
  • 具有至少 3 个 USB 端口的 Linux 计算机

软件:

  • GNU 工具链
  • 北欧 nRF5x 命令行工具
  • Secker J-Link 软件
  • OpenThread
  • Git

2. 开始使用

OpenThread 模拟

在开始之前,您可能需要运行 OpenThread Simcode Codelab,以熟悉基本的线程概念和 OpenThread CLI。

串行端口终端

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

Linux 机器

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

北欧半导体 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 守护程序:

$ script/cmake-build posix -DOT_DAEMON=ON

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

4.设置 RCP 连接器

构建和刷写

使用接合器和原生 USB 功能构建 OpenThread nRF52840 示例。设备使用 Joiner 角色安全地进行身份验证,并委托给线程网络。借助原生 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.png

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

$ ls /dev/ttyACM*
/dev/ttyACM0

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

c00d519ebec7e5f0.jpeg

转到 nRFx 命令行工具的位置,然后使用主板的序列号将 OpenThread RCP 十六进制文件刷写到 nRF52840 开发板上。请注意,如果您省略 --verify 标记,系统会显示一条警告消息,告知您 Flash 进程可能会失败,且不会报错。

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

为 Storyboard 添加“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.png

启动 OpenThread 守护程序

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

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

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

ot-daemon[228024]: Running OPENTHREAD/20191113-00831-gfb399104; POSIX; Jun  7 2020 18:05:15
ot-daemon[228024]: Thread version: 2
ot-daemon[228024]: RCP version: OPENTHREAD/20191113-00831-gfb399104; SIMULATION; Jun  7 2020 18:06:08

不要关闭此终端窗口,以便查看来自 ot-daemon 的日志。

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

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

$ ./build/posix/bin/ot-ctl
>

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

> state
disabled
Done

5. 设置 FTD

此 Codelab 中使用的另外两个线程节点是采用标准系统芯片 (SoC) 设计的全线程设备 (FTD)。在生产设置中,可以使用生产级网络接口驱动程序 wpantund 来控制 OpenThread NCP 实例,但在此 Codelab 中,我们将使用 ot-ctl,即 OpenThread CLI。

一个设备用作专员,用于安全地对设备进行身份验证并登录到该网络。其他设备将充当一个连接点,以便委员可以向线程网络进行身份验证。

构建和刷写

为 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 开发板的序列号:

c00d519ebec7e5f0.jpeg

转到 nRFx 命令行工具的位置,然后使用 OpenBoard CLI 开发板的序列号将 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.png

验证构建

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

$ screen /dev/ttyACM1 115200

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

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

使用 Ctrl+a →

d 与 FTD 调试程序 CLI 屏幕分离并返回 Linux 终端,以便刷写下一个开发板。您可以随时通过命令行使用 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 电源开关设置为 VDD

如果连接了第三个主板后,其他两个节点连接到了 Linux 机器,那么它们应显示为串行端口 /dev/ttyACM2

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

将 Jamboard 标记为“连接符”。

使用 Screen 进行验证时,您无需从命令行创建新的 Screen 实例,而是重新附加到现有实例并在该窗口中打开一个新窗口(用于 FTD 专员):

$ screen -r

在屏幕内按 Ctrl+a → c 创建新窗口。

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

$ screen /dev/ttyACM2 115200

在此新窗口中,按几次 Enter 键即可调出 OpenThread CLI > 提示符。启动 IPv6 接口并检查地址:

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

现在,FTD 连接器 CLI 与 FTD 专员在同一个屏幕实例中,您可以使用 Ctrl+a → n 在它们之间切换。

使用 Ctrl+a →

d 随时退出屏幕。

6.终端窗口设置

今后,您将经常在线程设备之间切换,因此请确保所有这些设备都处于活动状态且易于访问。到目前为止,我们一直在使用 Screen 访问两个 FTD,并且此工具也允许在同一终端窗口中显示分屏。使用此方法可查看一个节点如何响应在另一个节点上发出的命令。

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

  1. ot-daemon 服务 / 日志
  2. 通过 ot-ctl RCP 连接器
  3. 通过 OpenThread CLI 获取 FTD 专员
  4. 通过 OpenThread CLI 创建的 FTD 连接器

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

使用屏幕

为便于使用,请仅启动一个 Screen 会话。设置这两个 FTD 时,您应该已经有一个代码。

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

基本屏幕命令:

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

screen -r

退出“屏幕”会话

Ctrl+a → d

在“屏幕”会话中创建新窗口

Ctrl+a → c

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

Ctrl+a → n(转发)Ctrl+a → p(返回)

终止 Screen 会话中的当前窗口

Ctrl+a → k

分屏

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

f1cbf1258cf0a5a.png

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

如果您确实要遵循此 Codelab,应在同一屏幕实例上有两个窗口(FTD 专员,FTD 连接器)。若要在这两个屏幕间拆分屏幕,请先输入您现有的屏幕会话:

$ 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,这些服务必须在同一屏幕实例中启动。为此,请停止 ot-daemon 并退出 ot-ctl,然后在新的屏幕窗口中重启它们 (Ctrl+a → c)。

此设置并非必需设置,且仅供用户练习。

使用以下命令拆分和浏览窗口:

新建窗口

Ctrl+a → c

垂直拆分窗口

Ctrl+A →

水平分割窗口

Ctrl+a → S

跳转到下一个显示的窗口

Ctrl+a → Tab

将向前或向后切换窗口

Ctrl+a → np

重命名当前窗口

Ctrl+a → A

您随时可以使用 Ctrl+a → d 离开屏幕,然后使用命令行重新附加 screen -r

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

7. 创建线程网络

现在,您已经配置了所有终端窗口和屏幕,接下来让我们创建线程网络。在 FTD 专员上,创建一个新的操作数据集并将其作为活动数据集提交。Operational Dataset 在您要创建的线程网络进行配置。

## 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 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”网络。

通过 RCP 连接器上的 ot-ctl

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

> scan
| J | Network Name     | Extended PAN     | PAN  | MAC Address      | Ch | dBm | LQI |
+---+------------------+------------------+------+------------------+----+-----+-----+
| 0 | OpenThread-c0de  | c0de7ab5c0de7ab5 | c0de | 1ed687a9cb9d4b1d | 11 | -36 | 232 |

使用 FTD 联接器上的 OpenThread CLI:

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

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

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

您可能会注意到,在两次扫描中,网络似乎无法连接(RCP 连接器和 FTD 连接器上的 J 列)。这仅仅意味着线程调试在网络上处于非活动状态。它也可以通过在带外设备上手动输入网络密钥进行带外连接。

8. 添加 RCP 连接器

让我们使用带外进程将 RCP 连接器添加到我们刚刚创建的线程网络中。扫描 RCP 连接器上的网络:

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

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

如需加入,请在 RCP 连接器的活跃数据集中设置网络密钥(我们刚刚从 FTD 专员获得)。

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

> dataset networkkey 1234c0de7ab51234c0de7ab51234c0de
Done
> dataset commit active
Done

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

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

> dataset
Network Key: 1234c0de7ab51234c0de7ab51234c0de

启动线程,以便 RCP 连接器加入“Codelab”网络。等待几秒钟,检查状态、RLOC16 及其 IPv6 地址:

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

> 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 专员,检查路由器和子表,以确认这两台设备属于同一网络。使用 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

对 RCP 连接器的网格本地地址(从 RCP 连接器的 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

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

otcodelab_top01C_2nodes.png

拓扑图

在完成此 Codelab 的其余部分时,每当网络状态发生变化时,我们都会展示一个新的线程拓扑图。节点角色的表示方式如下:

b75a527be4563215.png

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

9. 委托 FTD 成员执行加入测试

现在,我们将第三个线程设备添加到“Codelab”网络。这次,我们将采用更安全的带内调试流程。在 FTD 连接器上,扫描网络:

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

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

J 列中的 0 表示设备未启用线程调试。

下面具体说明如何在该设备上进行调试,且仅允许 FTD 加入者加入。还是在 FTD 联接器上,获取 eui64,以便 FTD 专员可以识别它:

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

> eui64
2f57d222545271f1
Done

FTD 调试程序上,启动调试程序,并指定可以加入的设备的 eui64 以及联接器凭据,例如 J01NME。联接器凭据是所有大写字母数字字符(0-9 和 AY,不包括 I、O、Q 和 Z,以提高可读性)的设备特定字符串,长度在 6 到 32 个字符之间。

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

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

切换到 FTD 连接程序,然后重新扫描:

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

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

如 J 列中的 1 所示,线程调试现在在网络上处于有效状态。使用您刚刚在 FTD 专员上设置的联接器凭据启动联接器角色:

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

> ifconfig up
Done
> joiner start J01NME
Done

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

## 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 专员并检查路由器和子表,以确认“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 连接程序以终端设备(子级)的形式连接到网络。下面是更新后的拓扑:

otcodelab_top01C_ed01.png

10. 线程的实际运用

本 Codelab 中的线程设备是特定类型的全线程设备 (FTD),称为路由器符合条件的最终设备 (REED)。也就是说,设备可以充当路由器或最终用户设备,并且可以本身从端点设备升级为路由器。

线程可支持多达 32 个路由器,但会尝试将路由器数量保持在 16 到 23 个之间。如果 REED 作为最终用户设备(设备)挂接且路由器数量低于 16 个,则在两分钟内的随机时间段过后,该路由器会自动提升为路由器。

如果在添加 FTD 连接器后您的线程网络中有两个子级,则请等待至少两分钟,然后在 FTD 调试程序上重新检查路由器和子表:

## 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 连接符(扩展 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 连接符从路由器降级回最终用户设备来测试此行为。将状态更改为子级,并检查 RLOC16:

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

> state child
Done
> rloc16
0c03
Done

otcodelab_top01C_ed02.png

返回 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 的路由器。

otcodelab_top01C.png

移除领先变体

主要线程在所有线程路由器中是自选的。这意味着,如果当前主要成员从线程网络中移除,其他一个路由器将成为新的主要路由器。

FTD 专员上,关闭线程以将其从线程网络中移除:

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

> thread stop
Done
> ifconfig down
Done

在两分钟内,FTD Joiner 将成为新的 Thread Leader。检查 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 指示。为了将线程网络保持在一起,它将父路由器从 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 专员

具有两个节点的线程网络并不容易。让 FTD 专员恢复在线状态。

FTD 专员上,重启线程:

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

我们的线程网络再次包含三个节点。

11. 问题排查

管理位于不同终端窗口或屏幕窗口上的多台设备的线程网络可能很复杂。如果遇到问题,请参考这些提示来“重置”网络或工作区的状态。

Screen

如果您的配置丢失(屏幕屏幕过多或屏幕内部屏幕过多),请使用 Ctrl+a → k 终止屏幕窗口,直到不存在屏幕为止;screen -ls命令行输出No Sockets found中披露政府所要求信息的数量和类型。然后为每台设备重新创建屏幕窗口。即使屏幕已终止,设备状态仍将保留。

线程节点

如果线程网络拓扑与本 Codelab 中的描述不同,或者节点由于某种原因断开连接(可能是因为支持它们的 Linux 机器进入休眠状态),则您最好关闭线程,清除网络凭据,然后从 { 创建线程网络步骤。

如需重置 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. 使用多播

多播功能用于将信息一次性传递给一组设备。在线程网络中,特定地址会预留给多播设备用于不同设备组,具体取决于范围。

IPv6 地址

范围

收件人

ff02::1

本地链接

所有 FTD 和 MED

ff02::2

本地链接

所有 FTD 和边界路由器

ff03::1

网状网-本地

所有 FTD 和 MED

ff03::2

网状网-本地

所有 FTD 和边界路由器

在本 Codelab 中,我们没有使用边界路由器,因此我们来重点关注两个 FTD 和 MED 多播地址。

链路本地范围包括可通过单个无线装置传输或单个“跃点”到达的所有线程接口。 网络拓扑指示哪些设备响应 ping 以响应 ff02::1 多播地址。

FTD 专员那里执行 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 连接器和 RCP 连接器),但 FTD 专员仅收到了来自 FTD 连接器的本地链路地址 (LLA) 的响应。这意味着 FTD 连接程序是 FTD 专员可以在单个跃点上连接的唯一设备。

otcodelab_top02C_02_LL.png

现在从 FTD 连接程序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。

otcodelab_top02C_02_LL02.png

这意味着 FTD 连接器与 FTD 专员直接连接,而 RCP 连接器又确认我们的拓扑。

网状网-本地

Mesh-Local 范围包括可在同一线程网络内访问的所有线程接口。让我们来看一看对 ff03::1 多播地址的 ping 的响应。

FTD 专员那里执行 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 连接器的 Mesh-Local EID (ML-EID,以d55f结尾)。这是因为网格局部范围包含整个线程网络。无论设备处于网络中哪个位置,它都会订阅 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 专员所需的时间要比到达 FTD 连接器所需的时间长(23 毫秒)。这是因为它必须向 FTD 专员搭建两个跃点,而 FTD 加入者对应一个跃点。

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

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

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

> state
child

13. 使用 UDP 发送消息

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

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

获取 FTD 连接程序的 Mesh-Local EID 地址。我们使用以下地址,因为它可以从线程网络中的任何位置进行访问。

## 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,并使用其 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. 恭喜!

您已经创建了一个物理线程网络!

b915c433e7027cc7.png

您现在已经了解:

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

后续步骤

在此 Codelab 基础上,尝试进行以下练习:

  • 使用 ot-cli-mtd 二进制文件将 FTD 连接板重新刷写为 MTD,并观察它从未将自身升级为路由器,也未曾尝试成为领先变体
  • 向网络添加更多设备(尝试其他平台!),通过使用路由器和子表以及对多播地址的 ping 来草拟拓扑
  • 使用 pyspinel 控制 NCP
  • 使用 OpenThread Border Router 将 NCP 转换为边界路由器,并将您的线程网络连接到互联网

更多详情

查看 openthread.ioGitHub,以获取各种 OpenThread 资源,包括:

参考: