实战案例:打造会 “报时 + 提醒” 的智能音箱
本文介绍了一个低成本DIY智能音箱方案,使用ESP32开发板配合DS3231实时时钟模块和SYN6288语音合成模块,实现整点报时和自定义提醒功能。整套硬件成本控制在100元以内,无需焊接,通过面包板即可完成组装。详细说明了硬件连接方式、软件配置和完整代码实现,包含RTC时间读取、TTS语音播报、按键设置提醒等功能。同时提供了常见问题解决方法,如RTC初始化失败、按键无响应等。该方案特别适合电子爱
实战案例:打造会 “报时 + 提醒” 的智能音箱(含完整代码,100 元内搞定)
一、开篇:新手也能做的智能音箱 —— 核心功能 + 低成本方案
你是不是想过:“能不能自己做个智能音箱,不用连 WiFi,就能整点报时、设置喝水提醒?”
其实不用复杂模块!今天用 ESP32 + 时钟模块 + 离线 TTS 方案,实现两大核心功能:
✅ 整点报时:比如 “现在是下午 3 点整”;
✅ 按键设置提醒:按一下按键,设置 “10 分钟后提醒喝水”,到点自动播报;
全套零件成本≤100 元,不用焊接,面包板直插,代码复制就能跑,新手 3 小时内搞定!
二、工具清单(淘宝可搜,100 元内封顶)
|
零件名称 |
型号推荐 |
淘宝搜索关键词 |
价格范围 |
核心作用 |
说明书获取渠道 |
|
ESP32 开发板 |
ESP32-WROOM-32 |
ESP32 开发板 带排针 |
30-50 元 |
核心控制(读时间 + 触发播报) |
乐鑫官网→Espressif ESP32 Datasheet |
|
离线 TTS 模块 |
SYN6288(带功放) |
SYN6288 TTS 模块 |
15-25 元 |
文本转语音(报时 / 提醒内容) |
科大讯飞开放平台→搜索 “SYN6288 用户手册” |
|
DS3231 实时时钟模块 |
DS3231(带电池) |
DS3231 RTC 模块 备用电池 |
10-15 元 |
提供精准时间(掉电不丢时间) |
CSDN 搜 “DS3231 datasheet” |
|
轻触按键 |
6×6mm 四脚按键 |
轻触按键 6×6mm |
5 元 / 10 个 |
设置提醒(触发时间录入) |
淘宝卖家详情页(无需复杂说明书) |
|
微型喇叭 |
4Ω 1W |
4Ω 1W 小喇叭 带线 |
8-12 元 |
发声载体 |
淘宝卖家详情页 |
|
锂电池 + 充电模块 |
11.1V 18650(可选) |
18650 锂电池 充电模块 |
20-30 元 |
便携供电(脱离插电使用) |
淘宝卖家详情页 |
|
面包板 + 杜邦线 |
400 孔面包板 + 公对母 |
面包板 杜邦线 套装 |
10-15 元 |
连接零件(不用焊接) |
无需说明书 |
选型理由:
- 用 DS3231 而非软件计时:避免 ESP32 掉电后时间重置,报时更精准;
- 选带电池的 RTC 模块:断电后仍能保存时间,下次上电直接用;
- 锂电池供电:可做成便携音箱,放床头、书桌都方便(若不用便携,用 USB 供电也行)。
三、核心功能逻辑拆解(先懂原理,再动手)
1. 整点报时逻辑
|
graph TD A[DS3231实时时钟] --> B[ESP32每10秒读取时间] B --> C{是否到整点?(如14:00:00)} C -- 是 --> D[调用SYN6288播报“现在是下午2点整”] C -- 否 --> B |
- 关键:ESP32 通过 I2C 接口读 DS3231 的时间,判断 “分钟 = 0 且秒 = 0” 时触发报时;
- 优化:加 “上午 / 下午” 判断(比如 13 点→下午 1 点),符合日常习惯。
2. 按键设置提醒逻辑
|
graph TD A[按下轻触按键] --> B[ESP32检测到按键触发] B --> C[读取当前时间+设置提醒间隔(如10分钟)] C --> D[计算提醒时间(当前+10分钟)] D --> E[存储提醒时间到EEPROM(掉电不丢)] E --> F[ESP32循环对比当前时间vs提醒时间] F -- 时间一致 --> G[调用SYN6288播报“提醒您喝水啦!”] F -- 不一致 --> F |
- 关键:用 EEPROM 存储提醒时间,避免 ESP32 断电后提醒失效;
- 优化:加按键防抖(避免按一次触发多次),提醒后自动清除已完成的提醒。
四、分步骤实操:接线→代码→测试
(一)第一步:硬件接线(表格 + 文字说明,零焊接)
核心原则:ESP32 通过 I2C 接 RTC 模块,串口接 TTS 模块,GPIO 接按键,喇叭接 TTS 模块,所有零件共地(GND 连在一起)。
|
模块 / 零件 |
引脚名称 |
连接到 ESP32 的引脚 |
接线说明 |
|
DS3231 RTC 模块 |
VCC |
3.3V |
给 RTC 模块供电(3.3V,别接 5V!) |
|
DS3231 |
GND |
GND |
共地(必须接,否则读不到时间) |
|
DS3231 |
SDA |
GPIO21 |
I2C 数据引脚(ESP32 默认 I2C 引脚) |
|
DS3231 |
SCL |
GPIO22 |
I2C 时钟引脚 |
|
SYN6288 TTS 模块 |
VCC |
3.3V |
给 TTS 模块供电(ESP32 输出 3.3V 足够) |
|
SYN6288 |
GND |
GND |
共地(否则语音有杂音) |
|
SYN6288 |
RX |
GPIO17(TX) |
ESP32 给 TTS 模块发指令(TX→RX) |
|
SYN6288 |
TX |
GPIO16(RX) |
TTS 模块回传状态(新手可省略,不影响基础功能) |
|
SYN6288 |
SPK+ |
喇叭正极 |
TTS 模块功放输出→喇叭发声 |
|
SYN6288 |
SPK- |
喇叭负极 |
喇叭负极(接反没声音,换线即可) |
|
轻触按键 |
一脚 |
GPIO0 |
按键输入(GPIO0 下拉,按下去触发) |
|
轻触按键 |
另一脚 |
GND |
按键接地(形成回路) |
|
锂电池(可选) |
正极(通过充电模块) |
VBAT |
给 ESP32 供电(便携使用,USB 供电可省略) |
(二)第二步:软件准备(Arduino IDE + 库安装)
- 打开 Arduino IDE,安装以下库(直接搜名称安装):
- RTClib:用于读取 DS3231 时间(作者:Adafruit);
- Wire:ESP32 I2C 通信库(IDE 自带,无需额外安装);
- HardwareSerial:ESP32 串口通信库(IDE 自带);
- EEPROM:ESP32 存储提醒时间(IDE 自带)。
- 选择开发板:工具→开发板→ESP32 Dev Module,端口选 ESP32 连接电脑的 COM 口。
(三)第三步:完整代码(复制即用,含详细注释)
|
// 引入所需库 #include <Wire.h> #include <RTClib.h> #include <HardwareSerial.h> #include <EEPROM.h> // 1. 模块初始化配置 RTC_DS3231 rtc; // 初始化RTC模块(DS3231) HardwareSerial ttsSerial(2); // 初始化串口2(接SYN6288,TX=17,RX=16) // 2. 引脚定义 const int KEY_PIN = 0; // 按键引脚(GPIO0) const int EEPROM_ADDR = 0; // EEPROM存储提醒时间的地址(0-511) // 3. 变量定义 DateTime now; // 存储当前时间 unsigned long reminderTime = 0; // 存储提醒时间(单位:秒,从2000年1月1日开始算) bool hasReminder = false; // 是否有未完成的提醒 unsigned long lastKeyTime = 0; // 按键防抖:记录上次按键时间 const int DEBOUNCE_TIME = 500; // 防抖时间(500ms) void setup() { Serial.begin(115200); // 电脑串口调试(波特率115200) ttsSerial.begin(9600); // TTS模块波特率(固定9600,和SYN6288默认一致)
pinMode(KEY_PIN, INPUT_PULLUP); // 按键引脚设为上拉输入(按下去为低电平) EEPROM.begin(512); // 初始化EEPROM(大小512字节) // 初始化RTC模块 if (!rtc.begin()) { Serial.println("RTC模块初始化失败!检查接线!"); ttsSpeak("RTC模块初始化失败,请检查接线"); while (1); // 初始化失败则卡住,提示用户 } // 如果RTC模块没设置时间,手动设置一次(第一次使用时打开注释,设置后注释掉) // rtc.adjust(DateTime(F(__DATE__), F(__TIME__))); // 用编译时间设置RTC // 从EEPROM读取之前存储的提醒时间 EEPROM.get(EEPROM_ADDR, reminderTime); hasReminder = (reminderTime > 0); // 有非0的提醒时间,说明有未完成提醒 Serial.print("从EEPROM读取的提醒时间:"); Serial.println(reminderTime); ttsSpeak("智能音箱初始化完成,支持整点报时和按键设置提醒"); delay(2000); } void loop() { now = rtc.now(); // 读取当前时间 checkKey(); // 检查按键是否被按下(设置提醒) checkHourlyReport(); // 检查是否到整点(触发报时) checkReminder(); // 检查是否到提醒时间(触发提醒) delay(1000); // 每秒循环一次,避免频繁读取 } // 【功能1:TTS播报函数】给SYN6288发指令,播报文本 void ttsSpeak(String text) { if (text.length() == 0) return; // 空文本不播报
// SYN6288通信协议:帧头(AA) + 指令(01=播报) + 文本长度 + 文本 + 帧尾(BB) ttsSerial.write(0xAA); ttsSerial.write(0x01); ttsSerial.write(text.length()); ttsSerial.print(text); ttsSerial.write(0xBB);
Serial.print("已播报:"); Serial.println(text); delay(1000); // 等待播报开始,避免指令堆积 } // 【功能2:整点报时检查】判断是否到整点,生成报时文本 void checkHourlyReport() { int minute = now.minute(); int second = now.second();
// 每分钟的0秒检查是否到整点(避免每秒检查,节省资源) if (second == 0 && minute == 0) { String timeText = getTimeText(); // 获取“上午X点整”格式的文本 ttsSpeak("现在是" + timeText); delay(5000); // 避免同一整点重复播报(延迟5秒) } } // 【辅助函数:时间转中文文本】比如14:00→“下午2点整” String getTimeText() { int hour = now.hour(); String period = "上午";
// 判断上午/下午(24小时转12小时制) if (hour >= 12) { period = "下午"; if (hour > 12) hour -= 12; // 13点→1点,12点保持12点 } else if (hour == 0) { hour = 12; // 0点→凌晨12点 period = "凌晨"; }
return period + String(hour) + "点整"; } // 【功能3:按键设置提醒】检测按键按下,设置10分钟后提醒 void checkKey() { int keyState = digitalRead(KEY_PIN); unsigned long currentTime = millis();
// 按键防抖:按下且距离上次按键超过500ms if (keyState == LOW && (currentTime - lastKeyTime) > DEBOUNCE_TIME) { lastKeyTime = currentTime; // 更新上次按键时间
// 计算提醒时间:当前时间 + 10分钟(10*60秒) reminderTime = now.unixtime() + 10 * 60; hasReminder = true;
// 存储提醒时间到EEPROM(掉电不丢) EEPROM.put(EEPROM_ADDR, reminderTime); EEPROM.commit(); // 提交写入(ESP32必须加这行)
// 播报提醒设置成功 String remindText = "提醒设置成功,将在10分钟后提醒您喝水"; ttsSpeak(remindText); Serial.print("设置提醒时间(Unix时间):"); Serial.println(reminderTime); } } // 【功能4:提醒检查】对比当前时间和提醒时间,到点播报 void checkReminder() { if (!hasReminder) return; // 没有提醒则跳过
unsigned long currentUnixTime = now.unixtime();
// 当前时间 ≥ 提醒时间(到点或超时) if (currentUnixTime >= reminderTime) { ttsSpeak("提醒您喝水啦!请起身活动一下");
// 清除已完成的提醒(重置变量+EEPROM) reminderTime = 0; hasReminder = false; EEPROM.put(EEPROM_ADDR, reminderTime); EEPROM.commit();
Serial.println("提醒已触发,已清除提醒"); } } |
(四)第四步:测试步骤(分功能验证,确保成功)
1. 第一次使用:设置 RTC 时间
- 首次烧录代码后,打开代码中的rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));这行注释(删除 //);
- 重新烧录代码,此时 RTC 会用电脑的 “编译时间” 设置当前时间;
- 时间设置完成后,再把这行代码注释掉(避免下次烧录覆盖正确时间)。
2. 测试整点报时
- 烧录代码后,用 USB 给 ESP32 供电,打开串口监视器(波特率 115200);
- 串口会显示当前时间,等待到整点(比如 15:00:00),喇叭会播报 “现在是下午 3 点整”;
- 若没播报,检查:① DS3231 接线是否正确(SDA=21,SCL=22);② 串口监视器是否显示正确时间。
3. 测试按键设置提醒
- 按下轻触按键,喇叭会播报 “提醒设置成功,将在 10 分钟后提醒您喝水”;
- 串口监视器会显示 “设置提醒时间(Unix 时间):XXXXXX”;
- 等待 10 分钟,到点后喇叭会播报 “提醒您喝水啦!请起身活动一下”,串口显示 “提醒已触发,已清除提醒”;
- 若没提醒,检查:① 按键接线是否正确(GPIO0→按键→GND);② EEPROM 是否初始化(代码中EEPROM.begin(512)是否有)。
4. 测试便携供电(可选)
- 把锂电池通过充电模块连接到 ESP32 的 VBAT 引脚;
- 拔掉 USB 线,ESP32 会用锂电池供电,报时和提醒功能正常(锂电池满电可续航 2-3 天)。
五、避坑指南:新手必看的 5 个高频问题
1. 坑 1:RTC 读不到时间,串口显示 “初始化失败”
- 原因:① I2C 引脚接错(不是 21/22);② DS3231 模块没插好;③ 模块坏了;
- 解决:① 确认 DS3231 的 SDA 接 GPIO21,SCL 接 GPIO22;② 重新插拔模块;③ 用万用表测模块 VCC 是否有 3.3V 电压。
2. 坑 2:按键按了没反应,不设置提醒
- 原因:① 按键引脚接错(不是 GPIO0);② 没开上拉输入(代码中pinMode(KEY_PIN, INPUT_PULLUP)是否正确);③ 按键坏了;
- 解决:① 检查按键接线;② 串口打印digitalRead(KEY_PIN)的值(按下去应为 0,松开为 1);③ 换一个按键试试。
3. 坑 3:提醒设置后,断电再上电就没了
- 原因:① 没调用EEPROM.commit()(ESP32 必须提交写入,否则不保存);② EEPROM 地址冲突;
- 解决:① 确保代码中EEPROM.put后有EEPROM.commit();② 更换 EEPROM 地址(比如从 0 改成 10)。
4. 坑 4:报时 / 提醒声音小或有杂音
- 原因:① 喇叭功率太小(选 4Ω 1W 以上);② 模块和 ESP32 没共地(GND 没连在一起);③ 杜邦线太长;
- 解决:① 换功率大的喇叭;② 检查所有模块的 GND 是否都接 ESP32 的 GND;③ 用 10cm 以内的短线连接。
5. 坑 5:ESP32 上电后很快死机
- 原因:① 供电不足(USB 线是充电线,不是数据线,电流不够);② 锂电池电压太低(低于 3.3V);
- 解决:① 用支持数据传输的 USB 线(电流≥1A);② 给锂电池充电(用充电模块充到 4.2V)。
六、功能扩展建议(下一篇预告,吸引关注)
搞定基础版后,还能升级这些实用功能(下一篇详细讲,关注不迷路!):
- 自定义提醒间隔:加 3 个按键,分别设置 5 分钟、10 分钟、30 分钟提醒;
- WiFi 同步时间:不用手动设置 RTC,ESP32 连 WiFi 自动同步网络时间;
- 手机 APP 设置提醒:通过蓝牙 / WiFi,在手机上设置提醒内容(比如 “提醒开会”);
- 多语言报时:支持中英文切换(比如 “Now is 3 PM”)。
七、互动环节
- 你成功做出智能音箱了吗?评论区晒出你的成品照片,我会抽 3 个同学送 “智能音箱扩展代码包”(含 WiFi 同步时间、自定义提醒功能);
- 遇到具体问题?比如 “RTC 时间总不准”“提醒不播报”,评论区说清楚你的问题 + 接线 / 代码截图,我会逐条回复;
- 你想优先看哪个扩展功能?WiFi 同步时间扣 “51”,手机 APP 设置提醒扣 “52”,我按投票优先更!
附:紧急排坑工具包
- RTC 测试代码:单独读取 DS3231 时间,排除模块问题(https://blog.csdn.net/weixin_44363607/article/details/121462364);
- EEPROM 测试代码:验证 EEPROM 是否能正常读写(https://randomnerdtutorials.com/esp32-eeprom-arduino/);
- 串口调试技巧:打印now.unixtime()和reminderTime,对比是否正确(判断提醒时间计算是否有误)。
更多推荐


所有评论(0)