ids, resume function

This commit is contained in:
Stefan Ostermann 2023-10-21 21:54:56 +02:00
parent 578281d9d1
commit 9c4b1d4913
5 changed files with 213 additions and 31 deletions

View File

@ -1,7 +1,11 @@
#include "DirectoryNode.h" #include "DirectoryNode.h"
DirectoryNode::DirectoryNode(const String &nodeName) DirectoryNode::DirectoryNode(const String &nodeName)
: name(nodeName), currentPlaying(nullptr) {} : name(nodeName), currentPlaying(nullptr) {
id = DirectoryNode::idCounter;
DirectoryNode::idCounter++;
}
DirectoryNode::~DirectoryNode() DirectoryNode::~DirectoryNode()
{ {
@ -11,6 +15,12 @@ DirectoryNode::~DirectoryNode()
} }
} }
const uint16_t DirectoryNode::getId() const
{
return id;
}
const String &DirectoryNode::getName() const const String &DirectoryNode::getName() const
{ {
return name; return name;
@ -29,6 +39,11 @@ const std::vector<String> &DirectoryNode::getMP3Files() const
void DirectoryNode::setCurrentPlaying(const String *mp3File) void DirectoryNode::setCurrentPlaying(const String *mp3File)
{ {
currentPlaying = mp3File; currentPlaying = mp3File;
for (int i=0;i<mp3Files.size();i++) {
if (mp3Files[i] == *mp3File && ids.size()>i) {
currentPlayingId = ids[i];
}
}
} }
const String *DirectoryNode::getCurrentPlaying() const const String *DirectoryNode::getCurrentPlaying() const
@ -36,6 +51,17 @@ const String *DirectoryNode::getCurrentPlaying() const
return currentPlaying; return currentPlaying;
} }
const uint16_t DirectoryNode::getCurrentPlayingId() const
{
return currentPlayingId;
}
uint16_t DirectoryNode::getNextId() {
uint16_t next = DirectoryNode::idCounter;
DirectoryNode::idCounter++;
return next;
}
void DirectoryNode::addSubdirectory(DirectoryNode *subdirectory) void DirectoryNode::addSubdirectory(DirectoryNode *subdirectory)
{ {
subdirectories.push_back(subdirectory); subdirectories.push_back(subdirectory);
@ -44,6 +70,7 @@ void DirectoryNode::addSubdirectory(DirectoryNode *subdirectory)
void DirectoryNode::addMP3File(const String &mp3File) void DirectoryNode::addMP3File(const String &mp3File)
{ {
mp3Files.push_back(mp3File); mp3Files.push_back(mp3File);
ids.push_back(getNextId());
} }
void DirectoryNode::setSecondsPlayed(const uint32_t seconds) { void DirectoryNode::setSecondsPlayed(const uint32_t seconds) {
@ -65,7 +92,7 @@ void DirectoryNode::buildDirectoryTree(const char *currentPath)
break; break;
} }
if (entry.isDirectory() && strcmp(entry.name(),sys_dir.c_str())) if (entry.isDirectory() && entry.name()[0] != '.' && strcmp(entry.name(),sys_dir.c_str()))
{ {
DirectoryNode *newNode = new DirectoryNode(entry.name()); DirectoryNode *newNode = new DirectoryNode(entry.name());
subdirectories.push_back(newNode); subdirectories.push_back(newNode);
@ -74,6 +101,7 @@ void DirectoryNode::buildDirectoryTree(const char *currentPath)
else if (String(entry.name()).endsWith(".mp3")) else if (String(entry.name()).endsWith(".mp3"))
{ {
mp3Files.push_back(entry.name()); mp3Files.push_back(entry.name());
ids.push_back(getNextId());
} }
entry.close(); entry.close();
} }
@ -127,10 +155,43 @@ DirectoryNode *DirectoryNode::findFirstDirectoryWithMP3s()
void DirectoryNode::advanceToFirstMP3InThisNode() { void DirectoryNode::advanceToFirstMP3InThisNode() {
if (mp3Files.size()>0) { if (mp3Files.size()>0) {
currentPlaying = &mp3Files[0]; setCurrentPlaying(&mp3Files[0]);
} }
} }
DirectoryNode* DirectoryNode::advanceToMP3(const uint16_t id) {
for (auto subdir : subdirectories)
{
if (subdir->getId()==id) {
subdir->advanceToFirstMP3InThisNode();
return subdir;
}
// Have each subdirectory advance its song
for (size_t i = 0; i < subdir->ids.size(); i++)
{
if (id == subdir->ids[i])
{
// Found the current MP3 file
if (i < subdir->mp3Files.size() - 1)
{
subdir->currentPlaying = &subdir->mp3Files[i];
subdir->currentPlayingId = id;
return subdir;
}
}
}
}
// If we get here, there were no MP3 files or subdirectories left to check
currentPlaying = nullptr;
Serial.println("no more nodes found");
return this;
}
DirectoryNode* DirectoryNode::advanceToMP3(const String* currentGlobal) { DirectoryNode* DirectoryNode::advanceToMP3(const String* currentGlobal) {
for (auto subdir : subdirectories) for (auto subdir : subdirectories)
{ {
@ -147,7 +208,7 @@ DirectoryNode* DirectoryNode::advanceToMP3(const String* currentGlobal) {
// Found the current MP3 file // Found the current MP3 file
if (i < subdir->mp3Files.size() - 1) if (i < subdir->mp3Files.size() - 1)
{ {
subdir->currentPlaying = &subdir->mp3Files[i]; subdir->setCurrentPlaying(&subdir->mp3Files[i]);
return subdir; return subdir;
} }
@ -169,13 +230,13 @@ DirectoryNode* DirectoryNode::advanceToNextMP3(const String* currentGlobal)
{ {
for (size_t i = 0; i < mp3Files.size(); i++) for (size_t i = 0; i < mp3Files.size(); i++)
{ {
if (*currentGlobal == 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]; setCurrentPlaying(&mp3Files[i + 1]);
return this; return this;
} }
useFirst = true; useFirst = true;
@ -190,10 +251,9 @@ DirectoryNode* DirectoryNode::advanceToNextMP3(const String* currentGlobal)
// Therefore, we need to recursively look in our subdirectories. // Therefore, we need to recursively look in our subdirectories.
for (auto subdir : subdirectories) for (auto subdir : subdirectories)
{ {
Serial.println("searching next node");
if (useFirst && subdir->mp3Files.size()>0) { if (useFirst && subdir->mp3Files.size()>0) {
subdir->currentPlaying = &subdir->mp3Files[0]; subdir->setCurrentPlaying(&subdir->mp3Files[0]);
return subdir; return subdir;
} }
@ -206,7 +266,7 @@ DirectoryNode* DirectoryNode::advanceToNextMP3(const String* currentGlobal)
if (i < subdir->mp3Files.size() - 1) if (i < subdir->mp3Files.size() - 1)
{ {
// Advance to the next MP3 file in the same directory // Advance to the next MP3 file in the same directory
subdir->currentPlaying = &subdir->mp3Files[i + 1]; subdir->setCurrentPlaying(&subdir->mp3Files[i + 1]);
return subdir; return subdir;
} else { } else {
useFirst = true; useFirst = true;
@ -232,12 +292,12 @@ String DirectoryNode::getDirectoryStructureHTML() const
} }
if (name!="/") { if (name!="/") {
html += "<li><b>" + name + "</b></li>\n"; html += "<li data-id=\""+String(id)+"\"><b>" + name + "</b></li>\n";
} }
for (const String &mp3File : mp3Files) for (int i=0;i<mp3Files.size();i++)
{ {
html += "<li>" + mp3File + "</li>\n"; html += "<li data-id=\""+String(ids[i])+"\">" + mp3Files[i] + "</li>\n";
} }
for (DirectoryNode *childNode : subdirectories) for (DirectoryNode *childNode : subdirectories)

View File

@ -4,36 +4,52 @@
#include <SD.h> #include <SD.h>
#include <vector> #include <vector>
const String sys_dir = "system"; const String sys_dir = "system";
class DirectoryNode { class DirectoryNode {
private: private:
uint16_t id;
String name; String name;
std::vector<DirectoryNode*> subdirectories; std::vector<DirectoryNode*> subdirectories;
std::vector<String> mp3Files; std::vector<String> mp3Files;
std::vector<uint16_t> ids;
const String* currentPlaying; const String* currentPlaying;
uint16_t currentPlayingId = 0;
uint16_t secondsPlayed = 0; uint16_t secondsPlayed = 0;
public: public:
DirectoryNode(const String& nodeName); DirectoryNode(const String& nodeName);
~DirectoryNode(); ~DirectoryNode();
static uint16_t idCounter;
const String& getName() const; const String& getName() const;
const uint16_t getId() const;
const std::vector<DirectoryNode*>& getSubdirectories() const; const std::vector<DirectoryNode*>& getSubdirectories() const;
const std::vector<String>& getMP3Files() const; const std::vector<String>& getMP3Files() const;
void setCurrentPlaying(const String* mp3File); void setCurrentPlaying(const String* mp3File);
const String* getCurrentPlaying() const; const String* getCurrentPlaying() const;
const uint16_t getCurrentPlayingId() const;
void setSecondsPlayed(const uint32_t seconds); void setSecondsPlayed(const uint32_t seconds);
uint32_t getSecondsPlayed(); uint32_t getSecondsPlayed();
uint16_t getNextId();
void addSubdirectory(DirectoryNode* subdirectory); void addSubdirectory(DirectoryNode* subdirectory);
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;
DirectoryNode* advanceToMP3(const String* currentGlobal); DirectoryNode* advanceToMP3(const String* currentGlobal);
DirectoryNode* advanceToNextMP3(const String* currentGlobal); DirectoryNode* advanceToNextMP3(const String* currentGlobal);
DirectoryNode* advanceToMP3(const uint16_t id);
void advanceToFirstMP3InThisNode(); void advanceToFirstMP3InThisNode();
String getDirectoryStructureHTML() const; String getDirectoryStructureHTML() const;
void appendIndentation(String& html, int level) const; void appendIndentation(String& html, int level) const;
@ -41,4 +57,7 @@ public:
String getCurrentPlayingFilePath() const; String getCurrentPlayingFilePath() const;
}; };
#endif /* DIRECTORYNODE_H_ */ #endif /* DIRECTORYNODE_H_ */

View File

@ -50,8 +50,9 @@ const char index_html[] PROGMEM = R"rawliteral(
// Add click event listener to each <li> element // Add click event listener to each <li> element
liElements.forEach(function(li) { liElements.forEach(function(li) {
li.addEventListener('click', function() { li.addEventListener('click', function() {
var liText = this.innerText; //var liText = this.innerText;
playNamedSong(liText); var id = this.dataset.id;
playSongById(id);
}); });
}); });
@ -109,6 +110,22 @@ const char index_html[] PROGMEM = R"rawliteral(
} }
function playSongById(id) {
var url = "/playbyid";
var params = "id="+id;
var http = new XMLHttpRequest();
http.open("GET", url+"?"+params, true);
http.onreadystatechange = function()
{
if(http.readyState == 4 && http.status == 200) {
}
}
http.send(null);
}
function playNamedSong(song) { function playNamedSong(song) {
var xhr = new XMLHttpRequest(); var xhr = new XMLHttpRequest();
xhr.open("POST", "/playnamed"); xhr.open("POST", "/playnamed");

View File

@ -9,7 +9,7 @@ bool playFile(const char* filename, uint32_t resumeFilePos = 0);
void loop2(void* parameter); void loop2(void* parameter);
void named_song_action(AsyncWebServerRequest *request); void id_song_action(AsyncWebServerRequest *request);
void progress_action(AsyncWebServerRequest *request); void progress_action(AsyncWebServerRequest *request);
@ -71,12 +71,22 @@ const int startDelay = 250;
String lastUid = ""; String lastUid = "";
uint16_t currentSongId = 0;
uint32_t currentSongSeconds = 0;
boolean continuePlaying = false;
boolean prepareSleepMode = false;
const String sleep_sound = "sleep.mp3"; const String sleep_sound = "sleep.mp3";
const String startup_sound = "start.mp3"; const String startup_sound = "start.mp3";
const String mapping_file = "/mapping.txt"; const String mapping_file = "/mapping.txt";
const String progress_file = "/progress.txt";
std::map<String, String> rfid_map; std::map<String, String> rfid_map;
/* /*
@ -86,9 +96,10 @@ const long sleepMessageDelay = 28000;
const long sleepDelay = 30000; const long sleepDelay = 30000;
*/ */
const long sleepMessageDelay = 1798000; //const long sleepMessageDelay = 1798000;
const long sleepMessageDelay = 22000;
// wait until deep sleep: // wait until deep sleep:1800000
const long sleepDelay = 1800000; const long sleepDelay = 25000;
#endif #endif

View File

@ -58,9 +58,14 @@ uint rfid_loop = RFID_LOOP_INTERVAL;
AsyncWebServer server(80); AsyncWebServer server(80);
DNSServer dns; DNSServer dns;
//static variable has to be instantiated outside of class definition:
uint16_t DirectoryNode::idCounter = 0;
DirectoryNode rootNode("/"); DirectoryNode rootNode("/");
DirectoryNode *currentNode = NULL; DirectoryNode *currentNode = NULL;
volatile bool newRfidInt = false; volatile bool newRfidInt = false;
MFRC522 rfid(CS_RFID, RST_RFID); // instatiate a MFRC522 reader object. MFRC522 rfid(CS_RFID, RST_RFID); // instatiate a MFRC522 reader object.
@ -109,6 +114,24 @@ uint32_t getBatteryVoltageMv() {
} }
void playSongById(uint16_t id, uint32_t continueSeconds = 0)
{
currentNode = rootNode.advanceToMP3(id);
String mp3File = currentNode->getCurrentPlayingFilePath();
Serial.print("playing by id: ");
Serial.print(id);Serial.print(" ");Serial.println(continueSeconds);
Serial.println(mp3File.c_str());
deactivateRFID();
activateSD();
playFile(mp3File.c_str());
if (continueSeconds!=0) {
audio.setAudioPlayPosition(continueSeconds);
}
activateRFID();
deactivateSD();
}
void playSongByName(String song) void playSongByName(String song)
{ {
currentNode = rootNode.advanceToMP3(&song); currentNode = rootNode.advanceToMP3(&song);
@ -203,6 +226,39 @@ void unmute()
audio.setVolume(volume); audio.setVolume(volume);
} }
void writeSongProgress(const char *filename, uint16_t id, uint32_t seconds) {
File file = SD.open(filename, FILE_WRITE);
if (file) {
file.print(id);
file.print(" ");
file.println(seconds);
file.close();
Serial.println("Data written to file: ID-" + String(id) + ", Number-" + String(seconds));
} else {
Serial.println("Error opening file for writing.");
}
}
boolean readSongProgress(const char *filename) {
String data = "";
File file = SD.open(filename);
if (file) {
while (file.available()) {
char character = file.read();
data += character;
}
file.close();
// Parse the string into ID and number
sscanf(data.c_str(), "%d %d", &currentSongId, &currentSongSeconds);
Serial.println("Data read from file: " + data);
return true;
} else {
Serial.println("Error opening file for reading.");
return false;
}
}
String getState() String getState()
{ {
DynamicJsonDocument jsonState(1024); DynamicJsonDocument jsonState(1024);
@ -305,7 +361,7 @@ void next()
void audio_eof_mp3(const char *info) void audio_eof_mp3(const char *info)
{ {
Serial.println("audio file ended."); Serial.println("audio file ended.");
if (continuousMode) if (continuousMode && !prepareSleepMode)
playNextMp3(); playNextMp3();
} }
@ -364,6 +420,16 @@ void setup()
rootNode.printDirectoryTree(); rootNode.printDirectoryTree();
readDataFromFile(mapping_file.c_str()); readDataFromFile(mapping_file.c_str());
String progressPath = "/"+sys_dir+"/"+progress_file;
continuePlaying = readSongProgress(progressPath.c_str());
if (continuePlaying) {
Serial.print("deleting ");
Serial.println(progressPath.c_str());
SD.remove(progressPath.c_str());
}
deactivateSD(); deactivateSD();
activateRFID(); activateRFID();
@ -438,7 +504,7 @@ void setup()
request->send(200, "text/plain", "next"); request->send(200, "text/plain", "next");
next(); }); next(); });
server.on("/playnamed", HTTP_POST, named_song_action); server.on("/playbyid", HTTP_GET, id_song_action);
server.on("/progress", HTTP_POST, progress_action); server.on("/progress", HTTP_POST, progress_action);
@ -466,23 +532,24 @@ void setup()
Serial.println("initialization done."); Serial.println("initialization done.");
} }
void named_song_action(AsyncWebServerRequest *request) void id_song_action(AsyncWebServerRequest *request)
{ {
Serial.println("named song!"); Serial.println("song by id!");
int params = request->params(); int params = request->params();
for (int i = 0; i < params; i++) for (int i = 0; i < params; i++)
{ {
AsyncWebParameter *p = request->getParam(i); AsyncWebParameter *p = request->getParam(i);
if (p->name() == "title") if (p->name() == "id")
{ {
playSongByName(p->value()); playSongById(atoi(p->value().c_str()));
} }
} }
lastInteraction = millis(); lastInteraction = millis();
request->send_P(200, "text/plain", "ok"); request->send_P(200, "text/plain", "ok");
} }
void progress_action(AsyncWebServerRequest *request) void progress_action(AsyncWebServerRequest *request)
{ {
Serial.println("progress!"); Serial.println("progress!");
@ -529,13 +596,10 @@ void loop()
deactivateRFID(); deactivateRFID();
activateSD(); activateSD();
audio.loop(); audio.loop();
if (currentNode != NULL) if (currentNode != NULL && !prepareSleepMode)
{ {
currentNode->setSecondsPlayed(audio.getAudioCurrentTime()); currentNode->setSecondsPlayed(audio.getAudioCurrentTime());
} }
deactivateSD();
activateRFID();
} }
else if (asyncStart) else if (asyncStart)
{ {
@ -543,7 +607,11 @@ void loop()
start(); start();
} }
if (!startupSoundPlayed) { if (continuePlaying) {
continuePlaying = false;
startupSoundPlayed = true;
playSongById(currentSongId,currentSongSeconds);
} else if (!startupSoundPlayed) {
startupSoundPlayed = true; startupSoundPlayed = true;
String tempPath = "/"+sys_dir+"/"+startup_sound; String tempPath = "/"+sys_dir+"/"+startup_sound;
playSongByPath(tempPath.c_str()); playSongByPath(tempPath.c_str());
@ -554,8 +622,15 @@ void loop()
// send device to sleep: // send device to sleep:
long now = millis(); long now = millis();
if (!sleepSoundPlayed && now - lastInteraction > sleepMessageDelay) { if (!sleepSoundPlayed && now - lastInteraction > sleepMessageDelay) {
sleepSoundPlayed = true; sleepSoundPlayed = true;
prepareSleepMode = true;
if (currentNode != NULL) {
String progressPath = "/"+sys_dir+"/"+progress_file;
writeSongProgress(progressPath.c_str(),currentNode->getCurrentPlayingId(),currentNode->getSecondsPlayed());
}
String tempPath = "/"+sys_dir+"/"+sleep_sound; String tempPath = "/"+sys_dir+"/"+sleep_sound;
playSongByPath(tempPath.c_str()); playSongByPath(tempPath.c_str());
} }
@ -563,7 +638,7 @@ void loop()
if (now - lastInteraction > sleepDelay) { if (now - lastInteraction > sleepDelay) {
Serial.println("entering deep sleep..."); Serial.println("entering deep sleep...");
deactivateRFID(); deactivateRFID();
deactivateSD(); deactivateSD();
esp_deep_sleep_start(); esp_deep_sleep_start();
} }