DIY Arduino 触屏 MP3 播放器兼闹钟:全流程制作指南
本项目制作一款 Arduino 触屏 MP3 播放器兼闹钟。可观看视频或阅读下方图文教程。 主页: 大号数字时钟、日期、温度,两大图标按钮进入“音乐播放器”和“闹钟设置”。 音乐播放器: 中央大号播放/暂停键,两侧上/下曲。上方进度条显示已播/剩余时间。底部音量条及增减键。右上角小钟表,左上“Menu”键返回主页。 闹钟设置: 两按钮调整时/分,“Set”确认,“Clear”清除。闹钟触发时播放首曲、音量增大,持续响铃直到按“Dismiss”关闭。 核心:Arduino Mega + 3.2" TFT 电阻触摸屏(带 Mega 扩展板)+ BY8001-16P MP3 模块(MicroSD 存储,支持 MP3/WAV,内置 3W 功放直推喇叭)+ DS3231 RTC 实时时钟(提供时钟、日期、温度)。 元件链接: TFT 扩展板占用 Mega 大部分引脚,需自制加长排针插在扩展板与 Mega 之间引出空闲 IO。另需在扩展板 5V 引脚焊排针供外设取电。 建议先阅读 TFT 触摸屏、DS3231 专题教程。MP3 模块简介如下。 支持 MicroSD、MP3/WAV、3W 功放。5 个按键控制引脚或串口控制。串口 RX 需 1k 电阻分压(模块 3.3V 电平)。焊盘 A/B/C 三电阻决定控制模式:MCU 串口控制需去除三电阻。喇叭接引脚 6/7(直驱)或 4/5(外置功放)。 Arduino 端推荐用 引入库: 每秒更新七段数字字体时钟(绿色)、日期/温度(白色大字体)。触摸检测:点击音乐图标(坐标范围)→ 红圈反馈 → 切换 触摸按键处理:播放/暂停切换(调用 绘制界面:绿色七段数字显示设定时/分,H/M 按钮增减(24h/60min 循环),Set 生成 主循环末尾轮询:若 SolidWorks 设计钣金外壳,多功能工具切割铝板,台钳折弯。面板开孔安装喇叭(圆形孔阵),侧板热熔胶固定,喷漆上色。内部热熔胶固定所有模块,后盖螺丝锁紧。 完整源码见文末(含所有绘图辅助函数:界面概览




硬件架构

电路原理图


BY8001-16P MP3 模块简介


BY8001 库(GitHub)。初始化后调用库函数控制播放、暂停、上下曲、音量等。固件代码详解
UTFT、URTouch(屏幕/触摸)、SoftwareSerial、BY8001、DS3231。定义字体、图标位图(用 ImageConverter565 转换的 .c 文件)、大量状态变量。#include <UTFT.h> #include <URTouch.h> #include <SoftwareSerial.h> #include <BY8001.h> #include <DS3231.h>
UTFT myGLCD(SSD1289, 38, 39, 40, 41); // 参数按屏幕/扩展板型号调整
URTouch myTouch(6, 5, 4, 3, 2);
SoftwareSerial mp3Serial(11, 10); // RX, TX
BY8001 mp3; DS3231 rtc(SDA, SCL);
// 字体与图标声明
extern uint8_t SmallFont[]; extern uint8_t BigFont[]; extern uint8_t SevenSegNumFont[];
extern unsigned int MusicPlayerButton[0x1040]; extern unsigned int AlarmButton[0x1040];
extern unsigned int ButtonPlay[0x1AE9]; extern unsigned int ButtonPause[0x1AE9];
extern unsigned int PreviousButton[0x9C4]; extern unsigned int NextButton[0x9C4];
extern unsigned int VolumeDown[0x170]; extern unsigned int VolumeUp[0x3B8];
// 状态变量
int x, y; char currentPage, playStatus; int iV = 15; int trackNum = 1; int b = 16;
int aHours = 0, aMinutes = 0; boolean alarmNotSet = true; String alarmString = "";
float currentTemperature, temperature;
static word totalTime, elapsedTime, playback, minutes, seconds, lastSeconds, minutesR, secondsR;
String currentClock, currentHours, currentMinutes, currentSeconds, currentDate;
String timeString, hoursString, minutesString, secondsString, hoursS, minutesS, secondsS, dateS;setup() 初始化屏幕、触摸、RTC、MP3 串口(9600)、冷启动延时 800ms、绘制主页、设初始音量 15、读取初始温度/日期/时间。void setup() {
myGLCD.InitLCD(); myGLCD.clrScr(); myTouch.InitTouch(); myTouch.setPrecision(PREC_MEDIUM);
rtc.begin(); Serial.begin(9600); mp3Serial.begin(9600); mp3.setup(mp3Serial); delay(800);
drawHomeScreen(); currentPage = '0'; playStatus = '0'; mp3.setVolume(15); delay(100);
currentTemperature = rtc.getTemp(); currentDate = rtc.getDateStr(); currentClock = rtc.getTimeStr();
timeString = rtc.getTimeStr(); currentHours = timeString.substring(0,2); currentMinutes = timeString.substring(3,5); currentSeconds = timeString.substring(6,8);
}loop() 三大页面状态机:主页(
currentPage=='0')currentPage='1' 清屏绘制播放器界面;点击闹钟图标 → 切换 currentPage='2'。if (currentPage == '0') {
if (currentClock != rtc.getTimeStr()) { // 时钟更新
timeString = rtc.getTimeStr();
hoursS = timeString.substring(0,2); minutesS = timeString.substring(3,5); secondsS = timeString.substring(6,8);
myGLCD.setFont(SevenSegNumFont); myGLCD.setColor(0,255,0);
myGLCD.print(secondsS, 224, 50);
if (currentMinutes != minutesS) { myGLCD.print(minutesS, 128, 50); currentMinutes = minutesS; }
if (currentHours != hoursS) { myGLCD.print(hoursS, 32, 50); currentHours = hoursS; }
// 日期/温度更新同理...
}
if (myTouch.dataAvailable()) { // 触摸处理
myTouch.read(); x = myTouch.getX(); y = myTouch.getY();
if (x>=55 && x<=120 && y>=125 && y<=190) { drawFrame(87,157,33); currentPage='1'; myGLCD.clrScr(); delay(100); drawMusicPlayerScreen(); delay(100); }
if (x>=195 && x<=260 && y>=125 && y<=190) { drawFrame(227,160,29); currentPage='2'; myGLCD.clrScr(); }
}
}音乐播放器(
currentPage=='1')mp3.playTrackFromFolder()/pause()/play()、切换图标)、上/下曲(previousTrack()/nextTrack())、音量增减(increaseVolume()/decreaseVolume()、绘制音量条)、Menu 返回主页。播放时循环调用 trackPlayTime() 更新进度条、已播/剩余时间,右上角打印时钟。void trackPlayTime() {
totalTime = mp3.getTotalTrackPlaybackTime(); delay(10);
elapsedTime = mp3.getElapsedTrackPlaybackTime(); delay(10);
minutes = (int)elapsedTime / 60; seconds = (((float)elapsedTime/60)-minutes)*60;
playback = totalTime - elapsedTime; minutesR = (int)playback/60; secondsR = (((float)playback/60)-minutesR)*60;
myGLCD.setFont(SmallFont); myGLCD.setColor(255,255,255);
myGLCD.printNumI(minutes, 8, 48); myGLCD.print(":", 16, 48); myGLCD.printNumI((int)seconds, 24, 48, 2, '0');
myGLCD.print("-", 276, 48); myGLCD.printNumI(minutesR, 284, 48); myGLCD.print(":", 292, 48); myGLCD.printNumI((int)secondsR, 300, 48, 2, '0');
int trackBarX = map(elapsedTime, 0, totalTime, 0, 224);
myGLCD.setColor(255,0,0); myGLCD.fillRect(48, 50, 48+trackBarX, 58);
if (totalTime == elapsedTime) { mp3.nextTrack(); delay(30); myGLCD.setColor(255,255,255); myGLCD.fillRect(48,50,272,58); }
}闹钟设置(
currentPage=='2')alarmString(格式 "HH:MM:00" 与 RTC getTimeStr() 对齐),Clear 清空。Menu 退回主页。if (currentPage == '2') {
// 绘制界面...
alarmNotSet = true;
while (alarmNotSet) {
if (myTouch.dataAvailable()) {
myTouch.read(); x = myTouch.getX(); y = myTouch.getY();
// H按钮
if (x>=42 && x<=82 && y>=115 && y<=145) { drawRectFrame(42,115,82,145); aHours++; if(aHours>=24) aHours=0; myGLCD.setFont(SevenSegNumFont); myGLCD.setColor(0,255,0); myGLCD.printNumI(aHours, 32, 50, 2, '0'); }
// M按钮
if (x>=138 && x<=178 && y>=115 && y<=145) { drawRectFrame(138,115,178,145); aMinutes++; if(aMinutes>=60) aMinutes=0; myGLCD.setFont(SevenSegNumFont); myGLCD.setColor(0,255,0); myGLCD.printNumI(aMinutes, 128, 50, 2, '0'); }
// Set按钮
if (x>=215 && x<=303 && y>=60 && y<=80) { drawRectFrame(215,60,303,90); // 格式化 alarmString ... myGLCD.setFont(BigFont); myGLCD.print("Alarm set for:", CENTER, 165); myGLCD.print(alarmString, CENTER, 191); }
// Clear按钮
if (x>=215 && x<=303 && y>=115 && y<=145) { drawRectFrame(215,115,303,145); alarmString=""; myGLCD.setColor(0,0,0); myGLCD.fillRect(45,165,275,210); }
// Menu返回
if (x>=0 && x<=75 && y>=0 && y<=30) { alarmNotSet=false; currentPage='0'; myGLCD.clrScr(); drawHomeScreen(); }
}
}
}闹钟触发
alarmString == rtc.getTimeStr(),清屏、音量设 25、播放首曲、绘制“ALARM”界面及“DISMISS”按钮,进入阻塞循环等待触摸关闭,恢复音量 15、回主页。if (alarmNotSet == false) {
if (alarmString == rtc.getTimeStr()) {
myGLCD.clrScr(); mp3.setVolume(25); mp3.playTrackByIndexNumber(1); delay(100);
myGLCD.setFont(BigFont); myGLCD.setColor(255,255,255);
myGLCD.print("ALARM", CENTER, 90); myGLCD.drawBitmap(127,10,65,64,AlarmButton);
myGLCD.print(alarmString, CENTER, 114); myGLCD.drawRoundRect(94,146,226,170); myGLCD.print("DISMISS", CENTER, 150);
boolean alarmOn = true;
while (alarmOn) {
if (myTouch.dataAvailable()) { myTouch.read(); x=myTouch.getX(); y=myTouch.getY();
if (x>=94 && x<=226 && y>=146 && y<=170) { drawRectFrame(94,146,226,170); alarmOn=false; alarmString=""; myGLCD.clrScr(); mp3.stopPlayback(); delay(100); currentPage='0'; playStatus='0'; mp3.setVolume(15); drawHomeScreen(); }
}
}
}
}外壳制作




drawHomeScreen、drawMusicPlayerScreen、drawFrame、drawRectFrame、drawVolume 等)。欢迎提问交流,关注项目合集!
制造工艺
- Arduino POV 时钟:自制视觉暂留 LED 显示器
- 自制大尺寸LED主时钟带闹钟功能 — 12米外清晰可见
- Arduino Due 驱动 3.2″ TFT 实现温度监测与实时时钟
- Arduino UNO 与 DFPlayer Mini 播放音频完整指南
- TM1637 数码管时钟:Arduino Nano 实现时间设定与闹钟
- Arduino 网页控制 MP3 播放器:浏览器实现无线点歌
- Arduino 井字棋游戏:MAX7219 点阵屏 + 纸板外壳 DIY
- Arduino DIY 电压表:Nokia 5110 屏幕显示,手把手教学
- 使用 SparkFun 7 段数码管构建可靠的 Arduino 倒计时器
- eDOT:基于 Arduino 的精密时钟与气象站,内置红外遥控
- 自制 Arduino 遥控保时捷赛车:从改装底盘到手机 App 控制全攻略
- 用 DS1302 RTC 和 Arduino UNO 制作可靠闹钟