一台落满灰尘的旧式电话机,经过适度改造后,可以成为一台基于 ESP32-S3 的语音交互设备。这个项目保留电话机的外观和听筒结构,在内部加入 I2S 数字麦克风、I2S 功放模块和小智 AI 软件:拿起听筒后开始对话,放下听筒后结束交互。
项目同时涉及 ESP-IDF 环境搭建、小智 AI 源码编译、GPIO 配置、I2S 音频驱动和旧设备内部改造,适合具备一定嵌入式开发基础的创客。需要提前说明的是,项目能否稳定运行取决于硬件接线、供电、WiFi 连接以及小智 AI 服务端配置,并不是完成烧录后即可在所有设备上直接复现。

硬件组成
| 组件 | 型号或规格 | 数量 | 作用 |
|---|---|---|---|
| 主控板 | ESP32-S3-DevKitC-1,Flash 不低于 8MB,PSRAM 不低于 2MB | 1 | 负责运算、联网和外设控制 |
| I2S 麦克风 | INMP441 或其他 I2S 数字麦克风 | 1 | 采集语音 |
| I2S 功放模块 | MAX98357 | 1 | 放大数字音频信号 |
| 扬声器 | 8Ω、3W,或电话原装喇叭 | 1 | 播放语音回复 |
| 旧式电话机 | 任意带听筒的款式 | 1 | 作为项目外壳和交互载体 |
| 导线、铜条 | 若干 | — | 用于内部连接和摘机检测 |
| USB 数据线 | Type-C | 1 | 供电与烧录 |
ESP32-S3-DevKitC-1 的 Flash 和 PSRAM 容量需要与项目配置相匹配。原项目要求至少具备 2MB PSRAM,并建议使用更大容量的 PSRAM;如果使用 2MB PSRAM,需要同步检查软件配置。
电话机内部改造
处理听筒和扬声器
拆开电话机底盖后,先确认原有电路板、听筒线路以及摘机检测结构,再决定拆除哪些部件。原项目保留听筒中的四根线,其中两根原本连接麦克风,另外两根连接听筒扬声器。
拆开听筒,取下原装碳精麦克风,将 INMP441 等 I2S 麦克风模块安装到原麦克风位置。由于不同电话机的内部空间和固定方式并不相同,安装时需要避免麦克风被外壳挤压,并尽量减少导线松动。
电话机原有的外放喇叭可以继续使用,由 MAX98357 功放模块驱动。听筒中的小型扬声器在本方案中不再参与播放,语音主要通过电话机外放喇叭输出。
利用摘机开关检测状态
电话机内部通常有由听筒压下的弹片或铜片,用于接通和断开电话线路。改造时,可以将这组机械结构作为摘机检测开关:
- 铜条一端连接 ESP32-S3 的
GPIO_NUM_8; - 另一端连接
GND; - GPIO 配置为输入,并启用内部上拉电阻。
在当前接线方式下:
- 听筒放下时,铜片接触,GPIO 被拉到低电平;
- 听筒拿起时,铜片分离,GPIO 通过内部上拉变为高电平。
软件据此区分“挂机”和“摘机”。由于不同电话机的弹片结构可能相反,实际装配后应先用串口日志或调试方式确认电平变化,再决定软件中的有效电平和事件映射。
硬件接线
本项目采用以下 GPIO 分配方案。引脚可以在 config.h 中调整,但修改后必须同时检查软件配置和实际接线,避免多个外设使用同一个 GPIO。


I2S 麦克风接线
以 INMP441 为例:
WS→GPIO_NUM_4,字选择信号SCK→GPIO_NUM_5,时钟信号DIN→GPIO_NUM_6,数据输入L/R→GND,选择左声道VDD→3.3VGND→GND
供电和地线不能接反,否则可能损坏麦克风模块。原项目说明中提到部分连接可不接,但这不应理解为可以省略模块的正常供电。实际接线前,应以所用 INMP441 模块的引脚定义和电源要求为准。
I2S 功放接线
以 MAX98357 为例:
BCLK→GPIO_NUM_15,位时钟LRC→GPIO_NUM_16,左右声道时钟DIN→GPIO_NUM_7,音频数据输入GAIN→GND,较低增益SD→3.3V,使能功放VIN→3.3V或5VGND→GND
GAIN 的具体效果取决于模块设计和供电条件。如果音量过大或过小,可以尝试调整该引脚,但应先确认模块的电气规格。功放的电源和地线需要连接可靠,隐藏在元件下方的焊点尤其要检查是否存在虚焊或短路。


摘机检测接线
- 铜条 1 →
GPIO_NUM_8 - 铜条 2 →
GND - GPIO 启用内部上拉电阻
- 软件中设置
active_high = false
建议先在不连接其他音频模块的情况下,单独测试摘机开关的高低电平变化。确认听筒拿起和放下时的状态后,再接入完整的软件事件逻辑,可以减少排查难度。
核心软件逻辑
本项目基于开源的 xiaozhi-esp32 项目进行二次开发,摘机检测逻辑放在 compact_wifi_board.cc 中。代码的核心思路是:将电话机的机械弹片当作一个按钮,根据电平变化触发唤醒或退出事件。
// 在构造函数中初始化摘机检测按钮
CompactWifiBoard() :
boot_button_(BOOT_BUTTON_GPIO),
touch_button_(TOUCH_BUTTON_GPIO, false, 300, 100)
// 低电平有效,长按 3 秒触发
{
InitializeDisplayI2c();
InitializeSsd1306Display();
InitializeButtons();
InitializeTools();
}
// 在 InitializeButtons 中注册事件
void InitializeButtons() {
// 拿起听筒:唤醒小智
touch_button_.OnClick([this]() {
auto& app = Application::GetInstance();
app.WakeWordInvoke("你好小智");
GetDisplay()->ShowNotification("正在唤醒...");
});
// 放下听筒:退出对话
touch_button_.OnLongPress([this]() {
auto& app = Application::GetInstance();
app.WakeWordInvoke("退出");
GetDisplay()->ShowNotification("正在退出...");
});
}
这里有三个参数需要结合实际接线理解:
active_high = false:低电平被视为按钮有效状态。由于摘机开关一端接地,听筒放下时对应低电平;long_press_time = 300:长按判定时间为 300ms,即 3 秒,用于避免短暂抖动直接触发退出;OnClick与OnLongPress的具体触发结果,取决于按钮库对“按下”和“释放”的定义,以及电话机弹片的实际电平变化。
在当前逻辑中,听筒放下对应持续低电平,因此触发长按事件并发送“退出”;听筒拿起后电平恢复为高电平,对应释放或点击事件,从而发送“你好小智”。如果实际电话机的开关状态相反,需要调整接线或软件逻辑,不能直接照搬事件映射。
编译与烧录
项目使用 ESP-IDF v5.3 或更高版本开发。进入源码目录后,先设置目标芯片:
cd xiaozhi-esp32
idf.py set-target esp32s3
然后打开项目配置界面:
idf.py menuconfig
在 Xiaozhi Assistant → Board Type 中选择对应的开发板类型。如果实际使用的 GPIO 与默认配置不同,需要同步修改 GPIO 定义。例如:
#define AUDIO_I2S_MIC_GPIO_WS GPIO_NUM_4
#define AUDIO_I2S_MIC_GPIO_SCK GPIO_NUM_5
#define AUDIO_I2S_MIC_GPIO_DIN GPIO_NUM_6
#define AUDIO_I2S_SPK_GPIO_DOUT GPIO_NUM_7
#define AUDIO_I2S_SPK_GPIO_BCLK GPIO_NUM_15
#define AUDIO_I2S_SPK_GPIO_LRCK GPIO_NUM_16
#define BOOT_BUTTON_GPIO GPIO_NUM_
#define TOUCH_BUTTON_GPIO GPIO_NUM_8
完成配置后,执行编译和烧录:
idf.py build
idf.py flash -p [你的串口号]
烧录完成并重新上电后,设备会进入配网模式。使用手机连接名称类似 Xiaozhi-xxxx 的 WiFi 热点,再访问 配置网络。连接成功后,可以通过“你好,小智”测试语音交互。


工作原理
语音数据链路
用户说话时,安装在听筒中的 I2S 麦克风负责采集音频,并通过 I2S 总线传给 ESP32-S3。ESP32-S3 再通过 WebSocket 将音频数据发送到小智 AI 云端服务器。
服务端负责语音理解和回复生成,原项目提到可以使用 Qwen、DeepSeek 等大语言模型。生成的语音数据以流式方式返回 ESP32-S3,经 MAX98357 放大后,由电话机外放喇叭播放。
因此,这个项目并不是完全离线的语音设备。它需要稳定的 WiFi 连接,并依赖小智 AI 服务端完成后续处理。网络中断、服务端不可用或音频参数不匹配,都可能导致无法唤醒、回复延迟或没有声音。
摘机检测逻辑
GPIO_NUM_8 配置为输入,并启用内部上拉电阻。听筒放下时,铜条接触地线,GPIO 为低电平;听筒拿起时,铜条分离,GPIO 被上拉为高电平。
在 active_high = false 的条件下:
- 低电平被视为“按下”;
- 听筒放下,相当于持续按下,达到长按时间后触发
OnLongPress,发送“退出”; - 听筒拿起,相当于释放,触发
OnClick,发送“你好小智”。
实际使用时,如果机械触点存在抖动,可能出现重复触发或状态误判。长按判定能够减少一部分误触,但仍建议确保弹片安装牢固、接线长度适当,并在软件和硬件两侧分别确认状态变化。
扩展:让小智整理电脑照片
除了语音问答,这套系统还可以通过后端工具扩展功能。原项目给出的示例是:在小智 AI 后端注册一个文件整理工具,当用户提出整理照片的请求时,由服务端调用该函数处理指定目录中的图片。
工具调用方式
后端 Python 服务支持自定义 Tools。示例函数名为 organize_images,可以根据日期、扩展名或图片尺寸对文件进行分类移动:
# 文件整理工具函数
def organize_images(source_dir, target_base_dir="", mode="date"):
# ...完整实现见项目源码
return {"moved": moved_count, "errors": errors}
# 注册为 AI 工具
TOOLS = [
{
"type": "function",
"function": {
"name": "organize_images",
"description": "整理指定文件夹中的图片,按日期/扩展名/尺寸分类",
"parameters": {
"source_dir": {"type": "string"},
"mode": {"enum": ["date", "ext", "size"]}
}
}
}
]
示例中提到的文件位置是 mcp-calculator-main/pic.py。这类工具涉及真实文件移动,不能只把语音识别结果直接当作操作指令执行。使用前应明确源目录、目标目录和分类方式,并保留必要的备份;否则一旦路径识别错误或工具参数配置不完整,可能造成文件被移动到不易查找的位置。
功能验证
可以按照以下顺序测试:
- 设备上电,确认能够播放初始化提示音;
- 拿起听筒,检查屏幕是否显示“正在唤醒…”;
- 确认设备是否播报“你好,我在”;
- 对着听筒说话,检查外放喇叭能否播放语音回复;
- 如果已配置照片整理工具,再测试整理指定文件夹的语音请求;
- 放下听筒,确认屏幕显示“正在退出…”并结束对话。
建议先验证摘机检测、麦克风采集和扬声器播放,再测试网络问答,最后测试文件整理等涉及实际操作的扩展功能。这样可以将机械、电气、音频和服务端问题分开定位。
供电与安全注意事项
- 确认供电方案。 原项目提到使用 3.7V 锂电池供电,同时功放模块的
VIN可接 3.3V 或 5V。实际装配时应根据所用开发板、功放模块和电池管理方案确认电压范围,不能仅凭外观或接口判断。 - 检查麦克风电源。 INMP441 的
VDD和GND不能接反,否则可能损坏模块。电源、地线和信号线应分别核对。 - 谨慎调整功放增益。
GAIN接地时为较低增益,接 3.3V 时可能获得更高增益。调整后应先低音量测试,避免扬声器过载或出现明显失真。 - 确认 PSRAM 配置。 项目要求 ESP32-S3 至少具备 2MB PSRAM,并建议使用 8MB。如果使用 2MB PSRAM,需要检查并调整相关配置。
- 保证网络稳定。 设备需要通过 WiFi 与小智 AI 服务端通信,网络不稳定会影响语音识别、回复速度和播放连续性。
- 注意焊接安全。 改造过程涉及拆机、焊接和电路连接。上电前应检查是否短路、是否存在裸露焊点,以及功放电源和地线是否连接牢固。
- 保留原机结构。 如果希望以后恢复电话机原状,拆除原电路板和线路时应记录接线位置,并尽量采用可拆卸连接方式。
旧式电话机在这个项目中并不是简单的外壳,而是同时承担了交互结构、摘机检测和声音输出等功能。ESP32-S3 负责本地控制和联网,I2S 麦克风与功放负责音频输入输出,小智 AI 服务端负责语音交互的后续处理。三者之间任何一环配置不一致,都可能影响最终效果。
按原项目的配置,主控板、麦克风和功放模块的成本约为百元级,但实际花费会受到电话机、电源方案和改造材料的影响。对于创客教育,它可以用来展示旧物改造、GPIO 状态检测、I2S 音频和 AI 服务调用之间的关系;对于有嵌入式基础的开发者,则适合作为一个软硬件结合的练习项目。项目源码基于 xiaozhi-esp32 二次开发,正式制作前应先完成单模块测试,再进行整体装配。









- 最新
- 最热
只看作者