ESP32基于Arduino框架,SD卡+MAX98357模块+MP3播放器

news/2025/2/27 22:28:34/

ESP32基于Arduino框架,SD卡+MAX98357模块+MP3播放器


  • 🎬原创作者的制作教程讲解以及源码:

35 ESP32之简单的完整功能SD卡MP3播放器的制作讲解(ESP32-audioI2S库介绍)- 基于Arduino

链接:https://pan.baidu.com/s/1IxmHo1M8TLo13XMwSbZrfQ?pwd=2hgy
提取码:2hgy
  • 📺OLED播放效果演示:
    在这里插入图片描述

由于还没有购买MAX98357,不知道音质怎么样。

  • 📌MAX98357 I2S 音频放大器模块
    在这里插入图片描述

  • 📌MAX98357 I2S 音频放大器模块 无滤波D类放大器资料:

https://pan.baidu.com/s/1Bn6zpobgbs93CyBALzPxqg
提取码: j9y9

在这里插入图片描述

🌼SD卡接线说明

  • ✨使用VSPI接口接线
// #define SD_CS          5
// #define SPI_MOSI      23
// #define SPI_MISO      19
// #define SPI_SCK       18

🌷I2C OLED屏幕接线

原创代码定义的是4,15需要注意,可以根据自己的需求自行修改

OLED SCL-----> 21
OLED SDA ----> 22

📚所需库

  • 📑编译信息参考:
使用 2.0.0  版本的库 Wire 在文件夹: C:\Users\Administrator\AppData\Local\Arduino15\packages\esp32\hardware\esp32\2.0.5\libraries\Wire 
使用 2.0.0  版本的库 SPI 在文件夹: C:\Users\Administrator\AppData\Local\Arduino15\packages\esp32\hardware\esp32\2.0.5\libraries\SPI 
使用 2.33.15  版本的库 U8g2 在文件夹: C:\Users\Administrator\Documents\Arduino\libraries\U8g2 
使用 2.2.0  版本的库 SdFat 在文件夹: C:\Users\Administrator\Documents\Arduino\libraries\SdFat 
使用 2.2.1  版本的库 RBD_Button 在文件夹: C:\Users\Administrator\Documents\Arduino\libraries\RBD_Button 
使用 1.4.2  版本的库 RBD_Timer 在文件夹: C:\Users\Administrator\Documents\Arduino\libraries\RBD_Timer 
使用 2.0.0  版本的库 ESP32-audioI2S 在文件夹: C:\Users\Administrator\Documents\Arduino\libraries\ESP32-audioI2S 
使用 2.0.0  版本的库 WiFi 在文件夹: C:\Users\Administrator\AppData\Local\Arduino15\packages\esp32\hardware\esp32\2.0.5\libraries\WiFi 
使用 2.0.0  版本的库 WiFiClientSecure 在文件夹: C:\Users\Administrator\AppData\Local\Arduino15\packages\esp32\hardware\esp32\2.0.5\libraries\WiFiClientSecure 
"C:\\Users\\Administrator\\AppData\\Local\\Arduino15\\packages\\esp32\\tools\\xtensa-esp32-elf-gcc\\gcc8_4_0-esp-2021r2-patch3/bin/xtensa-esp32-elf-size" -A "d:\\arduino\\MyHexDir/real_player.ino.elf"
项目使用了 1084461 字节,占用了 (82%) 程序存储空间。最大为 1310720 字节。
全局变量使用了42272字节,(12%)的动态内存,余留285408字节局部变量。最大为327680字节。
  • U8g2
    在这里插入图片描述
  • RBD_Button
    在这里插入图片描述
  • RBD_Timer

在这里插入图片描述

  • SdFat
    在这里插入图片描述
  • esp32-audioi2s需要自己导入到Arduino IDE库当中
  • esp32-audioi2shttps://github.com/schreibfaul1/esp32-audioi2s

⛳注意事项

  • 需要按照原作者那样修改ESP32-audioI2S库代码。
  • Audio.h头文件
    在这里插入图片描述
  • 源文件Audio.cpp
    在这里插入图片描述
  • ⚡程序烧录后,需要初始化SD卡成功后,屏幕才能显,如果没有找到SD或识别到SD卡,则串口会打印error。(具体看代码)
    在这里插入图片描述

📝程序代码

#include <Arduino.h>
#include <Wire.h>
#include <SPI.h>
#include <U8g2lib.h>//点击这里会自动打开管理库页面: http://librarymanager/All#U8g2
#include "SdFat.h"//点击这里会自动打开管理库页面: http://librarymanager/All#SdFat
#include <iostream>
#include <RBD_Button.h>//点击这里会自动打开管理库页面: http://librarymanager/All#RBD_Button
#include "Audio.h"
#include "FileEnumerator.h"using namespace std;//#define SCL 17
//#define SDA 16// #define SD_CS          5
// #define SPI_MOSI      23
// #define SPI_MISO      19
// #define SPI_SCK       18#define V_SUB_PIN 32  // v-
#define V_ADD_PIN 33 // v+
#define STOP_PIN 16 //stop
#define PREV_PIN 17 //prev
#define NEXT_PIN 15 //next#define I2S_DOUT      25
#define I2S_BCLK      26
#define I2S_LRC       27//U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, /* clock=*/ SCL, /* data=*/ SDA, /* reset=*/ U8X8_PIN_NONE); // 没有重置显示的所有板
U8G2_SH1106_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, /* reset=*/ U8X8_PIN_NONE); // 1.3寸,默认引脚SCL:22,SDA:21
//U8G2_SH1106_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, /* reset=*/U8X8_PIN_NONE, /* clock=*/SCL, /* data=*/SDA); // 1.3寸,指定引脚
RBD::Button vsub_button(V_SUB_PIN, INPUT_PULLUP);
RBD::Button vadd_button(V_ADD_PIN, INPUT_PULLUP);
RBD::Button stop_button(STOP_PIN, INPUT_PULLUP);
RBD::Button prev_button(PREV_PIN, INPUT_PULLUP);
RBD::Button next_button(NEXT_PIN, INPUT_PULLUP);int volume = 10;   //音量
Audio audio;
FileEnumerator fe;
char buf[128];int utf8CutLen(const String &src, int utf8Len) {int utf8LenCnt = 0;int srcIdx = 0;int srcLen = src.length();int cutLen = 0;unsigned char tmp;while (utf8LenCnt < utf8Len &&  srcIdx < srcLen) {tmp = (unsigned char)src[srcIdx];if (tmp >= 252)cutLen = 6;else if (tmp >= 248)cutLen = 5;else if (tmp >= 240)cutLen = 4;else if (tmp >= 224)cutLen = 3;else if (tmp >= 192)cutLen = 2;else if (tmp >= 65 && tmp <=90)cutLen = 1;elsecutLen = 1;srcIdx += cutLen;++utf8LenCnt;}return srcIdx;
}void UpdateOLED()
{u8g2.clearBuffer();u8g2.setFont(u8g2_font_wqy14_t_gb2312b);  //换成中文字体string str = "音量: " + std::to_string(volume);if(audio.isRunning())str+="     播放中...";elsestr+="     未播放...";u8g2.drawUTF8(0, 15, str.c_str());String tmp = fe.CurrentFileName();int len = tmp.length();int len1 = utf8CutLen(tmp, 10);if(len>len1){u8g2.drawUTF8(0, 40, tmp.substring(0, len1).c_str());u8g2.drawUTF8(0, 55, tmp.substring(len1).c_str());}elseu8g2.drawUTF8(0, 40, fe.CurrentFileName());u8g2.sendBuffer();
}void PlayPrev()
{auto filename = fe.Prev();if(!filename)return;audio.stopSong();delay(3);audio.connecttoFS(SD, filename);Serial.print("play: ");Serial.println(filename);
}void PlayNext()
{auto filename = fe.Next();if(!filename)return;audio.stopSong();delay(3);audio.connecttoFS(SD, filename);Serial.print("play: ");Serial.println(filename);
}void setup() {Serial.begin(115200);delay(3000);if(!SD.begin(SdSpiConfig(5, DEDICATED_SPI, 18000000)))//if(!SD.begin(5, 18000000)){Serial.println("error");return;}FsFile dir;FsFile file;fe.Init();file.remove("/index.idx");if(!file.open("/index.idx", O_RDWR|O_CREAT)){Serial.println("crate file error");return;}// Open root directoryif (!dir.open("/")){Serial.println("dir.open failed");return;}while (file.openNext(&dir, O_RDONLY)) {memset(buf, 0, 128);file.getName(buf, 128);fe.Add(buf);file.close();}dir.close();fe.Flush();Wire.begin(SDA, SCL);u8g2.setBusClock(800000);  //设置时钟u8g2.begin(); //初始化u8g2.enableUTF8Print();  //允许UTF8vsub_button.setDebounceTimeout(20);vadd_button.setDebounceTimeout(20);stop_button.setDebounceTimeout(20);prev_button.setDebounceTimeout(20);next_button.setDebounceTimeout(20);audio.setPinout(I2S_BCLK, I2S_LRC, I2S_DOUT);audio.setVolume(10); // 0...21PlayNext();UpdateOLED();
}void audio_eof_mp3(const char *info)
{  PlayNext();UpdateOLED();
}void audio_id3data(const char *info){UpdateOLED();
}void loop() {if(vsub_button.onPressed()){// V-volume --;if(volume < 0)volume = 0;audio.setVolume(volume);UpdateOLED();}if(vadd_button.onPressed()){// V+volume ++;if(volume > 21)volume = 21;audio.setVolume(volume);UpdateOLED();}if(stop_button.onPressed()){//暂停,播放audio.pauseResume();UpdateOLED();}if(prev_button.onPressed()){PlayPrev();UpdateOLED();}if(next_button.onPressed()){PlayNext();UpdateOLED();}audio.loop();
}

📚工程代码

链接:https://pan.baidu.com/s/1XX_88x4WfDoW2DHvmN3gYA 
提取码:k72b

http://www.ppmy.cn/news/11322.html

相关文章

字符串函数介绍——C语言

文章目录 一、引言 二、函数的介绍与模拟实现 2、1 求字符串长度strlen&#xff08;&#xff09;函数 2、1、1 strlen&#xff08;&#xff09;函数介绍 2、1、2 strlen&#xff08;&#xff09;函数的模拟实现 2、2 字符串拷贝strcpy&#xff08;&#xff09;函数 2、2、1 s…

三、Gradle使用

文章目录三、Gradle使用1 在 idea 中创建普通 java 工程2 在 idea 中创建 ssm 工程3 项目部署3.1 本地tomcat部署项目3.2 Gretty 部署项目3.3 具体使用4 Gradle 对测试支持4.1 默认测试目录及标准输出4.2 Junit 使用4.3 包含和排除特定测试【尚硅谷】Gradle教程-讲师&#xff1…

data为什么设置成函数

1、组件中的data为什么要定义成一个函数而不是一个对象 但是&#xff0c;当我们需要多次使用此组件时 我们可以发现&#xff0c;此时组件的数据是共享的&#xff0c;一个改变&#xff0c;其他也改变&#xff0c;很显然&#xff0c;我们开发组件就是希望使用时能自己的数据不和其…

使用ResNet18实现CIFAR100数据集的训练

如果对你有用的话&#xff0c;希望能够点赞支持一下&#xff0c;这样我就能有更多的动力更新更多的学习笔记了。&#x1f604;&#x1f604; 使用ResNet进行CIFAR-10数据集进行测试&#xff0c;这里使用的是将CIFAR-10数据集的分辨率扩大到32X32&#xff0c;因为算力相关的…

Lichee_RV学习系列--CoreMark-Pro移植

Lichee_RV学习系列文章目录 Lichee_RV学习系列—认识Lichee Rv Dock、环境搭建和编译第一个程序 Lichee_RV学习系列—移植dhrystone 文章目录Lichee_RV学习系列文章目录一、CoreMark-Pro简介二、获取源码三、编译coremark-pro1、配置coremark-pro2、编译coremark-pro四、开发板…

C生万物 | 反汇编深挖【函数栈帧】的创建和销毁

&#x1f451;作者主页&#xff1a;Fire_Cloud_1 &#x1f3e0;学习社区&#xff1a;烈火神盾 &#x1f517;专栏链接&#xff1a;万物之源——C 一文彻底搞懂函数栈帧创建和销毁的过程一、 什么是函数栈帧二、 理解函数栈帧能解决什么问题呢&#xff1f;三、 函数栈帧的创建和…

详解动态库静态库、动态链接静态链接

目录 静态库&&动态库 举例 注意 动态链接 静态链接 静态库&&动态库 静态库是指编译链接时,把库文件的代码全部加入到可执行文件中,因此生成的文件比较大,但在运行时也就不再需要库文件了。其后缀名一般为“.a” libXXXXXXX.a (windows下是.lib) 动态…

RepVGG:让VGG风格的ConvNets再次伟大

论文地址&#xff1a;https://arxiv.org/abs/2101.03697 我们提出了一种简单但功能强大的卷积神经网络结构&#xff0c;该模型在推理时类似于VGG&#xff0c;只有33的卷积和ReLU堆叠而成&#xff0c;而训练时间模型具有多分支拓扑结构。训练时间和推理时间结构的这种解耦是通过…