Compare commits
2 Commits
2b97967326
...
f173272f8b
| Author | SHA1 | Date |
|---|---|---|
|
|
f173272f8b | |
|
|
2dd95a35eb |
13
README.md
13
README.md
|
|
@ -1,12 +1,17 @@
|
||||||
# HannaBox
|
# 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
|
## Pins
|
||||||
|
|
||||||
|
```
|
||||||
Amplifier:
|
Amplifier:
|
||||||
RX -> DIN
|
25 -> DIN
|
||||||
D8 -> BCLK
|
D8 -> BCLK
|
||||||
D4 -> LRC
|
D4 -> LRC
|
||||||
5V -> Vin
|
5V -> Vin
|
||||||
|
|
@ -19,6 +24,8 @@ CS -> D1
|
||||||
MOSI -> D7
|
MOSI -> D7
|
||||||
CLK -> D5
|
CLK -> D5
|
||||||
MISO -> D6
|
MISO -> D6
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
## Links
|
## Links
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,235 @@
|
||||||
|
#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 *> &DirectoryNode::getSubdirectories() const
|
||||||
|
{
|
||||||
|
return subdirectories;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::vector<String> &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::advanceToFirstMP3InThisNode() {
|
||||||
|
if (mp3Files.size()>0) {
|
||||||
|
currentPlaying = &mp3Files[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DirectoryNode* DirectoryNode::advanceToNextMP3(const String* currentGlobal)
|
||||||
|
{
|
||||||
|
bool useFirst = false;
|
||||||
|
Serial.println(currentGlobal->c_str());
|
||||||
|
if (currentGlobal != nullptr)
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < mp3Files.size(); 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];
|
||||||
|
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
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we get here, there were no MP3 files or subdirectories left to check
|
||||||
|
currentPlaying = nullptr;
|
||||||
|
Serial.println("no more nodes found");
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
String DirectoryNode::getDirectoryStructureHTML() const
|
||||||
|
{
|
||||||
|
String html;
|
||||||
|
html.reserve(1024); // Reserve memory for better performance
|
||||||
|
|
||||||
|
appendIndentation(html, 0);
|
||||||
|
html += "<ul>\n";
|
||||||
|
appendIndentation(html, 1);
|
||||||
|
html += "<li>" + name + "</li>\n";
|
||||||
|
|
||||||
|
for (const String &mp3File : mp3Files)
|
||||||
|
{
|
||||||
|
appendIndentation(html, 2);
|
||||||
|
html += "<li>" + mp3File + "</li>\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
for (DirectoryNode *childNode : subdirectories)
|
||||||
|
{
|
||||||
|
html += childNode->getDirectoryStructureHTML();
|
||||||
|
}
|
||||||
|
|
||||||
|
appendIndentation(html, 0);
|
||||||
|
html += "</ul>\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 "";
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,37 @@
|
||||||
|
#ifndef DIRECTORYNODE_H_
|
||||||
|
#define DIRECTORYNODE_H_
|
||||||
|
|
||||||
|
#include <SD.h>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
class DirectoryNode {
|
||||||
|
private:
|
||||||
|
String name;
|
||||||
|
std::vector<DirectoryNode*> subdirectories;
|
||||||
|
std::vector<String> mp3Files;
|
||||||
|
const String* currentPlaying;
|
||||||
|
|
||||||
|
public:
|
||||||
|
DirectoryNode(const String& nodeName);
|
||||||
|
~DirectoryNode();
|
||||||
|
|
||||||
|
const String& getName() const;
|
||||||
|
const std::vector<DirectoryNode*>& getSubdirectories() const;
|
||||||
|
const std::vector<String>& 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;
|
||||||
|
DirectoryNode* advanceToNextMP3(const String* currentGlobal);
|
||||||
|
void advanceToFirstMP3InThisNode();
|
||||||
|
String getDirectoryStructureHTML() const;
|
||||||
|
void appendIndentation(String& html, int level) const;
|
||||||
|
DirectoryNode* findFirstDirectoryWithMP3s();
|
||||||
|
String getCurrentPlayingFilePath() const;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* DIRECTORYNODE_H_ */
|
||||||
|
|
@ -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) {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,92 @@
|
||||||
|
#ifndef HELPER_H_
|
||||||
|
#define HELPER_H_
|
||||||
|
|
||||||
|
#include <SD.h>
|
||||||
|
|
||||||
|
|
||||||
|
struct DirectoryNode {
|
||||||
|
String name;
|
||||||
|
std::vector<DirectoryNode*> subdirectories;
|
||||||
|
std::vector<String> 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_ */
|
||||||
181
src/main.cpp
181
src/main.cpp
|
|
@ -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"
|
||||||
|
|
||||||
|
|
@ -17,50 +17,47 @@
|
||||||
#include <SD.h>
|
#include <SD.h>
|
||||||
|
|
||||||
#include "WebContent.h"
|
#include "WebContent.h"
|
||||||
|
#include "DirectoryNode.h"
|
||||||
|
|
||||||
File root;
|
File root;
|
||||||
File mp3File;
|
File mp3File;
|
||||||
|
|
||||||
void printDirectory(File dir, int numTabs);
|
|
||||||
|
|
||||||
unsigned long lastStart = 0;
|
unsigned long lastStart = 0;
|
||||||
|
|
||||||
const int startDelay = 250;
|
const int startDelay = 250;
|
||||||
|
|
||||||
Audio audio;
|
Audio audio;
|
||||||
|
|
||||||
// Set web server port number to 80
|
|
||||||
AsyncWebServer server(80);
|
AsyncWebServer server(80);
|
||||||
AsyncWebServer server2(81);
|
|
||||||
DNSServer dns;
|
DNSServer dns;
|
||||||
File next;
|
|
||||||
|
|
||||||
// Variable to store the HTTP request
|
DirectoryNode rootNode("/");
|
||||||
String header;
|
DirectoryNode *currentNode = NULL;
|
||||||
|
|
||||||
|
void stop();
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
@ -68,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()
|
||||||
|
|
@ -111,75 +140,53 @@ void setup()
|
||||||
}
|
}
|
||||||
Serial.println("SD initialization done.");
|
Serial.println("SD initialization done.");
|
||||||
|
|
||||||
root = SD.open("/");
|
rootNode.buildDirectoryTree("/");
|
||||||
|
rootNode.printDirectoryTree();
|
||||||
|
// 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("/stop", HTTP_GET, [] (AsyncWebServerRequest *request) {
|
server.on("/start", HTTP_GET, [](AsyncWebServerRequest *request)
|
||||||
stop();
|
{
|
||||||
request->send(200, "text/plain", "stop");
|
|
||||||
});
|
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.begin();
|
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()
|
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)
|
||||||
{
|
{
|
||||||
playNextMp3();
|
playNextMp3();
|
||||||
Serial.println("mp3 started.");
|
|
||||||
lastStart = now;
|
lastStart = now;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue