💡 划重点
- 用 ESP32 做主控,INMP441 采音、MAX98357A 放音,全部走 I2S 数字链路,底噪低、接线少。
- 拼图块识别不靠摄像头,靠拼图底座上的轻触/磁簧触点组合映射“拼图 ID”,成本低且对幼儿友好。
- 语音复读做“短句录制 + 变调回放”:孩子拼对一块,机器人用卡通音复述该块名称,强化认知闭环。
- SD 卡存放预置语音与录音缓存,离线可用,不依赖网络,适合家庭与幼儿园场景。
- 整机物料控制在 85 元内,外壳用 3D 打印或纸盒改造,适合亲子共创。
需求洞察
目标用户是 3 至 6 岁儿童及其家长、幼儿园教师。这个年龄段的孩子注意力短、识字量低,传统拼图只有“拼对/拼错”的视觉反馈,缺少语言强化;而市面上的语音玩具多为固定音效,不能跟随拼图内容变化,孩子玩两三次就失去兴趣。
真实痛点有三:其一,家长希望孩子在玩中练习发音与词汇,但陪玩时间有限,需要设备能“接话”;其二,幼儿对机械按键和屏幕不敏感,需要“放上去就有反应”的直接交互;其三,家庭场景对隐私敏感,不希望录音上传云端。
值得做的原因在于:拼图是几乎每个家庭都有的玩具,复读功能把“动手”与“开口”结合,符合语言敏感期的学习规律;同时硬件成本低、可复制性强,家长能带着孩子一起组装,项目本身也是一次亲子 STEM 体验。
方案设计
感知层:INMP441 数字麦克风通过 I2S 采集孩子跟读的声音,采样率 16 kHz、16 位单声道,够用且省存储。拼图底座在 6 个卡位下各放一个轻触开关,按下即代表该位置有拼图块,通过 GPIO 扩展读取组合状态。麦克风只在按下“录音键”或检测到拼图块放入后 2 秒内开启,减少无效采集。
控制层:ESP32 负责三件事——拼图状态扫描与去抖、录音缓存管理、语音播放调度。录音写入 SD 卡的 WAV 文件,播放时读取并做简单的变速变调(重采样实现“卡通音”)。主循环采用非阻塞状态机,避免录音与播放互相卡死。
执行层:MAX98357A 是 I2S 功放,直接驱动 4 欧 3 瓦小喇叭,无需外部 DAC。拼对时播放预置短句(如“这是小象”),并按需回放孩子刚才的录音,形成“你说—它学”的互动。另加一颗 RGB 灯珠做状态提示:蓝色录音、绿色正确、橙色重试。
日志/联网层:SD 卡记录每次拼图完成的时间、卡位组合与录音文件名,形成简单的学习日志,家长可用读卡器导出。ESP32 的 Wi-Fi 默认关闭,仅在长按按键 5 秒时开启热点,提供网页查看日志,兼顾隐私与可维护性。
物料清单
| 物料 | 数量 | 用途 | 估算单价(元) | 合计(元) |
|---|---|---|---|---|
| ESP32 开发板(DevKitC) | 1 | 主控 | 22 | 22 |
| INMP441 麦克风模块 | 1 | 采音 | 9 | 9 |
| MAX98357A 功放模块 | 1 | 音频输出 | 12 | 12 |
| 4Ω 3W 喇叭 | 1 | 发声 | 6 | 6 |
| Micro SD 卡模块 + 8GB 卡 | 1 | 语音与日志存储 | 14 | 14 |
| 轻触开关 | 6 | 拼图卡位检测 | 0.5 | 3 |
| RGB 灯珠(WS2812) | 1 | 状态提示 | 2 | 2 |
| 洞洞板、排针、导线 | 1 批 | 连接 | 6 | 6 |
| 外壳(纸盒/3D 打印) | 1 | 结构 | 5 | 5 |
| 电源(USB 5V 2A) | 1 | 供电 | 5 | 5 |
| 合计 | 84 |
接线与结构
按给定建议接线,I2S 共用一组时钟:BCLK=26、LRC=25。麦克风与功放的数据脚分开,避免半双工冲突:功放 DIN=22,麦克风 SD=32。麦克风的 SCK 与 WS 分别接 26 与 25,形成“同组时钟、双向数据”的接法。麦克风 L/R 脚接地选择左声道。
微调说明:原建议中麦克风写作“32/33/35”,本方案固定 SD=32,把 33、35 留给两颗拼图卡位开关,减少引脚浪费;6 个卡位开关使用 32 以外的 GPIO(如 4、5、13、14、16、17),其余通过二极管矩阵可再压缩,但首版优先可读性。
结构上,拼图底座做成浅槽,每个槽底开孔露出轻触开关的帽子;ESP32 与音频模块装在底座后仓,喇叭朝上,麦克风开小孔朝前。喇叭与麦克风之间加一层泡棉,降低回授啸叫。
核心代码
以下为 Arduino 骨架,基于 ESP32 Arduino Core,可编译。
#include <Arduino.h>
#include <driver/i2s.h>
#include <SPI.h>
#include <SD.h>
#define I2S_BCLK 26
#define I2S_LRC 25
#define I2S_DOUT 22 // MAX98357A DIN
#define I2S_MIC 32 // INMP441 SD
static const int kSlots[6] = {4, 5, 13, 14, 16, 17};
static bool lastState[6] = {false};
void i2sInit() {
i2s_config_t cfg = {};
cfg.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX | I2S_MODE_RX);
cfg.sample_rate = 16000;
cfg.bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT;
cfg.channel_format = I2S_CHANNEL_FMT_ONLY_LEFT;
cfg.communication_format = I2S_COMM_FORMAT_STAND_I2S;
cfg.dma_buf_count = 8;
cfg.dma_buf_len = 256;
cfg.use_apll = false;
i2s_driver_install(I2S_NUM_0, &cfg;, 0, NULL);
i2s_pin_config_t pin = {};
pin.mck_io_num = I2S_PIN_NO_CHANGE;
pin.bck_io_num = I2S_BCLK;
pin.ws_io_num = I2S_LRC;
pin.data_out_num = I2S_DOUT;
pin.data_in_num = I2S_MIC;
i2s_set_pin(I2S_NUM_0, &pin;);
}
void playWav(const char* path) {
File f = SD.open(path);
if (!f) return;
f.seek(44); // 跳过 WAV 头
uint8_t buf[512];
size_t n;
while ((n = f.read(buf, sizeof(buf))) > 0) {
size_t written = 0;
i2s_write(I2S_NUM_0, buf, n, &written;, portMAX_DELAY);
}
f.close();
}
void setup() {
Serial.begin(115200);
for (int i = 0; i < 6; i++) pinMode(kSlots[i], INPUT_PULLUP);
i2sInit();
if (!SD.begin(5)) Serial.println("SD 初始化失败");
}
void loop() {
for (int i = 0; i < 6; i++) {
bool now = (digitalRead(kSlots[i]) == LOW);
if (now && !lastState[i]) { // 去抖后的“放入”沿
char path[24];
snprintf(path, sizeof(path), "/p%d.wav", i);
playWav(path);
delay(200);
}
lastState[i] = now;
}
delay(20);
}
录音路径可用 i2s_read 把 MIC 数据写入 WAV 文件,注意写入前先写 44 字节头并回填长度;播放录音时可对采样做 1.25 倍重采样实现“变声”效果。
调试步骤
- 先只接 ESP32 与 MAX98357A,播放 SD 卡中的测试音,确认喇叭有声且无爆音;若无声,检查 SD 模式脚与功放 EN 脚电平。
- 接入 INMP441,用
i2s_read打印峰值,确认说话时数值明显变化;若一直为零,核对 L/R 是否接地、时钟是否与功放共用。 - 逐个测试 6 个卡位开关,串口打印状态,确认按下为 LOW,并加入 20 ms 去抖。
- 联调“放入即播”:先放预置语音,再测试录音回放,注意录放之间加 100 ms 静默,避免电流冲击。
- 整机装壳后测啸叫:把喇叭音量限制在 70%,麦克风与喇叭错位安装,必要时加泡棉隔断。
- 连续运行 30 分钟,观察 SD 写入是否掉卡、灯珠颜色是否正确。