在这之前,我已经让 ESP32-S3 通过 MAX98357A 和一只 4Ω/3W 喇叭播放出三声测试音。但三声只能证明接线和基础音频输出能工作,它还不能决定自己要说什么。
这次的变化是:我可以在电脑网页里输入一句新文字,点击发送,不重新修改和烧录 ESP32 代码,喇叭就会播放新内容。
巧克力和西红柿打架,巧克力赢了。为什么呢?因为巧克力棒。
这是我实际用来测试过的一段文字。
这次打通的不是一段录音,而是一条动态链路
如果把一句 MP3 固定写进固件,每次换内容都要重新编译和上传。网页方案把“设备程序”和“要说的内容”分开:ESP32 固件继续负责联网、领取任务和播放;网页负责接收随时变化的文字。
| 环节 | 负责的事情 | 本次结果 |
|---|---|---|
| 网页 | 输入文字并创建播放请求 | 已实际提交 |
| Mac 本地服务 | 调用 TTS、保存 MP3、建立任务 | 已生成音频 |
| 局域网任务接口 | 让设备领取任务并上报状态 | 已完成任务闭环 |
| ESP32-S3 | 下载 MP3、校验、解码并输出音频 | 已连续处理 5 个任务 |
| MAX98357A 与喇叭 | 把 I²S 数字音频变成可以推动喇叭的输出 | 用户已听见实际语音 |
乐鑫的 Arduino-ESP32 I²S 文档说明,I²S 使用位时钟、字选择和数据线传输 PCM 音频;在本项目中,对应的是 BCLK→GPIO16、LRC→GPIO17、DIN→GPIO18。ESP32 解码 MP3 后通过这三根信号线把音频送给 MAX98357A。
网页点击以后,设备实际做了什么
-
网页把输入文字提交给本地 FastAPI 服务。
-
服务调用当时使用的 TTS Provider 生成 MP3,并把音频保存为可领取的任务。
-
ESP32-S3 通过 Wi-Fi 轮询服务,领取排队任务。
-
设备下载 MP3,并把音频放入 PSRAM。
-
固件解码 MP3,把 PCM 数据写入 I²S。
-
MAX98357A 驱动喇叭播放,设备再依次回报 downloaded、playing 和 completed。
服务器数据库里原本排队的 5 个任务最后全部变成 completed,每个任务都有下载、播放中和完成事件;ESP32 串口逐条打印 playback complete。我也确认喇叭连续播放出了这些语音。这里的“5 条”是任务闭环的实测数量;现有记录不能证明上面的冷笑话一定属于这 5 条,所以我没有把两项证据强行对应。
为什么这一步比播放固定 MP3 更重要
对我来说,它最直接的意义是:我可以通过网页控制 ESP32 说什么,不必每换一句话就重新烧录代码。
这也改变了下一步的可能性。未来如果麦克风输入能够进入语音识别和 AI 生成,再把回答交给现在这条播放链路,设备才有机会形成真正的语音对话。现在的网页不是最终交互方式,但它先把“动态内容如何到达喇叭”独立验证了。
当前结论
这次验证把 ESP32 从一个只能播放固件内固定内容的设备,推进成了一个可以接收网页动态文字并播放语音的本地原型。硬件输出、网络任务和真实发声已经闭环;下一步是让它听见我说的话,再把输入链路接到现有输出链路上。