From 2dd95a35eb1d90c2a73cdde135ae131b1425be4a Mon Sep 17 00:00:00 2001 From: Stefan Ostermann Date: Sun, 4 Jun 2023 20:41:59 +0200 Subject: [PATCH] DirectoryNode structure --- README.md | 13 ++- src/DirectoryNode.cpp | 194 ++++++++++++++++++++++++++++++++++++++++++ src/DirectoryNode.h | 36 ++++++++ src/helper.h | 92 ++++++++++++++++++++ src/main.cpp | 67 ++++++--------- 5 files changed, 359 insertions(+), 43 deletions(-) create mode 100644 src/DirectoryNode.cpp create mode 100644 src/DirectoryNode.h create mode 100644 src/helper.h diff --git a/README.md b/README.md index 53526ee..5cae354 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,17 @@ # HannaBox -Manual install of wifi manager download zip in project dir using vs console: - pio lib install ~/Downloads/WiFiManager-2.0.16-rc.2.zip +## Microcontroller +D1 Mini ESP32 +Due to mp3 playback it needs a multicore esp 32: + +This library only works on multi-core ESP32 chips like the ESP32-S3. It does not work on the ESP32-S2 or the ESP32-C3 warning ## Pins + +``` Amplifier: -RX -> DIN +25 -> DIN D8 -> BCLK D4 -> LRC 5V -> Vin @@ -19,6 +24,8 @@ CS -> D1 MOSI -> D7 CLK -> D5 MISO -> D6 +``` + ## Links diff --git a/src/DirectoryNode.cpp b/src/DirectoryNode.cpp new file mode 100644 index 0000000..c0f3b76 --- /dev/null +++ b/src/DirectoryNode.cpp @@ -0,0 +1,194 @@ +#include "DirectoryNode.h" + +DirectoryNode::DirectoryNode(const String &nodeName) + : name(nodeName), currentPlaying(nullptr) {} + +DirectoryNode::~DirectoryNode() +{ + for (DirectoryNode *childNode : subdirectories) + { + delete childNode; + } +} + +const String &DirectoryNode::getName() const +{ + return name; +} + +const std::vector &DirectoryNode::getSubdirectories() const +{ + return subdirectories; +} + +const std::vector &DirectoryNode::getMP3Files() const +{ + return mp3Files; +} + +void DirectoryNode::setCurrentPlaying(const String *mp3File) +{ + currentPlaying = mp3File; +} + +const String *DirectoryNode::getCurrentPlaying() const +{ + return currentPlaying; +} + +void DirectoryNode::addSubdirectory(DirectoryNode *subdirectory) +{ + subdirectories.push_back(subdirectory); +} + +void DirectoryNode::addMP3File(const String &mp3File) +{ + mp3Files.push_back(mp3File); +} + +void DirectoryNode::buildDirectoryTree(const char *currentPath) +{ + File rootDir = SD.open(currentPath); + while (true) + { + File entry = rootDir.openNextFile(); + if (!entry) + { + break; + } + if (entry.isDirectory()) + { + DirectoryNode *newNode = new DirectoryNode(entry.name()); + subdirectories.push_back(newNode); + newNode->buildDirectoryTree((String(currentPath) + entry.name()).c_str()); + } + else if (String(entry.name()).endsWith(".mp3")) + { + mp3Files.push_back(entry.name()); + } + entry.close(); + } + rootDir.close(); +} + +void DirectoryNode::printDirectoryTree(int level) const +{ + for (int i = 0; i < level; i++) + { + Serial.print(" "); + } + Serial.println(name); + + for (const String &mp3File : mp3Files) + { + for (int i = 0; i <= level; i++) + { + Serial.print(" "); + } + Serial.println(mp3File); + } + + for (DirectoryNode *childNode : subdirectories) + { + childNode->printDirectoryTree(level + 1); + } +} + +DirectoryNode *DirectoryNode::findFirstDirectoryWithMP3s() +{ + if (!mp3Files.empty()) + { + // Found a directory with MP3 files + return this; + } + + for (DirectoryNode *subdirectory : subdirectories) + { + DirectoryNode *result = subdirectory->findFirstDirectoryWithMP3s(); + if (result != nullptr) + { + // Found a directory with MP3 files in the subdirectories + return result; + } + } + + // No directory with MP3 files found + return nullptr; +} + +void DirectoryNode::advanceToNextMP3() +{ + if (currentPlaying != nullptr) + { + for (size_t i = 0; i < mp3Files.size(); i++) + { + if (*currentPlaying == 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]; + return; + } + // Reached the end of the MP3 files in the directory + break; + } + } + } + // If not playing or reached the end, set the first MP3 file as the current playing + if (!mp3Files.empty()) + { + currentPlaying = &mp3Files[0]; + } +} + +String DirectoryNode::getDirectoryStructureHTML() const +{ + String html; + html.reserve(1024); // Reserve memory for better performance + + appendIndentation(html, 0); + html += "
    \n"; + appendIndentation(html, 1); + html += "
  • " + name + "
  • \n"; + + for (const String &mp3File : mp3Files) + { + appendIndentation(html, 2); + html += "
  • " + mp3File + "
  • \n"; + } + + for (DirectoryNode *childNode : subdirectories) + { + html += childNode->getDirectoryStructureHTML(); + } + + appendIndentation(html, 0); + html += "
\n"; + + return html; +} + +void DirectoryNode::appendIndentation(String &html, int level) const +{ + for (int i = 0; i < level; i++) + { + html += " "; + } +} + +String DirectoryNode::getCurrentPlayingFilePath() const +{ + if (currentPlaying != nullptr) + { + String filePath = "/" + name; + if (!filePath.endsWith("/")) + { + filePath += "/"; + } + filePath += *currentPlaying; + return filePath; + } + return ""; +} diff --git a/src/DirectoryNode.h b/src/DirectoryNode.h new file mode 100644 index 0000000..20dacdd --- /dev/null +++ b/src/DirectoryNode.h @@ -0,0 +1,36 @@ +#ifndef DIRECTORYNODE_H_ +#define DIRECTORYNODE_H_ + +#include +#include + +class DirectoryNode { +private: + String name; + std::vector subdirectories; + std::vector mp3Files; + const String* currentPlaying; + +public: + DirectoryNode(const String& nodeName); + ~DirectoryNode(); + + const String& getName() const; + const std::vector& getSubdirectories() const; + const std::vector& getMP3Files() const; + + void setCurrentPlaying(const String* mp3File); + const String* getCurrentPlaying() const; + + void addSubdirectory(DirectoryNode* subdirectory); + void addMP3File(const String& mp3File); + void buildDirectoryTree(const char* currentPath); + void printDirectoryTree(int level = 0) const; + void advanceToNextMP3(); + String getDirectoryStructureHTML() const; + void appendIndentation(String& html, int level) const; + DirectoryNode* findFirstDirectoryWithMP3s(); + String getCurrentPlayingFilePath() const; +}; + +#endif /* DIRECTORYNODE_H_ */ diff --git a/src/helper.h b/src/helper.h new file mode 100644 index 0000000..27de02c --- /dev/null +++ b/src/helper.h @@ -0,0 +1,92 @@ +#ifndef HELPER_H_ +#define HELPER_H_ + +#include + + +struct DirectoryNode { + String name; + std::vector subdirectories; + std::vector mp3Files; + String* currentPlaying; +}; + + + + + +void buildDirectoryTree(DirectoryNode* currentNode, const char* currentPath) { + File root = SD.open(currentPath); + while (true) { + File entry = root.openNextFile(); + if (!entry) { + break; + } + if (entry.isDirectory()) { + DirectoryNode* newNode = new DirectoryNode; + newNode->name = String(currentPath) + entry.name(); + currentNode->subdirectories.push_back(newNode); + buildDirectoryTree(newNode, (String(currentPath) + entry.name()).c_str()); + } else if (String(entry.name()).endsWith(".mp3")) { + currentNode->mp3Files.push_back(entry.name()); + } + entry.close(); + } + root.close(); +} + +void printDirectoryTree(DirectoryNode* currentNode, int level = 0) { + for (int i = 0; i < level; i++) { + Serial.print(" "); + } + Serial.println(currentNode->name); + + for (const String& mp3File : currentNode->mp3Files) { + for (int i = 0; i <= level; i++) { + Serial.print(" "); + } + Serial.println(mp3File); + } + + for (DirectoryNode* childNode : currentNode->subdirectories) { + printDirectoryTree(childNode, level + 1); + } +} + +void advanceToNextMP3(DirectoryNode* currentNode) { + if (currentNode->currentPlaying != nullptr) { + for (size_t i = 0; i < currentNode->mp3Files.size(); i++) { + if (*currentNode->currentPlaying == currentNode->mp3Files[i]) { + // Found the current playing MP3 file + if (i < currentNode->mp3Files.size() - 1) { + // Advance to the next MP3 file in the same directory + currentNode->currentPlaying = ¤tNode->mp3Files[i + 1]; + return; + } + // Reached the end of the MP3 files in the directory + break; + } + } + } + // If not playing or reached the end, set the first MP3 file as the current playing + if (!currentNode->mp3Files.empty()) { + currentNode->currentPlaying = ¤tNode->mp3Files[0]; + } +} + +DirectoryNode* findFirstMP3Node(DirectoryNode* currentNode) { + if (!currentNode->mp3Files.empty()) { + return currentNode; + } + + for (DirectoryNode* subdirectory : currentNode->subdirectories) { + DirectoryNode* tempNode = findFirstMP3Node(subdirectory); + if (!tempNode->mp3Files.empty()) { + return tempNode; + } + } + + return NULL; +} + +#endif /* HELPER_H_ */ \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index fdd3c0e..928b41b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -17,26 +17,30 @@ #include #include "WebContent.h" +#include "DirectoryNode.h" File root; File mp3File; -void printDirectory(File dir, int numTabs); - unsigned long lastStart = 0; const int startDelay = 250; Audio audio; -// Set web server port number to 80 AsyncWebServer server(80); -AsyncWebServer server2(81); DNSServer dns; File next; -// Variable to store the HTTP request -String header; + + +DirectoryNode rootNode("/"); +DirectoryNode* currentNode = NULL; + +void stop(); + +void start(); + void playNextMp3() { @@ -111,7 +115,11 @@ void setup() } Serial.println("SD initialization done."); - root = SD.open("/"); + rootNode.buildDirectoryTree("/"); + rootNode.printDirectoryTree(); + //printDirectoryTree(&rootNode, 0); + + // printDirectory(root, 0); audio.setPinout(I2S_BCLK, I2S_LRC, I2S_DOUT); audio.setVolume(12); // 0...21 @@ -129,44 +137,12 @@ void setup() request->send(200, "text/plain", "stop"); }); - server.begin(); } -void printDirectory(File dir, int numTabs) -{ - while (true) - { - - File entry = dir.openNextFile(); - if (!entry) - { - // no more files - break; - } - for (uint8_t i = 0; i < numTabs; i++) - { - Serial.print('\t'); - } - Serial.print(entry.name()); - if (entry.isDirectory()) - { - Serial.println("/"); - printDirectory(entry, numTabs + 1); - } - else - { - // files have sizes, directories do not - Serial.print("\t\t"); - Serial.println(entry.size(), DEC); - } - entry.close(); - } -} - void loop() { @@ -178,7 +154,18 @@ void loop() unsigned long now = millis(); if (now - lastStart > startDelay) { - playNextMp3(); + if (currentNode==NULL) { + 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; }