为什么全班同时测试时 Micro:bit 无线电消息会中断接收

当二十块 micro:bit 在同一个教室中默认的无线电组上进行广播时,数据包冲突和接收缓冲区溢出就会让你的项目陷入瘫痪。本文将介绍如何规划分组、发送间隔与有效负载,以实现稳定可靠的课堂测试。
当一对搭档学生在课桌前单独测试 micro:bit 无线电脚本时,几乎总是能正常运行。然而,当二十对学生同时测试相同的脚本时,网络就会彻底瘫痪:消息延迟数秒到达、时断时续,甚至完全消失。硬件并没有损坏,学校的 Wi-Fi 也没有产生干扰。你的课堂只是迎头撞上了两个根本性的网络瓶颈:无节制的物理数据包冲突以及接收缓冲区溢出。
两个隐形的性能瓶颈
要解决这一问题,你必须了解 BBC micro:bit 在运行 MakeCode 无线电积木时究竟在做什么。micro:bit 使用了工作在 2.4 GHz 频段的 Nordic Semiconductor 收发器(v1 上为 nRF51,v2 上为 nRF52)。默认情况下,它的运行既没有载波侦听握手机制,也没有自动重试协议。
当课堂脚本指示 micro:bit 发送信号时,规模化运行会引发两个明显的问题:
- 空中时间冲突(Airtime Collisions):如果两块 micro:bit 在完全相同的时刻于同一频段发射信号,它们的无线电信号就会在空中叠加重叠。接收天线会收到混杂错乱的信号。micro:bit 在校验数据包的循环冗余校验(CRC)时发现损坏,便会静默丢弃该数据包。系统不会抛出任何报错,数据就这样无声无息地消失了。
- 接收缓冲区耗尽(RX Buffer Exhaustion):micro:bit 的软件运行时(CODAL / DAL)将传入的无线电数据包存储在一个微小的硬件支持接收缓冲区中,在将它们传递给你的用户代码之前,通常只能容纳四个数据包。如果接收端在更新其 5×5 LED 点阵屏或处于暂停积木的等待期间,有四块 micro:bit 发送了消息,该缓冲区就会瞬间被填满。在此窗口期内到达的后续所有消息都会被直接丢弃。
真正的问题:“无限循环”造成的数据洪流
在几乎所有出现故障的课堂上,罪魁祸首都是类似下面这样的一种初学者编程模式:
basic.forever(function () {
radio.sendNumber(input.acceleration(Dimension.X))
})在没有设置明确延迟的标准 forever 循环中,一块 micro:bit 每秒钟会广播 30 到 50 个数据包。一对搭档学生每秒生成 40 个数据包,只会占用少量的本地空中信道时间。但如果一个教室里有十五对学生同时操作,整个房间瞬间就会被每秒 600 个数据包所淹没。
由于基础无线电代码是“盲发”的——即 micro:bit 在发送前既不会等待信道空闲,也不会请求确认应答——这种高频广播几乎注定了每一次发射都会在半空中与其他设备的数据包发生碰撞。
如何构建稳定可靠的课堂无线电网络
解决无线电的不稳定性并不需要多么高深的网络协议,只需遵循四项基本的课堂编程规范即可。
1. 绝不使用默认无线电组
教学教程常常指导学习者编写 radio.setGroup(1)。如果全班都使用组 1,那么每台接收设备都要处理所有发射设备的数据包,即便你随后在软件代码中进行了过滤。MakeCode 支持从 0 到 255 的分组编号。在学期伊始,就应该为每张课桌或每个小组分配一个专属的固定组号:
- 第 1 组使用
radio.setGroup(10) - 第 2 组使用
radio.setGroup(20) - 第 3 组使用
radio.setGroup(30)
组 ID 会更改芯片上的网络地址配置。虽然相近的组仍然共享相同的 2.4 GHz 信道空间,但在硬件地址级别进行过滤可以防止无关数据包涌入邻近小组的接收队列。
2. 禁止无节制的无延迟循环
除了极少数高频物理遥测项目外,学校里几乎没有任何项目需要每秒更新 50 次。遥控小车或倾斜控制器在每秒更新 5 到 10 次时就能保持非常顺滑的响应。因此,必须在每个连续发射循环中强制加入明确的暂停:
basic.forever(function () {
radio.sendValue("speed", currentSpeed)
basic.pause(100) // 最多每秒 10 个数据包
})更好的做法是从轮询机制转向事件驱动传输。控制器应当仅在用户按下按钮、倾斜幅度超过显著阈值或检测到障碍物时才进行广播。如果没有状态变化,无线电就应当保持静默。
3. 引入时间抖动机制
如果房间内的每块 micro:bit 都严格按照每 100 毫秒的间隔发送广播,它们的时间周期最终会发生步调一致的同步——这意味着两台设备将在每个周期中反复发生碰撞。你可以通过在循环延迟中添加伪随机“抖动(jitter)”来消除这种周期性碰撞:
basic.pause(100 + Math.randomRange(0, 30))这种 0 到 30 毫秒的轻微随机化能够确保即便两台设备在某一组数据帧中发生碰撞,它们的下一次传输也会错开相位,从而让数据包顺利传达。
4. 精简你的有效负载
无线电传输需要消耗物理时间;数据包越长,碰撞的时间窗口就越大。虽然 MakeCode 允许你使用 radio.sendString("FORWARD_LEFT") 发送长字符串,但字符串会消耗数十个字节。相反,应改用单个整数或单字符键值对:
| 预期动作 | 不良负载(占用空口时间长) | 规范负载(占用空口时间短) |
|---|---|---|
| 转向指令 | radio.sendString("DRIVE_LEFT") | radio.sendNumber(1) |
| 遥测读数 | radio.sendValue("temperature_c", 24) | radio.sendValue("T", 24) |
| 停止信号 | radio.sendString("EMERGENCY_STOP") | radio.sendNumber(0) |
当全班必须与单个接收端通信时该怎么做
在小组投票活动或气象站数据记录中,常常需要十五块学生 micro:bit 同时向单个中心 hub micro:bit 发送数据。这种架构极易发生缓冲区崩溃。
要实现不丢数据的稳定通信,应反转通信流程:采用轮询架构,而非各自为政的自由广播。中心 hub micro:bit 先向特定 ID 广播请求(例如发送数字 1)。只有配置为设备 1 的学生设备才发送其有效负载。收到后,中心 hub 设备再呼叫设备 2。这种轮转结构彻底消除了碰撞风险,并确保接收端 micro:bit 的缓冲区中在同一时刻绝不会有超过一个数据包在排队等待。
对于管理数十名学生开展联网硬件项目教学的体系化课程,Sheen Robotics 通过 Infinity for Schools 规划了模块化课程与课堂配置方案,助力硬件实践课平稳有序推进,让师生无需再为排查无形的频谱冲突而苦恼。



