Added volume up, down, mute, get options

This commit is contained in:
2023-12-07 20:24:46 +01:00
parent 072ec79050
commit 022c96ea13
5 changed files with 147 additions and 69 deletions

View File

@@ -11,12 +11,13 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI
For more information, please refer to <http://unlicense.org/> For more information, please refer to <http://unlicense.org/>
*/ */
#include "WiFi.h" // https://github.com/espressif/arduino-esp32/tree/master/libraries/WiFi/src #include "WiFi.h" // https://github.com/espressif/arduino-esp32/tree/master/libraries/WiFi/src
#include "Audio.h" // https://github.com/schreibfaul1/ESP32-audioI2S #include "Audio.h" // https://github.com/schreibfaul1/ESP32-audioI2S
#include "SPI.h" // https://github.com/espressif/arduino-esp32/tree/master/libraries/SPI/src #include "SPI.h" // https://github.com/espressif/arduino-esp32/tree/master/libraries/SPI/src
#include "SD.h" // https://github.com/espressif/arduino-esp32/tree/master/libraries/SD/src #include "SD.h" // https://github.com/espressif/arduino-esp32/tree/master/libraries/SD/src
#include "FS.h" // https://github.com/espressif/arduino-esp32/tree/master/libraries/FS/src #include "FS.h" // https://github.com/espressif/arduino-esp32/tree/master/libraries/FS/src
#include <WebServer.h> // https://github.com/espressif/arduino-esp32/tree/master/libraries/WebServer #include <WebServer.h> // https://github.com/espressif/arduino-esp32/tree/master/libraries/WebServer
#include <uri/UriBraces.h> // https://github.com/espressif/arduino-esp32/blob/master/libraries/WebServer
// define all constants // define all constants
@@ -47,9 +48,9 @@ const String apSSID = version; // ssid of access point open
const String apPSK = "aA16161Aa"; // pre-shared-key of access point opened when not able to connect to wifi const String apPSK = "aA16161Aa"; // pre-shared-key of access point opened when not able to connect to wifi
// create all needed variables // create all needed variables
int currentVolume = 21; // variable where current volume (0...21) is stored int currentVolume = 20; // variable where current volume (0...20) is stored
String currentSongPath; // path to currently playing song String currentSongPath; // path to currently playing song
bool audioPlaying = true; // play song or not? bool audioPlaying = true; // play song or not?
Audio audio; // Audio object (for playing audio, decoding mp3, ...) Audio audio; // Audio object (for playing audio, decoding mp3, ...)
int currentPlaylistPosition = 0; // the current position in the current playlist int currentPlaylistPosition = 0; // the current position in the current playlist
String currentPlaylist = "/audio/" + directoryPlaylistName + playlistExtension; // path to current playlist String currentPlaylist = "/audio/" + directoryPlaylistName + playlistExtension; // path to current playlist
@@ -69,7 +70,7 @@ void setup() {
delay(retrySDMountTreshold); delay(retrySDMountTreshold);
} }
if(operation_mode == 0) { // things only need to be done if in interconnected mode if (operation_mode == 0) { // things only need to be done if in interconnected mode
setupWiFi(); setupWiFi();
setupConfigWeb(); setupConfigWeb();
setupApiWeb(); setupApiWeb();
@@ -82,25 +83,25 @@ void setup() {
pinMode(readyPin, OUTPUT); pinMode(readyPin, OUTPUT);
} else { } else {
Serial.println("[FATAL] PLEASE CHOOSE A OPERATION MODE! VALID OPTIONS: 0; 1. SLEEPING FOREVER."); Serial.println("[FATAL] PLEASE CHOOSE A OPERATION MODE! VALID OPTIONS: 0; 1. SLEEPING FOREVER.");
while(true) delay(100); while (true) delay(100);
} }
setupAudio(); // setup audio library setupAudio(); // setup audio library
createPlaylistFromDirectory("/audio"); // create playlist from default dir ("/audio") createPlaylistFromDirectory("/audio"); // create playlist from default dir ("/audio")
audio.connecttoFS(SD, getSongFromPlaylist(currentPlaylist, currentPlaylistPosition).c_str()); // play first element of the playlist audio.connecttoFS(SD, getSongFromPlaylist(currentPlaylist, currentPlaylistPosition).c_str()); // play first element of the playlist
digitalWrite(readyPin, HIGH); // show that startup is done and everything works fine digitalWrite(readyPin, HIGH); // show that startup is done and everything works fine
} }
void loop() { void loop() {
if(operation_mode == 0) { // things only need to be done if in interconnected mode if (operation_mode == 0) { // things only need to be done if in interconnected mode
setupWiFi(); // if connection was lost setupWiFi(); // if connection was lost
if(audioPlaying) audio.loop(); // play audio if not paused if (audioPlaying) audio.loop(); // play audio if not paused
if(esp_timer_get_time()%100 == 0) { // REALLY NEEDED; audio playing won't work else! if (esp_timer_get_time() % 50 == 0) { // REALLY NEEDED; audio playing won't work else!
loopServer(); // listen on http ports all 100 ms loopServer(); // listen on http ports all 50 ms
} }
} }
if(operation_mode == 1) { // things only need to be done if in standalone mode if (operation_mode == 1) { // things only need to be done if in standalone mode
if(audioPlaying) audio.loop(); // play audio if not paused if (audioPlaying) audio.loop(); // play audio if not paused
loopBtnListeners(); loopBtnListeners();

View File

@@ -13,6 +13,7 @@ For more information, please refer to <http://unlicense.org/>
void setupAudio() { void setupAudio() {
audio.setPinout(I2S_BLCK, I2S_LRC, I2S_DOUT); // tell the audio library what output pins to use audio.setPinout(I2S_BLCK, I2S_LRC, I2S_DOUT); // tell the audio library what output pins to use
audio.setVolumeSteps(20);
Serial.printf("[SETUP] Set up audio card successfully (Pins: BLCK %d | LRC %d | DOUT %d)\n", I2S_BLCK, I2S_LRC, I2S_DOUT); Serial.printf("[SETUP] Set up audio card successfully (Pins: BLCK %d | LRC %d | DOUT %d)\n", I2S_BLCK, I2S_LRC, I2S_DOUT);
} }
@@ -73,11 +74,11 @@ void backwardButtonHandler() {
} }
void setAudioVolume() { void setAudioVolume() {
int newCurrentVolume = analogRead(audioVolumePin) / 195; // read voltage from audioVolumePin and divide by 195 (min. volume is 0, max. 21; 21 fits 195 times into 4095 (maximum input)) int newCurrentVolume = analogRead(audioVolumePin) / 204.75; // read voltage from audioVolumePin and divide by 204.75 (min. volume is 0, max. 20; 20 fits 204.75 times into 4095 (maximum input))
if(currentVolume != newCurrentVolume) { // just do it if the volume changed if(currentVolume != newCurrentVolume) { // just do it if the volume changed
currentVolume = newCurrentVolume; currentVolume = newCurrentVolume;
audio.setVolume(currentVolume); // set volume audio.setVolume(currentVolume); // set volume
Serial.printf("[INFO] Set volume to %d/21!\n", currentVolume); Serial.printf("[INFO] Set volume to %d/20!\n", currentVolume);
} }
} }

View File

@@ -65,6 +65,7 @@ String getWiFiPSK() {
return getWiFiPSK(wifiConfigPath); return getWiFiPSK(wifiConfigPath);
} }
bool createPlaylistFromDirectory(String folderpath) { // create a .m3u playlist from all directory contents (the directory 'folderpath' is used) bool createPlaylistFromDirectory(String folderpath) { // create a .m3u playlist from all directory contents (the directory 'folderpath' is used)
File folder; File folder;
File playlist; File playlist;
@@ -111,7 +112,7 @@ String getSongFromPlaylist(String path, int position) {
song = (char)playlist.read(); // read character... song = (char)playlist.read(); // read character...
while (playlist.available()) { while (playlist.available()) {
currentChar = (char)playlist.read(); currentChar = (char)playlist.read();
if (currentChar != '\n') { if (currentChar != '\n' && currentChar != '\r') {
song += currentChar; // ... except if and as long as a newline hits song += currentChar; // ... except if and as long as a newline hits
} else { } else {
break; // exit while loop break; // exit while loop

View File

@@ -11,37 +11,59 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI
For more information, please refer to <http://unlicense.org/> For more information, please refer to <http://unlicense.org/>
*/ */
String generate_api_json(bool success, String content, bool plain) {
String currentResourcePlaying = getSongFromPlaylist(currentPlaylist, currentPlaylistPosition);
String json = "{\"success\": "; // success
json += success ? "true" : "false";
json += ", ";
json += "\"state\": \""; // playing or paused?
json += audioPlaying ? "playing" : "paused";
json += "\", ";
json += "\"currentResource\": \"" + currentResourcePlaying + "\""; // current playing resource
if (!plain) {
json += ", ";
json += content;
}
json += "}";
return json;
}
String generate_api_json(bool success) { // if you just want the basic json frame with the basic info
return generate_api_json(success, "", true);
}
String generate_api_json(bool success, String content) { // when info is to be embedded
return generate_api_json(success, content, false);
}
void configRoot() { void configRoot() {
Serial.println("[HTTP] [Config] 200 - GET '/'"); Serial.println("[HTTP] [Config] 200 - GET '/'");
String html = "<html><head><meta charset='utf-8'><meta name='viewport' content='width=device-width,initial-scale=1'><title>"; String html = "<html><head><meta charset='utf-8'><meta name='viewport' content='width=device-width,initial-scale=1'><title>Configuration | ";
html += version; html += version;
html += "configuration</title></head><body><div style='text-align:center;'><br><br><p>Configuration of your</p><h3>NetSpeaker</h3><br><hr><br><p>Work in progress!</p></div></body></html>"; html += "</title></head><body><div style='text-align:center;'><br><br><p>Configuration of your</p><h3>NetSpeaker</h3><br><hr><br><p>Work in progress!</p></div></body></html>";
conf_server.send(200, "text/html", html); conf_server.send(200, "text/html", html);
} }
void apiRoot() { void apiRoot() {
Serial.println("[HTTP] [API] 200 - GET '/'"); Serial.println("[HTTP] [API] 200 - GET '/'");
String html = "<html><head><meta charset='utf-8'><meta name='viewport' content='width=device-width,initial-scale=1'><title>"; String html = "<html><head><meta charset='utf-8'><meta name='viewport' content='width=device-width,initial-scale=1'><title>API | ";
html += version; html += version;
html += "configuration</title></head><body><div style='text-align:center;'><br><br><p>API of your</p><br><h3>NetSpeaker</h3><br><hr><br><p>Work in progress!</p></div></body></html>"; html += "</title></head><body><div style='text-align:center;'><br><br><p>API of your</p><br><h3>NetSpeaker</h3><br><hr><br><p>Work in progress!</p></div></body></html>";
api_server.send(200, "text/html", html); api_server.send(200, "text/html", html);
} }
void api_v1_playback_toggle() { void api_v1_playback_toggle() {
Serial.println("[HTTP] [API] 200 - GET '/_api/v1/playback/toggle'"); Serial.println("[HTTP] [API] 200 - GET '/api/v1/playback/toggle'");
audioPlaying = !audioPlaying; audioPlaying = !audioPlaying;
Serial.printf("[INFO] Playback has switched: %s\n", audioPlaying ? "Playing" : "Paused"); Serial.printf("[INFO] Playback has switched: %s\n", audioPlaying ? "Playing" : "Paused");
String json = "{'success': true, 'state': '"; api_server.send(200, "application/json", generate_api_json(true));
json += audioPlaying ? "playing" : "paused";
json += "'}";
api_server.send(200, "application/json", json);
} }
void api_v1_playback_play() { void api_v1_playback_play() {
@@ -50,51 +72,104 @@ void api_v1_playback_play() {
audioPlaying = true; audioPlaying = true;
Serial.printf("[INFO] Playback has switched: Playing\n"); Serial.printf("[INFO] Playback has switched: Playing\n");
String json = "{'success': true, 'state': 'playing'}"; api_server.send(200, "application/json", generate_api_json(true));
api_server.send(200, "application/json", json);
} }
void api_v1_playback_pause() { void api_v1_playback_pause() {
Serial.println("[HTTP] [API] 200 - GET '/_api/v1/playback/pause'"); Serial.println("[HTTP] [API] 200 - GET '/api/v1/playback/pause'");
audioPlaying = false; audioPlaying = false;
Serial.printf("[INFO] Playback has switched: Paused\n"); Serial.printf("[INFO] Playback has switched: Paused\n");
String json = "{'success': true, 'state': 'paused'}"; api_server.send(200, "application/json", generate_api_json(true));
api_server.send(200, "application/json", json);
} }
void api_v1_playback_next() { void api_v1_playback_next() {
Serial.println("[HTTP] [API] 200 - GET '/_api/v1/playback/next'"); Serial.println("[HTTP] [API] 200 - GET '/api/v1/playback/next'");
String resourcePath = nextAudio(); nextAudio();
String json = "{'success': true, 'state': '"; api_server.send(200, "application/json", generate_api_json(true));
json += audioPlaying ? "playing" : "paused";
json += "', 'currentResource': '";
json += resourcePath;
json += "'}";
api_server.send(200, "application/json", json);
} }
void api_v1_playback_previous() { void api_v1_playback_previous() {
Serial.println("[HTTP] [API] 200 - GET '/_api/v1/playback/previous'"); Serial.println("[HTTP] [API] 200 - GET '/api/v1/playback/previous'");
String resourcePath = previousAudio(); previousAudio();
String json = "{'success': true, 'state': '"; api_server.send(200, "application/json", generate_api_json(true));
json += audioPlaying ? "playing" : "paused"; }
json += "', 'currentResource': '";
json += resourcePath; void api_v1_playback_volume() {
json += "'}"; String option = api_server.pathArg(0);
bool success = true;
api_server.send(200, "application/json", json); Serial.printf("[HTTP] [API] 200 - GET '/api/v1/volume/%s'\n", option);
if (option == "up") {
currentVolume++;
if (currentVolume > 20) currentVolume = 20;
} else if (option == "down") {
currentVolume--;
if (currentVolume < 0) currentVolume = 0;
} else if (option == "mute") {
currentVolume = 0; // TODO: remember the previous volume and just mute it; unmute has to be implemented then
} else if (option == "get") {
// just here that no 'success: false' is sent
} else if (option == "0") {
currentVolume = 0;
} else if (option == "1") {
currentVolume = 1;
} else if (option == "2") {
currentVolume = 2;
} else if (option == "3") {
currentVolume = 3;
} else if (option == "4") {
currentVolume = 4;
} else if (option == "5") {
currentVolume = 5;
} else if (option == "6") {
currentVolume = 6;
} else if (option == "7") {
currentVolume = 7;
} else if (option == "8") {
currentVolume = 8;
} else if (option == "9") {
currentVolume = 9;
} else if (option == "10") {
currentVolume = 10;
} else if (option == "11") {
currentVolume = 11;
} else if (option == "12") {
currentVolume = 12;
} else if (option == "13") {
currentVolume = 13;
} else if (option == "14") {
currentVolume = 14;
} else if (option == "15") {
currentVolume = 15;
} else if (option == "16") {
currentVolume = 16;
} else if (option == "17") {
currentVolume = 17;
} else if (option == "18") {
currentVolume = 18;
} else if (option == "19") {
currentVolume = 19;
} else if (option == "20") {
currentVolume = 20;
} else {
success = false;
}
audio.setVolume(currentVolume); // set volume
String content = "\"volume\": "; // prepare the http response
content += String(currentVolume);
api_server.send(200, "application/json", generate_api_json(success, content)); // generate json and send it
} }
void setupConfigWeb() { void setupConfigWeb() {
if(runConfServer) { if (runConfServer) {
conf_server.onNotFound([]() { conf_server.onNotFound([]() {
Serial.println("[HTTP] [Config] 404: Not Found"); Serial.println("[HTTP] [Config] 404: Not Found");
conf_server.send(404, "text/html", "<html><head><title>Not Found</title></head><body><h1>Not Found</h1><p>This resource does not exist on this server.</p></body></html>"); conf_server.send(404, "text/html", "<html><head><title>Not Found</title></head><body><h1>Not Found</h1><p>This resource does not exist on this server.</p></body></html>");
@@ -109,15 +184,16 @@ void setupConfigWeb() {
void setupApiWeb() { void setupApiWeb() {
api_server.onNotFound([]() { api_server.onNotFound([]() {
Serial.println("[HTTP] [API] 404: Not Found"); Serial.println("[HTTP] [API] 404: Not Found");
api_server.send(404, "text/html", "<html><head><title>Not Found</title></head><body><h1>Not Found</h1><p>This resource does not exist on this server.</p></body></html>"); api_server.send(404, "text/html", "<html><head><title>Not Found</title></head><body><h1>Not Found</h1><p>This resource does not exist on this server.</p></body></html>");
}); });
api_server.on("/", apiRoot); api_server.on("/", apiRoot);
api_server.on("/_api/v1/playback/toggle", api_v1_playback_toggle); api_server.on("/api/v1/playback/toggle", api_v1_playback_toggle);
api_server.on("/_api/v1/playback/play", api_v1_playback_play); api_server.on("/api/v1/playback/play", api_v1_playback_play);
api_server.on("/_api/v1/playback/pause", api_v1_playback_pause); api_server.on("/api/v1/playback/pause", api_v1_playback_pause);
api_server.on("/_api/v1/playback/next", api_v1_playback_next); api_server.on("/api/v1/playback/next", api_v1_playback_next);
api_server.on("/_api/v1/playback/previous", api_v1_playback_previous); api_server.on("/api/v1/playback/previous", api_v1_playback_previous);
api_server.on(UriBraces("/api/v1/volume/{}"), api_v1_playback_volume);
Serial.println("[HTTP] [API] Starting API server (http) on port " + String(webport_api)); Serial.println("[HTTP] [API] Starting API server (http) on port " + String(webport_api));
api_server.begin(); api_server.begin();
@@ -126,5 +202,5 @@ void setupApiWeb() {
void loopServer() { void loopServer() {
api_server.handleClient(); api_server.handleClient();
if(runConfServer) conf_server.handleClient(); if (runConfServer) conf_server.handleClient();
} }

View File

@@ -36,7 +36,7 @@ void setupWiFi() {
WiFi.mode(WIFI_AP_STA); WiFi.mode(WIFI_AP_STA);
WiFi.begin(WiFiSSID, WiFiPSK); WiFi.begin(WiFiSSID, WiFiPSK);
// wait for 10 seconds for wifi to connect // wait for 10 seconds for wifi to connect
int start_timer = millis(); while(WiFi.status() != WL_CONNECTED) { if((millis()-start_timer) > 10000) break; } int start_timer = millis(); while(WiFi.status() != WL_CONNECTED) { if((millis()-start_timer) > 20000) break; }
if(WiFi.status() != WL_CONNECTED) { if(WiFi.status() != WL_CONNECTED) {
currentWiFiMode = 1; currentWiFiMode = 1;
Serial.printf("[WiFi] Unable to connect to %s\n", WiFiSSID); Serial.printf("[WiFi] Unable to connect to %s\n", WiFiSSID);
@@ -58,5 +58,4 @@ void setupWiFi() {
} }
break; break;
} }
} }