基于Micro:bit的莫尔斯码通信系统:从编码原理到无线传输实践
1. 项目概述与核心价值
几年前,我在一个无线电爱好者的线下聚会里,第一次看到有人用两个巴掌大的小设备,通过滴滴答答的声音和闪烁的灯光,隔空传递了一段完整的英文句子。那不是什么复杂的数字调制,而是最原始的莫尔斯码。当时我就被这种“复古未来感”的组合打动了:最经典的编码方式,跑在最现代、最易得的开源硬件上。这就是我动手搭建这个“基于Micro:bit的莫尔斯码通信系统”的初衷。它不仅仅是一个教学Demo,更是一个能让你亲手触摸到“通信”本质的微型工程。
简单来说,这个项目就是用两块(或更多)BBC Micro:bit开发板,构建一套完整的、可交互的莫尔斯码收发系统。发送方可以通过板载的A、B按钮,像发电报一样“敲”出点(.)和划(-),系统实时将其转换为对应的字母并显示在5x5的LED点阵上;同时,通过板载的2.4GHz无线射频模块,将编码后的信息发送出去。接收方则同步解码,并在自己的LED屏幕上显示接收到的字符,甚至可以配合蜂鸣器发出对应的“滴答”声。整个过程,从输入、编码、无线传输到解码、显示,形成了一个完整的通信闭环。
它的核心价值在哪里?首先,对于嵌入式开发的学习者,尤其是学生和爱好者,这是一个绝佳的“全栈”入门项目。你不需要焊接复杂的电路,Micro:bit开箱即用,但它迫使你去思考并实现一个完整系统的所有环节:人机交互(按钮输入、LED/声音反馈)、数据处理(莫尔斯码表查询与转换)、通信协议(无线数据的封装与收发)以及状态机设计(处理输入、发送、接收等不同模式)。其次,对于通信原理的感性认知,没有比亲手实现一个通信系统更深刻的了。你会直观地理解什么是编码、什么是信道、什么是误码(虽然这个项目里很简单),这些都是书本上抽象概念的生动注解。最后,它具备实实在在的“玩具”属性,你可以用它和朋友在教室里、公园里进行简单的秘密通信,这种成就感是驱动持续学习和探索的最佳燃料。
2. 系统整体设计与思路拆解
2.1 硬件平台选型:为什么是Micro:bit v2?
选择Micro:bit作为核心平台,是经过深思熟虑的,绝非仅仅因为其流行。市面上类似的开发板如Arduino Uno、ESP32等也完全有能力实现类似功能,但Micro:bit在教育集成度和快速原型开发上具有独特优势。
- 高度集成的传感器与执行器:一块Micro:bit v2板载了我们需要用到的几乎所有硬件。5x5 LED点阵屏用于字符和状态显示;两个可编程按钮(A/B)完美对应莫尔斯码的“点”和“划”输入(或作为模式切换);内置麦克风和扬声器(蜂鸣器)可以实现声音的编码与解码反馈;最关键的,是集成了Nordic nRF52833芯片提供的2.4GHz射频模块,无需任何外接模块(如NRF24L01)即可实现板间无线通信,极大地简化了硬件连接和驱动复杂度。
- 极低的学习与开发门槛:项目使用了Microsoft的MakeCode图形化编程环境。对于初学者,拖拽积木块就能完成逻辑搭建,避免了语法错误的困扰,能快速聚焦于系统逻辑本身。对于进阶者,MakeCode也支持一键切换到JavaScript或Python文本编程,为功能深化提供了平滑的升级路径。这种“从图形到代码”的过渡,是教育场景下的黄金设计。
- 供电与便携性:Micro:bit可通过USB或外部电池盒(两节AAA电池)供电,使得整个通信系统可以脱离电脑独立运行,成为一个真正的便携设备,这大大扩展了其应用场景,从桌面实验走向了实地通信。
注意:虽然MakeCode极大地简化了开发,但在设计复杂状态逻辑时,图形化编程可能会变得难以管理和调试。我的经验是,在实现核心算法(如莫尔斯码转换)时,可以先用图形化块搭建原型,验证逻辑正确后,切换到JavaScript视图进行代码优化和封装,这样既能保证可读性,又能提升代码质量。
2.2 通信方案设计:简单的Radio协议
Micro:bit的无线通信功能在MakeCode中被抽象为“Radio”积木组。它本质上是一个简单的、基于数据包的广播/组播系统。在本项目中,我们利用其最基础的点对点通信模式。
设计考量:
- 信道(Group ID):所有需要互相通信的Micro:bit必须设置相同的“无线电组”编号(0-255)。这相当于为所有设备分配了同一个通信频率,避免了与其他无关设备的干扰。在代码初始化时,我们将其设置为一个固定值(例如7)。
- 数据包结构:Radio发送的是字符串(String)类型的数据。我们的设计是,发送方将识别出的字母(如‘A’、‘B’)或完整单词作为一个字符串直接发出。这种方式最简单直接,但缺乏纠错和抗干扰能力。对于教育项目,这已足够。
- 发送与接收事件:采用事件驱动模型。当发送方调用
radio.sendString(“A”)后,接收方只要处于同一无线电组,就会自动触发on radio received receivedString事件。我们在该事件处理程序中,将接收到的字符串显示在LED屏上。
为什么不使用更复杂的协议? 本项目的主要目标是阐明莫尔斯码编解码和无线通信的基本流程。引入复杂的校验、应答、重传机制(虽然Micro:bit的Radio库支持信号强度RSSI和数字校验包)会分散对核心概念的注意力。先让系统跑起来,理解基本流程,后续优化才有方向。
2.3 软件架构与状态机设计
系统的软件核心是一个有限状态机。设备在任何时刻都处于以下几种状态之一,状态之间的转换由按钮事件触发。
- 初始状态(IDLE):设备启动后,LED显示“NO”(表示无消息),等待用户输入。此时按下B键,进入输入状态。
- 莫尔斯码输入状态(INPUT):这是最核心的状态。在此状态下:
- 用户通过A键输入“点”(短按),通过B键输入“划”(长按,或设计为按A后按B切换,但原方案是直接用A/B区分)。每次按键,LED可以给出视觉反馈(如闪烁一下)。
- 需要一个缓冲区来存储当前正在输入的点划序列,例如“.-.”。
- 需要一个超时判定机制。当用户停止输入超过一个预设时间(例如1秒),系统就认为一个字符的输入结束,自动将缓冲区内的点划序列转换为字母。
- 字符转换与显示状态(CONVERT/DISPLAY):从输入状态超时转换而来。系统查询内置的莫尔斯码字典,将点划序列转换为对应的英文字母或数字,并立即在LED屏上显示该字符。同时,将此字符通过Radio发送出去。
- 消息接收状态(RECEIVE):这是一个并行、被动触发的状态。无论设备处于何种状态,只要Radio接收到字符串,就会中断当前任务(以非阻塞方式),在LED屏上显示接收到的字符。可以设计为滚动显示或覆盖显示。
这个状态机模型清晰地将复杂的交互流程分解为离散的、可管理的步骤,是嵌入式系统开发的经典模式。在MakeCode中,我们可以通过变量(如 state 变量)记录当前状态,并在 on button A/B pressed 和 on radio received 等事件处理函数中,根据当前状态执行不同的逻辑。
3. 核心模块实现与代码解析
下面,我将基于MakeCode积木块(并辅以转换后的JavaScript代码说明)来拆解各个核心模块的实现。我将采用一种“混合”讲解方式,先展示积木逻辑,再解释其对应的代码含义,这样无论你是图形化还是文本编程的爱好者,都能理解。
3.1 莫尔斯码字典的构建
莫尔斯码的编码规则是固定的,我们需要在程序中建立一个查询表。在MakeCode中,最合适的数据结构是数组或字符串操作。这里我推荐使用“数组映射”法,虽然MakeCode对复杂数据结构的支持有限,但我们可以用两个平行的数组来实现。
思路:
- 创建一个数组
letterList,按顺序存储所有可编码的字符(A-Z, 0-9)。 - 创建另一个数组
codeList,按照完全相同的顺序,存储每个字符对应的莫尔斯码点划序列(用字符串表示,如“.-”代表A)。 - 当需要查询时,遍历
letterList找到目标字符的索引,再从codeList中取出对应索引的码值。
在MakeCode中,初始化这两个数组可能稍显繁琐,需要多个“将项添加到数组”的积木。但一旦建立,查询逻辑非常清晰。在JavaScript模式下,这可以写得更简洁:
实操心得:在Micro:bit有限的内存中,存储完整的双向查询表可能会占用不少空间。如果只实现字母A-Z,问题不大。但如果想支持更多字符(如标点),需要留意内存使用。一个优化技巧是,只存储正向(字符->码)表,解码时通过遍历查找,虽然速度慢一点,但节省了内存。对于教学项目,速度完全可接受。
3.2 输入检测与点划识别逻辑
这是人机交互的核心。原方案提到用A键输入点,B键输入划。但这里有一个关键细节:如何区分用户是短按A(点)还是长按A(划)?或者,如何用两个按钮清晰地输入三种状态(点、划、字符结束)?
我推荐一个更符合电报操作直觉且逻辑清晰的方案:
- 短按A键:输入一个“点”(.)。同时,LED屏幕中央的LED亮起一小段时间(如100ms)作为反馈。
- 长按A键(超过0.5秒):输入一个“划”(-)。同时,LED屏幕中央的LED亮起较长时间(如500ms)作为反馈。
- 按下B键:表示当前字符输入结束。系统将之前输入的点划序列进行解码,并发送。
这样,A键专职输入,B键专职确认/发送,角色分明。在MakeCode中,实现长按检测需要使用“运行时间”积木来测量按键按下的时长。
3.3 超时自动判定与字符解码
为了让输入更自然,我们需要实现“超时自动判定字符结束”的功能。这需要一个后台循环来检查。
在MakeCode中,我们可以创建一个永远循环的 basic.forever 块,在其中检查当前时间与 lastInputTime 的差值。如果差值大于预设的字符间隔阈值(例如1000毫秒),并且 inputBuffer 不为空,就触发解码和发送流程。
morseDecode 函数就是利用前面构建的 reverseMap 进行查询。
3.4 无线数据收发与显示
无线电的初始化非常简单,只需在程序开始时设置相同的组ID。
发送操作已在超时判定部分完成 (radio.sendString)。接收处理则通过事件处理器完成:
注意事项:Radio通信是“尽力而为”的广播。在复杂电磁环境或多设备同时通信时,可能会发生数据包丢失(收不到)或冲突(显示乱码)。这是介绍通信可靠性的好机会。可以引导学习者思考:如何知道对方是否收到?可以引入“应答”机制,即接收方收到后也发回一个确认信号,发送方没收到确认就重发。
4. 系统集成、调试与功能拓展
4.1 完整工作流程与集成测试
将上述所有模块组合起来,就构成了完整的系统。程序启动后的逻辑流程如下:
- 初始化:设置无线电组,清空输入缓冲区,显示就绪图案(如笑脸或“READY”)。
- 等待输入:循环检查按钮和超时。
- 输入处理:用户通过A键输入点划,系统提供实时视觉/声音反馈,并更新缓冲区和最后输入时间。
- 自动解码发送:超时后,自动解码缓冲区内内容,显示并发送。
- 接收处理:随时监听无线电,收到信息立即显示并给出提示。
集成调试技巧:
- 分模块调试:不要一次性写完所有代码。先实现莫尔斯码的输入和本地显示(不涉及无线电),确保点划识别、超时、解码、显示全部正确。
- 添加调试输出:在关键步骤,用
serial.writeLine函数将变量值(如inputBuffer,decodedChar)输出到电脑的串口监视器,这是排查逻辑错误的神器。 - 一对一测试:先用两台设备进行最基本的功能测试。确保发送方能正确显示和发送,接收方能正确接收和显示。
- 距离与干扰测试:逐步拉开两台设备距离,观察在什么情况下通信开始不稳定。尝试在Wi-Fi路由器附近或其他2.4GHz设备旁测试,了解实际环境中的干扰。
4.2 常见问题与排查实录
在实际制作和教学过程中,我遇到了不少典型问题,这里列出一个速查表:
| 问题现象 | 可能原因 | 排查与解决思路 |
|---|---|---|
| 按下按钮无任何反应 | 1. 程序未成功烧录。 2. 按钮检测代码不在循环或事件中。 3. 电池电量不足。 |
1. 检查MakeCode下载流程,确认Micro:bit盘符出现又消失。 2. 确认使用了 input.onButtonPressed 事件或是在循环中检查 button.isPressed()。3. 更换电池或使用USB供电测试。 |
| 可以输入点划,但超时不解码 | 1. 超时判断逻辑错误。 2. lastInputTime 更新时机不对。3. 循环检查的频率太低。 |
1. 用串口打印 currentTime、lastInputTime 和它们的差值,检查判断条件。2. 确保每次有效输入(点或划)后都更新了 lastInputTime。3. 减少 basic.pause 的时间,提高检查频率。 |
| 解码出的字符错误 | 1. 莫尔斯码表数据错误。 2. 点划缓冲区 ( inputBuffer) 内容不对。3. 长按/短按判定阈值不准确。 |
1. 核对码表,特别是容易混淆的字符(如S(…)和O(—))。 2. 在解码前打印 inputBuffer 内容,看是否多出或少了下划线/点。3. 调整长按判定的时间阈值(如从500ms调到800ms)。 |
| 接收方收不到信息 | 1. 无线电组ID设置不一致。 2. 设备距离过远或有遮挡。 3. 发送方未成功执行 radio.sendString。4. 多设备干扰。 |
1. 确认发送和接收代码中 radio.setGroup 的值完全相同。2. 在开阔无遮挡环境下近距离(1米内)测试。 3. 在发送代码后添加一个LED闪烁或串口打印,确认发送函数被执行了。 4. 尝试更换一个不常用的组ID(如123)。 |
| 接收显示乱码或重复字符 | 1. 无线电数据包冲突或重复接收。 2. 接收事件处理函数被多次触发。 |
1. 这是无线通信的正常现象。可以在发送的数据包中加入序号,接收方只处理新的序号。 2. 确保接收事件处理函数中的显示逻辑是幂等的,或者在处理期间暂时禁用无线电接收。 |
4.3 功能拓展与进阶玩法
基础系统实现后,这里有几个方向可以让项目变得更有挑战性和趣味性:
- 加入声音反馈与训练模式:不仅用LED显示,还可以用蜂鸣器播放出当前输入或接收到的莫尔斯码声音(点短音,划长音)。甚至可以设计一个“听写训练模式”,设备随机播放一个字符的莫尔斯码声音,用户需要在限定时间内按下正确的按钮序列来应答。
- 实现简单加密:在发送前对字符进行简单的替换加密(如凯撒密码),接收方需要知道密钥才能解密显示。这引入了密码学的概念。
- 多跳中继通信:如果有三台以上的Micro:bit,可以设计一个协议,让消息能够通过中间设备中继,传递到更远距离或非直连的设备,模拟网络路由。
- 使用Python重写:跳出MakeCode的舒适区,用Micro:bit的Python模式重新实现整个系统。你将需要直接操作MicroPython的
radio、display、button模块,对状态机和错误处理有更底层的控制,是能力的一次飞跃。 - 外壳与电源设计:为你的Micro:bit设计并3D打印一个保护外壳,集成电池盒和挂绳孔,把它变成一个坚固、便携的“电报机”。工程项目的完整性不仅在于代码,也在于物理实现。
这个基于Micro:bit的莫尔斯码通信系统,就像一颗种子。它从最简单的输入输出和无线通信开始,但沿着不同的枝干生长,可以触及状态机设计、人机交互、通信协议、甚至简单的密码学和网络概念。最重要的是,它让你从“看”和“读”关于嵌入式系统的知识,转变为亲手“做”出一个能实际运行、与人交互的系统。这种从理论到实践的跨越,正是嵌入式开发魅力所在。当你第一次用自己的设备收到同伴从房间另一头发来的“HELLO”时,那种连接世界的兴奋感,是任何模拟器都无法给予的。