web interface
This commit is contained in:
parent
37f11de98a
commit
4fd7856736
|
|
@ -18,5 +18,6 @@ lib_deps =
|
|||
me-no-dev/ESP Async WebServer@^1.2.3
|
||||
alanswx/ESPAsyncWiFiManager@^0.31
|
||||
miguelbalboa/MFRC522@^1.4.10
|
||||
bblanchon/ArduinoJson@^6.21.3
|
||||
monitor_speed = 115200
|
||||
board_build.partitions = huge_app.csv
|
||||
|
|
|
|||
|
|
@ -46,6 +46,14 @@ void DirectoryNode::addMP3File(const String &mp3File)
|
|||
mp3Files.push_back(mp3File);
|
||||
}
|
||||
|
||||
void DirectoryNode::setSecondsPlayed(const uint32_t seconds) {
|
||||
secondsPlayed = seconds;
|
||||
}
|
||||
|
||||
uint32_t DirectoryNode::getSecondsPlayed() {
|
||||
return secondsPlayed;
|
||||
}
|
||||
|
||||
void DirectoryNode::buildDirectoryTree(const char *currentPath)
|
||||
{
|
||||
File rootDir = SD.open(currentPath);
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ private:
|
|||
std::vector<DirectoryNode*> subdirectories;
|
||||
std::vector<String> mp3Files;
|
||||
const String* currentPlaying;
|
||||
uint16_t secondsPlayed = 0;
|
||||
|
||||
public:
|
||||
DirectoryNode(const String& nodeName);
|
||||
|
|
@ -22,6 +23,9 @@ public:
|
|||
void setCurrentPlaying(const String* mp3File);
|
||||
const String* getCurrentPlaying() const;
|
||||
|
||||
void setSecondsPlayed(const uint32_t seconds);
|
||||
uint32_t getSecondsPlayed();
|
||||
|
||||
void addSubdirectory(DirectoryNode* subdirectory);
|
||||
void addMP3File(const String& mp3File);
|
||||
void buildDirectoryTree(const char* currentPath);
|
||||
|
|
|
|||
|
|
@ -9,13 +9,27 @@ const char index_html[] PROGMEM = R"rawliteral(
|
|||
<body>
|
||||
<h1>HannaBox</h1>
|
||||
|
||||
<span id="state">%PLAYING%</span><br/><br/>
|
||||
<span id="state"></span><br/><br/>
|
||||
|
||||
<button class="prev-button" onmouseup="toggleCheckbox('prev');" ontouchend="toggleCheckbox('prev');"></button>
|
||||
<button class="play-button" onmouseup="toggleCheckbox('start');" ontouchend="toggleCheckbox('start');"></button>
|
||||
<button class="next-button" onmouseup="toggleCheckbox('next');" ontouchend="toggleCheckbox('next');"></button><br/>
|
||||
|
||||
<div class="slidecontainer">
|
||||
<label for="progress" id="progressLabel"></label>
|
||||
<input name="progress" type="range" min="0" max="100" value="0" class="slider" id="progressSlider"
|
||||
onmouseup="postValue('progress',document.getElementById('progressSlider').value);"
|
||||
ontouchend="postValue('progress',document.getElementById('progressSlider').value);">
|
||||
</div>
|
||||
<div class="slidecontainer">
|
||||
<label for="volume">Vol</label>
|
||||
<input name="volume" type="range" min="0" max="21" value="12" class="slider" id="volumeSlider"
|
||||
onmouseup="postValue('volume',document.getElementById('volumeSlider').value);"
|
||||
ontouchend="postValue('volume',document.getElementById('volumeSlider').value);">
|
||||
</div>
|
||||
|
||||
<button onmouseup="toggleCheckbox('stop');" ontouchend="toggleCheckbox('stop');">Stop</button>
|
||||
<button class="prev-button" onmouseup="simpleGetCall('prev');" ontouchend="simpleGetCall('prev');"></button>
|
||||
<button class="play-button" onmouseup="simpleGetCall('toggleplaypause');" ontouchend="simpleGetCall('toggleplaypause');"></button>
|
||||
<button class="next-button" onmouseup="simpleGetCall('next');" ontouchend="simpleGetCall('next');"></button><br/>
|
||||
|
||||
<button onmouseup="simpleGetCall('stop');" ontouchend="simpleGetCall('stop');">Stop</button>
|
||||
<form action='playnamed' method='post'>
|
||||
<div>
|
||||
<label for="title">Song title to play</label>
|
||||
|
|
@ -46,9 +60,17 @@ const char index_html[] PROGMEM = R"rawliteral(
|
|||
});
|
||||
});
|
||||
|
||||
function toggleCheckbox(x) {
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open("GET", "/" + x, true);
|
||||
function simpleGetCall(endpoint) {
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open("GET", "/" + endpoint, true);
|
||||
xhr.send();
|
||||
}
|
||||
|
||||
function postValue(endpoint,value) {
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open("POST", "/" + endpoint, true);
|
||||
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
|
||||
xhr.send("value="+encodeURIComponent(value));
|
||||
xhr.send();
|
||||
}
|
||||
|
||||
|
|
@ -57,7 +79,7 @@ const char index_html[] PROGMEM = R"rawliteral(
|
|||
|
||||
xhr.onreadystatechange = function() {
|
||||
if (xhr.readyState === 4) {
|
||||
displayState(xhr.response);
|
||||
displayState(JSON.parse(xhr.response));
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -67,14 +89,23 @@ const char index_html[] PROGMEM = R"rawliteral(
|
|||
}
|
||||
|
||||
function displayState(state) {
|
||||
document.getElementById("state").innerHTML = state;
|
||||
document.getElementById("state").innerHTML = state['title'];
|
||||
document.getElementById("progressLabel").innerHTML = state['time'];
|
||||
var elements = document.getElementsByClassName('play-button');
|
||||
var btn = elements[0];
|
||||
if (state=='-') {
|
||||
btn.classList.remove('paused');
|
||||
} else {
|
||||
btn.classList.add('paused');
|
||||
}
|
||||
var btn = elements[0];
|
||||
|
||||
if (state['playing']) {
|
||||
btn.classList.add('paused');
|
||||
} else {
|
||||
btn.classList.remove('paused');
|
||||
}
|
||||
var progress = document.getElementById('progressSlider');
|
||||
progress.value = state['time'];
|
||||
progress.max = state['length'];
|
||||
|
||||
var volume = document.getElementById('volumeSlider');
|
||||
volume.value = state['volume'];
|
||||
|
||||
}
|
||||
|
||||
function playNamedSong(song) {
|
||||
|
|
|
|||
|
|
@ -11,6 +11,10 @@ void loop2(void* parameter);
|
|||
|
||||
void named_song_action(AsyncWebServerRequest *request);
|
||||
|
||||
void progress_action(AsyncWebServerRequest *request);
|
||||
|
||||
void volume_action(AsyncWebServerRequest *request);
|
||||
|
||||
boolean buttonPressed(const uint8_t pin);
|
||||
|
||||
/**
|
||||
|
|
|
|||
104
src/main.cpp
104
src/main.cpp
|
|
@ -6,6 +6,8 @@
|
|||
|
||||
#include <ESPAsyncWebServer.h> //Local WebServer used to serve the configuration portal
|
||||
#include <ESPAsyncWiFiManager.h> //https://github.com/tzapu/WiFiManager WiFi Configuration Magic
|
||||
#include <ArduinoJson.h>
|
||||
|
||||
|
||||
#include "Audio.h"
|
||||
|
||||
|
|
@ -131,6 +133,11 @@ void playNextMp3()
|
|||
{
|
||||
currentNode = rootNode.advanceToNextMP3(currentNode->getCurrentPlaying());
|
||||
}
|
||||
|
||||
if (currentNode!=NULL) {
|
||||
currentNode->setSecondsPlayed(0);
|
||||
}
|
||||
|
||||
Serial.print("Now advancing to ");
|
||||
String mp3File = currentNode->getCurrentPlayingFilePath();
|
||||
Serial.println(mp3File.c_str());
|
||||
|
|
@ -163,35 +170,22 @@ void unmute()
|
|||
|
||||
String getState()
|
||||
{
|
||||
String state = String();
|
||||
DynamicJsonDocument jsonState(1024);
|
||||
jsonState["playing"] = audio.isRunning();
|
||||
if (currentNode!=NULL)
|
||||
jsonState["title"] = *currentNode->getCurrentPlaying();
|
||||
|
||||
jsonState["time"] = audio.getAudioCurrentTime();
|
||||
jsonState["volume"] = audio.getVolume();
|
||||
jsonState["length"] = audio.getAudioFileDuration();
|
||||
String output;
|
||||
serializeJson(jsonState,output);
|
||||
return output;
|
||||
|
||||
|
||||
if (audio.isRunning())
|
||||
{
|
||||
state += "Playing ";
|
||||
state += audio.getAudioCurrentTime();
|
||||
state += " / ";
|
||||
state += audio.getAudioFileDuration();
|
||||
state += " ";
|
||||
}
|
||||
else
|
||||
{
|
||||
return "-";
|
||||
}
|
||||
if (currentNode)
|
||||
{
|
||||
state += currentNode->getName();
|
||||
state += " ";
|
||||
if (currentNode->getCurrentPlaying())
|
||||
state += *currentNode->getCurrentPlaying();
|
||||
}
|
||||
return state;
|
||||
}
|
||||
|
||||
String processor(const String &var)
|
||||
{
|
||||
if (var == "PLAYING" && currentNode)
|
||||
return getState();
|
||||
if (var == "DIRECTORY")
|
||||
{
|
||||
return rootNode.getDirectoryStructureHTML();
|
||||
|
|
@ -203,8 +197,11 @@ void stop()
|
|||
{
|
||||
if (audio.isRunning())
|
||||
{
|
||||
Serial.println("stopping audio.");
|
||||
Serial.println("stopping audio.");
|
||||
audio.stopSong();
|
||||
if (currentNode!=NULL) {
|
||||
currentNode->setSecondsPlayed(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -219,6 +216,16 @@ void start()
|
|||
playNextMp3();
|
||||
}
|
||||
|
||||
void togglePlayPause()
|
||||
{
|
||||
if (currentNode != NULL)
|
||||
{
|
||||
audio.pauseResume();
|
||||
} else {
|
||||
playNextMp3();
|
||||
}
|
||||
}
|
||||
|
||||
void next()
|
||||
{
|
||||
playNextMp3();
|
||||
|
|
@ -311,7 +318,7 @@ void setup()
|
|||
server.on("/state", HTTP_GET, [](AsyncWebServerRequest *request)
|
||||
{
|
||||
String state = getState();
|
||||
request->send(200, "text/plain", state.c_str()); });
|
||||
request->send(200, "application/json", state.c_str()); });
|
||||
|
||||
server.on("/start", HTTP_GET, [](AsyncWebServerRequest *request)
|
||||
{
|
||||
|
|
@ -319,6 +326,13 @@ void setup()
|
|||
request->send(200, "text/plain", "start");
|
||||
start(); });
|
||||
|
||||
|
||||
server.on("/toggleplaypause", HTTP_GET, [](AsyncWebServerRequest *request)
|
||||
{
|
||||
|
||||
request->send(200, "text/plain", "toggleplaypause");
|
||||
togglePlayPause(); });
|
||||
|
||||
server.on("/stop", HTTP_GET, [](AsyncWebServerRequest *request)
|
||||
{
|
||||
|
||||
|
|
@ -333,6 +347,10 @@ void setup()
|
|||
|
||||
server.on("/playnamed", HTTP_POST, named_song_action);
|
||||
|
||||
server.on("/progress", HTTP_POST, progress_action);
|
||||
|
||||
server.on("/volume", HTTP_POST, volume_action);
|
||||
|
||||
server.begin();
|
||||
|
||||
xTaskCreatePinnedToCore(
|
||||
|
|
@ -358,7 +376,35 @@ void named_song_action(AsyncWebServerRequest *request) {
|
|||
playSongByName(p->value());
|
||||
}
|
||||
}
|
||||
request->send_P(200, "text/html", index_html, processor);
|
||||
request->send_P(200, "text/plain", "ok");
|
||||
}
|
||||
|
||||
void progress_action(AsyncWebServerRequest *request) {
|
||||
Serial.println("progress!");
|
||||
|
||||
int params = request->params();
|
||||
for (int i = 0; i < params; i++) {
|
||||
AsyncWebParameter* p = request->getParam(i);
|
||||
Serial.printf("POST[%s]: %s\n", p->name().c_str(), p->value().c_str());
|
||||
if (p->name()=="value") {
|
||||
audio.setAudioPlayPosition(atoi(p->value().c_str()));
|
||||
}
|
||||
}
|
||||
request->send_P(200, "text/plain", "ok");
|
||||
}
|
||||
|
||||
void volume_action(AsyncWebServerRequest *request) {
|
||||
Serial.println("volume!");
|
||||
|
||||
int params = request->params();
|
||||
for (int i = 0; i < params; i++) {
|
||||
AsyncWebParameter* p = request->getParam(i);
|
||||
Serial.printf("POST[%s]: %s\n", p->name().c_str(), p->value().c_str());
|
||||
if (p->name()=="value") {
|
||||
audio.setVolume(atoi(p->value().c_str()));
|
||||
}
|
||||
}
|
||||
request->send_P(200, "text/plain", "ok");
|
||||
}
|
||||
|
||||
void loop()
|
||||
|
|
@ -372,6 +418,10 @@ void loop()
|
|||
deactivateRFID();
|
||||
activateSD();
|
||||
audio.loop();
|
||||
if (currentNode!=NULL) {
|
||||
currentNode->setSecondsPlayed(audio.getAudioCurrentTime());
|
||||
}
|
||||
|
||||
deactivateSD();
|
||||
activateRFID();
|
||||
} else if (asyncStart) {
|
||||
|
|
|
|||
Loading…
Reference in New Issue