低功耗无线:6LoWPAN、IEEE802.15.4 和 Raspberry Pi
物联网 (IoT) 是新兴领域之一。它承诺将传感器和执行器连接到互联网,实现双向数据流动,一旦连接到互联网,就成为新兴且令人兴奋的业务系统的一部分,最终进入大数据和人工智能领域。
物联网应用将依赖于庞大而复杂的系统。其中一个组成部分将是传感器和执行器与互联网之间的连接。这很可能是无线的,并且必须是低功耗的。如果您有数千个传感器,它们很可能依靠电池供电,并且您希望这些电池能持续数年而不是数天。
低功耗无线目前正朝着两个方向发展:跨度达 20-30 米的个人区域网络 (LoWPAN) 和跨度达 20 公里或更远的广域网络 (LPWAN)。物理层的技术完全不同,并导致不同的 Linux 解决方案。本文仅讨论 LoWPAN。
LoWPAN 的物理层由 IEEE802.15.4 规范定义。这定义了使用各种无线频段(例如 2.4GHz)的通信,范围约为 10 米,数据传输速率为 250kb/s——对于大多数传感器来说足够好,但不足以流式传输 MP3!
在 IEEE802.15.4 之上是各种协议:Zigbee、Z-Wave、Thread 等等。在这些协议中,只有 IETF 6LoWPAN 是开放标准,这也是 Linux 开发社区的落脚点。本文仅涵盖 6LoWPAN。我也忽略了其他无线系统,例如 Bluetooth LE。
6LoWPAN 和 Linux6LoWPAN 是基于 IEEE802.15.4 无线的 IPv6。这并非易事。IPv6 是为当前的互联网设计的,而 IEEE802.15.4 是为不同的环境设计的。您无需担心这种不匹配是如何克服的,但这确实意味着您需要意识到这里处理的是两个不同的层次:让两个无线设备相互通信,以及让网络层通过这些设备进行通信。
设备层是物理硬件选择发挥作用的地方。Linux 支持多种设备,例如 AT86RF230 系列、MRF24J40 和其他几种。内核需要编译进这些设备驱动程序,或者使其作为动态可加载模块提供。
网络层需要 6LoWPAN 支持。同样,内核需要编译进此支持或使其作为模块提供。这些模块是 ieee802154_6LoWPAN、ieee802154 和 mac802154 模块。
6LoWPAN 设备和 Raspberry PiRaspberry Pi 是一款很棒的玩具或功能齐全的 Linux 计算机,具体取决于您的观点。凭借其 GPIO 引脚,它可以充当连接传感器和执行器领域的桥梁,而借助以太网(以及 RPi3 上的 Wi-Fi),它可以成为 LAN 和 WAN 的一部分。对于物联网,它(和 Arduino)构成了物理世界和 ICT 世界之间的绝佳桥梁。但是,现在有 IEEE802.15.4 模块可用,它们可用于将 RPi 转变为“全功能 6LoWPAN 设备”。
我将 RPi 与 OpenLabs “Raspberry Pi 802.15.4 radio” 配合使用。这是一个 Atmel AT86RF233 无线电模块,位于一个小板上,带有一个接头,可将其直接插入 RPi 的引脚 15-26。它可以朝外或朝内插入——朝内是正确的插入方式。
我开始使用标准的 Raspbian 发行版(日期为 2016 年 5 月 27 日)。可以将其设置为识别无线电模块,但是——哎呀!——它使用的 4.4 Linux 内核具有 6LoWPAN 模块,但这些模块在该内核中无法正常工作。即使是 ping 自己,IPv6 数据包也会损坏,因此此 Raspbian 发行版不支持 6LoWPAN。
因此,现在的目标是找到一种设置,使 RPi 能够通过 AT86RF233 无线电模块支持 6LoWPAN。这很痛苦:许多有用的站点已经过时,或者其中的说明我就是无法使其工作。我最终在 Sebastian Meiling 的页面 “Create a generic Raspbian image with 6LoWPAN support” 上找到了答案。总而言之,需要的是上游 Linux 内核 4.7 或 4.8、最新的固件以及 /boot/config.txt 文件的适当配置。在撰写本文时,这些说明仅适用于 RPi 1 和 2。RPi 3 尚无法工作,但可能在本文发布时可以工作。
安装 6LoWPAN 内核在本文中,我将在 RPi 2B 上使用 OpenLabs 模块。对于其他模块和 RPi,请参阅 Sebastian 的页面。我还假设您具有安装软件和从源代码构建的合理 Linux 知识。
首先安装最新的 Raspbian 镜像。如果它运行 4.7(或更高版本)内核,您可能已经可以了;否则,您需要构建并安装上游 4.7 内核。您可能需要额外的工具来执行此操作,例如 rpi-update
、git
、libncurses5-dev
、bc
以及您可以使用 apt-get
安装的开发工具。
在执行任何其他操作之前,请通过运行以下命令确保您的系统是最新的
rpi-update
这将安装最新的固件引导加载程序。
使用以下命令将 4.7 内核下载到 linux-rpi2 目录中
git clone --depth 1 https://github.com/raspberrypi/linux.git \
--branch rpi-4.7.y --single-branch linux-rpi2
构建内核意味着编译大量文件,并且在 RPi 上非常慢。大多数人建议交叉编译,但这更复杂,我喜欢简单的事情。因此,我更喜欢在 RPi 本身上构建。这只需要大约 5 个小时,所以启动它,然后睡觉或外出,听听爵士乐并晚点回来。
在 linux-rpi2 目录中,使用以下命令为 RPi 2B 设置配置文件
make bcm2709_defconfig
然后运行 menuconfig
来执行两件事
1) 从菜单项安装设备驱动程序作为模块
Device Drivers
--> Network device support
--> IEEE 802.15.4 drivers
2) 从菜单项安装 6LoWPAN 支持作为模块
Networking support
--> Networking options
--> IEEE Std 802.15.4 Low-Rate Wireless Personal Area
Networks support
使用以下命令构建内核和关联文件
make zImage modules dtbs -j4
五个小时后,安装模块和 dtbs 文件
sudo make modules_install dtbs_install
安装内核最安全的方法是将其复制到适当的位置。当我在源树中运行 make kernelversion
时,它告诉我我已经构建了 4.7.2。因此,我在复制内核时使用了该编号
sudo cp arch/arm/boot/zImage /boot/kernel.4.7.2.img
这样我就不会破坏任何现有镜像,因此我可以安全地回退到之前的系统。
最后,您需要告诉 RPi 启动到新内核中。以 root 用户身份,编辑 /boot/config.txt 并在末尾添加以下行
kernel=kernel.4.7.2.img
device_tree=bcm2709-rpi-2-b.dtb
dtoverlay=at86rf233
这有什么作用?首先,它告诉 RPi 使用新的引导镜像 kernel.4.7.2.img。其次——这目前是 ARM 特有的——它告诉 RPi 使用来自 bcm2709-rpi-2-b.dtb 的设备树系统来获取硬件默认值。第三——这是 RPi 特有的——它说要将 at86rf233 设备添加到设备树文件的附加文件中。
最后...重启。如果一切顺利,您应该已经运行了新内核。使用以下命令检查
uname -a
它应该显示类似这样的内容
Linux raspberrypi 4.7.2-v7+ #1 SMP Fri Aug 26 15:45:29 UTC 2016
↪armv7l GNU/Linux
如果它没有启动或显示错误的内核,请将您的 SD 卡带回其他地方,以便您可以注释掉您添加到 /boot/config.txt 的行。回到 RPi 上,重新启动回默认内核,并尝试找出哪个步骤出了问题。我跳过了 Sebastian 指南中的一些步骤,因为我不需要它们,但如果您的系统无法正常工作,请密切关注他的指南。他似乎非常认真地更新它。
设置 6LoWPAN您到了吗?抱歉,还没有。您已经构建并安装了带有 6LoWPAN 支持的上游内核。不过,您已经完成了一半以上。要配置 6LoWPAN 堆栈,您需要另一个工具 wpan-tools
。从 GitHub 获取它
git clone --depth 1 https://github.com/linux-wpan/wpan-tools.git
↪wpan-tools
不过,在构建它之前,您需要 autoreconf
sudo apt-get install dh-autoreconf
然后在 wpan-tools 目录中,您可以运行
./autogen.sh
./configure CFLAGS='-g -O0' --prefix=/usr --sysconfdir=/etc
↪--libdir=/usr/lib
make
sudo make install
这里发生了什么?Linux 是 UNIX 操作系统家族(包括 BSD 和许多其他操作系统)的一部分。它们都有怪癖,源代码作者必须处理这些怪癖。已经有很多工具可以简化这种管理,wpan-tools
使用 autoreconf
构建配置文件,然后使用 configure
来计算您的 RPi 系统的具体细节,以便当您 make
您的应用程序时,所有正确的部件都已就位。
这样做的结果是,应用程序 iwpan
现在位于 /usr/bin 目录中以供使用。
您快到了!请记住,在内核配置中,您将 6LoWPAN 和设备驱动程序设置为动态模块。它们不会像您期望的模块那样默认安装。这就是所有这些设备树内容的目的——在系统无法正常检测到设备时将其引入系统。因此,下一步是加载模块
sudo modprobe at86rf230
然后 lsmod
应该包含类似这样的内容
Module Size Used by
ieee802154_6LoWPAN 19335 0
6LoWPAN 13191 8 nhc_fragment,ieee802154_6LoWPAN
at86rf230 22211 0
mac802154 49035 1 at86rf230
ieee802154 55698 2 ieee802154_6LoWPAN,mac802154
crc_ccitt 1278 1 mac802154
现在——哒哒!——iwpan list
显示类似这样的内容
wpan_phy phy0
supported channels:
page 0: 11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26
current_page: 0
current_channel: 13, 2415 MHz
cca_mode: (1) Energy above threshold
cca_ed_level: -77
tx_power: 4
capabilities:
iftypes: node,monitor
channels:
page 0:
[11] 2405 MHz, [12] 2410 MHz, [13] 2415 MHz,
[14] 2420 MHz, [15] 2425 MHz, [16] 2430 MHz,
[17] 2435 MHz, [18] 2440 MHz, [19] 2445 MHz,
[20] 2450 MHz, [21] 2455 MHz, [22] 2460 MHz,
[23] 2465 MHz, [24] 2470 MHz, [25] 2475 MHz,
[26] 2480 MHz
tx_powers: 4,3.7,3.4,3,2.5,2,1,0,-1,-2,-3,-4,-6,-8,-12,-17
cca_ed_levels: -91,-89,-87,-85,-83,-81,-79,-77,-75,-73,-71,
↪-69,-67,-65,-63,-61
cca_modes:
(1) Energy above threshold
(2) Carrier sense only
(3, cca_opt: 0) Carrier sense with energy above threshold
↪(logical operator is 'and')
(3, cca_opt: 1) Carrier sense with energy above threshold
↪(logical operator is 'or')
min_be: 0,1,2,3,4,5,6,7,8
max_be: 3,4,5,6,7,8
csma_backoffs: 0,1,2,3,4,5
frame_retries: 0,1,2,3,4,5,6,7
lbt: false
Supported commands:
...
您的 6LoWPAN 设备现在已为 Linux 系统所知。
配置 6LoWPAN所以现在您有了一个新内核,您已经识别了 at86rf230 设备,并且 6LoWPAN 网络堆栈已就位。最后一步是配置网络并启动设备。您可能习惯了 Wi-Fi 网络具有 SSID。IEEE802.15.4 网络具有类似的概念,即 PAN ID。只有当两个设备具有相同的 PAN ID 时,它们才会在同一个网络上。您可以使用 iwpan
来设置它
iwpan dev wpan0 set pan_id 0xbeef
0xbeef
的 ID 不是固定的,但每个示例似乎都使用它!
然后,您可以使用正常的网络工具启动接口
ip link add link wpan0 name lowpan0 type lowpan
ifconfig wpan0 up
ifconfig lowpan0 up
您现在有什么?ifconfig
返回类似这样的内容
lowpan0 Link encap:UNSPEC HWaddr
↪EE-0B-FB-0F-76-B9-F3-93-00-00-00-00-00-00-00-00
inet6 addr: fe80::ec0b:fb0f:76b9:f393/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1280 Metric:1
RX packets:38 errors:0 dropped:0 overruns:0 frame:0
TX packets:39 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1
RX bytes:5205 (5.0 KiB) TX bytes:5205 (5.0 KiB)
wpan0 Link encap:UNSPEC HWaddr
↪EE-0B-FB-0F-76-B9-F3-93-00-00-00-00-00-00-00-00
UP BROADCAST RUNNING NOARP MTU:123 Metric:1
RX packets:58 errors:0 dropped:0 overruns:0 frame:0
TX packets:55 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:300
RX bytes:4111 (4.0 KiB) TX bytes:4904 (4.7 KiB)
接口 wpan0
是无线设备。接口 lowpan0
是 6LoWPAN 网络设备,就像 eth0
、环回设备等等一样。请注意它如何具有 IPv6 地址,但没有 IPv4 地址——这只是下一代 IP!
您完成了!好吧,几乎。有一部古老的 B.C. 漫画,其中一个角色发明了电话。“我们给谁打电话?”他的朋友问道。“我只发明了一个”,这是回复。您需要有人交谈。因此,使用另一个 RPi 再次执行所有这些操作。您确实购买了两个 RPi 和两个无线模块,不是吗?
ifconfig
命令告诉您 6LoWPAN 设备的 IPv6 地址。从 other
设备,一旦您设置好它,执行
ping6 -I lowpan0 fe80::ec0b:fb0f:76b9:f393 # IPv6 address of
# the other device
或
ping6 fe80::ec0b:fb0f:76b9:f393%lowpan0
ping6
命令是 ping
的 IPv6 版本。每个网络接口的 IPv6 地址都是自动分配的,并且是链路本地地址。
如果您有多个接口,则每个接口都可以位于具有不可路由链路本地地址的网段上。这些不同网段上的主机可以具有相同的地址。这些就像 IPv4 链路本地地址 169.254.0.0/16,它们无法跨不同的网段路由。因此,在 Linux 中,您需要指定要使用的接口 (lowpan0
) 以避免可能的混淆。有两种方法可以做到这一点:要么使用 -I lowpan0
选项,要么将 %lowpan0
附加到 IPv6 地址。
在我的系统上,这会产生
$ping6 -I lowpan0 fe80::ec0b:fb0f:76b9:f393
PING fe80::ec0b:fb0f:76b9:f393(fe80::ec0b:fb0f:76b9:f393) from
↪fe80::f0f9:a4ed:3cad:d1de lowpan0: 56 data bytes
64 bytes from fe80::ec0b:fb0f:76b9:f393: icmp_seq=1 ttl=64
↪time=11.6 ms
64 bytes from fe80::ec0b:fb0f:76b9:f393: icmp_seq=2 ttl=64
↪time=11.1 ms
64 bytes from fe80::ec0b:fb0f:76b9:f393: icmp_seq=3 ttl=64
↪time=10.5 ms
成功!这两个设备可以通过 6LoWPAN 相互 ping 通。如果它不起作用怎么办?好吧,它在我这里很长一段时间都没有工作,找出故障发生在哪里是很痛苦的。事实证明是 6LoWPAN 的内核错误。要进行故障排除,首先保持运行 ifconfig
。这会告诉您哪些接口正在获取和发送数据包。它告诉我无线层 (wpan0
) 正在获取和接收数据包,但网络层没有。然后我使用选择器 ip6
在数据包上运行 wireshark
,它向我显示了网络层的错误。命令 dmesg
给了我黄金,告诉我 IPv6 数据包已损坏,即使是 ping 自己时也是如此。
在绝望中,我向 Sebastian 求助,向他提供了尽可能多的信息(uname
、使用 /opt/vc/bin/vcgencmd 的固件版本、/boot/config.txt 的内容、使用 dtc -I fs /proc/device-tree
反编译设备树,然后是 wireshark
和 dmesg
报告)。他只需要第一行:错误的内核。但是,花时间整理一份详细的报告至少表明您是认真的。“Duh,它不起作用”对维护人员没有帮助!
您实际上不需要 6LoWPAN 在 Raspberry Pi 之间进行通信。Wi-Fi 和以太网更好。但是现在假设其中一个是一个由电池或太阳能电池板供电的传感器。据估计,Wi-Fi 会在两周内耗尽电池电量;而 6LoWPAN 在电池上的预期运行时间为数年。我在这里模拟这种情况,为了方便起见,将其中一个 RPi 用作传感器。
要继续操作,您需要设置一个客户端-服务器系统。通常,人们认为服务器是某个地方的大型笨重机器,但在物联网世界中,传感器将是服务器,处理来自网络中其他位置的客户端的值请求。
服务器就像一个普通的 IPv6 服务器,如 Python 文档:18.1. socket
— 低级网络接口 中所述。但请注意,就像上面的 ping6
命令一样,您需要指定要使用的网络接口。这意味着您必须使用 Python 3 而不是 Python 2,因为 Python 3 具有套接字函数 socket.if_nametoindex()
,您可以使用该函数指定 IPv6 “scope id”,即您使用的接口。
我不想用如何在 RPi 上添加传感器来使本文复杂化。相反,我将只测量 RPi 的 CPU 温度,因为可以通过从 shell 运行此命令轻松找到它
vcgencmd measure_temp
这将返回一个类似这样的字符串
temp=36.9'C
在 Python 中,您创建一个进程来使用 Popen
运行此命令并从 stdout
管道读取。
这是一个 IPv6 TCP 服务器,它等待连接,发送温度,然后关闭连接
#!/usr/bin/python3
import socket
from subprocess import PIPE, Popen
HOST = '' # Symbolic name meaning all available interfaces
PORT = 2016 # Arbitrary non-privileged port
def get_cpu_temperature():
process = Popen(['vcgencmd', 'measure_temp'], stdout=PIPE)
output, _error = process.communicate()
return output
def main():
s6 = socket.socket(socket.AF_INET6, socket.SOCK_STREAM, 0)
scope_id = socket.if_nametoindex('lowpan0')
s6.bind((HOST, PORT, 0, scope_id))
s6.listen(1)
while True:
conn, addr = s6.accept()
conn.send(get_cpu_temperature())
conn.close()
if __name__ == '__main__':
main()
并且,这是一个客户端,它打开连接并每十秒读取一次温度
#!/usr/bin/python3
import socket
import time
ADDR = 'fd28:e5e1:86:0:e40c:932d:df85:4be9' # the other RPi
PORT = 2016
def main():
# scope_id = socket.if_nametoindex('lowpan0')
while True:
s6 = socket.socket(socket.AF_INET6, socket.SOCK_STREAM, 0)
s6.connect((ADDR, PORT, 0, 0))
data = s6.recv(1024)
print(data.decode('utf-8'), end='')
# get it again after 10 seconds
time.sleep(10)
if __name__ == '__main__':
main()
输出看起来像这样
temp=37.4'C
temp=37.4'C
temp=37.9'C
我的服务器地址是什么?
所以想象一下,您现在在野外散布了 1,000 个这样的传感器,并且它们都在运行 IPv6 服务器。它们的地址是什么?您如何与它们交谈?不幸的是,OpenLabs 模块每次启动时都会生成一个新的 MAC 地址,因此每次都会生成一个新的 IPv6 地址。不建议在这些低功耗网络上运行多播发现,因为它会消耗电量。我将在下一篇文章中稍微作弊一下,但在第三篇文章中展示更好的方法。
结论上一节中介绍的场景仍然有点不切实际。如果您有足够的电力来驱动 RPi 作为传感器,您可能也有足够的电力让它使用 Wi-Fi 或以太网。但很快就会出现使用 6LoWPAN 的真正低功耗传感器,本文向您展示了如何将它们引入一个特定的 Linux 系统。这非常困难,但目前这都是前沿技术,所以要准备好付出一些努力!
在我的下一篇文章中,我将描述如何将 6LoWPAN 网络引入标准 IPv6 世界,在第三篇文章中,我计划研究 CoAP,它是低功耗网络的 HTTP 等价物。
资源OpenLabs Raspberry Pi 802.15.4 Radio
Python API: socket
— Low-level networking interface
IETF RFC4944: Transmission of IPv6 Packets over IEEE 802.15.4 Networks (6LoWPAN)
6LoWPAN: The Wireless Embedded Internet by Zach Shelby, Wiley 2009
Create a generic Raspbian image with 6LoWPAN support by Sebastian Meiling