mirror of
https://github.com/Crocmagnon/buzzer.git
synced 2024-11-24 00:48:03 +01:00
Refactor & split into multiple files
This commit is contained in:
parent
30e46ebfab
commit
a9ca2150d4
10 changed files with 536 additions and 339 deletions
11
src/config.cpp
Normal file
11
src/config.cpp
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
#include <Adafruit_SSD1306.h>
|
||||||
|
#include <ESPAsyncWebServer.h>
|
||||||
|
#include <Audio.h>
|
||||||
|
#include <Preferences.h>
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
|
||||||
|
AsyncWebServer server(80);
|
||||||
|
Audio audio;
|
||||||
|
Preferences preferences;
|
56
src/config.h
Normal file
56
src/config.h
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
#ifndef __CONFIG_H__
|
||||||
|
#define __CONFIG_H__
|
||||||
|
|
||||||
|
// Uncomment to switch to AP mode.
|
||||||
|
// Leave commented for wifi station mode.
|
||||||
|
// #define B_WIFI_AP
|
||||||
|
|
||||||
|
// DAC
|
||||||
|
#define I2S_DOUT 32
|
||||||
|
#define I2S_BCLK 25
|
||||||
|
#define I2S_LRC 27
|
||||||
|
|
||||||
|
// SD CARD
|
||||||
|
#define SPI_MISO 18
|
||||||
|
#define SPI_MOSI 19
|
||||||
|
#define SPI_SCK 23
|
||||||
|
#define SD_CS 5
|
||||||
|
|
||||||
|
// GPIO
|
||||||
|
#define LED 2
|
||||||
|
#define BUTTON 33
|
||||||
|
|
||||||
|
// Screen
|
||||||
|
#define SSD1306_NO_SPLASH
|
||||||
|
#define SCREEN_WIDTH 128
|
||||||
|
#define SCREEN_HEIGHT 64
|
||||||
|
#define OLED_RESET -1
|
||||||
|
#define SCREEN_ADDRESS 0x3C
|
||||||
|
|
||||||
|
#define SCREEN_MSG_X 0
|
||||||
|
#define SCREEN_MSG_Y 24
|
||||||
|
|
||||||
|
#define LINE_LENGTH 21
|
||||||
|
|
||||||
|
// Volume
|
||||||
|
#define VOLUME_MIN 0
|
||||||
|
#define VOLUME_MAX 21
|
||||||
|
|
||||||
|
// Preference keys
|
||||||
|
#define SELECTED_FILE "selectedFile"
|
||||||
|
#define CURRENT_VOLUME "currentVolume"
|
||||||
|
#define WIFI_IP "wifiIP"
|
||||||
|
#define WIFI_SSID "wifiSSID"
|
||||||
|
#define WIFI_PASSWORD "wifiPassword"
|
||||||
|
|
||||||
|
#include <Adafruit_SSD1306.h>
|
||||||
|
#include <ESPAsyncWebServer.h>
|
||||||
|
#include <Audio.h>
|
||||||
|
#include <Preferences.h>
|
||||||
|
|
||||||
|
extern Adafruit_SSD1306 display;
|
||||||
|
extern AsyncWebServer server;
|
||||||
|
extern Audio audio;
|
||||||
|
extern Preferences preferences;
|
||||||
|
|
||||||
|
#endif
|
|
@ -1,3 +1,6 @@
|
||||||
// Copy to creds.h or creds_ap.h and edit
|
// Copy to creds.h or creds_ap.h and edit
|
||||||
|
#ifndef __CREDS_H__
|
||||||
|
#define __CREDS_H__
|
||||||
const char *ssid = "buzzer";
|
const char *ssid = "buzzer";
|
||||||
const char *password = "123456789";
|
const char *password = "123456789";
|
||||||
|
#endif
|
||||||
|
|
360
src/main.cpp
360
src/main.cpp
|
@ -1,361 +1,43 @@
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
#include <ESPAsyncWebServer.h>
|
|
||||||
#include <SPIFFS.h>
|
|
||||||
#include <AsyncJson.h>
|
|
||||||
#include <Audio.h>
|
|
||||||
#include <SPI.h>
|
|
||||||
#include <Wire.h>
|
|
||||||
#include <Adafruit_GFX.h>
|
|
||||||
#include <Adafruit_SSD1306.h>
|
#include <Adafruit_SSD1306.h>
|
||||||
|
#include <ESPAsyncWebServer.h>
|
||||||
|
#include <Audio.h>
|
||||||
#include <Preferences.h>
|
#include <Preferences.h>
|
||||||
#include <AsyncElegantOTA.h>
|
|
||||||
|
|
||||||
// Toggle on to switch to AP mode.
|
#include "setup.h"
|
||||||
// Leave commented for wifi station mode.
|
#include "utils.h"
|
||||||
// #define B_WIFI_AP
|
#include "config.h"
|
||||||
|
|
||||||
#ifdef B_WIFI_AP
|
|
||||||
#include "creds_ap.h"
|
|
||||||
#else
|
|
||||||
#include "creds.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// DAC
|
|
||||||
#define I2S_DOUT 32
|
|
||||||
#define I2S_BCLK 25
|
|
||||||
#define I2S_LRC 27
|
|
||||||
|
|
||||||
// SD CARD
|
|
||||||
#define SPI_MISO 18
|
|
||||||
#define SPI_MOSI 19
|
|
||||||
#define SPI_SCK 23
|
|
||||||
#define SD_CS 5
|
|
||||||
|
|
||||||
#define LED 2
|
|
||||||
#define BUTTON 33
|
|
||||||
|
|
||||||
#define SSD1306_NO_SPLASH
|
|
||||||
#define SCREEN_WIDTH 128
|
|
||||||
#define SCREEN_HEIGHT 64
|
|
||||||
#define OLED_RESET -1
|
|
||||||
#define SCREEN_ADDRESS 0x3C
|
|
||||||
|
|
||||||
#define SCREEN_MSG_X 0
|
|
||||||
#define SCREEN_MSG_Y 24
|
|
||||||
|
|
||||||
#define VOLUME_MIN 0
|
|
||||||
#define VOLUME_MAX 21
|
|
||||||
|
|
||||||
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
|
|
||||||
|
|
||||||
String selectedFile = "";
|
|
||||||
|
|
||||||
AsyncWebServer server(80);
|
|
||||||
Audio audio;
|
|
||||||
Preferences preferences;
|
|
||||||
|
|
||||||
byte buttonLastState = HIGH;
|
byte buttonLastState = HIGH;
|
||||||
byte currentVolume = 12;
|
|
||||||
|
|
||||||
bool fileIsValid(String fileName)
|
|
||||||
{
|
|
||||||
return !fileName.startsWith(".") && (fileName.endsWith(".mp3") || fileName.endsWith(".wav"));
|
|
||||||
}
|
|
||||||
|
|
||||||
void clearMessageArea()
|
|
||||||
{
|
|
||||||
display.fillRect(SCREEN_MSG_X, SCREEN_MSG_Y, SCREEN_WIDTH - SCREEN_MSG_X, SCREEN_HEIGHT - SCREEN_MSG_Y, BLACK);
|
|
||||||
display.setCursor(SCREEN_MSG_X, SCREEN_MSG_Y);
|
|
||||||
}
|
|
||||||
|
|
||||||
void displayText(String text)
|
|
||||||
{
|
|
||||||
clearMessageArea();
|
|
||||||
display.println(text);
|
|
||||||
display.display();
|
|
||||||
}
|
|
||||||
|
|
||||||
void play()
|
|
||||||
{
|
|
||||||
String path = "/" + selectedFile;
|
|
||||||
Serial.println("Playing file: " + path);
|
|
||||||
audio.stopSong();
|
|
||||||
audio.connecttoFS(SD, path.c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
void onStop(AsyncWebServerRequest *request)
|
|
||||||
{
|
|
||||||
Serial.println("Stop playing");
|
|
||||||
audio.stopSong();
|
|
||||||
request->send(200);
|
|
||||||
}
|
|
||||||
|
|
||||||
void onPlay(AsyncWebServerRequest *request)
|
|
||||||
{
|
|
||||||
play();
|
|
||||||
request->send(200);
|
|
||||||
}
|
|
||||||
|
|
||||||
void onStatus(AsyncWebServerRequest *request)
|
|
||||||
{
|
|
||||||
Serial.println("Status");
|
|
||||||
AsyncResponseStream *response = request->beginResponseStream("application/json");
|
|
||||||
|
|
||||||
StaticJsonDocument<96> root;
|
|
||||||
root["files"]["selected"] = selectedFile.c_str();
|
|
||||||
|
|
||||||
JsonObject volume = root.createNestedObject("volume");
|
|
||||||
volume["current"] = currentVolume;
|
|
||||||
volume["canDecrease"] = currentVolume > 0;
|
|
||||||
volume["canIncrease"] = currentVolume < 21;
|
|
||||||
|
|
||||||
serializeJson(root, *response);
|
|
||||||
request->send(response);
|
|
||||||
}
|
|
||||||
|
|
||||||
void onListFiles(AsyncWebServerRequest *request)
|
|
||||||
{
|
|
||||||
Serial.print("List files cursor=");
|
|
||||||
int cursor = 0;
|
|
||||||
if (request->hasParam("cursor")) {
|
|
||||||
String s_cursor = request->getParam("cursor")->value();
|
|
||||||
cursor = s_cursor.toInt();
|
|
||||||
}
|
|
||||||
Serial.println(cursor);
|
|
||||||
|
|
||||||
AsyncResponseStream *response = request->beginResponseStream("application/json");
|
|
||||||
|
|
||||||
StaticJsonDocument<512> root;
|
|
||||||
root["next"] = -1;
|
|
||||||
JsonArray files = root.createNestedArray("files");
|
|
||||||
File music = SD.open("/");
|
|
||||||
File file = music.openNextFile();
|
|
||||||
int index = 0;
|
|
||||||
while (file)
|
|
||||||
{
|
|
||||||
String fileName = file.name();
|
|
||||||
if (fileIsValid(fileName))
|
|
||||||
{
|
|
||||||
index++;
|
|
||||||
if (index >= cursor)
|
|
||||||
files.add(fileName);
|
|
||||||
}
|
|
||||||
file.close();
|
|
||||||
|
|
||||||
if (root.overflowed())
|
|
||||||
{
|
|
||||||
root["next"] = index;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
file = music.openNextFile();
|
|
||||||
}
|
|
||||||
if (root["next"] == -1) {
|
|
||||||
root.remove("next");
|
|
||||||
}
|
|
||||||
|
|
||||||
serializeJson(root, *response);
|
|
||||||
request->send(response);
|
|
||||||
}
|
|
||||||
|
|
||||||
void onSelectFile(AsyncWebServerRequest *request)
|
|
||||||
{
|
|
||||||
Serial.print("Select file: ");
|
|
||||||
if (request->hasParam("fileName", true))
|
|
||||||
{
|
|
||||||
selectedFile = request->getParam("fileName", true)->value();
|
|
||||||
preferences.putString("selectedFile", selectedFile);
|
|
||||||
Serial.print(selectedFile);
|
|
||||||
displayText("Selectionne : " + selectedFile);
|
|
||||||
}
|
|
||||||
Serial.println();
|
|
||||||
onStatus(request);
|
|
||||||
}
|
|
||||||
|
|
||||||
void onChangeVolume(AsyncWebServerRequest *request)
|
|
||||||
{
|
|
||||||
Serial.print("Volume: ");
|
|
||||||
if (request->hasParam("modifier", true))
|
|
||||||
{
|
|
||||||
String s_modifier = request->getParam("modifier", true)->value();
|
|
||||||
int modifier = s_modifier.toInt();
|
|
||||||
currentVolume += modifier;
|
|
||||||
if (currentVolume > VOLUME_MAX)
|
|
||||||
currentVolume = VOLUME_MAX;
|
|
||||||
else if (currentVolume < VOLUME_MIN)
|
|
||||||
currentVolume = VOLUME_MIN;
|
|
||||||
preferences.putUChar("currentVolume", currentVolume);
|
|
||||||
audio.setVolume(currentVolume);
|
|
||||||
Serial.print(currentVolume);
|
|
||||||
clearMessageArea();
|
|
||||||
display.print("Volume : ");
|
|
||||||
display.println(currentVolume);
|
|
||||||
display.display();
|
|
||||||
}
|
|
||||||
Serial.println();
|
|
||||||
onStatus(request);
|
|
||||||
}
|
|
||||||
|
|
||||||
void onUpload(AsyncWebServerRequest *request)
|
|
||||||
{
|
|
||||||
Serial.println("onUpload");
|
|
||||||
request->send(200);
|
|
||||||
}
|
|
||||||
|
|
||||||
void onUploadFile(AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final)
|
|
||||||
{
|
|
||||||
if (!index)
|
|
||||||
{
|
|
||||||
Serial.printf("UploadStart: %s\n", filename.c_str());
|
|
||||||
String filePath = "/" + filename;
|
|
||||||
request->_tempFile = SD.open(filePath, FILE_WRITE);
|
|
||||||
}
|
|
||||||
if (!request->_tempFile)
|
|
||||||
{
|
|
||||||
Serial.println("Couldn't open file.");
|
|
||||||
request->redirect("/");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (len)
|
|
||||||
{
|
|
||||||
request->_tempFile.write(data, len);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (final)
|
|
||||||
{
|
|
||||||
Serial.printf("UploadEnd: %s, %u B\n", filename.c_str(), index + len);
|
|
||||||
request->_tempFile.close();
|
|
||||||
request->redirect("/");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void onNotFound(AsyncWebServerRequest *request)
|
|
||||||
{
|
|
||||||
Serial.println("not found");
|
|
||||||
request->send(400);
|
|
||||||
}
|
|
||||||
|
|
||||||
void setup()
|
void setup()
|
||||||
{
|
{
|
||||||
// Setup serial
|
// Setup GPIO
|
||||||
Serial.begin(115200);
|
|
||||||
pinMode(LED, OUTPUT);
|
pinMode(LED, OUTPUT);
|
||||||
digitalWrite(LED, LOW);
|
digitalWrite(LED, LOW);
|
||||||
|
|
||||||
preferences.begin("buzzer", false);
|
|
||||||
|
|
||||||
// Screen
|
|
||||||
if (!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS))
|
|
||||||
{
|
|
||||||
Serial.println("Display init failed");
|
|
||||||
while (true);
|
|
||||||
}
|
|
||||||
display.clearDisplay();
|
|
||||||
display.setTextSize(1);
|
|
||||||
display.setTextColor(WHITE, BLACK);
|
|
||||||
display.setCursor(0, 0);
|
|
||||||
display.println("Chargement...");
|
|
||||||
display.display();
|
|
||||||
|
|
||||||
pinMode(BUTTON, INPUT_PULLUP);
|
pinMode(BUTTON, INPUT_PULLUP);
|
||||||
buttonLastState = digitalRead(BUTTON);
|
buttonLastState = digitalRead(BUTTON);
|
||||||
|
|
||||||
// Setup SPIFFS
|
Serial.begin(115200);
|
||||||
if (!SPIFFS.begin())
|
Serial.println("Serial... OK");
|
||||||
{
|
|
||||||
Serial.println("SPIFFS error. Exiting.");
|
|
||||||
display.println("Impossible d'acceder aux fichiers...");
|
|
||||||
display.display();
|
|
||||||
while (true);
|
|
||||||
}
|
|
||||||
|
|
||||||
pinMode(SD_CS, OUTPUT);
|
preferences.begin("buzzer", false);
|
||||||
digitalWrite(SD_CS, HIGH);
|
Serial.println("Preferences... OK");
|
||||||
SPI.begin(SPI_SCK, SPI_MISO, SPI_MOSI);
|
|
||||||
|
|
||||||
if (!SD.begin(SD_CS))
|
setupScreen();
|
||||||
{
|
setupSPIFFS();
|
||||||
Serial.println("Error talking to SD card!");
|
setupSDCard();
|
||||||
display.println("Impossible d'acceder a la carte SD...");
|
setupAudio();
|
||||||
display.display();
|
selectDefaultFile();
|
||||||
while (true);
|
setupWifi();
|
||||||
}
|
setupWebServer();
|
||||||
|
diagnosticPrintln("Configuration OK!");
|
||||||
|
|
||||||
selectedFile = preferences.getString("selectedFile", "");
|
displayWifiCreds();
|
||||||
if (selectedFile == "" || !fileIsValid(selectedFile))
|
displaySelectedFile();
|
||||||
{
|
|
||||||
File root = SD.open("/");
|
|
||||||
File file = root.openNextFile();
|
|
||||||
while (file)
|
|
||||||
{
|
|
||||||
String fileName = file.name();
|
|
||||||
if (fileIsValid(fileName))
|
|
||||||
{
|
|
||||||
selectedFile = fileName;
|
|
||||||
Serial.println("Selected " + fileName);
|
|
||||||
display.println("Selectionne : " + fileName);
|
|
||||||
display.display();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
file.close();
|
|
||||||
file = root.openNextFile();
|
|
||||||
}
|
|
||||||
root.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
display.clearDisplay();
|
|
||||||
display.setCursor(0, 0);
|
|
||||||
// Wifi
|
|
||||||
#ifdef B_WIFI_AP
|
|
||||||
Serial.println("Setting up AP...");
|
|
||||||
WiFi.softAP(ssid, password);
|
|
||||||
String wifiIP = WiFi.softAPIP().toString();
|
|
||||||
String wifiMode = "AP";
|
|
||||||
display.print("Wifi: ");
|
|
||||||
display.println(ssid);
|
|
||||||
display.print("Pass: ");
|
|
||||||
display.println(password);
|
|
||||||
#else
|
|
||||||
Serial.print("Connecting to wifi...");
|
|
||||||
WiFi.begin(ssid, password);
|
|
||||||
while (WiFi.status() != WL_CONNECTED)
|
|
||||||
{
|
|
||||||
Serial.print(".");
|
|
||||||
delay(500);
|
|
||||||
}
|
|
||||||
Serial.println();
|
|
||||||
String wifiIP = WiFi.localIP().toString();
|
|
||||||
String wifiMode = "client";
|
|
||||||
#endif
|
|
||||||
String wifiMessage = wifiMode + " IP: " + wifiIP;
|
|
||||||
Serial.println(wifiMessage);
|
|
||||||
display.print("IP: ");
|
|
||||||
display.println(wifiIP);
|
|
||||||
display.display();
|
|
||||||
|
|
||||||
// Server
|
|
||||||
server.on("/play", HTTP_GET, onPlay);
|
|
||||||
server.on("/stop", HTTP_GET, onStop);
|
|
||||||
server.on("/status", HTTP_GET, onStatus);
|
|
||||||
server.on("/list-files", HTTP_GET, onListFiles);
|
|
||||||
server.on("/select-file", HTTP_POST, onSelectFile);
|
|
||||||
server.on("/change-volume", HTTP_POST, onChangeVolume);
|
|
||||||
server.on("/upload", HTTP_POST, onUpload, onUploadFile);
|
|
||||||
server.onNotFound(onNotFound);
|
|
||||||
server.serveStatic("/", SPIFFS, "/www/").setDefaultFile("index.html");
|
|
||||||
AsyncElegantOTA.begin(&server);
|
|
||||||
server.begin();
|
|
||||||
|
|
||||||
Serial.println("Server ready!");
|
|
||||||
clearMessageArea();
|
|
||||||
display.println("Pret !");
|
|
||||||
display.display();
|
|
||||||
|
|
||||||
// Audio
|
|
||||||
audio.setPinout(I2S_BCLK, I2S_LRC, I2S_DOUT);
|
|
||||||
currentVolume = preferences.getUChar("currentVolume", 12);
|
|
||||||
audio.setVolume(currentVolume);
|
|
||||||
|
|
||||||
// Setup is done, light up the LED
|
// Setup is done, light up the LED
|
||||||
|
Serial.println("All setup & ready to go!");
|
||||||
digitalWrite(LED, HIGH);
|
digitalWrite(LED, HIGH);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
140
src/setup.cpp
Normal file
140
src/setup.cpp
Normal file
|
@ -0,0 +1,140 @@
|
||||||
|
#include <SPIFFS.h>
|
||||||
|
#include <Wire.h> // Required by display.begin
|
||||||
|
#include <AsyncElegantOTA.h>
|
||||||
|
#include <Audio.h>
|
||||||
|
|
||||||
|
#ifdef B_WIFI_AP
|
||||||
|
#include "creds_ap.h"
|
||||||
|
#else
|
||||||
|
#include "creds.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
#include "utils.h"
|
||||||
|
#include "webHandlers.h"
|
||||||
|
|
||||||
|
void setupScreen()
|
||||||
|
{
|
||||||
|
if (!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS))
|
||||||
|
{
|
||||||
|
Serial.println("Display init failed");
|
||||||
|
while (true);
|
||||||
|
}
|
||||||
|
display.clearDisplay();
|
||||||
|
display.setTextSize(1);
|
||||||
|
display.setTextColor(WHITE, BLACK);
|
||||||
|
display.setCursor(0, 0);
|
||||||
|
display.println("Ecran... OK");
|
||||||
|
display.display();
|
||||||
|
Serial.println("Ecran... OK");
|
||||||
|
}
|
||||||
|
|
||||||
|
void setupSPIFFS()
|
||||||
|
{
|
||||||
|
diagnosticPrint("Fichiers... ");
|
||||||
|
if (!SPIFFS.begin())
|
||||||
|
{
|
||||||
|
Serial.println("SPIFFS error. Exiting.");
|
||||||
|
display.println("KO");
|
||||||
|
display.display();
|
||||||
|
while (true);
|
||||||
|
}
|
||||||
|
diagnosticPrintln("OK");
|
||||||
|
}
|
||||||
|
|
||||||
|
void setupSDCard()
|
||||||
|
{
|
||||||
|
diagnosticPrint("Carte SD... ");
|
||||||
|
pinMode(SD_CS, OUTPUT);
|
||||||
|
digitalWrite(SD_CS, HIGH);
|
||||||
|
SPI.begin(SPI_SCK, SPI_MISO, SPI_MOSI);
|
||||||
|
|
||||||
|
if (!SD.begin(SD_CS))
|
||||||
|
{
|
||||||
|
Serial.println("Error talking to SD card!");
|
||||||
|
display.println("KO");
|
||||||
|
display.display();
|
||||||
|
while (true);
|
||||||
|
}
|
||||||
|
diagnosticPrintln("OK");
|
||||||
|
}
|
||||||
|
|
||||||
|
void setupAudio()
|
||||||
|
{
|
||||||
|
diagnosticPrint("Audio... ");
|
||||||
|
audio.setPinout(I2S_BCLK, I2S_LRC, I2S_DOUT);
|
||||||
|
byte volume = preferences.getUChar(CURRENT_VOLUME, 12);
|
||||||
|
audio.setVolume(volume);
|
||||||
|
diagnosticPrintln("OK");
|
||||||
|
}
|
||||||
|
|
||||||
|
void selectDefaultFile()
|
||||||
|
{
|
||||||
|
String linePrefix = "Son: ";
|
||||||
|
diagnosticPrint(linePrefix);
|
||||||
|
String selectedFile = preferences.getString(SELECTED_FILE, "");
|
||||||
|
if (selectedFile == "" || !fileIsValid(selectedFile) || !fileExists(selectedFile))
|
||||||
|
{
|
||||||
|
File root = SD.open("/");
|
||||||
|
File file = root.openNextFile();
|
||||||
|
while (file)
|
||||||
|
{
|
||||||
|
String fileName = file.name();
|
||||||
|
if (fileIsValid(fileName))
|
||||||
|
{
|
||||||
|
Serial.println("Selected " + fileName);
|
||||||
|
selectedFile = fileName;
|
||||||
|
preferences.putString(SELECTED_FILE, fileName);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
file.close();
|
||||||
|
file = root.openNextFile();
|
||||||
|
}
|
||||||
|
root.close();
|
||||||
|
}
|
||||||
|
diagnosticPrintln(selectedFile.substring(0, LINE_LENGTH - linePrefix.length()));
|
||||||
|
}
|
||||||
|
|
||||||
|
void setupWifi()
|
||||||
|
{
|
||||||
|
diagnosticPrint("Wifi...");
|
||||||
|
preferences.putString(WIFI_SSID, ssid);
|
||||||
|
preferences.putString(WIFI_PASSWORD, password);
|
||||||
|
#ifdef B_WIFI_AP
|
||||||
|
WiFi.softAP(ssid, password);
|
||||||
|
String wifiIP = WiFi.softAPIP().toString();
|
||||||
|
#else
|
||||||
|
WiFi.begin(ssid, password);
|
||||||
|
while (WiFi.status() != WL_CONNECTED)
|
||||||
|
{
|
||||||
|
Serial.print(".");
|
||||||
|
delay(500);
|
||||||
|
}
|
||||||
|
String wifiIP = WiFi.localIP().toString();
|
||||||
|
#endif
|
||||||
|
diagnosticPrintln(" OK");
|
||||||
|
Serial.print("IP: ");
|
||||||
|
Serial.println(wifiIP);
|
||||||
|
preferences.putString(WIFI_IP, wifiIP);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setupWebServer()
|
||||||
|
{
|
||||||
|
Serial.print("Server... ");
|
||||||
|
display.print("Serveur... ");
|
||||||
|
display.display();
|
||||||
|
server.on("/play", HTTP_GET, onPlay);
|
||||||
|
server.on("/stop", HTTP_GET, onStop);
|
||||||
|
server.on("/status", HTTP_GET, onStatus);
|
||||||
|
server.on("/list-files", HTTP_GET, onListFiles);
|
||||||
|
server.on("/select-file", HTTP_POST, onSelectFile);
|
||||||
|
server.on("/change-volume", HTTP_POST, onChangeVolume);
|
||||||
|
server.on("/upload", HTTP_POST, onUpload, onUploadFile);
|
||||||
|
server.onNotFound(onNotFound);
|
||||||
|
server.serveStatic("/", SPIFFS, "/www/").setDefaultFile("index.html");
|
||||||
|
AsyncElegantOTA.begin(&server);
|
||||||
|
server.begin();
|
||||||
|
Serial.println("OK");
|
||||||
|
display.println("OK");
|
||||||
|
display.display();
|
||||||
|
}
|
17
src/setup.h
Normal file
17
src/setup.h
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
#ifndef __SETUP_H__
|
||||||
|
#define __SETUP_H__
|
||||||
|
|
||||||
|
#include <ESPAsyncWebServer.h>
|
||||||
|
#include <Adafruit_SSD1306.h>
|
||||||
|
#include <Preferences.h>
|
||||||
|
#include <Audio.h>
|
||||||
|
|
||||||
|
void setupScreen();
|
||||||
|
void setupSPIFFS();
|
||||||
|
void setupSDCard();
|
||||||
|
void setupAudio();
|
||||||
|
void selectDefaultFile();
|
||||||
|
void setupWifi();
|
||||||
|
void setupWebServer();
|
||||||
|
|
||||||
|
#endif
|
87
src/utils.cpp
Normal file
87
src/utils.cpp
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
#include <Arduino.h>
|
||||||
|
#include <Audio.h>
|
||||||
|
#include <Preferences.h>
|
||||||
|
#include <Adafruit_SSD1306.h>
|
||||||
|
#include <SD.h>
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
bool fileIsValid(String fileName)
|
||||||
|
{
|
||||||
|
return !fileName.startsWith(".") && (fileName.endsWith(".mp3") || fileName.endsWith(".wav"));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool fileExists(String fileName)
|
||||||
|
{
|
||||||
|
return SD.exists("/" + fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
void clearMessageArea()
|
||||||
|
{
|
||||||
|
display.fillRect(SCREEN_MSG_X, SCREEN_MSG_Y, SCREEN_WIDTH - SCREEN_MSG_X, SCREEN_HEIGHT - SCREEN_MSG_Y, BLACK);
|
||||||
|
display.setCursor(SCREEN_MSG_X, SCREEN_MSG_Y);
|
||||||
|
}
|
||||||
|
|
||||||
|
void displayText(String text)
|
||||||
|
{
|
||||||
|
clearMessageArea();
|
||||||
|
display.println(text);
|
||||||
|
display.display();
|
||||||
|
}
|
||||||
|
|
||||||
|
void play()
|
||||||
|
{
|
||||||
|
String selectedFile = preferences.getString(SELECTED_FILE);
|
||||||
|
String path = "/" + selectedFile;
|
||||||
|
Serial.println("Playing file: " + path);
|
||||||
|
audio.stopSong();
|
||||||
|
audio.connecttoFS(SD, path.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void diagnosticPrint(String text)
|
||||||
|
{
|
||||||
|
Serial.print(text);
|
||||||
|
display.print(text);
|
||||||
|
display.display();
|
||||||
|
}
|
||||||
|
void diagnosticPrintln(String text)
|
||||||
|
{
|
||||||
|
Serial.println(text);
|
||||||
|
display.println(text);
|
||||||
|
display.display();
|
||||||
|
}
|
||||||
|
|
||||||
|
void displayWifiCreds()
|
||||||
|
{
|
||||||
|
display.clearDisplay();
|
||||||
|
display.setCursor(0, 0);
|
||||||
|
|
||||||
|
String ssid = preferences.getString(WIFI_SSID, "");
|
||||||
|
display.print("Wifi: ");
|
||||||
|
display.println(ssid);
|
||||||
|
|
||||||
|
display.print("Pass: ");
|
||||||
|
#ifdef B_WIFI_AP
|
||||||
|
String password = preferences.getString(WIFI_PASSWORD, "");
|
||||||
|
display.println(password);
|
||||||
|
#else
|
||||||
|
display.println("****");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
display.print("IP: ");
|
||||||
|
String ip = preferences.getString(WIFI_IP, "");
|
||||||
|
display.println(ip);
|
||||||
|
display.display();
|
||||||
|
}
|
||||||
|
|
||||||
|
void displaySelectedFile()
|
||||||
|
{
|
||||||
|
String prefix = "Son: ";
|
||||||
|
displayText(prefix + preferences.getString(SELECTED_FILE, "").substring(0, LINE_LENGTH - prefix.length()));
|
||||||
|
}
|
||||||
|
|
||||||
|
void displayVolume() {
|
||||||
|
String volume = String(preferences.getUChar(CURRENT_VOLUME));
|
||||||
|
displayText("Volume: " + volume);
|
||||||
|
}
|
20
src/utils.h
Normal file
20
src/utils.h
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
#ifndef __UTILS_H__
|
||||||
|
#define __UTILS_H__
|
||||||
|
|
||||||
|
#include <Arduino.h>
|
||||||
|
#include <Adafruit_SSD1306.h>
|
||||||
|
#include <Preferences.h>
|
||||||
|
|
||||||
|
bool fileIsValid(String fileName);
|
||||||
|
bool fileExists(String fileName);
|
||||||
|
void clearMessageArea();
|
||||||
|
void displayText(String text);
|
||||||
|
void play();
|
||||||
|
|
||||||
|
void displayWifiCreds();
|
||||||
|
void displaySelectedFile();
|
||||||
|
|
||||||
|
void diagnosticPrint(String text);
|
||||||
|
void diagnosticPrintln(String text);
|
||||||
|
|
||||||
|
#endif
|
161
src/webHandlers.cpp
Normal file
161
src/webHandlers.cpp
Normal file
|
@ -0,0 +1,161 @@
|
||||||
|
#include <Arduino.h>
|
||||||
|
#include <ESPAsyncWebServer.h>
|
||||||
|
#include <AsyncJson.h>
|
||||||
|
#include <ArduinoJson.h>
|
||||||
|
#include <Adafruit_SSD1306.h>
|
||||||
|
#include <Preferences.h>
|
||||||
|
#include <SD.h>
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
#include "utils.h"
|
||||||
|
|
||||||
|
void onStop(AsyncWebServerRequest *request)
|
||||||
|
{
|
||||||
|
Serial.println("Stop playing");
|
||||||
|
audio.stopSong();
|
||||||
|
request->send(200);
|
||||||
|
}
|
||||||
|
|
||||||
|
void onPlay(AsyncWebServerRequest *request)
|
||||||
|
{
|
||||||
|
play();
|
||||||
|
request->send(200);
|
||||||
|
}
|
||||||
|
|
||||||
|
void onStatus(AsyncWebServerRequest *request)
|
||||||
|
{
|
||||||
|
Serial.println("Status");
|
||||||
|
AsyncResponseStream *response = request->beginResponseStream("application/json");
|
||||||
|
|
||||||
|
StaticJsonDocument<96> root;
|
||||||
|
root["files"]["selected"] = preferences.getString(SELECTED_FILE, "").c_str();
|
||||||
|
|
||||||
|
JsonObject volume = root.createNestedObject("volume");
|
||||||
|
byte currentVolume = preferences.getUChar(CURRENT_VOLUME);
|
||||||
|
volume["current"] = currentVolume;
|
||||||
|
volume["canDecrease"] = currentVolume > 0;
|
||||||
|
volume["canIncrease"] = currentVolume < 21;
|
||||||
|
|
||||||
|
serializeJson(root, *response);
|
||||||
|
request->send(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
void onListFiles(AsyncWebServerRequest *request)
|
||||||
|
{
|
||||||
|
Serial.print("List files cursor=");
|
||||||
|
int cursor = 0;
|
||||||
|
if (request->hasParam("cursor")) {
|
||||||
|
String s_cursor = request->getParam("cursor")->value();
|
||||||
|
cursor = s_cursor.toInt();
|
||||||
|
}
|
||||||
|
Serial.println(cursor);
|
||||||
|
|
||||||
|
AsyncResponseStream *response = request->beginResponseStream("application/json");
|
||||||
|
|
||||||
|
StaticJsonDocument<512> root;
|
||||||
|
root["next"] = -1;
|
||||||
|
JsonArray files = root.createNestedArray("files");
|
||||||
|
File music = SD.open("/");
|
||||||
|
File file = music.openNextFile();
|
||||||
|
int index = 0;
|
||||||
|
while (file)
|
||||||
|
{
|
||||||
|
String fileName = file.name();
|
||||||
|
if (fileIsValid(fileName))
|
||||||
|
{
|
||||||
|
index++;
|
||||||
|
if (index >= cursor)
|
||||||
|
files.add(fileName);
|
||||||
|
}
|
||||||
|
file.close();
|
||||||
|
|
||||||
|
if (root.overflowed())
|
||||||
|
{
|
||||||
|
root["next"] = index;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
file = music.openNextFile();
|
||||||
|
}
|
||||||
|
if (root["next"] == -1) {
|
||||||
|
root.remove("next");
|
||||||
|
}
|
||||||
|
|
||||||
|
serializeJson(root, *response);
|
||||||
|
request->send(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
void onSelectFile(AsyncWebServerRequest *request)
|
||||||
|
{
|
||||||
|
Serial.print("Select file: ");
|
||||||
|
if (request->hasParam("fileName", true))
|
||||||
|
{
|
||||||
|
String selectedFile = request->getParam("fileName", true)->value();
|
||||||
|
preferences.putString(SELECTED_FILE, selectedFile);
|
||||||
|
Serial.print(selectedFile);
|
||||||
|
displaySelectedFile();
|
||||||
|
}
|
||||||
|
Serial.println();
|
||||||
|
onStatus(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
void onChangeVolume(AsyncWebServerRequest *request)
|
||||||
|
{
|
||||||
|
Serial.print("Volume: ");
|
||||||
|
if (request->hasParam("modifier", true))
|
||||||
|
{
|
||||||
|
String s_modifier = request->getParam("modifier", true)->value();
|
||||||
|
int modifier = s_modifier.toInt();
|
||||||
|
byte currentVolume = preferences.getUChar(CURRENT_VOLUME);
|
||||||
|
currentVolume += modifier;
|
||||||
|
if (currentVolume > VOLUME_MAX)
|
||||||
|
currentVolume = VOLUME_MAX;
|
||||||
|
else if (currentVolume < VOLUME_MIN)
|
||||||
|
currentVolume = VOLUME_MIN;
|
||||||
|
preferences.putUChar(CURRENT_VOLUME, currentVolume);
|
||||||
|
audio.setVolume(currentVolume);
|
||||||
|
Serial.print(currentVolume);
|
||||||
|
String s_volume = String(currentVolume);
|
||||||
|
displayText("Volume : " + s_volume);
|
||||||
|
}
|
||||||
|
Serial.println();
|
||||||
|
onStatus(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
void onUpload(AsyncWebServerRequest *request)
|
||||||
|
{
|
||||||
|
Serial.println("onUpload");
|
||||||
|
request->send(200);
|
||||||
|
}
|
||||||
|
|
||||||
|
void onUploadFile(AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final)
|
||||||
|
{
|
||||||
|
if (!index)
|
||||||
|
{
|
||||||
|
Serial.printf("UploadStart: %s\n", filename.c_str());
|
||||||
|
String filePath = "/" + filename;
|
||||||
|
request->_tempFile = SD.open(filePath, FILE_WRITE);
|
||||||
|
}
|
||||||
|
if (!request->_tempFile)
|
||||||
|
{
|
||||||
|
Serial.println("Couldn't open file.");
|
||||||
|
request->redirect("/");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (len)
|
||||||
|
{
|
||||||
|
request->_tempFile.write(data, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (final)
|
||||||
|
{
|
||||||
|
Serial.printf("UploadEnd: %s, %u B\n", filename.c_str(), index + len);
|
||||||
|
request->_tempFile.close();
|
||||||
|
request->redirect("/");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void onNotFound(AsyncWebServerRequest *request)
|
||||||
|
{
|
||||||
|
Serial.println("not found");
|
||||||
|
request->send(400);
|
||||||
|
}
|
20
src/webHandlers.h
Normal file
20
src/webHandlers.h
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
#ifndef __MAIN_H__
|
||||||
|
#define __MAIN_H__
|
||||||
|
|
||||||
|
#include <Arduino.h>
|
||||||
|
#include <ESPAsyncWebServer.h>
|
||||||
|
#include <Adafruit_SSD1306.h>
|
||||||
|
#include <Preferences.h>
|
||||||
|
|
||||||
|
|
||||||
|
void onStop(AsyncWebServerRequest *request);
|
||||||
|
void onPlay(AsyncWebServerRequest *request);
|
||||||
|
void onStatus(AsyncWebServerRequest *request);
|
||||||
|
void onListFiles(AsyncWebServerRequest *request);
|
||||||
|
void onSelectFile(AsyncWebServerRequest *request);
|
||||||
|
void onChangeVolume(AsyncWebServerRequest *request);
|
||||||
|
void onUpload(AsyncWebServerRequest *request);
|
||||||
|
void onUploadFile(AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final);
|
||||||
|
void onNotFound(AsyncWebServerRequest *request);
|
||||||
|
|
||||||
|
#endif
|
Loading…
Reference in a new issue