小智语音板(evo-voice-v1)
基于 xiaozhi-esp32 的板卡定制包。裸 I2S 方案:INMP441 数字麦克风 + MAX98357A 数字功放, 没有 I2C codec 芯片。5 小时从零 bring-up,踩了 7 类问题。
独立开发ESP32-S3I2S硬件排障
为什么用裸 I2S 而不是 codec
INMP441 直接输出 I2S 数字流,MAX98357A 直接吃 I2S 数字流——两个都是数字器件, 中间不需要 ADC/DAC,也不需要 I2C 去配寄存器,省掉了 codec 和它的控制线。
代价:采样率、槽宽、声道全得自己在软件里配对。 而这也正是"四连坑"的来源——见 排障案例 2。
接线图
左边收、右边发,中间两块都是数字器件;功放供电是唯一需要特别注意的地方(图底部红框)。
接线表(逐脚对照)
| 模块 | 引脚 | 说明 |
|---|---|---|
| INMP441 I2S 麦克风 |
SCK → GPIO12 | 位时钟(BCLK),主机输出 |
WS → GPIO13 | 左右声道时钟(帧同步) | |
SD → GPIO21 | 数据:麦克风 → ESP32 | |
L/R → GND | ⭐ 这是 WS 的硬件版本,接 GND = 输出左声道 | |
VDD → 3.3V | ||
GND → GND | ||
| MAX98357A I2S 功放 |
BCLK → GPIO4 | 位时钟 |
LRC → GPIO14 | 左右声道时钟 | |
DIN → GPIO18 | 数据:ESP32 → 功放 | |
SD → GPIO5 | 使能:高=开、低=关。固件上电先拉低(静音),出声才拉高 | |
GAIN → 悬空 | 不接 = 默认 9 dB | |
VIN | ⚠️ 见下方供电说明 | |
| 板载 | LED → GPIO2 / BOOT 键 → GPIO0 | 短按:启动中进配网,运行中切换对话 |
MCLK 不接(两个器件都不需要主时钟)。
软件侧必须对齐的参数
| 项 | 值 | 为什么 |
|---|---|---|
| 采样率 | 16 kHz 收发一致 | |
| I2S 端口 | I2S0 = 麦克风(RX) I2S1 = 功放(TX) | 两路独立通道,均主机模式 |
| 槽位宽 | 16-bit(不是 32-bit) | 32-bit 下 BCLK 翻倍到 1.024 MHz,与麦克风时序不匹配 → 输出 8192 步长乱码 |
| BCLK | 16k × 2 × 16 = 512 kHz | 能现场代公式,也能对上实测值 |
| 声道模式 | 立体声槽(TX 需 L=R 复制) | 否则单声道被当双声道 → 播放速度翻倍 |
| 麦克风增益 | ×16 + 限幅 | INMP441 输出幅度只有满幅 ~1.5% |
⚠️ 供电:这块板最容易翻车的地方
功放从 3.3V 轨取电,播放瞬间的瞬态大电流会把 3.3V 轨拉垮, 电压跌破 BOD 阈值(实测跌到 2.1V 以下)→ 硬件欠压复位。 现象是"无规律复位、且没有任何日志"——因为硬件复位比软件写日志快得多。
必须用充电器 / 独立 5V 电源供电(电脑 USB 口 500mA 限制撑不住)。 改善方案:功放 VIN 改接 5V 轨,或就地并联 ≥470µF 电容储能。
完整排障链(含我走的弯路)见 排障案例 1。
引脚避让(接错必挂)
- GPIO33–37:PSRAM 总线(OCT 模式)→ 占用会让 I2S 初始化直接挂起
- GPIO26–32:Flash 总线
- GPIO15 / 16:实测异常——用 PCNT 计数器飞线测到 1.126 MHz / 120 kHz 这种"不该存在"的频率, 疑似板载信号占用。麦克风 BCLK/WS 因此从 15/16 迁到了 12/13
让服务器能对空闲设备主动播报(keep-alive)
语音板空闲一段时间后,服务器推播报会返回 404 no active device——设备侧连接被释放了。
我给语音链路加了一个 WebSocket keep-alive 补丁,让服务端能随时推送 TTS 播报(告警 / 通知)。
边界验证:空闲 18.5 分钟后仍能推送成功(定时 700 秒,特意超过旧的 660 秒超时阈值来验证)。