#include "SPIFFS.h"
#include <I2S.h>
#include "AudioFileSourceSPIFFS.h"
#include "AudioGeneratorMP3.h"
#include "AudioOutputI2SNoDAC.h"
#include <Adafruit_SSD1306.h>
#include <Arduino.h>
#include "ESP8266Audio.h"
#include <FFat.h>
#include <FS.h>
#include <HTTPClient.h>
#include <WiFi.h>
#include <Wire.h>
#include <arduinoFFT.h>
// 定义屏幕的宽度和高度
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define PIN_SDA 21
#define PIN_SCL 22
#define PIN_BUZZER 23
#define FORMAT_SPIFFS_IF_FAILED true
void connWIFI();
void initFileSysytem();
void initScreen();
void downloadMP3(const char *url, const char *file_path);
void listDir(const char *dirname);
void listDir_screen(const char *dirname);
void playMP3(const char *mp3name);
void MDCallback(void *cbData, const char *type, bool isUnicode, const char *string);
// 创建显示对象
Adafruit_SSD1306 display1(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);
Adafruit_SSD1306 display2(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);
// 管理音乐播放
AudioFileSourceSPIFFS *audio;
AudioGeneratorMP3 *audioGen;
AudioOutputI2SNoDAC *audioOut;
AudioFileSourceID3 *id3;
// FFT 相关的
const uint16_t samples = 512; // 多少个样本记录一次,只能是 2 的 n 次方
const float samplingFrequency = 10000; // Hz
float vReal[samples]; // 实部
float vImag[samples]; // 虚部
float res[samples / 2]; // 存储频率结果的数组
int16_t sampleBuffer[samples];
ArduinoFFT<float> FFT = ArduinoFFT<float>(vReal, vImag, samples, samplingFrequency);
void setup()
{
Serial.begin(115200); // 初始化串口监视器
Wire.begin(PIN_SDA, PIN_SCL); // 初始化I2C
tone(PIN_BUZZER, 200, 1000);
// 初始化文件系统
if (!SPIFFS.begin(true)) {
Serial.println("An Error has occurred while mounting SPIFFS");
return;
}
connWIFI();
initScreen();
downloadMP3("https://pomf2.lain.la/f/e7g3dg9e.mp3", "/jntm.mp3");
listDir("/");
listDir_screen("/");
playMP3("/jntm.mp3");
Serial.println("Setup complete");
}
int count = 1;
void loop()
{
// nothing
}
// Called when a metadata event occurs (i.e. an ID3 tag, an ICY block, etc.
void MDCallback(void *cbData, const char *type, bool isUnicode, const char *string)
{
(void)cbData;
Serial.printf("ID3 callback for: %s = '", type);
if (isUnicode) {
string += 2;
}
while (*string) {
char a = *(string++);
if (isUnicode) {
string++;
}
Serial.printf("%c", a);
}
Serial.printf("'\n");
Serial.flush();
}
void playMP3(const char *mp3name)
{
audio = new AudioFileSourceSPIFFS(mp3name);
id3 = new AudioFileSourceID3(audio);
id3->RegisterMetadataCB(MDCallback, (void *)"ID3TAG");
audioOut = new AudioOutputI2SNoDAC();
audioGen = new AudioGeneratorMP3();
audioGen->begin(id3, audioOut);
if (!audioGen->isRunning()) {
Serial.print("MP3 is not running, somrthing wrong happed");
audioGen->stop();
return;
}
static uint32_t lastms = 0;
static int index = 0;
int16_t sample = 0;
while (audio->loop()) {
FFT.windowing(FFTWindow::Hamming, FFTDirection::Forward);
FFT.compute(FFTDirection::Forward);
FFT.complexToMagnitude();
}
}
// 测试屏幕工作
void test_screen()
{
count += 1;
if (count % 2 == 1) {
display1.clearDisplay();
display1.setTextSize(2);
display1.setTextColor(SSD1306_WHITE);
display1.setCursor(0, 2);
display1.println(count);
display1.display();
delay(1000);
} else {
display2.clearDisplay();
display2.setTextSize(2);
display2.setTextColor(SSD1306_WHITE);
display2.setCursor(0, 2);
display2.println(count);
display2.display();
delay(1000);
}
}
// 下载 MP3 文件
void downloadMP3(const char *url, const char *file_path)
{
HTTPClient http;
http.begin(url); // 指定URL
int httpCode = http.GET(); // 初始化请求
// 检查请求返回的状态码
if (httpCode != HTTP_CODE_OK || httpCode != HTTP_CODE_MOVED_PERMANENTLY) {
Serial.printf("HTTP request failed! Error: %d\n", httpCode);
http.end();
return;
}
int total = http.getSize(); // 获取下载文件的总大小
int len = total;
// 打开文件准备写入
File file = SPIFFS.open(file_path, FILE_WRITE);
if (!file) {
Serial.println("Failed to open file for writing");
return;
}
// 开始下载文件
WiFiClient *stream = http.getStreamPtr();
Serial.println("Downloading file...");
uint8_t buffer[1024] = {0};
int bytesRead;
while (http.connected() && (len > 0 || len == -1)) {
// 获取可用数据大小
size_t size = stream->available();
if (size) {
// 读取数据
int c = stream->readBytes(buffer, sizeof(buffer));
file.write(buffer, c);
if (len > 0) {
len -= c;
}
}
delay(10);
}
file.close();
Serial.println("Download completed!");
http.end(); // 断开连接
}
// 连接到 WIFI
void connWIFI()
{
Serial.begin(115200);
Serial.print("Connecting to WiFi");
WiFi.begin("Wokwi-GUEST", "", 6);
while (WiFi.status() != WL_CONNECTED) {
delay(100);
Serial.print(".");
}
Serial.println(" Connected!");
}
// 初始化屏幕
void initScreen()
{
// 初始化第一个屏幕
if (!display1.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
Serial.println("SSD1306 allocation failed for display1");
return;
} else {
Serial.print("display1 init complete");
display1.display();
}
delay(1000);
// 初始化第二个屏幕
if (!display2.begin(SSD1306_SWITCHCAPVCC, 0x3D)) {
Serial.println("SSD1306 allocation failed for display2");
return;
} else {
Serial.print("display2 init complete");
display2.display();
}
delay(1000);
}
// 初始化文件系统
void initFileSysytem()
{
if (!SPIFFS.begin(true)) {
Serial.println("SPIFFS Mount Failed");
return;
}
delay(3000);
Serial.println("SPIFFS mounted successfully");
}
// 输出目录的下非文件夹的文件
void listDir_screen(const char *dirname)
{
File root = SPIFFS.open(dirname);
if (!root) {
display1.println("Failed to open directory");
return;
}
if (!root.isDirectory()) {
display1.println("Not a directory");
return;
}
File file = root.openNextFile();
while (file) {
if (!file.isDirectory()) {
display1.clearDisplay();
display1.setTextSize(1);
display1.setTextColor(SSD1306_WHITE);
display1.setCursor(0, 2);
display1.print("FILE: ");
display1.print(file.name());
display1.print("\n\n");
display1.print("SIZE: ");
display1.print(file.size() / 1024);
display1.println(" KB");
display1.display();
}
file = root.openNextFile();
}
}
// 输出目录的下非文件夹的文件
void listDir(const char *dirname)
{
Serial.printf("Listing directory: %s\r\n", dirname);
File root = SPIFFS.open(dirname);
if (!root) {
Serial.println("- failed to open directory");
return;
}
if (!root.isDirectory()) {
Serial.println(" - not a directory");
return;
}
File file = root.openNextFile();
while (file) {
if (file.isDirectory()) {
Serial.print("DIR: ");
Serial.println(file.name());
Serial.println("Skip dir");
} else {
Serial.print("FILE: ");
Serial.print(file.name());
Serial.print("\tSIZE: ");
Serial.print(file.size() / 1024);
Serial.print(" KB");
Serial.print("\n");
}
file = root.openNextFile();
}
}