本文是对 Thoughts on going down the network stack 的整理与翻译。
内容结构概览
- 文章背景:作者手头还有 ELF packer 系列和 Rust book/series,但又开始看嵌入式硬件。
- 写作方法:作者最喜欢边学边写,而不是只整理已经掌握的知识。
- 新玩具:BBC micro:bit:因为价格合适、能寄到法国、且有人跑过 Rust,作者买了 micro:bit。
- 核心芯片:nRF51822:它是一个带 ARM Cortex-M0、Flash/RAM 和 2.4GHz radio 的 SoC。
- 第一反应:2.4GHz 能做什么:微波炉、Bluetooth Low Energy、802.11g/Wi-Fi 都和 2.4GHz 有关系。
- 离谱想法:既然 micro:bit 有 2.4GHz radio,能不能让它不跑 BLE,而是尝试跑 802.11g?
- 为什么这个想法很吸引人:作者喜欢让项目接入真实世界系统,就像自制动态加载器能跑 GCC 产物,自制 ICMP 能 ping 真实服务器。
- 初步怀疑一:无线功率不够:BLE 是低功耗短距离协议,Wi-Fi 对功率和通信方式要求不同。
- 初步怀疑二:CPU 太慢:802.11 某些应答时序非常短,大概在 10 微秒量级。
- 真正阻碍:调制方式不同:BLE 和 802.11g 的低速模式使用不同调制方式,硬件 radio 不是"随便读写 bit"的通用接口。
- AM / FM 类比:模拟调制可以改变振幅或频率,数字调制则用波形状态表达 0 和 1。
- BLE 的 GFSK:Bluetooth 使用 Gaussian Frequency Shift-Keying,用不同频率表达 0 和 1。
- 802.11g 的 BPSK:802.11g 低速模式使用 Binary Phase Shift Keying,改变的是波的相位。
- 为什么 nRF51 不能直接做:调制/解调不是 CPU 软件随便控制,而是 radio 硬件负责;BLE 硬件不等于 Wi-Fi 硬件。
- FPGA 的可能性:理论上可以用 FPGA 自己做调制/解调,但这需要额外硬件和昂贵测量设备。
- 作者没有放弃:micro:bit 还能玩 LED array;更重要的是,他对调制方式产生了兴趣。
- 新方向:软件模拟网络底层:不真实模拟物理空间里的波,而是加一点随机噪声和信号损耗,做一个可跟读者一起实现的系列。
- 文章意义:这不是一篇"我已经做成了 Wi-Fi"的文章,而是一次从网络栈继续往下、走到无线信号层的探索起点。
fasterthanlime 的很多文章都有一个共同特点:作者不是站在终点写总结,而是边学边写。比如之前的 ELF executable packer 系列,他不是先把 ELF 全部研究透,再写一篇教科书式文章;而是一边遇到问题,一边查资料,一边把自己掉进坑里的过程写出来。这样写出来的内容往往更有现场感,因为读者能跟着作者一起问问题,而不是只看结论。
这篇文章也是这种路数。
作者当时手头还有不少写作项目:一个是自制 executable packer 系列的最后几篇还没编辑发布,另一个是刚宣布要写 Rust book/series。但他又开始看新的东西:嵌入式硬件。
为什么是嵌入式?因为他看到很多 "Rust on embedded" 的讨论,而小硬件天然有一种很酷的吸引力。于是他买了一块 BBC micro:bit。
然后,一场很典型的 fasterthanlime 式探索开始了:看到板子上有 2.4GHz radio,他立刻想,既然都是 2.4GHz,那能不能让它跑 Wi-Fi?
答案当然没有这么简单。真正的问题,也不是软件工程师一开始最容易想到的那些问题。
一、为什么要买 micro:bit
作者买的是 BBC micro:bit。
理由很朴素:价格合理,能比较快寄到法国,而且他已经看到有人在它上面跑 Rust。对于一个想从嵌入式入门的人来说,这些理由都很正常。micro:bit 本来就是面向教育和入门场景的开发板,门槛不高,可玩性也不错。
micro:bit 上的核心芯片是 nRF51822。它是 Nordic Semiconductor 的一个 SoC,也就是 System on a Chip。这个芯片围绕 32 位 ARM Cortex-M0 CPU 构建,有一定容量的 flash 和 RAM,同时带有一个 2.4GHz radio。
这几个词放在一起,对软件工程师来说就很诱人:
text
ARM Cortex-M0
低功耗
Bluetooth Low Energy
2.4GHz radio
可以跑 Rust
嵌入式项目最常见的入门玩法当然是点灯。板子上有 LED array,可以让灯闪起来、滚动文字、做小游戏。作者也知道这很自然。
但他的注意力被另一个东西吸引了:2.4GHz radio。
2.4GHz 这个频段太熟悉了。蓝牙在这里,Wi-Fi 也在这里,微波炉也在这里。于是他开始想:既然 micro:bit 的芯片有 2.4GHz radio,那能不能让它做点更野的事?
比如,跑 802.11g。
也就是 Wi-Fi。
二、一个很傻,但很有趣的想法
这个想法听起来确实很傻:拿一个本来面向 Bluetooth Low Energy 的小芯片,试图让它实现 802.11g。
作者自己也知道这个想法很可能行不通。但他喜欢这种"很傻但很有趣"的想法。
这和他以前做过的一些项目很一致。他喜欢那种能接入真实世界系统的项目。比如自制 ELF dynamic loader,如果它能运行 GCC 编译出来的真实程序,就很有成就感;再比如自制 userland ICMP implementation,如果它能 ping 真实互联网服务器,也会比只在玩具环境里模拟更有意思。
所以,如果能让一块 micro:bit 跟真实 Wi-Fi 网络互动,那就太酷了。
当然,刚开始他也列出了一些可能的阻碍。
第一,radio 功率可能不够。Bluetooth Low Energy 是短距离、低功耗通信。Wi-Fi 对功率、速率、距离、协议时序都有不同要求。也许 micro:bit 的 radio 太弱,根本没法像 Wi-Fi 设备那样通信。
但作者想,也许如果把 micro:bit 贴得离 access point 特别近,说不定有机会。
第二,CPU 可能太慢。nRF51822 使用 ARM Cortex-M0,这不是一个很强的 CPU。而 802.11 里有些交互时序非常短。有些消息的处理和响应窗口可能在 10 微秒量级。对一个小 MCU 来说,这可能非常困难。
这两个怀疑都很合理。
但后来作者和一些懂无线网络的人聊过之后,发现真正的停止点不是这些。
真正的问题是:Bluetooth LE 和 802.11 使用的调制方式不同。
这句话是整篇文章的核心。
三、软件工程师容易犯的天真错误:以为 radio 就是在收发 bit
从软件工程师视角看,数据通常是字节。字节是 8 个 bit。我们经常把世界抽象成:
text
发送方写一串 bit
接收方读一串 bit
中间有某种管道负责传输
如果你不太了解无线通信,很容易以为 radio 模块也是这样:它给你一个接口,让你按某个速率读写 bit。只要频段对了,协议栈自己写就行。
但真实世界不是这样。
数据从天线出去时,不是以"bit"这种形式在空气里飘。它会变成模拟信号,变成电磁波。也就是说,数字信息必须被编码进波形里。接收端再从波形中恢复出数字信息。
这个过程就是调制和解调。
所以问题不只是"这个 radio 能不能发 2.4GHz 信号",而是"这个 radio 的硬件支持哪种调制方式,能不能产生和接收 802.11 需要的波形"。
这就不是随便写点 Rust 或 ARM 汇编能解决的了。
四、AM 和 FM:先从模拟调制理解
文章用 AM 和 FM 做类比。
AM,也就是 Amplitude Modulation,调的是振幅。振幅可以粗略理解成波上下摆动的幅度。你可以把信息编码到波的"高低强弱"里。
FM,也就是 Frequency Modulation,调的是频率。频率可以粗略理解成波上下摆动得有多快。你可以把信息编码到波的"快慢变化"里。
这两个词大家应该都听过,比如 AM radio、FM radio。它们是模拟调制的例子。音频本身也是波,所以把音频信息叠到无线电波上,可以通过改变振幅或频率来做。
数字调制也有类似思想。只不过你不是传连续的音频波形,而是传数字信息。你要定义某些波形状态代表 0,某些波形状态代表 1,或者更进一步代表多个 bit 的组合。
这时就有很多调制方式。
Bluetooth LE 使用的是 GFSK,也就是 Gaussian Frequency Shift-Keying。Frequency Shift-Keying 的核心思想是用不同频率表达不同数字状态。比如某个频率代表 0,另一个频率代表 1。Gaussian 的部分表示在变化时做了某种高斯滤波,使频率切换更平滑,减少带外干扰。
而 802.11g 在作者当时看的低速模式里,使用的是 BPSK,也就是 Binary Phase Shift Keying。它不是改变频率,也不是改变振幅,而是改变相位。
相位可以粗略理解成波当前处在周期里的哪个位置。两个波频率和振幅都一样,但如果一个整体向前或向后偏移了一点,它们的相位就不同。BPSK 就利用这种相位变化来表达 0 和 1。
所以,BLE 和 802.11g 虽然都可以工作在 2.4GHz 频段,但它们不是"同一种 radio 软件协议"的差别,而是底层波形编码方式就不同。
这就像两个人都在同一个房间里说话,但一个用普通话,一个用摩斯电码敲墙,另一个用灯光闪烁。它们共享空间,不代表能互相理解。
五、为什么不能用 CPU 自己做调制
那能不能绕过硬件 radio 的调制能力,用 CPU 自己生成想要的信号?
理论上,软件定义无线电,也就是 SDR,确实可以做类似事情。你可以用更通用的硬件采样和生成信号,再在软件中处理调制、解调、滤波、同步等流程。
但 nRF51 这类芯片不是那样设计的。
在 Bluetooth LE 芯片里,协议栈的一部分可以跑在 CPU 上,比如连接管理、包处理、状态机等。但真正的 modulation / demodulation,也就是把数字信号变成无线波形、再从无线波形恢复数字信号,通常由专用 radio 硬件完成。
你可以配置一些参数,但不能随便让它变成另一种完全不同的调制器。
作者进一步查资料后发现,理论上如果你用 FPGA,是有可能自己做调制/解调的。FPGA 可以让你搭出比较底层的数字逻辑,配合额外硬件,去实现信号处理链路。
但这已经远远超出"买一块 micro:bit 玩 Rust"的范围了。
首先,FPGA 编程本身就不如写 ARM 汇编那么舒服。其次,还需要额外硬件。再其次,调试这种东西需要非常贵的测量设备。普通软件调试可以打日志、跑单测、开 debugger;无线信号调试要看波形、频谱、时序,示波器和频谱仪都不便宜。
所以,对一个第一个嵌入式硬件项目来说,拿 micro:bit 做 Wi-Fi 基本可以判定为不现实。
不是因为想法不酷,而是因为底层物理和硬件边界在那里。
六、这个失败为什么反而有价值
有意思的是,作者并没有因此沮丧。
一方面,micro:bit 仍然能做别的项目。它有 LED array,有按钮,有传感器,有 BLE。哪怕只是点灯,也能做出很多可玩的东西。
另一方面,更重要的是:这个失败把作者引到了一个新的好奇点。
他本来以为自己只是想玩嵌入式硬件,结果真正吸引他的变成了调制方式、无线信号和网络栈更底层的部分。
这也是文章标题 "going down the network stack" 的意思。
我们平时讲网络栈,通常从应用层往下看:
text
HTTP
TLS
TCP / UDP
IP
Ethernet / Wi-Fi
物理层
很多软件工程师即使懂一点 TCP/IP,也通常停在比较高的层次。比如会写 HTTP client,会抓包看 TCP handshake,会理解 DNS、TLS、QUIC、ICMP、ARP,但很少继续往下看:以太网帧到底如何变成电信号,Wi-Fi 包如何变成无线波形,0 和 1 如何被调制进电磁波里。
作者之前做过 userland ICMP implementation,那已经比普通应用层低很多了。自己构造 ICMP 包、IPv4 包、ARP 包、Ethernet frame,已经很接近链路层。
但这篇文章想继续往下。
从网络协议栈走到物理层,就要面对完全不同的世界:波、频率、相位、噪声、损耗、滤波、同步、采样、调制、解调。
这对软件工程师来说是陌生的,但也很诱人。
七、既然没有硬件,那就模拟
作者最后提出了一个新想法:既然真实硬件做不了,那就用软件模拟。
当然,他不是要做精确的物理空间模拟。不是要模拟电磁波在三维空间里传播、反射、叠加、被墙体吸收、被天线接收。这太复杂,也不是写作项目的合适范围。
他想做的是更可控、更教学向的模拟:
text
在软件里实现调制和解调
加一点随机噪声
加一点人为信号损耗
观察数据如何被编码成波形
再从带噪声的波形里恢复出来
这就很有价值。
如果我们能在软件里实现一个简化版调制/解调流程,就能一步步理解:
text
bit 如何变成符号
符号如何变成波形
噪声如何影响信号
接收端如何采样
如何判断收到的是 0 还是 1
同步为什么重要
信号损耗会带来什么问题
再往上,就可以在这个模拟物理层之上继续构建协议。也许先做最简单的点对点传输,再做帧结构、校验和、重传、碰撞处理,甚至模拟一个小型网络。
这正符合作者一贯喜欢的写作方式:从一个问题出发,边学边实现,做一个读者可以跟着敲的系列。
硬件项目暂时受阻,但软件模拟反而可能变成更适合文章系列的方向。
八、为什么这种选题很适合 fasterthanlime
fasterthanlime 的文章有一个特点:它经常选择那些"软件工程师以为自己懂,但其实只懂上半截"的主题。
比如动态链接。你知道程序能运行,但不一定知道 ELF、relocation、dynamic loader、GOT、PLT 到底怎么工作。
比如 ICMP。你知道 ping 命令,但不一定知道 ICMP 包、IPv4 header、checksum、ARP、Ethernet frame 怎么拼。
比如 Rust async。你知道 .await,但不一定知道 Future、poll、Waker、Pin 背后是什么。
这篇文章的方向也是这样:你知道 Wi-Fi,知道蓝牙,知道 2.4GHz,知道网络栈,但不一定知道调制方式是什么。你可能以为 radio 就是读写 bit,结果发现空气里没有 bit,只有波。
这种"发现自己原来想得太天真"的瞬间,正是好技术文章的起点。
它不是为了嘲笑无知,而是为了把无知变成可探索的问题。
作者甚至在结尾引用了一句互联网老梗:在互联网上获得正确答案的最好方法,是发布错误答案。
这句话很适合这篇文章。作者承认自己不是无线通信专家,也担心一个人进入陌生领域会有点不安。但他已经有机会向一些懂无线网络的人问傻问题,以后也会继续得到反馈。
这正是边学边写的好处:你不需要一开始就成为专家。你只需要提出问题,认真追下去,并愿意被纠正。
九、从软件视角理解调制:为什么值得学
对普通后端工程师、系统工程师来说,调制方式好像离日常工作很远。你写 HTTP 服务、Kubernetes controller、数据库查询、RPC、日志系统,似乎不需要知道 BPSK 或 GFSK。
但理解它仍然有价值。
第一,它能让你更真实地理解"网络是不可靠的"。
我们经常说网络会丢包、会延迟、会乱序。但这些现象不是抽象规则。它们最终来自现实世界:信号会衰减,会被噪声干扰,会被其他设备占用频段,会被墙体影响,会因为硬件限制而无法正确恢复。
第二,它能打破软件抽象的幻觉。
很多软件工程师习惯把所有东西看成 API。socket 是 API,网卡是 API,radio 也是 API。但越往下走,抽象越不稳定,物理世界越明显。不是所有东西都能靠写软件绕过。
第三,它能帮助理解为什么协议设计这么复杂。
无线网络不是把 bit 丢进空气这么简单。它要解决同步、信道占用、冲突、重传、纠错、功率、带宽、兼容性等问题。越理解底层,就越能理解为什么上层协议会有那么多看似奇怪的设计。
第四,它能带来新的项目灵感。
如果你能用软件模拟调制/解调,就可以做很多教育向项目:从 bit 到 wave,再从 wave 回到 bit;从理想信道到噪声信道;从单个发送者到多个发送者;从无校验到有校验;从不重传到重传。这些都非常适合写成系列文章。
所以,这篇文章虽然短,但打开的门很大。
十、这篇文章和"Making our own ping"的关系
如果你读过作者的 "Making our own ping" 系列,会发现这篇文章像是那个系列的自然延伸。
在 "Making our own ping" 里,作者从用户态一点点构造 ping 所需的东西:ICMP 包、IPv4 包、Ethernet frame、ARP 查询、网关 MAC 地址、校验和、raw socket、抓包、发送和接收真实网络包。最终目标是不用系统自带 ICMP API,而是自己构建一部分协议栈。
那已经是在往网络栈下面走了。
但它仍然是在现有硬件和操作系统能力之上工作。网卡负责真正把 Ethernet frame 发出去,底层物理信号也由硬件处理。软件只是构造正确的包,让网卡发送。
这篇文章想继续追问:如果再往下呢?
Ethernet frame 或 Wi-Fi frame 发送出去之前,它到底怎么变成信号?bit 怎么在物理介质中表达?为什么 Bluetooth radio 不能直接发 Wi-Fi 包?为什么同样是 2.4GHz,协议仍然不能互通?
这就是从数据链路层继续走向物理层。
如果 "Making our own ping" 是"自己拼网络包",那这篇文章暗示的下一个系列可能是"自己模拟信号和调制"。
这两个方向合在一起,会构成一个非常完整的学习路径:
text
应用层:ping 命令
传输/网络层:ICMP、IPv4
链路层:Ethernet、ARP
物理层:调制、解调、信号、噪声
这正是"going down the network stack"的意思。
十一、micro:bit 的价值没有消失
虽然 micro:bit 不能直接变成 Wi-Fi 设备,但这不代表它没用了。
作者明确说,他还会找别的东西在 micro:bit 上做。LED array 本身就可以玩。嵌入式入门不一定非要一开始就做无线协议栈。点灯、按钮输入、简单显示、BLE 通信、传感器读取、低功耗模式、裸机 Rust、RTIC、embedded-hal,这些都是很好的方向。
有时候,项目失败并不是坏事。因为它帮你校准边界。
这次失败让作者知道:
text
nRF51 的 radio 不是通用软件无线电
BLE 和 Wi-Fi 的底层调制方式不同
CPU 速度和发射功率不是唯一问题
真正的无线底层需要更专业硬件
FPGA/SDR 是可能路径,但门槛更高
这些知识本身就是收获。
很多学习路径都是这样。你一开始提出一个幼稚问题,然后发现它幼稚在哪里,再顺着"为什么不行"继续学。最后你可能没有做成最初那个项目,但你获得了更清楚的问题地图。
十二、文章的现实意义
这篇文章对读者最大的价值,不是教你如何用 micro:bit 做 Wi-Fi。事实上,它明确说明这个方向不现实。
它真正展示的是一种学习方式:
text
看到一个硬件特性
提出一个大胆问题
先列出自己以为的阻碍
去问懂的人
发现真正阻碍不是自己想的那样
回过头补基础概念
把失败转化成新的可探索方向
这种方式非常适合工程师。
因为工程学习不是只读文档。很多时候,你必须亲自问一个"看起来有点傻"的问题。比如:
text
既然都是 2.4GHz,为什么 BLE 芯片不能跑 Wi-Fi?
radio 不是收发 bit 吗?
CPU 能不能自己生成波形?
FPGA 为什么可以?
为什么还需要示波器?
什么是调制?
什么是相位?
什么是 BPSK?
什么是 GFSK?
这些问题一个个追下去,就从"我想玩 micro:bit"变成了"我想理解无线通信底层"。
这就是好问题的力量。
十三、总结
这篇文章记录了作者从网络协议栈继续往下探索的一次起点。作者原本还有 ELF packer 系列和 Rust book/series 等写作项目,但他发现自己最擅长的写作方式是边学边写。于是,在看到很多 "Rust on embedded" 讨论后,他买了一块 BBC micro:bit,准备进入嵌入式硬件领域。
micro:bit 上的 nRF51822 SoC 带有 ARM Cortex-M0 CPU、一定容量的 flash/RAM,以及 2.4GHz radio。作者看到 2.4GHz 后,立刻想到 Bluetooth Low Energy、微波炉,以及 802.11g,也就是 Wi-Fi。于是产生了一个很离谱但很有趣的想法:能不能让这个 BLE 芯片跑 802.11g?这个想法之所以吸引人,是因为作者喜欢让项目接入真实世界系统。就像自制 ELF loader 能跑 GCC 产物,自制 userland ICMP 能 ping 真实服务器,如果 micro:bit 能和真实 Wi-Fi 网络通信,那会非常酷。
作者一开始猜测阻碍可能是 radio 功率不够,毕竟 BLE 是低功耗短距离通信;也可能是 ARM Cortex-M0 CPU 太慢,因为 802.11 某些应答时序可能只有 10 微秒左右。但和懂无线网络的人交流后,他发现真正阻碍不是这些,而是调制方式不同。软件工程师很容易把 radio 想成一个按速率读写 bit 的设备,但数据离开天线时不是 bit,而是模拟波形。数字信息必须通过调制编码进波里,接收端再解调回来。
文章用 AM 和 FM 解释调制:AM 改变振幅,FM 改变频率。数字调制也类似,只是用某些波形状态表示 0 和 1。Bluetooth LE 使用 GFSK,也就是 Gaussian Frequency Shift-Keying,通过不同频率表达数字状态;而 802.11g 在作者关注的低速模式中使用 BPSK,也就是 Binary Phase Shift Keying,通过改变相位表达数字状态。虽然 BLE 和 Wi-Fi 都在 2.4GHz 频段,但底层波形编码方式不同。nRF51 的 radio 硬件支持 BLE 的调制/解调,不等于它能随便生成 Wi-Fi 所需的调制信号。
理论上,可以用 FPGA 和额外硬件自己做调制/解调,但这已经超出第一个嵌入式项目的范围。FPGA 编程不如写 ARM 汇编舒服,调试还需要昂贵的示波器、频谱仪等测量设备。因此,用 micro:bit 实现 802.11g 基本不可行。
但作者没有因此放弃。micro:bit 仍然有 LED array 和其他可玩方向,更重要的是,这次失败让他对调制方式和无线底层产生了兴趣。于是他提出了一个新的方向:既然没有合适硬件,可以先用软件模拟。不是精确模拟真实物理空间中的电磁波传播、反射和干扰,而是做一个教学向模型:在软件里实现调制和解调,加一点随机噪声和人工信号损耗,然后观察 bit 如何变成波形,波形如何被接收端恢复成 bit。
这篇文章的意义不在于"作者成功让 micro:bit 跑 Wi-Fi",而在于展示了一个学习路径:从熟悉的网络栈继续往下,走到物理层;从 ICMP、IPv4、ARP、Ethernet frame,继续追问 bit 是如何进入空气的;从"radio 不就是收发 bit 吗"这种天真想法,走到 GFSK、BPSK、相位、频率、调制、解调、FPGA 和信号测量。它不是终点,而是一个新系列可能的起点。
如果说 "Making our own ping" 是从应用层一路走到链路层,那么这篇文章就是在问:链路层下面是什么?答案不再只是代码和协议,而是波、噪声、频率、相位和硬件边界。对软件工程师来说,这正是一个陌生但迷人的方向。