Directories working, web interface

This commit is contained in:
Stefan Ostermann 2023-06-04 23:15:11 +02:00
parent 2dd95a35eb
commit f173272f8b
4 changed files with 149 additions and 82 deletions

View File

@ -116,31 +116,72 @@ DirectoryNode *DirectoryNode::findFirstDirectoryWithMP3s()
return nullptr; return nullptr;
} }
void DirectoryNode::advanceToNextMP3() void DirectoryNode::advanceToFirstMP3InThisNode() {
if (mp3Files.size()>0) {
currentPlaying = &mp3Files[0];
}
}
DirectoryNode* DirectoryNode::advanceToNextMP3(const String* currentGlobal)
{ {
if (currentPlaying != nullptr) bool useFirst = false;
Serial.println(currentGlobal->c_str());
if (currentGlobal != nullptr)
{ {
for (size_t i = 0; i < mp3Files.size(); i++) for (size_t i = 0; i < mp3Files.size(); i++)
{ {
if (*currentPlaying == mp3Files[i]) if (*currentGlobal == mp3Files[i])
{ {
// Found the current playing MP3 file // Found the current playing MP3 file
if (i < mp3Files.size() - 1) if (i < mp3Files.size() - 1)
{ {
// Advance to the next MP3 file in the same directory // Advance to the next MP3 file in the same directory
currentPlaying = &mp3Files[i + 1]; currentPlaying = &mp3Files[i + 1];
return; return this;
}
useFirst = true;
// Reached the end of the MP3 files in the directory
break;
}
}
}
// We are either not playing, or we've exhausted all the MP3 files in this directory.
// Therefore, we need to recursively look in our subdirectories.
for (auto subdir : subdirectories)
{
Serial.println("searching next node");
if (useFirst && subdir->mp3Files.size()>0) {
subdir->currentPlaying = &subdir->mp3Files[0];
return subdir;
}
// Have each subdirectory advance its song
for (size_t i = 0; i < subdir->mp3Files.size(); i++)
{
if (*currentGlobal == subdir->mp3Files[i])
{
// Found the current playing MP3 file
if (i < subdir->mp3Files.size() - 1)
{
// Advance to the next MP3 file in the same directory
subdir->currentPlaying = &subdir->mp3Files[i + 1];
return subdir;
} else {
useFirst = true;
} }
// Reached the end of the MP3 files in the directory // Reached the end of the MP3 files in the directory
break; break;
} }
} }
} }
// If not playing or reached the end, set the first MP3 file as the current playing
if (!mp3Files.empty()) // If we get here, there were no MP3 files or subdirectories left to check
{ currentPlaying = nullptr;
currentPlaying = &mp3Files[0]; Serial.println("no more nodes found");
} return this;
} }
String DirectoryNode::getDirectoryStructureHTML() const String DirectoryNode::getDirectoryStructureHTML() const

View File

@ -26,7 +26,8 @@ public:
void addMP3File(const String& mp3File); void addMP3File(const String& mp3File);
void buildDirectoryTree(const char* currentPath); void buildDirectoryTree(const char* currentPath);
void printDirectoryTree(int level = 0) const; void printDirectoryTree(int level = 0) const;
void advanceToNextMP3(); DirectoryNode* advanceToNextMP3(const String* currentGlobal);
void advanceToFirstMP3InThisNode();
String getDirectoryStructureHTML() const; String getDirectoryStructureHTML() const;
void appendIndentation(String& html, int level) const; void appendIndentation(String& html, int level) const;
DirectoryNode* findFirstDirectoryWithMP3s(); DirectoryNode* findFirstDirectoryWithMP3s();

View File

@ -5,11 +5,10 @@ const char index_html[] PROGMEM = R"rawliteral(
<title>HannaBox</title> <title>HannaBox</title>
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<style> <style>
body { font-family: Arial; text-align: center; margin:0px auto; padding-top: 30px;} body { font-family: Arial; margin:5px auto; padding: 30px;}
.button { .button {
padding: 10px 20px; padding: 10px 20px;
font-size: 24px; font-size: 24px;
text-align: center;
outline: none; outline: none;
color: #fff; color: #fff;
background-color: #2f4468; background-color: #2f4468;
@ -36,9 +35,15 @@ const char index_html[] PROGMEM = R"rawliteral(
<body> <body>
<h1>HannaBox</h1> <h1>HannaBox</h1>
Playing %PLAYING%<br/><br/> <span id="state">%PLAYING%</span><br/><br/>
<button class="button" onmouseup="toggleCheckbox('start');" ontouchend="toggleCheckbox('start');">On!</button><br/><br/> <button class="button" onmouseup="toggleCheckbox('start');" ontouchend="toggleCheckbox('start');">Start</button><br/><br/>
<button class="button" onmouseup="toggleCheckbox('stop');" ontouchend="toggleCheckbox('stop');">Off!</button><br/><br/> <button class="button" onmouseup="toggleCheckbox('stop');" ontouchend="toggleCheckbox('stop');">Stop</button><br/><br/>
<button class="button" onmouseup="toggleCheckbox('next');" ontouchend="toggleCheckbox('next');">Next</button><br/><br/>
<p>
<h2>Content of SD Card:</h2>
%DIRECTORY%
</p>
<script> <script>
setInterval(getState, 500); setInterval(getState, 500);
function toggleCheckbox(x) { function toggleCheckbox(x) {

View File

@ -1,11 +1,11 @@
#if defined(ESP8266) #if defined(ESP8266)
#include <ESP8266WiFi.h> //https://github.com/esp8266/Arduino #include <ESP8266WiFi.h> //https://github.com/esp8266/Arduino
#else #else
#include <WiFi.h> #include <WiFi.h>
#endif #endif
#include <ESPAsyncWebServer.h> //Local WebServer used to serve the configuration portal #include <ESPAsyncWebServer.h> //Local WebServer used to serve the configuration portal
#include <ESPAsyncWiFiManager.h> //https://github.com/tzapu/WiFiManager WiFi Configuration Magic #include <ESPAsyncWiFiManager.h> //https://github.com/tzapu/WiFiManager WiFi Configuration Magic
#include "Audio.h" #include "Audio.h"
@ -30,41 +30,34 @@ Audio audio;
AsyncWebServer server(80); AsyncWebServer server(80);
DNSServer dns; DNSServer dns;
File next;
DirectoryNode rootNode("/"); DirectoryNode rootNode("/");
DirectoryNode* currentNode = NULL; DirectoryNode *currentNode = NULL;
void stop(); void stop();
void start(); void start();
void playNextMp3() void playNextMp3()
{ {
next = root.openNextFile(); stop();
if (currentNode == NULL)
if (!next)
{ {
root = SD.open("/"); currentNode = rootNode.findFirstDirectoryWithMP3s();
} if (currentNode)
while (!String(next.name()).endsWith(".mp3"))
{
next = root.openNextFile();
if (!next)
{ {
Serial.println("no more files found."); currentNode->advanceToFirstMP3InThisNode();
return;
} }
} }
Serial.print("initialized"); else
Serial.print(next.name()); {
Serial.print("\n"); currentNode = rootNode.advanceToNextMP3(currentNode->getCurrentPlaying());
audio.stopSong(); }
audio.connecttoSD(next.name()); Serial.print("Now advancing to ");
String mp3File = currentNode->getCurrentPlayingFilePath();
Serial.println(mp3File.c_str());
audio.connecttoSD(mp3File.c_str());
} }
void audio_info(const char *info) void audio_info(const char *info)
@ -72,25 +65,57 @@ void audio_info(const char *info)
// Serial.print("info "); Serial.println(info); // Serial.print("info "); Serial.println(info);
} }
String processor(const String& var) String getState()
{ {
if(var == "PLAYING") String state = String();
return F(next.name()); if (audio.isRunning())
{
state += "Playing ";
}
else
{
state += "Stopped ";
}
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();
}
return String(); return String();
} }
void stop() { void stop()
if (audio.isRunning()) { {
Serial.println("stopping audio.");
if (audio.isRunning())
{
audio.stopSong(); audio.stopSong();
} }
} }
void start() { void start()
if (!next) { {
playNextMp3(); currentNode->setCurrentPlaying(NULL);
} else { currentNode = NULL;
audio.connecttoSD(next.name()); playNextMp3();
} }
void next()
{
playNextMp3();
} }
void setup() void setup()
@ -117,56 +142,51 @@ void setup()
rootNode.buildDirectoryTree("/"); rootNode.buildDirectoryTree("/");
rootNode.printDirectoryTree(); rootNode.printDirectoryTree();
//printDirectoryTree(&rootNode, 0); // printDirectoryTree(&rootNode, 0);
// printDirectory(root, 0); // printDirectory(root, 0);
audio.setPinout(I2S_BCLK, I2S_LRC, I2S_DOUT); audio.setPinout(I2S_BCLK, I2S_LRC, I2S_DOUT);
audio.setVolume(12); // 0...21 audio.setVolume(12); // 0...21
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) server.on("/", HTTP_GET, [](AsyncWebServerRequest *request)
{ request->send_P(200, "text/html", index_html,processor); }); { request->send_P(200, "text/html", index_html, processor); });
server.on("/start", HTTP_GET, [] (AsyncWebServerRequest *request) { server.on("/state", HTTP_GET, [](AsyncWebServerRequest *request)
start(); {
request->send(200, "text/plain", "start"); String state = getState();
}); request->send(200, "text/plain", state.c_str()); });
server.on("/start", HTTP_GET, [](AsyncWebServerRequest *request)
{
request->send(200, "text/plain", "start");
start(); });
server.on("/stop", HTTP_GET, [](AsyncWebServerRequest *request)
{
request->send(200, "text/plain", "stop");
stop(); });
server.on("/next", HTTP_GET, [](AsyncWebServerRequest *request)
{
request->send(200, "text/plain", "next");
next(); });
server.on("/stop", HTTP_GET, [] (AsyncWebServerRequest *request) {
stop();
request->send(200, "text/plain", "stop");
});
server.begin(); server.begin();
} }
void loop() void loop()
{ {
audio.loop(); audio.loop();
// Serial.print(digitalRead(D2));
if (digitalRead(D3) == LOW) if (digitalRead(D3) == LOW)
{ {
unsigned long now = millis(); unsigned long now = millis();
if (now - lastStart > startDelay) if (now - lastStart > startDelay)
{ {
if (currentNode==NULL) { playNextMp3();
currentNode = rootNode.findFirstDirectoryWithMP3s();
if (currentNode) {
currentNode->advanceToNextMP3();
}
} else {
currentNode->advanceToNextMP3();
}
Serial.print("Now advancing to ");
String mp3File = currentNode->getCurrentPlayingFilePath();
Serial.println(mp3File.c_str());
audio.connecttoSD(mp3File.c_str());
Serial.println("mp3 started.");
lastStart = now; lastStart = now;
} }
} }