diff --git a/src/DirectoryNode.cpp b/src/DirectoryNode.cpp index 5bbdb6e..a0dd3a8 100644 --- a/src/DirectoryNode.cpp +++ b/src/DirectoryNode.cpp @@ -1,7 +1,11 @@ #include "DirectoryNode.h" + DirectoryNode::DirectoryNode(const String &nodeName) - : name(nodeName), currentPlaying(nullptr) {} + : name(nodeName), currentPlaying(nullptr) { + id = DirectoryNode::idCounter; + DirectoryNode::idCounter++; + } DirectoryNode::~DirectoryNode() { @@ -11,6 +15,12 @@ DirectoryNode::~DirectoryNode() } } + +const uint16_t DirectoryNode::getId() const +{ + return id; +} + const String &DirectoryNode::getName() const { return name; @@ -29,6 +39,11 @@ const std::vector &DirectoryNode::getMP3Files() const void DirectoryNode::setCurrentPlaying(const String *mp3File) { currentPlaying = mp3File; + for (int i=0;ii) { + currentPlayingId = ids[i]; + } + } } const String *DirectoryNode::getCurrentPlaying() const @@ -36,6 +51,17 @@ const String *DirectoryNode::getCurrentPlaying() const 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) { subdirectories.push_back(subdirectory); @@ -44,6 +70,7 @@ void DirectoryNode::addSubdirectory(DirectoryNode *subdirectory) void DirectoryNode::addMP3File(const String &mp3File) { mp3Files.push_back(mp3File); + ids.push_back(getNextId()); } void DirectoryNode::setSecondsPlayed(const uint32_t seconds) { @@ -65,7 +92,7 @@ void DirectoryNode::buildDirectoryTree(const char *currentPath) 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()); subdirectories.push_back(newNode); @@ -74,6 +101,7 @@ void DirectoryNode::buildDirectoryTree(const char *currentPath) else if (String(entry.name()).endsWith(".mp3")) { mp3Files.push_back(entry.name()); + ids.push_back(getNextId()); } entry.close(); } @@ -127,10 +155,43 @@ DirectoryNode *DirectoryNode::findFirstDirectoryWithMP3s() void DirectoryNode::advanceToFirstMP3InThisNode() { 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) { for (auto subdir : subdirectories) { @@ -147,7 +208,7 @@ DirectoryNode* DirectoryNode::advanceToMP3(const String* currentGlobal) { // Found the current MP3 file if (i < subdir->mp3Files.size() - 1) { - subdir->currentPlaying = &subdir->mp3Files[i]; + subdir->setCurrentPlaying(&subdir->mp3Files[i]); return subdir; } @@ -169,13 +230,13 @@ DirectoryNode* DirectoryNode::advanceToNextMP3(const String* currentGlobal) { for (size_t i = 0; i < mp3Files.size(); i++) { - if (*currentGlobal == mp3Files[i]) + if (*currentGlobal==mp3Files[i]) { // Found the current playing MP3 file if (i < mp3Files.size() - 1) { // Advance to the next MP3 file in the same directory - currentPlaying = &mp3Files[i + 1]; + setCurrentPlaying(&mp3Files[i + 1]); return this; } useFirst = true; @@ -190,10 +251,9 @@ DirectoryNode* DirectoryNode::advanceToNextMP3(const String* currentGlobal) // 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]; + subdir->setCurrentPlaying(&subdir->mp3Files[0]); return subdir; } @@ -206,7 +266,7 @@ DirectoryNode* DirectoryNode::advanceToNextMP3(const String* currentGlobal) if (i < subdir->mp3Files.size() - 1) { // Advance to the next MP3 file in the same directory - subdir->currentPlaying = &subdir->mp3Files[i + 1]; + subdir->setCurrentPlaying(&subdir->mp3Files[i + 1]); return subdir; } else { useFirst = true; @@ -232,12 +292,12 @@ String DirectoryNode::getDirectoryStructureHTML() const } if (name!="/") { - html += "
  • " + name + "
  • \n"; + html += "
  • " + name + "
  • \n"; } - for (const String &mp3File : mp3Files) + for (int i=0;i\n"; + html += "
  • " + mp3Files[i] + "
  • \n"; } for (DirectoryNode *childNode : subdirectories) diff --git a/src/DirectoryNode.h b/src/DirectoryNode.h index 0859ef9..91db6c1 100644 --- a/src/DirectoryNode.h +++ b/src/DirectoryNode.h @@ -4,36 +4,52 @@ #include #include + const String sys_dir = "system"; + + class DirectoryNode { private: + uint16_t id; String name; std::vector subdirectories; std::vector mp3Files; + std::vector ids; const String* currentPlaying; + uint16_t currentPlayingId = 0; uint16_t secondsPlayed = 0; + + + public: DirectoryNode(const String& nodeName); ~DirectoryNode(); + static uint16_t idCounter; + const String& getName() const; + const uint16_t getId() const; const std::vector& getSubdirectories() const; const std::vector& getMP3Files() const; void setCurrentPlaying(const String* mp3File); const String* getCurrentPlaying() const; + const uint16_t getCurrentPlayingId() const; void setSecondsPlayed(const uint32_t seconds); uint32_t getSecondsPlayed(); + uint16_t getNextId(); + void addSubdirectory(DirectoryNode* subdirectory); void addMP3File(const String& mp3File); void buildDirectoryTree(const char* currentPath); void printDirectoryTree(int level = 0) const; DirectoryNode* advanceToMP3(const String* currentGlobal); DirectoryNode* advanceToNextMP3(const String* currentGlobal); + DirectoryNode* advanceToMP3(const uint16_t id); void advanceToFirstMP3InThisNode(); String getDirectoryStructureHTML() const; void appendIndentation(String& html, int level) const; @@ -41,4 +57,7 @@ public: String getCurrentPlayingFilePath() const; }; + + + #endif /* DIRECTORYNODE_H_ */ diff --git a/src/WebContent.h b/src/WebContent.h index 702b75f..4f34d2a 100644 --- a/src/WebContent.h +++ b/src/WebContent.h @@ -50,8 +50,9 @@ const char index_html[] PROGMEM = R"rawliteral( // Add click event listener to each
  • element liElements.forEach(function(li) { li.addEventListener('click', function() { - var liText = this.innerText; - playNamedSong(liText); + //var liText = this.innerText; + 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) { var xhr = new XMLHttpRequest(); xhr.open("POST", "/playnamed"); diff --git a/src/globals.h b/src/globals.h index 2e82dd9..b3646f3 100644 --- a/src/globals.h +++ b/src/globals.h @@ -9,7 +9,7 @@ bool playFile(const char* filename, uint32_t resumeFilePos = 0); void loop2(void* parameter); -void named_song_action(AsyncWebServerRequest *request); +void id_song_action(AsyncWebServerRequest *request); void progress_action(AsyncWebServerRequest *request); @@ -71,12 +71,22 @@ const int startDelay = 250; String lastUid = ""; +uint16_t currentSongId = 0; + +uint32_t currentSongSeconds = 0; + +boolean continuePlaying = false; + +boolean prepareSleepMode = false; + const String sleep_sound = "sleep.mp3"; const String startup_sound = "start.mp3"; const String mapping_file = "/mapping.txt"; +const String progress_file = "/progress.txt"; + std::map rfid_map; /* @@ -86,9 +96,10 @@ const long sleepMessageDelay = 28000; const long sleepDelay = 30000; */ -const long sleepMessageDelay = 1798000; +//const long sleepMessageDelay = 1798000; +const long sleepMessageDelay = 22000; -// wait until deep sleep: -const long sleepDelay = 1800000; +// wait until deep sleep:1800000 +const long sleepDelay = 25000; #endif \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index adc7fac..1e8a680 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -58,9 +58,14 @@ uint rfid_loop = RFID_LOOP_INTERVAL; AsyncWebServer server(80); DNSServer dns; +//static variable has to be instantiated outside of class definition: +uint16_t DirectoryNode::idCounter = 0; + DirectoryNode rootNode("/"); DirectoryNode *currentNode = NULL; + + volatile bool newRfidInt = false; 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) { currentNode = rootNode.advanceToMP3(&song); @@ -203,6 +226,39 @@ void unmute() 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", ¤tSongId, ¤tSongSeconds); + Serial.println("Data read from file: " + data); + return true; + } else { + Serial.println("Error opening file for reading."); + return false; + } +} + String getState() { DynamicJsonDocument jsonState(1024); @@ -305,7 +361,7 @@ void next() void audio_eof_mp3(const char *info) { Serial.println("audio file ended."); - if (continuousMode) + if (continuousMode && !prepareSleepMode) playNextMp3(); } @@ -364,6 +420,16 @@ void setup() rootNode.printDirectoryTree(); 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(); activateRFID(); @@ -438,7 +504,7 @@ void setup() request->send(200, "text/plain", "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); @@ -466,23 +532,24 @@ void setup() 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(); for (int i = 0; i < params; 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(); request->send_P(200, "text/plain", "ok"); } + void progress_action(AsyncWebServerRequest *request) { Serial.println("progress!"); @@ -529,13 +596,10 @@ void loop() deactivateRFID(); activateSD(); audio.loop(); - if (currentNode != NULL) + if (currentNode != NULL && !prepareSleepMode) { currentNode->setSecondsPlayed(audio.getAudioCurrentTime()); } - - deactivateSD(); - activateRFID(); } else if (asyncStart) { @@ -543,7 +607,11 @@ void loop() start(); } - if (!startupSoundPlayed) { + if (continuePlaying) { + continuePlaying = false; + startupSoundPlayed = true; + playSongById(currentSongId,currentSongSeconds); + } else if (!startupSoundPlayed) { startupSoundPlayed = true; String tempPath = "/"+sys_dir+"/"+startup_sound; playSongByPath(tempPath.c_str()); @@ -554,8 +622,15 @@ void loop() // send device to sleep: long now = millis(); - if (!sleepSoundPlayed && now - lastInteraction > sleepMessageDelay) { + if (!sleepSoundPlayed && now - lastInteraction > sleepMessageDelay) { 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; playSongByPath(tempPath.c_str()); } @@ -563,7 +638,7 @@ void loop() if (now - lastInteraction > sleepDelay) { Serial.println("entering deep sleep..."); - deactivateRFID(); + deactivateRFID(); deactivateSD(); esp_deep_sleep_start(); }