
本文详解如何使用树莓派 gpio 按钮采集摩尔斯电码信号,通过时间间隔分析区分点(·)、划(−)及各类间隔,并自动解码为可读英文文本,含完整状态采样、容错匹配与字典映射实现。
本文详解如何使用树莓派 gpio 按钮采集摩尔斯电码信号,通过时间间隔分析区分点(·)、划(−)及各类间隔,并自动解码为可读英文文本,含完整状态采样、容错匹配与字典映射实现。
在树莓派上用物理按钮输入摩尔斯电码并准确解码,核心挑战不在于“按下→记录”,而在于精确捕获时序特征:点(·)与划(−)由按压时长区分;字符内空(character space)、字母间空(letter space)和单词间空(word space)则依赖释放时长判断。初学者常误将逻辑简化为“只测按下时间”,导致无法识别空格结构,最终解码失败。本教程提供一套鲁棒、低延迟、易调试的端到端方案。
✅ 关键设计原则
- 双向采样:同时记录「按下」与「释放」两个状态的持续时间,构成交替时间序列;
-
高频轮询 + 微休眠:以
0.1 × 最短有效时长(即0.015s)为采样周期,避免time.sleep(character_space)类粗粒度等待导致的时序漂移; -
动态终止机制:检测超长静默(如
5 × 最大符号时长 = 5.25s),自动结束当前消息录入; -
最近邻匹配:对每个实测时长,在预设符号时长字典中查找最接近的符号(
./-/`(字母分隔)// `(单词分隔)),提升抗抖动能力。
? 核心代码实现
首先定义摩尔斯符号与标准时长映射(单位:秒):
# 符号 → 标准时长(秒)
time_by_symbol = {
".": 0.15, # 点
"-": 0.45, # 划
"": 0.15, # 字符内空(无符号,仅占位)
" ": 0.45, # 字母间空
" / ": 1.05 # 单词间空(用" / "作为唯一标识符)
}
接着构建摩尔斯字母表与拉丁字母一一对应关系(含空格):
abc_latin = "abcdefghijklmnopqrstuvwxyz " # 26字母 + 空格
abc_morse = [
".-", "-...", "-.-.", "-..", ".", "..-.", "--.", "....", "..", ".---",
"-.-", ".-..", "--", "-.", "---", ".--.", "--.-", ".-.", "...", "-",
"..-", "...-", ".--", "-..-", "-.--", "--..", "/" # "/" 对应空格
]
⚙️ 实时信号采集(主循环)
以下代码持续监听按钮状态变化,记录每次状态切换(按下↔释放)的时间差,形成 times 列表(索引 0 为初始释放时长,1 为首次按下时长,2 为再次释放时长……):
import time
from gpiozero import Button
button = Button(2) # 假设按钮接 GPIO2(BCM 编号)
times = []
was_previously_pressed = False
start_time = time.time()
print("开始监听摩尔斯电码(长静默后自动结束)...")
try:
while True:
is_pressed = button.is_pressed
if was_previously_pressed != is_pressed:
# 状态翻转:记录上一状态持续时间
toggle_time = time.time()
times.append(toggle_time - start_time)
if len(times) > 1: # 跳过初始静默
print(f"[{len(times)-1}] {'按下' if not is_pressed else '释放'} → {times[-1]:.3f}s")
was_previously_pressed = is_pressed
start_time = toggle_time
# 长静默 → 结束录入(释放状态下超时)
if not is_pressed and (time.time() - start_time) > 5 * max(time_by_symbol.values()):
print("检测到长静默,结束录入。")
break
time.sleep(0.015) # 10× 高于最短时长,保障精度且不占满 CPU
except KeyboardInterrupt:
print("\n手动中断。")
? 注意:
times[0]是初始静默(通常忽略);从times[1:]开始,奇数索引(1,3,5…)对应按下时长(→.或-),偶数索引(2,4,6…)对应释放时长(→ 空格类型)。
? 解码为摩尔斯字符串
基于上述规律,将 times[1:] 映射为摩尔斯符号序列:
decoded_morse = ""
for i, t in enumerate(times[1:], start=1):
if i % 2 == 1: # 奇数位:按下 → 点或划
candidates = [".", "-"]
else: # 偶数位:释放 → 空格类型
candidates = ["", " ", " / "]
# 找最接近预设时长的符号
closest = min(candidates, key=lambda c: abs(t - time_by_symbol[c]))
decoded_morse += closest
print(f"解码得到摩尔斯串: '{decoded_morse}'")
# 示例输出: '.... . .-.. .-.. --- / .-- --- .-. .-.. -..'
? 翻译为英文文本
利用 abc_morse 和 abc_latin 的索引对齐特性,将摩尔斯词组(以空格分割)逐个查表转换:
# 将摩尔斯串按空格切分为字母单元(" / " 会被切为 "/")
morse_words = decoded_morse.split(" / ")
latin_words = []
for morse_word in morse_words:
latin_word = ""
for morse_char in morse_word.strip().split():
if morse_char in abc_morse:
idx = abc_morse.index(morse_char)
latin_word += abc_latin[idx]
else:
latin_word += "?" # 未知符号标记
latin_words.append(latin_word)
result = " ".join(latin_words)
print(f"最终译文: '{result}'") # 如:'hello world'
⚠️ 注意事项与优化建议
-
硬件去抖:物理按钮存在机械抖动,建议在电路中添加 0.1μF 电容,或软件增加
time.sleep(0.02)消抖(本方案已通过高频采样+最近邻匹配隐式缓解); -
时长校准:实际环境(如按钮弹性、用户习惯)可能导致标准时长偏移,首次使用前建议用已知摩尔斯序列(如
SOS→... --- ...)测试并微调time_by_symbol; -
扩展性:如需支持数字、标点,只需向
abc_morse和abc_latin追加对应项(如"1": ".----"); -
健壮性增强:可在解码前过滤掉明显异常的极短/极长间隔(如
或 <code>>1.5s),避免误触发。
本方案摒弃了传统“计时+分支判断”的脆弱逻辑,转而采用状态驱动+时序聚类思想,兼顾教学清晰性与工程可用性,是树莓派摩尔斯电码交互项目的可靠实践范式。










