我把 ESP32 小喇叭升级成了 TTS 播报版:网页输入文字,设备直接出声
原文链接我把 ESP32 小喇叭升级成了 TTS 播报版:网页输入文字,设备直接出声
原文链接:https://mp.weixin.qq.com/s/X1Ud811saY39poEefazSjQ?scene=1 作者:PHPer技术 发布时间:2026-05-22 GitHub:https://github.com/Kaiii-create/kai-sound-tts
摘要
从「网页输入 MP3 地址 ESP32 播放」升级为「网页输入任意文本,ESP32 直接出声」。整条链路:网页 → PHP 调度 → Python 火山引擎 TTS → MQTT 下发 → ESP32 HTTP 拉流 → MAX98357A 播放。作者重点不是炫技,而是把每个坑都拆清楚了——长文本断流、MQTT 控制链 vs HTTP 音频链的混淆、插播比想的难得多。
完整链路
网页输入文本
↓
PHP 编排请求
↓
Python 调用火山引擎 TTS
↓
生成可播放 stream_url
↓
MQTT 下发给 ESP32
↓
ESP32 拉取 HTTP MP3 流
↓
MAX98357A 播放
版本定义:v1.0.0-mqtt
三个关键坑
1. 长文本整段播,中途容易断
不是 TTS 生成不了,是播放时间越长 ESP32 越容易撞 WiFi 抖动,HTTP 音频流就断。长文本真正的问题不是「能不能生成」,而是「单条流拉太久,网络一抖就断」。
2. MQTT 控制链和 HTTP 播放链不是一回事
调试时最容易混淆的两条链:
- MQTT:控制链,告诉 ESP32 去播哪个地址
- HTTP:音频链,ESP32 自己去拉流
混在一起想就容易判断错问题到底出在哪一层。
3. 插播没想的那么简单
ESP32 在播放 HTTP 流时 MQTT 并不总是稳定,第二条命令未必能在播放中稳稳送到。作者选择先不搞强插播,把顺序播报稳定做扎实。
收敛方案:四个优化
-
PHP 自动断句分段:按
。!?;优先断,,、:再切,长度兜底。不再让 ESP32 一次扛整段长文本。 -
PHP 预生成每一段 TTS:先全部生成好,播完一段立刻发下一段,省掉「现播现做现等」的等待时间。
-
ESP32 侧加 AudioFileSourceBuffer 32KB 本地缓冲:网络抖一下不至于立刻断。
-
最终结构:
用户文本 → PHP 断句分段 → PHP 预生成 TTS stream_url
→ MQTT 下发 tts + url → ESP32 打开 HTTP stream
→ AudioFileSourceBuffer 本地缓冲 → I2S 播放 → MAX98357A 输出
已知边界
| 问题 | 现状 |
|---|---|
| 段间停顿 | 预生成优化后比「现播现做」顺很多,但仍非完全无缝 |
| WiFi 依赖 | 播放稳定性很吃现场网络环境 |
| 插播 | 不主打,优先顺序播报稳定 |
| 传输层 | MQTT 控制 + HTTP 拉流,下一版想试 UDP 方向 |
适合场景
- 本地语音提示 / 设备播报
- 物联网语音输出 / 定时提醒
- 文字转语音硬件联动(ESP32 + MQTT + I2S 音频 + 本地控制页面)
作者的核心感悟
一个硬件项目真正难的地方,往往不是代码本身,而是把整条链路一层一层确认清楚——页面有没有发出请求、PHP 有没有下发 MQTT、Python 有没有生成 TTS、ESP32 有没有收到命令、有没有真的打开流、WiFi 有没有中途抖动、I2S 有没有真的在播。只要有一层没确认清楚,表面现象就可能完全像另一层出了问题。
这个版本的意义不在于「它已经完美了」,而在于「它已经不是一个想法了,而是一条真正跑通、而且已经能工作的链路」。