1. 项目概述:当电位器开口“说话”
最近在捣鼓一个智能家居的小原型,想实现一个能“报数”的旋钮。比如,旋转一个旋钮调节灯光亮度时,设备能实时用语音告诉我当前的亮度百分比。这个想法听起来简单,但涉及硬件交互、数据采集和语音合成,对新手来说,几个环节的串联容易让人卡壳。正好手头有一块PinPong开发板,它号称能用 Python 像搭积木一样玩转开源硬件,我决定用它来试试水,核心目标就一个:让电位器的旋转值,通过语音实时播报出来。
这个项目麻雀虽小,五脏俱全。它完美串联了嵌入式开发中的几个核心概念:模拟信号采集(ADC)、数据处理与映射、以及软件层面的功能调用(TTS)。对于刚接触硬件编程,特别是从纯软件转向“软硬结合”的开发者来说,这是一个绝佳的练手项目。你不需要去啃晦涩的寄存器手册,也不用纠结于复杂的驱动安装,PinPong 库的封装让这一切变得异常简单。通过这个项目,你不仅能理解电位器、ADC、PWM(虽然本项目未直接使用)这些基础元件和概念是如何协同工作的,更能掌握一种快速原型开发的方法论。
我选择的硬件核心是 PinPong 板配合一个普通的旋转电位器。PinPong 板本质上是一块集成了特定主控(如 Arduino UNO、micro:bit 等兼容架构)并预装了固件的开发板,其最大魅力在于其同名 Python 库。这个库将硬件操作抽象成了高级的 Python 函数,比如analog_read()读模拟值,say()进行语音合成,让我们可以专注于业务逻辑,而非底层通信细节。下面,我就把从硬件连接到代码调试的完整过程,以及踩过的坑和总结的技巧,毫无保留地分享出来。
2. 硬件选型与连接思路解析
工欲善其事,必先利其器。在这个项目中,硬件选型直接决定了项目的复杂度和稳定性。我的核心思路是:在满足功能的前提下,最大限度降低硬件连接和驱动的复杂度,让开发者能聚焦于代码逻辑本身。
2.1 为什么选择 PinPong 板?
市面上能运行 Python 的硬件平台不少,比如树莓派、ESP32-MicroPython、Jetson Nano 等。我选择 PinPong 板基于以下几个关键考量:
- 极致的开发体验:PinPong 库的“一键式”操作是最大亮点。它通过 Firmata 协议或自身固件,将硬件引脚的操作封装成了简单的 Python 函数。你想读取模拟引脚,直接调用
analog_read(pin);你想控制舵机,就用servo_write(pin, angle)。这种抽象程度,对于快速验证想法、进行创客教育或制作交互艺术装置来说,效率极高。 - 跨平台与免驱:PinPong 库支持 Windows、macOS、Linux,连接 USB 后通常能被识别为标准串口,无需安装复杂的芯片专用驱动。这对于在教室、工作坊等需要快速部署的环境下非常友好。
- 丰富的生态兼容:虽然我们只用一个电位器,但 PinPong 板兼容 Arduino 生态的传感器和执行器。这意味着你的项目可以轻松扩展,后续想加个温湿度传感器、OLED 屏幕或继电器模块,都有现成的库函数或示例可以参考,学习成本曲线平缓。
- 成本与易得性:相对于树莓派等更“重型”的平台,基于 ATmega328P 或类似 MCU 的 PinPong 板成本更低,功耗也更小,非常适合这种单一的、实时性要求不极高的交互场景。
注意:PinPong 板有不同的版本,对应不同的主控(如 UNO、Leonardo、micro:bit)。请根据你购买的板子型号,在安装库时选择对应的初始化方法。本项目以最常见的PinPong UNO为例。
2.2 电位器与 ADC 通道的选择
电位器,即可变电阻,是我们项目的“输入设备”。旋转其旋钮,中心抽头对两端的电压分压比会改变,从而输出一个 0V 到 VCC(通常是5V或3.3V)之间的连续变化的模拟电压。
- 电位器规格:选择一个普通的10kΩ 线性(B型)电位器即可。阻值太大,输入阻抗高,抗噪声能力强但可能驱动电流不足;阻值太小,功耗会增大。10kΩ 是一个在性能和功耗间取得良好平衡的常见值。线性电位器意味着旋转角度与电阻值(输出电压)成线性关系,这对于我们直观地映射数值至关重要。
- ADC 引脚:PinPong UNO 板上有标注 “A0” 至 “A5” 的模拟输入引脚。它们内部连接到了微控制器的ADC(模数转换器)模块。ADC 的作用就是将引脚上连续的模拟电压值,离散化为一个数字值。UNO 的 ADC 是10位精度,这意味着它可以将 0-5V 的电压范围,划分为 2^10 = 1024 个等级,输出 0 到 1023 之间的整数。我们选择A0引脚作为信号输入。
2.3 电路连接详解与安全须知
连接非常简单,但细节决定成败。请务必按照以下步骤操作,避免烧毁元件或板卡。
准备材料:
- PinPong UNO 板 x1
- 10kΩ 线性电位器 x1
- 公对母杜邦线 x3
- USB 数据线(方口或Type-C,取决于你的板子)x1
连接电路:
- 将电位器的三个引脚分别视为:左侧引脚(L)、中间引脚(信号端/M)、右侧引脚(R)。
- 使用一根杜邦线,将电位器的左侧引脚(L)连接到 PinPong 板的5V引脚。这是提供参考高电压。
- 使用第二根杜邦线,将电位器的右侧引脚(R)连接到 PinPong 板的GND(接地)引脚。这是参考低电压。
- 使用第三根杜邦线,将电位器的中间引脚(M)连接到 PinPong 板的A0模拟输入引脚。这个引脚将读取分压后的电压。
- 最后,用 USB 数据线将 PinPong 板连接到电脑。
电路原理:这就构成了一个经典的分压电路。5V(VCC)和 GND 加在电位器两端,形成一个固定的总电压。中间抽头的位置决定了 A0 引脚对地的电压V_out = VCC * (R_between_M_and_GND) / (Total_R)。旋转电位器,R_between_M_and_GND变化,V_out随之在 0-5V 间线性变化。
重要安全提示:
- 切勿将电源反接:确保 5V 接电位器一端,GND 接另一端。如果接反,虽然可能不会立刻损坏(因为电位器是无源元件),但读取的逻辑会完全颠倒(最大值变最小值)。
- 避免短路:在连接时,确保杜邦线的金属头没有相互触碰,特别是 5V 和 GND 直接短路,会瞬间产生大电流,可能损坏 USB 端口或开发板。建议在断电(不插USB)状态下完成所有连线,检查无误后再上电。
- 静电防护:干燥环境下,人体可能带静电。触摸电子元件前,可以先触摸一下接地的金属物体(如电脑机箱外壳)释放静电。
3. 软件环境搭建与核心库剖析
硬件连接妥当后,我们需要在电脑上搭建 Python 环境,并安装关键的 PinPong 库。这个过程是项目从“物理电路”走向“智能程序”的桥梁。
3.1 Python 环境配置要点
虽然你很可能已经安装了 Python,但为了项目的可复现性,这里强调几个关键点:
- Python 版本:PinPong 库支持 Python 3.6 及以上版本。推荐使用Python 3.8 或 3.9,它们在兼容性和稳定性上表现很好。避免使用 Python 3.10 以上某些过新的版本,以防第三方库尚未完全适配。你可以在命令行输入
python --version或python3 --version来检查。 - 包管理工具:务必使用
pip进行安装。如果你的系统同时有 Python 2 和 Python 3,请明确使用pip3命令来为 Python 3 安装库。 - 虚拟环境(强烈推荐):这是一个好习惯,可以避免不同项目间的库版本冲突。使用
venv模块创建独立环境:
激活后,命令行提示符前会出现# 在项目目录下 python -m venv venv # 激活虚拟环境 # Windows: venv\Scripts\activate # macOS/Linux: source venv/bin/activate(venv)标识,之后所有pip install操作都只影响这个环境。
3.2 安装 PinPong 库及其依赖
PinPong 库的安装非常简单,但它背后依赖了一些用于串口通信和语音合成的库。
使用 pip 一键安装:
pip install pinpong这条命令会自动从 PyPI 下载 PinPong 库及其核心依赖(如
pyserial用于串口通信)。安装过程可能会持续一两分钟。验证安装与查找板子: 安装完成后,我们可以写一个简单的脚本来测试库是否可用,并找到开发板连接的串口。
# test_com_port.py from pinpong.board import Board import serial.tools.list_ports # 列出所有可用串口 ports = list(serial.tools.list_ports.comports()) for port in ports: print(f"找到串口: {port.device} - {port.description}") # 通常,你的 PinPong 板会显示为类似 'COM3' (Windows) 或 '/dev/ttyUSB0' (Linux) 或 '/dev/cu.usbmodemXXXX' (macOS)运行这个脚本,记下你的板子对应的串口设备名。这是后续初始化 Board 对象的关键参数。
3.3 PinPong 库核心模块解析
了解库的结构,能帮助我们更好地使用它。PinPong 库主要包含以下几个模块:
board:这是核心模块,负责与开发板建立通信。Board()类是入口,初始化时会连接指定的串口。pin:定义了引脚类Pin,用于配置引脚模式(输入/输出)和读写数字信号。虽然本项目主要用模拟读,但了解其结构有益。extension:包含了一些高级功能封装,比如我们即将用到的语音合成(TTS)功能就在其中。Speech类提供了say()方法。- 针对特定板型的模块(如
uno):内部实现了与具体板卡(如 Arduino UNO)的引脚映射和指令集。
关键设计思想:PinPong 采用了“板卡对象”和“引脚对象”分离的设计。我们先初始化一个Board对象(代表整块物理板子),然后通过这个板子对象来获取具体的引脚对象进行操作。这种设计清晰且易于管理多个板卡。
4. 代码实现与逻辑逐行详解
接下来是核心部分:编写 Python 脚本,实现“读取-处理-播报”的完整逻辑。我会将代码分段,并详细解释每一行的作用和背后的原理。
4.1 主程序框架与初始化
首先,我们导入必要的模块,并初始化开发板。
# main.py import time from pinpong.board import Board, Pin from pinpong.extension.speech import Speech # 1. 初始化开发板 # 将这里的 'COM3' 替换为你实际查到的串口 board = Board("uno", "COM3") # 对于 Windows # board = Board("uno", "/dev/ttyUSB0") # 对于 Linux # board = Board("uno", "/dev/cu.usbmodem14101") # 对于 macOS board.begin() # 开始通信 # 2. 初始化语音合成模块 speech = Speech(board) # 3. 初始化电位器引脚 potentiometer_pin = board.get_pin('A0') # 获取 A0 引脚对象 potentiometer_pin.set_mode(Pin.ANALOG) # 设置为模拟输入模式 print("系统初始化完成,开始读取电位器值...")代码解析:
Board(“uno”, “COM3”):创建板卡对象。第一个参数是板型,这里是”uno”;第二个参数是串口地址,务必替换成你电脑上识别到的实际端口。board.begin()方法会打开串口连接,并发送握手协议,准备接收指令。Speech(board):创建语音合成对象,它需要依赖一个已初始化的board对象来进行通信。board.get_pin(‘A0’):这是获取引脚对象的推荐方式。它返回一个Pin对象,代表物理上的 A0 引脚。pin.set_mode(Pin.ANALOG):将引脚模式设置为模拟输入。这是必须的一步,告诉微控制器这个引脚准备接收模拟信号,内部会连接到 ADC 模块。如果忘记设置,读取的值将是未定义的。
4.2 核心循环:读取、映射与播报
初始化完成后,我们进入一个无限循环,持续读取电位器值并触发语音播报。
# 4. 定义一些常量与变量 ADC_MAX = 1023 # UNO板ADC的10位最大值 VOLUME_MIN = 0 # 我们映射到的最小值(如音量0%) VOLUME_MAX = 100 # 我们映射到的最大值(如音量100%) last_spoken_value = -1 # 记录上一次播报的值,用于去重 speak_threshold = 5 # 变化阈值,只有当变化超过此值时才播报,防止过于频繁 try: while True: # 5. 读取原始ADC值 raw_value = potentiometer_pin.read_analog() # 读取A0引脚的模拟值,范围0-1023 # print(f"原始ADC值: {raw_value}") # 调试时可用 # 6. 将ADC值映射到有意义的范围(0-100) # 使用线性映射公式:y = (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min mapped_value = int((raw_value - 0) * (VOLUME_MAX - VOLUME_MIN) / (ADC_MAX - 0) + VOLUME_MIN) # 确保值在边界内(防止计算误差) mapped_value = max(VOLUME_MIN, min(VOLUME_MAX, mapped_value)) # 7. 判断是否需要播报(防抖与节流逻辑) if abs(mapped_value - last_spoken_value) >= speak_threshold: # 构造播报文本 speech_text = f"当前值 {mapped_value}" print(f"播报: {speech_text}") # 调用语音合成 speech.say(speech_text) # 更新上一次播报的值 last_spoken_value = mapped_value # 8. 短暂延时,控制循环频率,降低CPU占用 time.sleep(0.1) # 延时100毫秒,即每秒读取约10次 except KeyboardInterrupt: # 9. 捕获 Ctrl+C 中断,优雅退出 print("\n程序被用户中断。") board.close() # 关闭串口连接 print("已释放串口资源。")逻辑深度解析:
- 常量定义:
ADC_MAX=1023是硬件决定的。VOLUME_MIN和VOLUME_MAX定义了映射的目标范围,这里设为 0 到 100,代表百分比。你可以轻松修改为 0-10、1-5 或其他任何范围,以适应不同场景(如档位)。 - 读取模拟值:
pin.read_analog()是 PinPong 库封装的方法。它向板子发送指令,板子上的 ADC 执行一次转换,并将结果(0-1023)通过串口传回。这个过程通常需要几毫秒。 - 值映射:这是数据处理的关键一步。原始 ADC 值(0-1023)对我们人类不直观。通过线性映射公式,我们将其转换为更有意义的百分比。例如,
raw_value=512(大约中间值),计算得mapped_value = int((512)*100/1023) ≈ 50。int()是为了取整,因为语音播报整数更自然。 - 防抖与节流逻辑:这是提升体验的核心技巧。如果不加处理,程序会以每秒10次的速度不断播报,即使数值只波动了1,也会产生嘈杂的、重复的语音。
last_spoken_value:记录上次成功播报的映射值。speak_threshold:设定一个变化阈值(这里是5)。只有当本次映射值与上次播报值的绝对值差大于等于5时,才触发新的播报。- 作用:避免了在电位器旋钮轻微抖动或停留在某个值附近时,语音频繁启停的问题。只有用户有明显旋转动作时才会播报,体验更佳。这个阈值可以根据你的电位器精度和需求调整。
- 语音合成:
speech.say(text)是 PinPong 封装的 TTS 功能。其底层原理是,Python 脚本将文本通过串口发送给开发板,开发板上的固件可能调用内置的语音合成芯片(如果板子支持)或者通过额外的音频模块处理。对于大多数基础板型,它可能是通过一个简单的蜂鸣器以特定频率模拟语音,或者需要外接语音合成模块(如 SYN6288)。你需要确认你的 PinPong 板是否支持硬件 TTS,或者该功能是否依赖于特定扩展板。如果不支持,你会听到蜂鸣器发出的“哔哔”声而不是语音。这是本项目最容易遇到的坑之一。 - 延时与资源管理:
time.sleep(0.1)让循环每次执行后暂停0.1秒。这有两个目的:一是降低 CPU 使用率;二是给硬件 ADC 转换和串口通信留出足够时间,避免读取冲突。最后,用try…except KeyboardInterrupt捕获 Ctrl+C 信号,确保在退出前调用board.close()关闭串口。这是一个好习惯,可以避免串口被占用,导致下次运行程序时报错。
5. 高级优化与功能扩展
基础功能实现后,我们可以从稳定性、用户体验和功能丰富度上进行优化和扩展。
5.1 软件滤波:让读数更稳定
实际读取 ADC 值时,由于电源噪声、电磁干扰或电位器接触电阻微小变化,即使电位器不动,读到的原始值也可能在几个数字之间跳动(例如在 511-514 之间波动)。这会导致映射后的值跳动,可能误触发语音播报。
解决方案:引入软件滤波。这里介绍两种简单有效的方法:
移动平均滤波:取最近 N 次读数的平均值作为有效值。这能平滑掉随机噪声。
# 在循环外初始化一个列表 read_buffer = [] BUFFER_SIZE = 5 # 在循环内部,读取原始值后 raw_value = potentiometer_pin.read_analog() read_buffer.append(raw_value) if len(read_buffer) > BUFFER_SIZE: read_buffer.pop(0) # 保持缓冲区大小固定 filtered_raw_value = int(sum(read_buffer) / len(read_buffer)) # 计算平均值 # 后续使用 filtered_raw_value 进行映射中值滤波:取最近 N 次读数的中位数。这对消除偶发的、幅度较大的脉冲噪声(尖峰)特别有效。
import statistics # ... 类似地维护一个缓冲区 ... filtered_raw_value = int(statistics.median(read_buffer))
实操建议:对于缓慢变化的电位器信号,移动平均滤波简单且效果足够。将BUFFER_SIZE设为 5 或 10。你会立即发现映射后的值变得非常稳定,只有在你确实旋转电位器时才会变化。
5.2 播报策略优化
除了基础的阈值防抖,还可以让播报更智能、更人性化。
分级播报:对于 0-100 的范围,我们可能不需要精确到个位数。可以设置每 10 个单位播报一次。
# 修改播报判断条件 current_level = mapped_value // 10 # 整除,得到十位数(0-10级) last_level = last_spoken_value // 10 if last_spoken_value >=0 else -1 if current_level != last_level: speech_text = f"第 {current_level} 档" # ... 播报 ... last_spoken_value = mapped_value # 仍然记录原始值用于其他逻辑添加单位与语境:让语音信息更完整。例如,如果是调光,播报“亮度百分之五十”;如果是调速,播报“速度等级五”。
context = "亮度" speech_text = f"{context} {mapped_value} 百分之"非阻塞播报与队列:目前的
speech.say()是阻塞的,即它播报时,整个循环会停下来等待播报完成。对于长句子,这会导致控制不跟手。一个更高级的思路是使用线程或异步,将播报任务放入队列,主循环继续运行。但这对初学者复杂度较高,PinPong 库的简单 API 可能不支持。一个折中方案是确保播报的短语非常简短。
5.3 可视化与调试辅助
在开发阶段,将数据可视化能极大帮助调试和理解信号。
命令行实时绘图:可以使用简单的字符图。
# 在打印播报信息的同时,打印一个进度条 bar_length = 20 filled = int(mapped_value / 100 * bar_length) bar = '[' + '=' * filled + ' ' * (bar_length - filled) + ']' print(f"ADC: {raw_value:4d} -> 映射: {mapped_value:3d}% {bar}")输出类似:
ADC: 512 -> 映射: 50% [========== ]使用 Matplotlib 实时图表(高级):如果你熟悉
matplotlib,可以创建一个动态更新的折线图来观察 ADC 值的实时变化和滤波效果。这需要用到matplotlib.animation模块,代码量会大一些,但对于分析信号特性非常有用。
5.4 扩展硬件功能
单一电位器控制略显单调。结合 PinPong 库的易用性,可以轻松扩展:
- 多电位器控制:再接一个电位器到 A1 引脚,同时读取两个值,可以控制二维参数,比如播报“X轴 30, Y轴 70”。
- 结合按钮切换模式:增加一个 tactile 按钮连接到数字引脚(如 D2),并设置为上拉输入。通过单击按钮,可以在不同的播报模式间切换(例如,“百分比模式”、“档位模式”、“静音模式”)。
- 增加视觉反馈:连接一个 RGB LED 灯带(如 WS2812)到数字引脚。根据电位器的值改变 LED 的颜色或亮度,实现“旋钮调色,语音报色”的多感官交互。
- 网络化:如果使用支持 WiFi 的 PinPong 板(如 ESP8266/ESP32 版本),可以将读取到的电位器值通过 MQTT 协议发送到 Home Assistant 或 Node-RED 等智能家居平台,从而实现用实体旋钮远程控制家里的虚拟设备,并同步语音反馈。
6. 常见问题排查与解决方案实录
在实际操作中,你几乎一定会遇到下面这些问题。我把它们和解决方案整理成了表格,方便你快速查阅。
| 问题现象 | 可能原因 | 排查步骤与解决方案 |
|---|---|---|
导入pinpong库时报错ModuleNotFoundError | 1. PinPong 库未安装。 2. 在虚拟环境中操作,但未激活虚拟环境。 3. 系统中有多个 Python 版本,pip 装错了位置。 | 1. 在当前命令行环境下,执行pip list,检查列表中是否有pinpong。2. 如果没有,请确保命令行提示符前有 (venv)标识(如果用了虚拟环境),然后运行pip install pinpong。3. 确认使用的 python和pip命令来自同一个安装。可以用which python和which pip(macOS/Linux)或where python和where pip(Windows)查看路径。 |
运行代码时报错SerialException: Could not open port ‘COM3’: … | 1. 串口号错误。 2. 串口被其他程序占用(如 Arduino IDE 的串口监视器未关闭)。 3. 驱动未正确安装(某些 CH340 芯片的板子)。 | 1.最重要:重新运行 3.2 节的test_com_port.py脚本,确认正确的端口号,并修改代码中的Board()初始化参数。2. 关闭所有可能占用该串口的软件,包括 Arduino IDE、Putty、其他终端程序等。 3. 对于 Windows,如果设备管理器中端口显示黄色叹号,需要安装 CH340 或 CP2102 等 USB 转串口芯片的驱动。去芯片官网或卖家提供的链接下载安装。 |
代码运行无报错,但print输出的 ADC 值始终为 0 或 1023,或是一个不变化的固定值 | 1. 电路连接错误或接触不良。 2. 引脚模式未设置为模拟输入 Pin.ANALOG。3. 电位器损坏。 | 1.断电后,用万用表通断档检查杜邦线和电位器焊接点是否连通。重点检查中间抽头(信号线)是否确实连接到了 A0 引脚。 2. 确认代码中执行了 pin.set_mode(Pin.ANALOG)。3. 用万用表电阻档测量电位器两端电阻(应为标称值如10kΩ),旋转旋钮,测量中间脚与任一脚的电阻,应平滑变化。如果不变或跳变,电位器可能损坏。 |
| ADC 读数跳动剧烈(即使手不碰) | 1. 电源噪声。 2. 未使用滤波。 3. 杜邦线过长或接触不良引入干扰。 | 1. 尝试给 PinPong 板使用独立的 USB 电源适配器供电,而非电脑 USB 口,看是否有改善。 2.立即实施 5.1 节的软件滤波,这是解决该问题最有效、最简单的方法。 3. 缩短连接线,或使用质量更好的带屏蔽的导线。在电位器的 VCC 和 GND 引脚之间并联一个0.1uF(104)的陶瓷电容,可以滤除高频噪声。 |
| 语音播报没有声音,或只有“滴滴”声 | 1. 板载无硬件 TTS 功能,speech.say()可能仅驱动蜂鸣器。2. 未连接扬声器或耳机。 3. 音量被调至最低或静音。 | 1.这是最常见的困惑点。查阅你的 PinPong 板说明书,确认是否支持语音合成。如果不支持,你需要外接一个 TTS 模块(如 SYN6288、XFS5152),并修改代码使用对应的扩展库。 2. 如果板子有音频输出孔,请插入耳机或有源音箱。 3. 有些板子可能有物理音量旋钮或可通过代码调节音量,检查并调整。 |
| 语音播报严重延迟或卡顿 | 1. 播报的文本过长,合成和播放需要时间。 2. 主循环因 speech.say()阻塞太久。3. 串口通信速率(波特率)设置过低。 | 1. 缩短播报文本,例如只播报数字。 2. 考虑使用5.2 节提到的分级播报,减少播报频率。 3. 在 Board.begin()中尝试设置更高的波特率(需确认固件支持),如board.begin(baudrate=115200)。但注意,波特率太高可能导致通信不稳定。 |
| 程序占用 CPU 过高 | 主循环中没有延时time.sleep(),或延时太短。 | 确保循环内有time.sleep(),即使是time.sleep(0.001)也能极大降低 CPU 占用。对于本项目,0.05 到 0.2 秒的延时都是合理的。 |
我的个人实操心得:
- 先调试,后加语音:建议先注释掉语音播报相关的代码,只打印 ADC 值和映射值到控制台。确保硬件连接和读数都完全正确、稳定后,再启用语音功能。这能帮你快速定位问题是出在硬件/数据采集端,还是语音合成端。
- 滤波是必选项:对于任何模拟传感器读数,软件滤波几乎是标准预处理步骤。不要相信“裸”的 ADC 值。一个简单的移动平均滤波能提升整个项目的稳定性和专业感。
- 管理预期:明确你使用的 PinPong 板的具体功能。如果它不支持硬件 TTS,那么
speech.say()产生蜂鸣音是正常现象。想要高质量语音,外接 TTS 模块是更靠谱的方案。 - 利用好打印信息:在代码关键节点添加
print()语句(如打印原始值、映射值、判断逻辑的结果),是调试嵌入式程序最朴素也最有效的方法。