无线电
远距离互动,像魔法一样。
魔法对精灵、巫师、独角兽或许有用——它们只存在于故事里。
但有比魔法更好的东西:物理学!
无线通信就是物理学:无线电波(一种电磁辐射,类似可见光)的振幅、相位或脉宽等性质,由发射端调制,从而编码信息并广播。电波遇到导体(天线)时产生交变电流,信息被提取并还原。
一层又一层
还记得吗?网络是分层搭建的。
最底层仍是某种连接,让信号能从 A 到 B。网络 教程里用 I/O 引脚上的线;有了 radio 模块,可以去掉线,用上面概括的物理过程作为看不见的连接。
再往上的「信号层」也与有线例子不同。有线时用数字 0/1;micro:bit 内置无线电里,信号的最小有用单位是字节。
字节
字节通常是 8 个位的信息单位。位是最小单位,只有开/关两种状态。
字节有点像算盘:每一位对应一个数——在字节里是 128、64、32、16、8、4、2、1。位通过空中传输,接收端再拼回字节。
发现规律了吗?(提示:二进制。)
把为「开」的位置上的数相加,就能表示 0~255。下图用 5 位从 0 数到 32:

若约定每个 0~255 的数字代表什么(比如一个字符),就能一次传一个字符的文本。
人们早就想到了——用字节编解码信息很常见,大致对应有线例子里摩尔斯码那一层。
寻址
无线电的问题是:你不能只发给某一个人。任何合适的天线都能收到你发的内容,所以要能区分谁该收。
micro:bit 内置无线电的做法很直接:
-
可以调频道(0~83),像儿童对讲机:大家调到同一频道,就能听到该频道上的广播。相邻频道可能略有干扰。
-
模块还可设 address 和 group:address 像邮政地址,group 像地址里的具体收件人。无线电会过滤与自身 address/group 不匹配的消息,因此应用前要事先约好用哪组。
micro:bit 仍会收到其他 address/group 的广播,只是程序里不用管;但若有人足够「聪明」,可以嗅探所有无线流量——这时就需要加密,只有目标收件人能读。密码学很有趣,但超出本教程范围。
萤火虫
这是一只萤火虫:

它用生物发光(无需导线)向同伴发信号。成群闪烁时像这样:

我们要用 radio 模块做类似萤火虫群彼此闪烁的效果。
先 import radio。此时模块已设为合理默认值;频道、寻址、发射功率、接收队列占用 RAM 等都可配置,详见 radio API 参考。
若默认即可,发送最简单:
radio.send("a message")
send 广播字符串 "a message"。接收更简单:
new_message = radio.receive()
收到的消息进入队列;receive 返回队列中最旧的一条字符串并腾出位置。队列满了,新消息会被丢弃。
就这些!(radio 还能发任意类型数据,不只是字符串,见 API 文档。)
萤火虫程序
知道这些后,很容易写出萤火虫效果:
# micro:bit 萤火虫
# 公有领域示例
import radio
import random
from bibo import display, Image, button_a, sleep
# 「闪一下」动画帧。你能看出怎么做的吗?
flash = [Image().invert()*(i/9) for i in range(9, -1, -1)]
while True:
# 按 A 发送 "flash"
if button_a.was_pressed():
radio.send('flash')
incoming = radio.receive()
if incoming == 'flash':
sleep(random.randint(50, 350))
display.show(flash, delay=100, wait=False)
# 约 1/10 概率再广播,让多台设备持续联动
if random.randint(0, 9) == 0:
sleep(500)
radio.send('flash')
关键在事件循环:检测 A 是否按下,按下则 radio.send('flash');用 radio.receive() 读队列。若收到 "flash",随机短停后播放闪烁动画;再以 1/10 概率延时半秒后再次广播 "flash",多台 micro:bit 就能连成一片「虫群」。while True 让这一切不断重复。
多台 micro:bit 一起用时,效果大致如下:
