💡 划重点
- 主控:ESP32-WROOM-32E(双核 240 MHz,内置 WiFi / 蓝牙)
- 音频输入:INMP441 全向 MEMS 麦克风
- 音频输出:MAX98357A I2S 3W D 类功放 + 3W 喇叭
- 存储:Micro SD 卡模块(SPI),保存音频日志与自定义音效
- 交互:实体大按键 + WS2812 RGB 灯环,适合 3-6 岁儿童抓握与操作
- 成本:全部物料合计 ≤ 85 元
- 核心玩法:按下拼图块,机器人复读孩子说的话,并用灯光闪动回应,支持 8 段独立录音/回放
需求洞察
目标用户:3-6 岁学龄前儿童及其家长,尤其关注语言启蒙与亲子互动的家庭。
真实痛点:
- 市面故事机只能单向播放,儿童缺少即时声音反馈,互动性弱。
- 大量产品依赖手机屏幕,家长担心视力与成瘾问题。
- 简易录音玩具音质差、存储少,无法记录成长轨迹。
- 拼图类玩具静态无反馈,难以持续吸引注意力。
为什么值得做:项目将拼图形式与“听见自己的声音”结合,让儿童在动手拼摆时得到即时语音强化,促进语言发育与因果认知。硬件成本低、模块成熟,便于家长或创客自制,也可作为 STEAM 教具进入幼儿园。ESP32 的无线能力预留了后续远程互动、云端故事下载的扩展空间。
方案设计
感知层:
- 8 路拼图检测(微动开关或磁簧):连接到 ESP32 的 8 个 GPIO,内部上拉,拼图放置时电平变化,触发录音/回放。
- INMP441 麦克风:I2S 接口,高灵敏度拾取儿童语音。
- 录音按钮(实体蘑菇头按键):防抖,由 GPIO 外部中断驱动。
控制层:
- ESP32 双核分工:Core 0 处理 Wi-Fi 与日志上传(可选),Core 1 专注音频与 I/O 轮询。
- FreeRTOS 任务:录音任务、回放任务、LED 动画任务、SD 写入任务。
执行层:
- MAX98357A + 3W 喇叭:播放录制音频或预置鼓励音效。
- WS2812 灯环(12 灯珠):根据录音/回放状态显示不同颜色与流水效果。
- 状态 LED(红/绿):指示电源与 SD 卡状态。
日志/联网层:
- SD 卡:存储每次录音的 WAV 文件,按时间戳命名,便于家长手机导出。
- Wi-Fi(可选):连接家庭网络后,通过 MQTT 将使用记录(按键次数、录音时长)上报到私有服务器,或拉取云端新音效。
系统工作流程:
- 上电初始化 I2S 输入输出、SD 卡、按键、灯环。
- 主循环扫描拼图检测引脚,当某个拼图块被放入且录音按钮被按下时,开始录音 3 秒,灯环变红呼吸,录音数据通过 INMP441 采集并 DMA 缓冲写入 SD 卡。
- 释放按钮后立即停止录音,灯环变绿,自动回放录音,喇叭出声。
- 若未放拼图块直接按录音键,播放预置的欢迎语“来和我说话吧”。
- 长按录音键 5 秒进入 Wi-Fi 配置模式(SmartConfig),LED 灯环蓝色闪烁。
- 所有操作日志追加写入 log.txt,包含时间、事件、文件路径。
物料清单
| 序号 | 名称 | 数量 | 用途 | 单价(元) | 小计(元) |
|---|---|---|---|---|---|
| 1 | ESP32-WROOM-32E 开发板 | 1 | 主控 | 22 | 22 |
| 2 | INMP441 麦克风模块 | 1 | 音频输入 | 12 | 12 |
| 3 | MAX98357A I2S 功放模块 | 1 | 音频放输出 | 9 | 9 |
| 4 | 3W 4Ω 内磁喇叭 | 1 | 发声 | 5 | 5 |
| 5 | Micro SD 卡模块(SPI) | 1 | 存储 | 4 | 4 |
| 6 | 32GB Micro SD 卡 | 1 | 存储介质 | 15 | 15 |
| 7 | WS2812 灯环(12灯) | 1 | 交互反馈 | 6 | 6 |
| 8 | 12mm 蘑菇头按键 | 1 | 录音触发 | 2 | 2 |
| 9 | 微动开关模块 | 8 | 拼图检测 | 0.8 | 6.4 |
| 10 | 5V 2A USB 电源适配器 | 1 | 供电 | 8 | 8 |
| 11 | 跳线、排针、热缩管 | 若干 | 连接 | 3 | 3 |
| 12 | 3D 打印外壳 + 木制拼图块 | 1 套 | 结构 | 本人加工 | 0 |
| 合计 | 92.4 |
*注:总价 92.4 元略超 85 元预算,可将 SD 卡替换为 8GB(10 元),喇叭选更低成本(3 元),或微动开关使用普通轻触开关+PCB(成本再降 3 元),最终可控制在 84.4 元以内。*
接线与结构
ESP32 引脚分配(以实际代码为准,此处为推荐连接):
| 功能 | 模块引脚 | ESP32 GPIO | 备注 |
|---|---|---|---|
| I2S 位时钟 | MAX98357A BCLK | 26 | 与麦克风共用 BCLK |
| I2S 左右时钟 | MAX98357A LRC | 25 | 与麦克风共用 LRC |
| I2S 数据输入(DAC) | MAX98357A DIN | 22 | 输出音频数据 |
| I2S 位时钟 | INMP441 SCK | 32 | 共用 BCLK |
| I2S 左右时钟 | INMP441 WS | 33 | 共用 LRC |
| I2S 数据输出(ADC) | INMP441 SD | 35 | 麦克风数据到 ESP |
| SD 卡 SPI CS | CS | 5 | |
| SD 卡 SPI MOSI | MOSI | 23 | |
| SD 卡 SPI MISO | MISO | 19 | |
| SD 卡 SPI SCK | SCK | 18 | |
| WS2812 灯环 | DIN | 13 | |
| 录音按键 | — | 34 | 外部上拉,低电平有效 |
| 拼图检测 0~7 | — | 27,14,12,4,15,16,17,21 | 均配置为 INPUT_PULLUP |
理由:I2S 两设备共用 BCLK 和 LRC,必须严格同步。本配置将 INMP441 的 SCK/WS 连接到与 MAX98357A 不同的 GPIO 32/33,但代码中需配置为相同信号。实际上,一个 I2S 外设只有一组 BCLK/LRC,因此需将两模块的时钟线并联。ESP32 的 I2S0 支持全双工,但默认 BCLK 和 LRC 只能从同一组引脚输出。正确做法:MAX98357A 的 BCLK、LRC 分别接 GPIO26、25;INMP441 的 SCK、WS 也必须短接至 GPIO26、25,实现时钟同步。本接线表保留原给的 32/33 用于麦克风时钟的建议,但在调试步骤中会说明需飞线短接至 26/25,以确保双工 I2S 稳定工作。如果开发者希望使用独立 I2S 接口,则需改用 ESP32-S3 或外接 I2S 分支芯片,受成本限制不做采用。
结构:外壳为圆饼形机器人脸,中心嵌灯环,下方透气网罩内置喇叭,侧面插入加密 SD 卡槽,顶部蘑菇头按键适合小手拍打。背部磁吸拼图底板,内嵌 8 个微动开关,当对应形状木块嵌入时压下开关。所有电路封装在腹部,USB 供电线从底座引出。
核心代码
以下使用 Arduino IDE 框架,需安装 ESP32-AudioI2S、SD、Adafruit NeoPixel 库。代码实现双工 I2S 录音与播放、按键扫描、SD 存储、灯控逻辑。骨架可编译,细节函数留空做伪代码示意。
#include <Arduino.h>
#include <WiFi.h>
#include <SD.h>
#include <SPI.h>
#include <Adafruit_NeoPixel.h>
#include <driver/i2s.h>
// I2S 引脚定义(共用 BCK/LCK)
#define I2S_BCK 26
#define I2S_LCK 25
#define I2S_DOUT 22 // MAX98357A DIN
#define I2S_DIN 35 // INMP441 SD(与 35 编号一致)
// SD 卡
#define SD_CS 5
#define SD_SCK 18
#define SD_MISO 19
#define SD_MOSI 23
// 按键与 LED
#define BTN_REC 34
#define NUMPIXELS 12
#define NEOPIXEL_PIN 13
Adafruit_NeoPixel pixels(NUMPIXELS, NEOPIXEL_PIN, NEO_GRB + NEO_KHZ800);
// 拼图检测引脚
const int puzzlePins[8] = {27,14,12,4,15,16,17,21};
// 录音参数
#define SAMPLE_RATE 16000
#define RECORD_TIME 3 // 秒
File audioFile;
bool isRecording = false;
void setupI2S() {
i2s_config_t i2s_config = {
.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX | I2S_MODE_RX),
.sample_rate = SAMPLE_RATE,
.bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT,
.channel_format = I2S_CHANNEL_FMT_ONLY_LEFT,
.communication_format = I2S_COMM_FORMAT_STAND_I2S,
.intr_alloc_flags = ESP_INTR_FLAG_LEVEL1,
.dma_buf_count = 8,
.dma_buf_len = 1024
};
i2s_pin_config_t pin_config = {
.bck_io_num = I2S_BCK,
.ws_io_num = I2S_LCK,
.data_out_num = I2S_DOUT,
.data_in_num = I2S_DIN
};
i2s_driver_install(I2S_NUM_0, &i2s;_config, 0, NULL);
i2s_set_pin(I2S_NUM_0, &pin;_config);
}
void setupSD() {
if (!SD.begin(SD_CS)) {
// 红灯常亮表示错误
}
}
void recordToSD(const char* filename) {
audioFile = SD.open(filename, FILE_WRITE);
// 写 WAV 头预留
size_t bytesRecorded = 0;
int16_t sampleBuffer[512];
unsigned long start = millis();
while (millis() - start < RECORD_TIME * 1000) {
size_t bytesRead;
i2s_read(I2S_NUM_0, sampleBuffer, sizeof(sampleBuffer), &bytesRead;, portMAX_DELAY);
audioFile.write((uint8_t*)sampleBuffer, bytesRead);
bytesRecorded += bytesRead;
}
audioFile.seek(0);
// 补充 WAV 头
audioFile.close();
}
void playFromSD(const char* filename) {
audioFile = SD.open(filename);
// 跳过 WAV 头,循环读数据送 I2S 输出
audioFile.seek(44);
while (audioFile.available()) {
int16_t buffer[512];
size_t bytesRead = audioFile.read((uint8_t*)buffer, sizeof(buffer));
size_t bytesWritten;
i2s_write(I2S_NUM_0, buffer, bytesRead, &bytesWritten;, portMAX_DELAY);
}
audioFile.close();
}
void setup() {
Serial.begin(115200);
pixels.begin();
pixels.clear();
for (int i = 0; i < 8; i++) pinMode(puzzlePins[i], INPUT_PULLUP);
setupI2S();
setupSD();
}
void loop() {
// 扫描拼图
int activePuzzle = -1;
for (int i = 0; i < 8; i++) {
if (digitalRead(puzzlePins[i]) == LOW) {
activePuzzle = i;
break;
}
}
// 录音按键按下且拼图存在则录音
if (digitalRead(BTN_REC) == LOW && activePuzzle >= 0) {
char fname[32];
sprintf(fname, "/puzzle%d_%lu.wav", activePuzzle, millis());
pixels.fill(pixels.Color(255,0,0)); // 红色呼吸
pixels.show();
recordToSD(fname);
delay(100);
// 回放
pixels.show();
playFromSD(fname);
pixels.clear();
}
// 无拼图按下录音键,播放预置音效
else if (digitalRead(BTN_REC) == LOW && activePuzzle < 0) {
playFromSD("/system/hello.wav");
delay(500);
}
delay(50);
}
调试步骤
- 供电检查:使用 USB 电源适配器通电,测量 5V 和 3.3V 是否正常,板载 LED 亮起。
- I2S 时钟同步:用示波器或逻辑分析仪确认 GPIO26 和 25 分别产生 1.024 MHz 位时钟和 16 kHz 左右时钟。若麦克风无数据,检查 INMP441 的 SCK/WS 是否已飞线短接至 26/25,SD 引脚电平是否与 DOUT 冲突。
- SD 卡调试:先运行简单的 SD 列表程序,确保在
/system目录下放入hello.wav(16 kHz 单声道)能被识别。 - 首次录音:插入任意拼图块,按下蘑菇头,观察串口监视器是否打印文件创建信息,灯环由红转绿。播放时用手机靠近喇叭验证是否有声音。
- 噪音排查:若播放有底噪,检查 I2S 走线是否远离电源线,并在 MAX98357A 的 VDD 和 GND 间加 100μF 电解电容。
- 多路拼图:逐个放入 8 块拼图,确认每个微动开关可靠触发,串口输出对应编号。
- 长按配网:长按录音键 5 秒,ESP32 进入 SmartConfig 模式,使用 ESPTOUCH APP 配网,获取 IP 后尝试向本地服务器发送测试 MQTT 消息。
- 结构装配:将电路板固定在外壳中,喇叭朝向网罩,SD 卡槽对外,确保按键手感良好,灯环可见。
设计图
*流程图简要描述了数据与控制信号的流转:感知信号(按键、麦克风)送入 ESP32,控制核心根据逻辑驱动喇叭、灯环与存储,日志可选上报云端。*