diff --git a/.gitignore b/.gitignore index 2d52016..c95af00 100644 --- a/.gitignore +++ b/.gitignore @@ -7,5 +7,5 @@ schema/hannabox/hannabox-backups/ schema/hannabox/*.lck .copilot - +web/cleaned .codegpt \ No newline at end of file diff --git a/src/DirectoryNode.cpp b/src/DirectoryNode.cpp index 2e3180f..81d61a5 100644 --- a/src/DirectoryNode.cpp +++ b/src/DirectoryNode.cpp @@ -111,7 +111,7 @@ void DirectoryNode::buildDirectoryTree(const char *currentPath) break; } - if (entry.isDirectory() && entry.name()[0] != '.' && strcmp(entry.name(), sys_dir.c_str())) + if (entry.isDirectory() && entry.name()[0] != '.' && strcmp(entry.name(), sys_dir)) { dirNames.push_back(String(entry.name())); } @@ -533,50 +533,6 @@ DirectoryNode *DirectoryNode::advanceToNextMP3(const String *currentGlobal) return this; } -String DirectoryNode::getDirectoryStructureHTML() const { - // Calculate required size first (prevents reallocations) - size_t htmlSize = calculateHTMLSize(); - String html; - html.reserve(htmlSize); // Precise allocation - no wasted RAM - - // Helper lambda to append without temporary Strings - auto append = [&html](const __FlashStringHelper* fstr) { - html += fstr; - }; - auto appendId = [&html](uint32_t id) { - html += id; // Direct numeric append (NO temporary String) - }; - - if (name == "/") { - append(F("\n")); - } - - return html; -} void DirectoryNode::streamDirectoryHTML(Print &out) const { if (name == "/") { @@ -616,33 +572,6 @@ void DirectoryNode::streamDirectoryHTML(Print &out) const { } } -// NEW: Calculate exact required size first -size_t DirectoryNode::calculateHTMLSize() const { - size_t size = 0; - - // Opening/closing tags - if (name == "/") size += 6; // \n - - return size; -} void DirectoryNode::appendIndentation(String &html, int level) const { diff --git a/src/DirectoryNode.h b/src/DirectoryNode.h index 6ba20be..d2c1012 100644 --- a/src/DirectoryNode.h +++ b/src/DirectoryNode.h @@ -55,9 +55,7 @@ public: void buildFlatMP3List(std::vector>& allMP3s); DirectoryNode* advanceToMP3(const uint16_t id); void advanceToFirstMP3InThisNode(); - String getDirectoryStructureHTML() const; void streamDirectoryHTML(Print &out) const; - size_t calculateHTMLSize() const; void appendIndentation(String& html, int level) const; DirectoryNode* findFirstDirectoryWithMP3s(); String getCurrentPlayingFilePath() const; diff --git a/src/config.cpp b/src/config.cpp index 78525d5..842325a 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -6,7 +6,15 @@ Config config; const String getConfigFilePath() { - return "/" + sys_dir + "/config.txt"; + static String config_dir; + if (config_dir.isEmpty()) { + config_dir.concat("/"); + config_dir.concat(sys_dir); + config_dir.concat("/"); + config_dir.concat(config_dir); + } + + return config_dir; } void setDefaultConfig() { @@ -27,14 +35,14 @@ bool loadConfig() { String configPath = getConfigFilePath(); if (!SD.exists(configPath)) { - Serial.println("Config file not found, using defaults"); + Serial.println(F("Config file not found, using defaults")); setDefaultConfig(); return saveConfig(); // Create config file with defaults } File file = SD.open(configPath, FILE_READ); if (!file) { - Serial.println("Failed to open config file"); + Serial.println(F("Failed to open config file")); setDefaultConfig(); return false; } @@ -106,46 +114,45 @@ bool saveConfig() { File file = SD.open(configPath, FILE_WRITE); if (!file) { - Serial.println("Failed to create config file"); + Serial.println(F("Failed to create config file")); return false; } // Write config file with comments for user reference - file.println("# HannaBox Configuration File"); - file.println("# Values are in the format: key=value"); - file.println("# Lines starting with # are comments"); + file.println("# HannaBox Conf File"); + file.println("# format: key=value"); file.println(""); - file.println("# Audio Settings"); + file.println("# Audio"); file.print("initialVolume="); file.println(config.initialVolume); file.print("maxVolume="); file.println(config.maxVolume); file.println(""); - file.println("# Power Management (times in milliseconds)"); + file.println("# Power Management (in milliseconds)"); file.print("sleepTime="); file.println(config.sleepTime); file.print("sleepDelay="); file.println(config.sleepDelay); file.print("sleepMessageDelay="); file.println(config.sleepMessageDelay); file.println(""); - file.println("# Battery Settings (voltage in millivolts)"); + file.println("# Battery (in millivolts)"); file.print("minVoltage="); file.println(config.minVoltage); file.print("voltage100Percent="); file.println(config.voltage100Percent); file.println(""); - file.println("# RFID Settings"); + file.println("# RFID"); file.print("rfidLoopInterval="); file.println(config.rfidLoopInterval); file.println(""); - file.println("# Playback Settings"); + file.println("# Playback"); file.print("startAtStoredProgress="); file.println(config.startAtStoredProgress ? "true" : "false"); file.println(""); - file.println("# WiFi Settings (leave empty to use current WiFiManager)"); + file.println("# WiFi (leave empty to use current WiFiManager)"); file.print("wifiSSID="); file.println(config.wifiSSID); file.print("wifiPassword="); file.println(config.wifiPassword); file.close(); - Serial.println("Config saved successfully"); + Serial.println(F("Config saved successfully")); return true; } diff --git a/src/globals.h b/src/globals.h index ac1fbd9..6a51291 100644 --- a/src/globals.h +++ b/src/globals.h @@ -1,24 +1,39 @@ #ifndef GLOBALS_H_ #define GLOBALS_H_ +static const char* sys_dir = "system"; + +static const char* sleep_sound = "sleep.mp3"; + +static const char* startup_sound = "start.mp3"; + +static const char* index_file = "index.html"; + +static const char* style_file = "style.css"; + +static const char* script_file = "script.js"; + +static const char* mapping_file = "mapping.txt"; + +static const char* progress_file = "progress.txt"; + +static const char* config_file = "config.txt"; + +static const char* txt_html_charset = "text/html; charset=UTF-8"; + +static const char* txt_plain = "text/plain; charset=UTF-8"; + +static const char* hdr_cache_control_key = "Cache-Control"; + +static const char* hdr_cache_control_val = "no-store"; + +static const char* hdr_connection_key = "Connection"; + +static const char* hdr_connection_val = "close"; - - - -const String sys_dir = "system"; - -const String sleep_sound = "sleep.mp3"; - -const String startup_sound = "start.mp3"; - -const String mapping_file = "mapping.txt"; - -const String progress_file = "progress.txt"; - - /* const long sleepMessageDelay = 28000; diff --git a/src/main.cpp b/src/main.cpp index 4832fde..ef948ff 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -124,7 +124,7 @@ void handleUpload(AsyncWebServerRequest *request, String filename, size_t index, // Validate filename and file extension if (filename.length() == 0) { - request->send(400, "text/plain", "Invalid filename"); + request->send(400, txt_plain, F("Invalid filename")); return; } @@ -133,7 +133,7 @@ void handleUpload(AsyncWebServerRequest *request, String filename, size_t index, if (!lowerFilename.endsWith(".mp3") && !lowerFilename.endsWith(".wav") && !lowerFilename.endsWith(".m4a") && !lowerFilename.endsWith(".ogg")) { - request->send(400, "text/plain", "Invalid file type. Only audio files are allowed."); + request->send(400, txt_plain, F("Invalid file type. Only audio files are allowed.")); return; } @@ -142,7 +142,7 @@ void handleUpload(AsyncWebServerRequest *request, String filename, size_t index, if (freeSpace < 10) { // Less than 10MB free - request->send(507, "text/plain", "Insufficient storage"); + request->send(507, txt_plain, F("Insufficient storage")); return; } @@ -174,10 +174,10 @@ void handleUpload(AsyncWebServerRequest *request, String filename, size_t index, if (counter >= 100) { - request->send(409, "text/plain", "Too many files with similar names"); + request->send(409, txt_plain, F("Too many files w sim names")); return; } - Serial.print("File exists, using: "); + Serial.print(F("File exists, using: ")); Serial.println(filepath); } @@ -187,7 +187,7 @@ void handleUpload(AsyncWebServerRequest *request, String filename, size_t index, if (!request->_tempFile) { sd_lock_release(); - request->send(500, "text/plain", "Failed to create file on SD card"); + request->send(500, txt_plain, F("Failed to create file")); return; } sd_lock_release(); @@ -198,7 +198,7 @@ void handleUpload(AsyncWebServerRequest *request, String filename, size_t index, // Check if file handle is valid if (!request->_tempFile) { - request->send(500, "text/plain", "File handle invalid"); + request->send(500, txt_plain, F("File handle invalid")); return; } @@ -210,7 +210,7 @@ void handleUpload(AsyncWebServerRequest *request, String filename, size_t index, // ensure we close while holding the lock to keep SD state consistent request->_tempFile.close(); sd_lock_release(); - request->send(500, "text/plain", "Write error - SD card may be full"); + request->send(500, txt_plain, "Write error"); return; } @@ -252,11 +252,11 @@ void handleUpload(AsyncWebServerRequest *request, String filename, size_t index, rootNode.buildDirectoryTree("/"); dir_lock_release(); - request->send(200, "text/plain", "Upload successful"); + request->send(200, txt_plain, "Upload successful"); } else { - request->send(500, "text/plain", "Upload failed - file handle was invalid"); + request->send(500, txt_plain, "Upload failed"); } } } @@ -278,12 +278,12 @@ void handleMoveFile(AsyncWebServerRequest *request) dir_lock_acquire(); rootNode.buildDirectoryTree("/"); dir_lock_release(); - request->send(200, "text/plain", "File moved successfully."); + request->send(200, txt_plain, F("File moved successfully.")); } else { Serial.println("File not found: " + from); - request->send(404, "text/plain", "File not found."); + request->send(404, txt_plain, "File not found."); } } @@ -303,12 +303,12 @@ void handleDeleteFile(AsyncWebServerRequest *request) dir_lock_acquire(); rootNode.buildDirectoryTree("/"); dir_lock_release(); - request->send(200, "text/plain", "File deleted successfully."); + request->send(200, txt_plain, "File deleted."); } else { Serial.println("File not found: " + filename); - request->send(404, "text/plain", "File not found."); + request->send(404, txt_plain, "File not found."); } } @@ -372,14 +372,15 @@ void playSongByName(String song) { if (song.length() == 0) { - Serial.println("Empty song name provided"); + Serial.println(F("Empty song name provided")); return; } currentNode = rootNode.advanceToMP3(&song); if (currentNode == nullptr) { - Serial.println("No node found for song: " + song); + Serial.print(F("No node found for song: ")); + Serial.println(song); return; } @@ -387,7 +388,8 @@ void playSongByName(String song) if (currentNode->getCurrentPlaying() == nullptr) { currentNode = nullptr; - Serial.println("No song found for name: " + song); + Serial.print(F("No song found for name: ")); + Serial.println(song); return; } @@ -395,7 +397,8 @@ void playSongByName(String song) if (mp3File.length() == 0) { currentNode = nullptr; - Serial.println("Empty file path for song: " + song); + Serial.print(F("Empty file path for song: ")); + Serial.println(song); return; } @@ -420,7 +423,7 @@ void playSongByRFID(String id) { if (id.length() == 0) { - Serial.println("Empty RFID ID provided"); + Serial.println(F("Empty RFID ID provided")); return; } @@ -438,7 +441,7 @@ void playSongByRFID(String id) return; } - Serial.print("RFID mapping found. Target: "); + Serial.print(F("RFID mapping found. Target: ")); Serial.print(entry.target); Serial.print(" Mode: "); Serial.println(entry.mode); @@ -456,14 +459,16 @@ void playSongByRFID(String id) currentNode = rootNode.advanceToMP3(&entry.target); if (currentNode == nullptr || currentNode->getCurrentPlaying() == nullptr) { - Serial.println("No node/file found for mapping target: " + entry.target); + Serial.print(F("No node/file found for mapping target: ")); + Serial.println(entry.target); return; } String mp3File = currentNode->getCurrentPlayingFilePath(); if (mp3File.length() == 0) { - Serial.println("Empty file path for mapping target: " + entry.target); + Serial.print(F("Empty file path for mapping target: ")); + Serial.println(entry.target); return; } @@ -716,8 +721,11 @@ boolean readSongProgress(const char *filename) currentSongId = (uint16_t)tempId; currentSongSeconds = (uint32_t)tempSeconds; +#ifdef DEBUG Serial.println("Data read from file: " + data); Serial.println("Parsed ID: " + String(currentSongId) + ", s: " + String(currentSongSeconds)); +#endif + return true; } @@ -727,11 +735,9 @@ String getState() static DynamicJsonDocument jsonState(512); static String output; - output.reserve(512); // Pre-allocate string buffer output.clear(); - - jsonState.clear(); // Clear previous data - + output.reserve(512); // Pre-allocate string buffer + jsonState["playing"] = audio.isRunning(); if (currentNode != nullptr) @@ -752,6 +758,7 @@ String getState() jsonState["heap"] = free_heap; serializeJson(jsonState, output); + jsonState.clear(); return output; } @@ -772,11 +779,11 @@ void saveMappingToFile(const String filename) file.println(pair.second.mode); } file.close(); - Serial.println("Mapping saved to file."); + Serial.println(F("Mapping saved to file.")); } else { - Serial.println("Error opening file for writing."); + Serial.println(F("Error opening file for writing.")); } } @@ -802,11 +809,11 @@ void editMapping(AsyncWebServerRequest *request) rfid_map[rfid] = MappingEntry(song, mode); saveMappingToFile(getSysDir(mapping_file)); - request->send(200, "text/plain", "Mapping updated"); + request->send(200, txt_plain, "Mapping updated"); } else { - request->send(400, "text/plain", "Invalid parameters"); + request->send(400, txt_plain, "Invalid parameters"); } } @@ -842,8 +849,9 @@ void readDataFromFile(String filename) if (mstr.length() > 0) mode = mstr.charAt(0); } - +#ifdef DEBUG Serial.println("found rfid mapping for " + target + " mode " + String(mode)); +#endif // Add key-value pair to the map rfid_map[key] = MappingEntry(target, mode); } @@ -852,20 +860,13 @@ void readDataFromFile(String filename) } else { - Serial.print("Error opening file "); + Serial.print(F("Error opening file ")); Serial.println(filename); } } String processor(const String &var) { - if (var == "DIRECTORY") - { - dir_lock_acquire(); - String out = rootNode.getDirectoryStructureHTML(); - dir_lock_release(); - return out; - } if (var == "MAPPING") { @@ -903,9 +904,9 @@ String processor(const String &var) mappingVal += "|"; mappingVal += pair.second.mode; html.concat(htmlEscape(mappingVal)); - html.concat(""); + html.concat(F("")); } - html.concat(""); + html.concat(F("")); return html; } @@ -961,7 +962,7 @@ void previous() lastInteraction = millis(); if (currentNode == NULL) { - Serial.println("previous(): currentNode is null"); + Serial.println(F("previous(): currentNode is null")); return; } @@ -970,7 +971,9 @@ void previous() const String *currentSong = currentNode->getCurrentPlaying(); if (currentSong == NULL) { - Serial.println("previous(): currentPlaying is null, cannot go to previous"); +#ifdef DEBUG + Serial.println(F("previous(): currentPlaying is null, cannot go to previous")); +#endif return; } @@ -1013,7 +1016,9 @@ void previous() else { // Need to find previous song globally (across directories) - Serial.println("previous(): Looking for previous song globally"); +#ifdef DEBUG + Serial.println(F("previous(): Looking for previous song globally")); +#endif DirectoryNode *globalPrevNode = rootNode.findPreviousMP3Globally(currentSong); if (globalPrevNode != NULL) @@ -1021,20 +1026,24 @@ void previous() const String *globalPrevSong = globalPrevNode->getCurrentPlaying(); if (globalPrevSong != NULL) { - Serial.print("previous(): Found previous song globally: "); + Serial.print(F("previous(): Found previous song globally: ")); Serial.println(*globalPrevSong); currentNode = globalPrevNode; stop(); playFile(currentNode->getCurrentPlayingFilePath().c_str()); } +#ifdef DEBUG else { - Serial.println("prev: Global previous song is null"); + Serial.println(F("prev: Global previous song is null")); } +#endif } else { - Serial.println("prev: No previous song found, beginning again"); +#ifdef DEBUG + Serial.println(F("prev: No previous song found, beginning again")); +#endif // Optionally restart current song or do nothing audio.setAudioPlayPosition(0); currentNode->setSecondsPlayed(0); @@ -1150,17 +1159,26 @@ server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) request->onDisconnect([](){ webreq_exit(); }); deactivateRFID(); activateSD(); - String htmlPath = getSysDir("index.html"); + String htmlPath = getSysDir(index_file); if (SD.exists(htmlPath)) { - AsyncWebServerResponse *response = request->beginResponse(SD, htmlPath, "text/html"); - response->addHeader("Cache-Control", "no-store"); + uint32_t fsize = 0; + { + File f = SD.open(htmlPath); + if (f) { fsize = f.size(); f.close(); } + } +#ifdef DEBUG + Serial.printf("Serving %s size=%u heap=%u webreq_cnt=%u\n", htmlPath.c_str(), (unsigned)fsize, (unsigned)xPortGetFreeHeapSize(), (unsigned)webreq_cnt); +#endif + AsyncWebServerResponse *response = request->beginResponse(SD, htmlPath, txt_html_charset); + response->addHeader(hdr_cache_control_key, hdr_cache_control_val); + response->addHeader(hdr_connection_key, hdr_connection_val); request->send(response); } else { // Fallback: serve minimal error if file not found - request->send(404, "text/plain", "ERROR: /system/index.html not found!"); + request->send(404, txt_plain, F("ERROR: /system/index.html not found!")); } }); @@ -1172,19 +1190,28 @@ server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) deactivateRFID(); activateSD(); // Ensure SD is active and RFID is deactivated while serving files. - String cssPath = getSysDir("style.css"); + String cssPath = getSysDir(style_file); if (SD.exists(cssPath)) { + uint32_t fsize = 0; { - AsyncWebServerResponse *resp = request->beginResponse(SD, cssPath, "text/css"); - resp->addHeader("Cache-Control", "public, max-age=300"); + File f = SD.open(cssPath); + if (f) { fsize = f.size(); f.close(); } + } +#ifdef DEBUG + Serial.printf("Serving %s size=%u heap=%u webreq_cnt=%u\n", cssPath.c_str(), (unsigned)fsize, (unsigned)xPortGetFreeHeapSize(), (unsigned)webreq_cnt); +#endif + { + AsyncWebServerResponse *resp = request->beginResponse(SD, cssPath, F("text/css")); + resp->addHeader(hdr_cache_control_key, F("public, max-age=300")); + resp->addHeader(hdr_connection_key, hdr_connection_val); request->send(resp); } } else { // Fallback: serve minimal CSS if file not found - request->send(404, "text/plain", "ERROR: /system/style.css not found!"); + request->send(404, txt_plain, F("ERROR: /system/style.css not found!")); } }); @@ -1195,19 +1222,28 @@ server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) request->onDisconnect([](){ webreq_exit(); }); deactivateRFID(); activateSD(); - String jsPath = getSysDir("script.js"); + String jsPath = getSysDir(script_file); if (SD.exists(jsPath)) { + uint32_t fsize = 0; { - AsyncWebServerResponse *resp = request->beginResponse(SD, jsPath, "application/javascript"); - resp->addHeader("Cache-Control", "public, max-age=300"); + File f = SD.open(jsPath); + if (f) { fsize = f.size(); f.close(); } + } +#ifdef DEBUG + Serial.printf("Serving %s size=%u heap=%u webreq_cnt=%u\n", jsPath.c_str(), (unsigned)fsize, (unsigned)xPortGetFreeHeapSize(), (unsigned)webreq_cnt); +#endif + { + AsyncWebServerResponse *resp = request->beginResponse(SD, jsPath, F("application/javascript")); + resp->addHeader(hdr_cache_control_key, F("public, max-age=300")); + resp->addHeader(hdr_connection_key, hdr_connection_val); request->send(resp); } } else { // Fallback: serve minimal JS if file not found - request->send(404, "text/plain", "ERROR: /system/script.js not found!"); + request->send(404, txt_plain, F("ERROR: /system/script.js not found!")); } }); @@ -1218,9 +1254,12 @@ server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) webreq_enter(); request->onDisconnect([](){ webreq_exit(); }); // Stream the response directly from the directory tree to avoid large temporary Strings - AsyncResponseStream* stream = request->beginResponseStream("text/html; charset=UTF-8"); - stream->addHeader("Cache-Control", "no-store"); - stream->addHeader("Connection", "close"); + AsyncResponseStream* stream = request->beginResponseStream(txt_html_charset); +#ifdef DEBUG + Serial.printf("Serving /directory heap=%u webreq_cnt=%u\n", (unsigned)xPortGetFreeHeapSize(), (unsigned)webreq_cnt); +#endif + stream->addHeader(hdr_cache_control_key, hdr_cache_control_val); + stream->addHeader(hdr_connection_key, hdr_connection_val); // Generate HTML directly into the stream under lock dir_lock_acquire(); rootNode.streamDirectoryHTML(*stream); @@ -1233,9 +1272,12 @@ server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) webreq_enter(); request->onDisconnect([](){ webreq_exit(); }); // Stream mapping to avoid Content-Length mismatches and reduce heap spikes - AsyncResponseStream* stream = request->beginResponseStream("text/html; charset=UTF-8"); - stream->addHeader("Cache-Control", "no-store"); - stream->addHeader("Connection", "close"); + AsyncResponseStream* stream = request->beginResponseStream(txt_html_charset); +#ifdef DEBUG + Serial.printf("Serving /mapping heap=%u webreq_cnt=%u\n", (unsigned)xPortGetFreeHeapSize(), (unsigned)webreq_cnt); +#endif + stream->addHeader(hdr_cache_control_key, hdr_cache_control_val); + stream->addHeader(hdr_connection_key, hdr_connection_val); String html = processor(String("MAPPING")); stream->print(html); request->send(stream); @@ -1246,44 +1288,48 @@ server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) webreq_enter(); request->onDisconnect([](){ webreq_exit(); }); String state = getState(); - AsyncWebServerResponse* resp = request->beginResponse(200, "application/json; charset=UTF-8", state); - resp->addHeader("Cache-Control", "no-store"); - resp->addHeader("Connection", "close"); +#ifdef DEBUG + Serial.printf("Serving /state heap=%u webreq_cnt=%u\n", (unsigned)xPortGetFreeHeapSize(), (unsigned)webreq_cnt); +#endif + + AsyncWebServerResponse* resp = request->beginResponse(200, F("application/json; charset=UTF-8"), state); + resp->addHeader(hdr_cache_control_key, hdr_cache_control_val); + resp->addHeader(hdr_connection_key, hdr_connection_val); request->send(resp); }); server.on("/start", HTTP_GET, [](AsyncWebServerRequest *request) { webreq_enter(); request->onDisconnect([](){ webreq_exit(); }); - request->send(200, "text/plain; charset=UTF-8", "start"); + request->send(200, txt_plain, "start"); start(); }); server.on("/toggleplaypause", HTTP_GET, [](AsyncWebServerRequest *request) { webreq_enter(); request->onDisconnect([](){ webreq_exit(); }); - request->send(200, "text/plain; charset=UTF-8", "toggleplaypause"); + request->send(200, txt_plain, "toggleplaypause"); togglePlayPause(); }); server.on("/stop", HTTP_GET, [](AsyncWebServerRequest *request) { webreq_enter(); request->onDisconnect([](){ webreq_exit(); }); - request->send(200, "text/plain", "stop"); + request->send(200, txt_plain, "stop"); stop(); }); server.on("/next", HTTP_GET, [](AsyncWebServerRequest *request) { webreq_enter(); request->onDisconnect([](){ webreq_exit(); }); - request->send(200, "text/plain", "next"); + request->send(200, txt_plain, "next"); next(); }); server.on("/previous", HTTP_GET, [](AsyncWebServerRequest *request) { webreq_enter(); request->onDisconnect([](){ webreq_exit(); }); - request->send(200, "text/plain", "previous"); + request->send(200, txt_plain, "previous"); previous(); }); server.on("/playbyid", HTTP_GET, id_song_action); @@ -1327,9 +1373,9 @@ void setup() RFIDActive = false; SDActive = false; - Serial.print("Initializing SD card..."); + Serial.print(F("Initializing SD card...")); activateSD(); - Serial.println("SD initialization done."); + Serial.println(F("SD initialization done.")); // Seed RNG for shuffle mode #if defined(ESP32) @@ -1339,7 +1385,7 @@ void setup() #endif // Load configuration from SD card - Serial.println("Loading configuration..."); + Serial.println(F("Loading configuration...")); loadConfig(); // deep sleep wakeup @@ -1406,7 +1452,9 @@ void setup() wifiManager.setConnectTimeout(15); // Faster connection attempts wifiManager.setConfigPortalTimeout(60); // Shorter portal timeout - Serial.println("Deactivating Brownout detector..."); +#ifdef DEBUG + Serial.println(F("Deactivating Brownout detector...")); +#endif WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0); // disable brownout detector // wifiManager.resetSettings(); @@ -1419,10 +1467,10 @@ void setup() } else { - Serial.println("Wifi timed out. Fallback."); + Serial.println(F("Wifi timed out. Fallback.")); } - Serial.println("Activating Brownout detector..."); + Serial.println(F("Activating Brownout detector...")); WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 1); // enable brownout detector xTaskCreatePinnedToCore( @@ -1452,7 +1500,7 @@ void id_song_action(AsyncWebServerRequest *request) } } lastInteraction = millis(); - request->send_P(200, "text/plain", "ok"); + request->send_P(200, txt_plain, "ok"); } void progress_action(AsyncWebServerRequest *request) @@ -1470,7 +1518,7 @@ void progress_action(AsyncWebServerRequest *request) } } lastInteraction = millis(); - request->send_P(200, "text/plain", "ok"); + request->send_P(200, txt_plain, "ok"); } void volume_action(AsyncWebServerRequest *request) @@ -1488,7 +1536,7 @@ void volume_action(AsyncWebServerRequest *request) } } lastInteraction = millis(); - request->send_P(200, "text/plain", "ok"); + request->send_P(200, txt_plain, "ok"); } const String getSysDir(const String filename) @@ -1505,7 +1553,7 @@ const String getSysDir(const String filename) void loop() { if (webreq_cnt > 0 && webrequest_blockings > MAX_WEBREQUEST_BLOCKINGS) { - Serial.println("excessive webrequest blocking - suppress reset"); + Serial.println(F("excessive webrequest blocking - suppress reset")); // Avoid resetting server mid-response to prevent mixing headers/body or truncation webreq_cnt = 0; webrequest_blockings = 0; @@ -1574,7 +1622,7 @@ void loop() if (now - lastInteraction > config.sleepDelay) { - Serial.println("entering deep sleep.."); + Serial.println(F("entering deep sleep..")); deactivateRFID(); deactivateSD(); esp_deep_sleep_start(); @@ -1676,7 +1724,7 @@ void loop() { if (voltage_threshold_counter > 3) { - Serial.println("deep sleep due to low volts.."); + Serial.println(F("deep sleep due to low volts..")); lastInteraction = millis() - config.sleepMessageDelay; voltage_threshold_counter = 0; } diff --git a/web/script.js b/web/script.js index 794d6b5..2b92362 100644 --- a/web/script.js +++ b/web/script.js @@ -245,7 +245,7 @@ function displayState(state) { var uidEl = document.getElementById("uid"); if (uidEl) uidEl.innerHTML = 'Last NFC ID: ' + (state['uid'] || ''); - /* ==== Autofill convenience fields ==== */ + /* Autofill convenience fields */ var fm = document.getElementById('fileManager'); if (state['filepath'] && fm && fm.style.display == 'none') { var moveFrom = document.getElementById('moveFrom'); @@ -301,7 +301,6 @@ function playNamedSong(song) { var xhr = new XMLHttpRequest(); xhr.open("POST", "/playnamed"); xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8"); - //application/x-www-form-urlencoded var body = song; xhr.send("title="+encodeURIComponent(body)); } @@ -327,8 +326,8 @@ function editMapping() { // Validate file before upload function validateFile(file) { var maxSize = 50 * 1024 * 1024; // 50MB limit - var allowedTypes = ['audio/mpeg', 'audio/wav', 'audio/flac', 'audio/mp4', 'audio/ogg']; - var allowedExtensions = ['.mp3', '.wav', '.flac', '.m4a', '.ogg']; + var allowedTypes = ['audio/mpeg', 'audio/wav']; + var allowedExtensions = ['.mp3']; if (file.size > maxSize) { return 'File too large. Maximum size is 50MB.'; @@ -338,7 +337,7 @@ function validateFile(file) { var hasValidExtension = allowedExtensions.some(ext => fileName.endsWith(ext)); if (!hasValidExtension) { - return 'Invalid file type. Only audio files (.mp3, .wav, .flac, .m4a, .ogg) are allowed.'; + return 'Invalid file type'; } return null; // No error @@ -443,7 +442,7 @@ function resetUploadForm() { document.getElementById('uploadFile').value = ''; } -/* ================= File Manager Functions ================= */ +/* File Manager Functions */ function toggleFileManager() { var fm = document.getElementById('fileManager');