做可视门铃的工程师大概率都碰到过这个需求:有人按铃,设备要实时播报"门口有人"或者"欢迎光临XX公司"。早几年大家的做法是预录几段MP3存进Flash,需要时拉UART发个播放指令——问题是一旦要改播报内容,得重新烧录,量产之后几乎没法改。TTS(文本转语音)芯片就是来解决这个问题的:主控把中文文本直接发给芯片,它实时合成音频输出,不用存任何语音文件。
下面把ESP32和WT3000T系列TTS芯片的联动方案从硬件接线、通信协议、驱动代码到量产注意事项拆开讲一遍。
先说结论:TTS方案在内容灵活性和量产维护成本上完胜预录语音,但在音质自然度和响应延迟上要接受一定的妥协。做方案选型之前,这两者的权衡要先想清楚。
预录MP3方案的实际痛点
预录方案的工作流程是:用PC端软件录制或生成"欢迎光临""请稍等"等提示音→存为MP3/WAV文件→通过USB或串口烧录进芯片外挂的SPI Flash或TF卡→主控通过UART指令指定编号播放。这个流程在内容固定的场景下没有问题,比如家用门铃只需要播"叮咚,有人按铃"。但一旦涉及下面几种情况,维护成本会急剧上升:
多语言版本:产品要卖到不同地区,需要英文、西班牙文、阿拉伯文播报。预录方案要存N套语音文件,Flash空间吃紧,BOM成本直接上升。
公司/酒店名称定制:商用门铃需要播报"欢迎光临XX酒店",每个客户的名称不同。预录方案要么每单都重新烧录(量产噩梦),要么让客户自己通过PC工具烧录(售后灾难)。
动态信息播报:需要播报"快递已放在门口,取件码是XXXX"。预录方案做不到,因为取件码是实时生成的。
TTS方案的核心优势就在这里:上面三种场景,主控只需要通过UART发送一段GB2312编码的中文文本给TTS芯片,芯片实时合成音频输出,内容完全由主控决定,不需要预先存储任何音频文件。
TTS芯片选型的实际考量
市面上能做TTS的芯片/模块分两大类:一类是云端TTS(把文本发给云服务,返回合成好的音频),另一类是离线TTS(芯片本地完成合成)。可视门铃这个场景,必须选离线TTS,原因有三个:
响应延迟:云端TTS需要联网→请求→等待返回,延迟通常在500ms~2s之间。门铃按铃后等2秒才听到播报,用户体验很差。离线TTS芯片通常在50~200ms内开始输出音频。
网络连接依赖:可视门铃的语音播报是核心安防功能,不能依赖Wi-Fi连接状态。断网时云端TTS完全不可用。
持续运营成本:云端TTS按调用次数或时长收费,量产产品每台设备都要产生持续费用,对成本敏感的消费电子产品不可接受。
基于以上分析,本文选用WT3000T系列作为TTS语音芯片方案来展开。选用理由是:该公司做语音芯片近30年,WT2605/WT2003等产品在门铃行业已有广泛量产案例;WT3000T系列支持离线TTS,采用UART控制,ESP32侧软件工作量很小,开发金简单;芯片待机功耗<20μA,适合电池供电的门铃产品。
为什么推荐用模块而不是裸芯片
WT3000TX芯片本身采用QFN32封装(4mm×4mm),需要外挂晶振、阻抗匹配电路、电源滤波,对中小团队来说,自己画芯片电路并调试到稳定状态的周期成本很高。WT3000T-M01模块把芯片、晶振、LDO、功放、阻抗匹配全部集成在PCB上,对外只露出6个接插件引脚,直接接5V电源、UART、喇叭就能工作。
建议量产初期直接用模块,等出货量稳定后再考虑裸芯片方案以压缩BOM成本。模块到芯片的迁移只需要改PCB,主控软件几乎不用动(UART协议相同)。
WT3000T-M01模块管脚定义
| 引脚 | 名称 | 功能说明 |
| 1 | 5V | 电源输入,支持2.6V~5.5V宽电压。门铃产品推荐用3.3V或5V供电。 |
| 2 | GND | 电源地。务必与ESP32共地,否则UART通信会出现异常。 |
| 3 | TX | 模块UART发送端。接ESP32的UART_RX引脚。 |
| 4 | RX | 模块UART接收端。接ESP32的UART_TX引脚。这是发送TTS文本的主通道。 |
| 5 | SPK+ | PWM功放输出正极。直接接4~8Ω喇叭的一个引脚。 |
| 6 | SPK- | PWM功放输出负极。接喇叭的另一个引脚。模块内置0.5W D类功放。 |
注意:WT3000T-M01模块的UART电平是3.3V TTL。ESP32的UART接口也是3.3V,两者可以直接相连,不需要电平转换。但如果主控用的是5V单片机,5V单片机的TX输出可能需要串联一个220Ω电阻再接到模块的RX,以防损坏模块。
ESP32与WT3000T-M01的典型接线
下面是实际项目中最常用的连接方式(以ESP32-WROOM-32为例):
ESP32 ←→ WT3000T-M01 接线关系
ESP32 GPIO16 (UART_RX) ←→ 模块 Pin3 (TX)
ESP32 GPIO17 (UART_TX) ←→ 模块 Pin4 (RX)
ESP32 3V3 ←→ 模块 Pin1 (5V) [注:模块支持2.6~5.5V供电]
ESP32 GND ←→ 模块 Pin2 (GND)
模块 Pin5 (SPK+) ←→ 4~8Ω喇叭正极
模块 Pin6 (SPK-) ←→ 4~8Ω喇叭负极
补充说明(来自实际调试经验):WT3000T-M01模块有一个容易被忽略但很重要的特性——Busy引脚(对应芯片的P01/Pin27)。这个引脚在TTS合成播放期间输出低电平,播放结束后恢复高电平。在门铃应用场景中,这个信号很有用:当主控连续发送多条TTS文本时,应该先查一下Busy引脚的状态(或发状态查询指令),确认上一条播完了再发下一条,否则新的合成指令会中断当前正在进行的合成。
电源和接地的实操建议
门铃产品的电源环境通常比较恶劣(尤其是接了老旧门铃线路的场景,或者电池供电场景下的电源纹波),以下几点是实际项目中总结出来的:
电源滤波:模块的VCC(Pin1)到GND之间,建议在PCB上放置一个10μF电解电容并联0.1μF陶瓷电容。若用独立LDO给模块供电,LDO输入端也要加滤波电容。实际测试发现,电源纹波过大时,TTS合成的音质会明显下降。
共地:ESP32和WT3000T模块必须共地。如果两者由不同的电源供电,务必把两个电源的地连在一起,否则UART通信会出现乱码或者完全无法通信。
喇叭走线:SPK+和SPK-走线要远离UART信号线(TX/RX),否则功放输出的开关噪声可能耦合到UART信号上,导致通信误码。实际项目中遇到过SPK线紧贴UART走线导致偶发乱码的问题,把两根线拉开3cm以上就解决了。
WT3000TX系列的UART默认参数:波特率9600(可修改,但默认是9600)、8位数据位、无奇偶校验、1位停止位、3.3V TTL电平。这个参数组合在ESP32的UART驱动中是最基础的配置,几乎不用调试就能通。
协议帧格式——与旧款MP3芯片的区别
WT3000TX的UART协议帧和唯创的旧款MP3播放芯片(如WT2003系列)的帧格式不完全兼容。最大的区别是WT3000TX引入了帧长度(2字节)、流水号(1字节)和数据帧来源(1字节)这几个字段。直接把WT2003的驱动代码拿来用在WT3000T上是行不通的,需要重新写发送函数。
WT3000TX UART 协议帧格式(发送方向:主控→TTS芯片)
起始码 7E // 1字节,固定为0x7E
帧长度 XXXX // 2字节,高位在前
流水号 NN // 1字节,每发一帧自动+1
应答标志 00 // 1字节,固定填0x00
数据来源 02/03 // 0x02=来自芯片端,0x03=来自MCU端
N个命令 [cmd+len+data] // 每个命令:2字节命令码 + 1字节数据长度 + N字节数据
校验和 SS // 1字节,低字节累加和
结束码 EF // 1字节,固定为0xEF
关于累加和校验的计算——这是实际项目中最容易出Bug的地方:校验和的计算方法是,把"帧长度(2字节)+流水号(1字节)+应答标志(1字节)+数据帧来源(1字节)+所有命令信息(N字节)"的每一个字节相加,取结果的低8位(即与0xFF做按位与)。注意帧长度本身是2个字节,这2个字节都要参与校验和计算。
TTS合成命令的构造方法
TTS合成是最核心的功能:主控发送一段中文文本(GB2312编码),芯片收到后实时合成音频并从DAC(或PWM功放)输出。WT3000T8版本(A版)支持中文和英文字母合成,WT3000T3版本(D版)支持中英文混读合成。两个版本每次合成的文本量最多支持2048字节(约1000个汉字)。如果文本超过这个长度,需要分多次发送。
// 发送TTS文本的完整UART帧示例(框架示意,具体命令码以官方指令表为准)
void send_tts_text(char* text, int text_len) {
uint8_t frame[2048];
int idx = 0;
frame[idx++] = 0x7E; // 1. 起始码
frame[idx++] = 0x00; // 2. 帧长度高位(先占位)
frame[idx++] = 0x00; // 帧长度低位(待计算)
frame[idx++] = seq_num++; // 3. 流水号
frame[idx++] = 0x00; // 4. 应答标志
frame[idx++] = 0x03; // 5. 数据来源:MCU→芯片
frame[idx++] = 0x01; // 6. 命令码高字节(示例)
frame[idx++] = 0x03; // 命令码低字节(示例)
frame[idx++] = text_len; // 数据长度
for (int i = 0; i < text_len; i++) // 7. 文本内容(GB2312编码)
frame[idx++] = text[i];
int frame_len = idx - 3; // 8. 回填帧长度
frame[1] = (frame_len >> 8) & 0xFF;
frame[2] = frame_len & 0xFF;
uint8_t checksum = 0; // 9. 校验和
for (int i = 1; i < idx; i++) checksum += frame[i];
frame[idx++] = checksum & 0xFF;
frame[idx++] = 0xEF; // 10. 结束码
uart_write_bytes(UART_NUM, frame, idx); // 11. 发送
}
提醒:以上是框架示意代码,WT3000TX的具体命令码以官方《WT3000Tx语音合成指令表》为准。在写驱动之前,务必向唯创的技术人员索要最新的指令表文档。文本编码方式(GB2312还是UTF-8)也需要确认。
简单调用 vs 标准调用——什么时候需要查Busy状态
WT3000TX支持两种调用方式,选择哪种取决于你的应用场景:
简单调用:主控只管发文本,不管芯片有没有在忙。适用于单条播报的场景(比如按铃后播报一次访客信息)。局限:如果前一条文本还没合成完就发下一条,前一条会被中断。
标准调用:每次发文本之前,先确认芯片处于空闲状态(通过查Busy引脚电平,或发状态查询命令)。适用于多条连续播报的场景。比如商用门铃需要依次播报"欢迎光临"+"请到前台登记"两条信息。
查Busy状态有两种方法:硬件方式(读芯片的Busy引脚电平,低电平=忙/正在合成,高电平=空闲)和软件方式(发状态查询命令帧,芯片回传当前状态字)。硬件方式更实时,软件方式更灵活。
下面给的代码是基于ESP-IDF(ESP32官方开发框架)的C语言实现。如果你用的是Arduino-ESP32环境,代码逻辑完全相同,只是UART操作函数名不同(Arduino用Serial2.write()之类)。
UART初始化
// ESP32 UART初始化(UART_NUM_1,GPIO16=RX, GPIO17=TX)
#include "driver/uart.h"
#define UART_NUM UART_NUM_1
#define TX_PIN 17
#define RX_PIN 16
#define BAUD_RATE 9600
#define BUF_SIZE 2048
void tts_uart_init(void) {
uart_config_t uart_config = {
.baud_rate = BAUD_RATE,
.data_bits = UART_DATA_8_BITS,
.parity = UART_PARITY_DISABLE,
.stop_bits = UART_STOP_BITS_1,
.flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
};
uart_driver_install(UART_NUM, BUF_SIZE, 0, 0, NULL, 0);
uart_param_config(UART_NUM, &uart_config);
uart_set_pin(UART_NUM, TX_PIN, RX_PIN,
UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE);
}
完整TTS发送函数(含校验和计算)
// 可直接复制到项目中使用的TTS发送函数
#define TTS_FRAME_START 0x7E
#define TTS_FRAME_END 0xEF
static uint8_t tts_seq = 0; // 流水号
void tts_send_text(const char *text, int len) {
uint8_t frame[512];
int pos = 0;
frame[pos++] = 0x7E; // 起始码
frame[pos++] = 0x00; // 帧长度高(待回填)
frame[pos++] = 0x00; // 帧长度低(待回填)
frame[pos++] = tts_seq++; // 流水号
frame[pos++] = 0x00; // 应答标志
frame[pos++] = 0x03; // 数据来源:MCU→芯片
frame[pos++] = 0x01; // 命令码字节1(示例)
frame[pos++] = 0x03; // 命令码字节2(示例)
frame[pos++] = len; // 文本数据长度
for (int i = 0; i < len; i++)
frame[pos++] = text[i]; // GB2312文本
int flen = pos - 3; // 回填帧长度
frame[1] = (flen >> 8) & 0xFF;
frame[2] = flen & 0xFF;
uint8_t cs = 0; // 校验和
for (int i = 1; i < pos; i++) cs += frame[i];
frame[pos++] = cs & 0xFF;
frame[pos++] = 0xEF; // 结束码
uart_write_bytes(UART_NUM, frame, pos);
}
门铃按铃触发TTS播报的完整流程
实际门铃项目的触发逻辑通常这样:ESP32通过GPIO检测到按铃事件(硬件中断或轮询)→ 通过Wi-Fi或4G网络获取访客信息(如"快递员"或"张先生")→ 组装播报文本 → 调用tts_send_text()发送 → 等待播报完成(查Busy引脚)→ 进入低功耗待机。
// 门铃按键中断回调函数(伪代码流程)
void doorbell_handler(void) {
// 1. 防抖处理
// 2. 组装播报文本
char *msg = "叮咚,门口有人按铃";
// 3. 可选:查Busy状态
// 4. 发送TTS文本
tts_send_text(msg, strlen(msg));
// 5. 若需连续播报多条,等Busy变高后再发下一条
}
音量调节
WT3000TX支持31级音量调节(0~30级)。建议在初始化完成后发一条音量设置命令。门铃通常装在门口,环境噪声较大,建议设置在20~25级;若是室内使用的公司门铃,15级左右就够用了。注意:WT3000T-M01模块是DAC输出经过内置PWM功放驱动喇叭,音量调节是通过改变DAC输出幅度实现的,不是数字音量(会有一定的底噪,在安静环境下能听到)。
TTS和固定提示音混用
WT3000T8版本内置30秒固定语音空间,WT3000T3版本内置500秒。固定语音是在芯片出厂前由厂家烧录进去的(需要联系唯创商务,提供音频文件和定制需求),TTS是运行时实时合成的。实际项目中可以这样搭配使用:把"叮咚"这样的按键提示音做成固定语音(响应快、音质好),把访客姓名、动态通知等可变内容用TTS合成。两者通过不同的UART命令触发,芯片内部会自动处理播放优先级。
低功耗设计——电池供电门铃的关键
如果是电池供电的门铃(比如用CR123A或18650电池),功耗是要重点考虑的。WT3000TX芯片在深度休眠模式下功耗小于20μA,但一旦开始TTS合成,工作电流会上升到几十毫安(具体数值与音量、是否驱动喇叭有关)。
休眠策略:门铃产品大部分时间处于待机状态(没有按铃事件)。此时ESP32可以进入轻睡眠,WT3000T芯片也进入休眠。按铃事件通过GPIO中断唤醒ESP32,再由ESP32通过UART发唤醒命令唤醒TTS芯片。
唤醒命令:WT3000TX支持通过UART命令让它进入深度休眠,也支持通过UART命令唤醒(或硬件引脚唤醒)。具体命令码同样需要查官方指令表。
WT3000T系列TTS芯片(或M01模块)在可视门铃项目中的核心价值是:用很小的软件代价实现完全灵活的语音播报内容,解决了预录MP3方案在多语言、定制化、动态信息播报等场景下的维护难题。
量产初期推荐:用WT3000T-M01模块。BOM成本约XX元/片(具体价格与采购量和唯创商务确认),比自己画芯片电路的时间成本划算得多。模块到手接线就能调试,1天内可以跑通基本功能。
大批量产推荐:用WT3000T8-32N或WT3000T3-32N裸芯片(QFN32,4mm×4mm)。需要自己画PCB电路(晶振、电源滤波、阻抗匹配),但BOM成本可以压到X元/片左右。WT3000T3版本内置500秒固定语音,对商用门铃更合适。
最后说一点:TTS合成的音质和自然度不如预录的高品质MP3。如果对播报音质有很高要求(比如高端酒店的门铃),可以考虑"预录MP3+核心提示音"和"TTS+动态内容"混合使用的方案——固定提示音用预录,动态内容用TTS,两者通过UART命令的插播功能协调,互不干扰。
全部0条评论
快来发表一下你的评论吧 !