diff --git a/src/main.cpp b/src/main.cpp index bf7a1d5..a292727 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -36,7 +36,7 @@ void activateSD() if (!SD.begin(CS_SDCARD)) { - Serial.println("SD initialization failed!"); + Serial.println(F("SD initialization failed!")); } SDActive = true; } @@ -112,15 +112,23 @@ void handleUpload(AsyncWebServerRequest *request, String filename, size_t index, activateSD(); // Check if file already exists and create backup name if needed - String filepath = "/" + filename; + String filepath; + filepath.reserve(1 + filename.length()); + filepath = "/"; + filepath += filename; if (SD.exists(filepath)) { String baseName = filename.substring(0, filename.lastIndexOf('.')); String extension = filename.substring(filename.lastIndexOf('.')); int counter = 1; + filepath.reserve(1 + baseName.length() + 1 + 10 + extension.length()); do { - filepath = "/" + baseName + "_" + String(counter) + extension; + filepath = "/"; + filepath += baseName; + filepath += "_"; + filepath += counter; + filepath += extension; counter++; } while (SD.exists(filepath) && counter < 100); @@ -162,13 +170,13 @@ 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, txt_plain, "Write error"); + request->send_P(500, txt_plain, PSTR("Write error")); return; } // Flush data periodically to ensure it's written - if (index % 2048 == 0) - { // Flush every 2KB + if (index % buffer_size == 0) + { // Flush every so often request->_tempFile.flush(); } sd_lock_release(); @@ -186,7 +194,7 @@ void handleUpload(AsyncWebServerRequest *request, String filename, size_t index, Serial.print(F("Upload Complete: ")); Serial.print(filename); - Serial.print(", size: "); + Serial.print(F(", size: ")); Serial.println(humanReadableSize(index + len)); // Rebuild directory tree to include new file (guarded) @@ -194,11 +202,11 @@ void handleUpload(AsyncWebServerRequest *request, String filename, size_t index, rootNode.buildDirectoryTree("/"); sd_lock_release(); - request->send(200, txt_plain, "Upload successful"); + request->send_P(200, txt_plain, PSTR("Upload successful")); } else { - request->send(500, txt_plain, "Upload failed"); + request->send_P(500, txt_plain, PSTR("Upload failed")); } } } @@ -215,7 +223,7 @@ void handleMoveFile(AsyncWebServerRequest *request) sd_lock_acquire(); SD.rename(from, to); sd_lock_release(); - Serial.println("Moved file: " + from + " to " + to); + Serial.print(F("Moved file: ")); Serial.print(from); Serial.print(F(" to ")); Serial.println(to); // Rebuild directory tree to update file list (guarded) sd_lock_acquire(); rootNode.buildDirectoryTree("/"); @@ -224,8 +232,8 @@ void handleMoveFile(AsyncWebServerRequest *request) } else { - Serial.println("File not found: " + from); - request->send(404, txt_plain, "File not found."); + Serial.print(F("File not found: ")); Serial.println(from); + request->send_P(404, txt_plain, PSTR("File not found.")); } } @@ -240,17 +248,17 @@ void handleDeleteFile(AsyncWebServerRequest *request) sd_lock_acquire(); SD.remove(filename.c_str()); sd_lock_release(); - Serial.println("Deleted file: " + filename); + Serial.print(F("Deleted file: ")); Serial.println(filename); // Rebuild directory tree to update file list (guarded) sd_lock_acquire(); rootNode.buildDirectoryTree("/"); sd_lock_release(); - request->send(200, txt_plain, "File deleted."); + request->send(200, txt_plain, F("File deleted.")); } else { - Serial.println("File not found: " + filename); - request->send(404, txt_plain, "File not found."); + Serial.print(F("File not found: ")); Serial.println(filename); + request->send_P(404, txt_plain, PSTR("File not found.")); } } @@ -288,12 +296,12 @@ void playSongById(uint16_t id, uint32_t continueSeconds = 0) if (mp3File.length() == 0) { currentNode = nullptr; - Serial.print("Empty file path for ID: "); + Serial.print(F("Empty file path for ID: ")); Serial.println(id); return; } - Serial.print("Playing by ID: "); + Serial.print(F("Playing by ID: ")); Serial.println(id); Serial.println(mp3File); @@ -314,7 +322,7 @@ void playSongById(uint16_t id, uint32_t continueSeconds = 0) } } -void playSongByName(String song) +void playSongByName(const String &song) { if (song.length() == 0) { @@ -348,24 +356,24 @@ void playSongByName(String song) return; } - Serial.println("Playing song: " + mp3File); + Serial.print(F("Playing song: ")); Serial.println(mp3File); deactivateRFID(); activateSD(); if (!playFile(mp3File.c_str())) { - Serial.println("Failed to play file: " + mp3File); + Serial.print(F("Failed to play file: ")); Serial.println(mp3File); currentNode = nullptr; return; } } -void playSongByPath(String path) +void playSongByPath(const String &path) { playFile(path.c_str()); } -void playSongByRFID(String id) +void playSongByRFID(const String &id) { if (id.length() == 0) { @@ -463,7 +471,7 @@ void playSongByRFID(String id) folderFlatIndex = 0; DirectoryNode *startNode = folderFlatList[0].first; int fileIdx = folderFlatList[0].second; - Serial.print("Shuffle start: "); + Serial.print(F("Shuffle start: ")); Serial.println(startNode->getMP3Files()[fileIdx]); startNode->setCurrentPlaying(startNode->getMP3Files()[fileIdx]); currentNode = startNode; @@ -553,7 +561,7 @@ void playNextMp3() currentNode->setSecondsPlayed(0); } - Serial.print("Advancing to "); + Serial.print(F("Advancing to ")); String mp3File = currentNode->getCurrentPlaying(); // FIXME crash here if last song. if (mp3File.isEmpty()) @@ -598,12 +606,12 @@ void writeSongProgress(const char *filename, uint16_t id, uint32_t seconds) file.println(seconds); file.close(); #ifdef DEBUG - Serial.println("Progress written: ID " + String(id) + ", s " + String(seconds)); + Serial.print(F("Progress written: ID ")); Serial.print(id); Serial.print(F(", s ")); Serial.println(seconds); #endif } else { - Serial.print("Error opening file for writing: "); + Serial.print(F("Error opening file for writing: ")); Serial.println(filename); } } @@ -614,7 +622,7 @@ boolean readSongProgress(const char *filename) if (!file) { - Serial.print("Error opening file for reading: "); + Serial.print(F("Error opening file for reading: ")); Serial.println(filename); return false; } @@ -660,13 +668,13 @@ boolean readSongProgress(const char *filename) // Validate ranges before assignment if (tempId < 0 || tempId > 65535) { - Serial.println("Invalid song in progress: " + String(tempId)); + Serial.print(F("Invalid song in progress: ")); Serial.println(tempId); return false; } if (tempSeconds > 4294967295UL) { - Serial.println("Invalid seconds in progress: " + String(tempSeconds)); + Serial.print(F("Invalid seconds in progress: ")); Serial.println(tempSeconds); return false; } @@ -674,8 +682,8 @@ boolean readSongProgress(const char *filename) currentSongSeconds = (uint32_t)tempSeconds; #ifdef DEBUG - Serial.println("Data read from file: " + data); - Serial.println("Parsed ID: " + String(currentSongId) + ", s: " + String(currentSongSeconds)); + Serial.print(F("Data read from file: ")); Serial.println(data); + Serial.print(F("Parsed ID: ")); Serial.print(currentSongId); Serial.print(F(", s: ")); Serial.println(currentSongSeconds); #endif return true; @@ -683,7 +691,7 @@ boolean readSongProgress(const char *filename) // Function to save the rfid_map to the mapping file -void saveMappingToFile(const String filename) +void saveMappingToFile(const String &filename) { File file = SD.open(filename, FILE_WRITE); if (file) @@ -728,15 +736,15 @@ void editMapping(AsyncWebServerRequest *request) rfid_map[rfid] = MappingEntry(song, mode); saveMappingToFile(getSysDir(mapping_file)); - request->send(200, txt_plain, "Mapping updated"); + request->send_P(200, txt_plain, PSTR("Mapping updated")); } else { - request->send(400, txt_plain, "Invalid parameters"); + request->send_P(400, txt_plain, PSTR("Invalid parameters")); } } -void readDataFromFile(String filename) +void readDataFromFile(const String &filename) { File file = SD.open(filename); @@ -769,7 +777,7 @@ void readDataFromFile(String filename) mode = mstr.charAt(0); } #ifdef DEBUG - Serial.println("found rfid mapping for " + target + " mode " + String(mode)); + Serial.print(F("found rfid mapping for ")); Serial.print(target); Serial.print(F(" mode ")); Serial.println(mode); #endif // Add key-value pair to the map rfid_map[key] = MappingEntry(target, mode); @@ -943,7 +951,7 @@ void stop() { if (audio.isRunning()) { - Serial.println("stopping audio."); + Serial.println(F("stopping audio.")); audio.stopSong(); if (currentNode != NULL) { @@ -1003,14 +1011,14 @@ void previous() return; } - Serial.print("previous(): Current song: "); + Serial.print(F("previous(): Current song: ")); Serial.println(currentSong); // Use audio library's current time instead of tracked seconds for more accuracy uint32_t currentAudioTime = audio.getAudioCurrentTime(); - Serial.print("previous(): Current audio time: "); + Serial.print(F("previous(): Current audio time: ")); Serial.print(currentAudioTime); - Serial.println(" seconds"); + Serial.println(F(" seconds")); // Try to go to previous within current directory first DirectoryNode *newNode = currentNode->goToPreviousMP3(2); // Use 2 second threshold @@ -1023,14 +1031,14 @@ void previous() if (currentSong == newSong && currentAudioTime > 2) { // Restart current song if it's been playing for more than 2 seconds - Serial.println("previous(): Restarting current song"); + Serial.println(F("previous(): Restarting current song")); audio.setAudioPlayPosition(0); currentNode->setSecondsPlayed(0); } else if (currentSong != newSong) { // Move to previous song in same directory - Serial.print("previous(): Moving to previous song in directory: "); + Serial.print(F("previous(): Moving to previous song in directory: ")); Serial.println(newSong); currentNode = newNode; stop(); @@ -1079,7 +1087,7 @@ void previous() void audio_eof_mp3(const char *info) { - Serial.println("audio file ended."); + Serial.println(F("audio file ended.")); if (prepareSleepMode) return; @@ -1169,7 +1177,7 @@ void readRFID() stop(); lastUid = newUid; - Serial.print("Card UID: "); + Serial.print(F("Card UID: ")); Serial.println(lastUid); // rfid.PICC_DumpDetailsToSerial(&(rfid.uid)); @@ -1278,7 +1286,7 @@ void init_webserver() { webreq_enter(); request->onDisconnect([](){ webreq_exit(); }); // Stream the response directly from the directory tree to avoid large temporary Strings - AsyncResponseStream* stream = request->beginResponseStream(txt_html_charset, 512); + AsyncResponseStream* stream = request->beginResponseStream(txt_html_charset, buffer_size); #ifdef DEBUG Serial.printf("Serving /directory heap=%u webreq_cnt=%u numOfFiles=%u\n", (unsigned)xPortGetFreeHeapSize(), (unsigned)webreq_cnt, rootNode.getNumOfFiles()); #endif @@ -1294,7 +1302,7 @@ void init_webserver() { webreq_enter(); request->onDisconnect([](){ webreq_exit();}); // Stream mapping to avoid building a large HTML String - AsyncResponseStream* stream = request->beginResponseStream(txt_html_charset, 512); + AsyncResponseStream* stream = request->beginResponseStream(txt_html_charset, buffer_size); #ifdef DEBUG Serial.printf("Serving /mapping heap=%u webreq_cnt=%u\n", (unsigned)xPortGetFreeHeapSize(), (unsigned)webreq_cnt); #endif @@ -1324,35 +1332,35 @@ void init_webserver() { { webreq_enter(); request->onDisconnect([](){ webreq_exit(); }); - request->send(200, txt_plain, "start"); + request->send_P(200, txt_plain, PSTR("start")); start(); }); server.on("/toggleplaypause", HTTP_GET, [](AsyncWebServerRequest *request) { webreq_enter(); request->onDisconnect([](){ webreq_exit(); }); - request->send(200, txt_plain, "toggleplaypause"); + request->send_P(200, txt_plain, PSTR("toggleplaypause")); togglePlayPause(); }); server.on("/stop", HTTP_GET, [](AsyncWebServerRequest *request) { webreq_enter(); request->onDisconnect([](){ webreq_exit(); }); - request->send(200, txt_plain, "stop"); + request->send_P(200, txt_plain, PSTR("stop")); stop(); }); server.on("/next", HTTP_GET, [](AsyncWebServerRequest *request) { webreq_enter(); request->onDisconnect([](){ webreq_exit(); }); - request->send(200, txt_plain, "next"); + request->send_P(200, txt_plain, PSTR("next")); next(); }); server.on("/previous", HTTP_GET, [](AsyncWebServerRequest *request) { webreq_enter(); request->onDisconnect([](){ webreq_exit(); }); - request->send(200, txt_plain, "previous"); + request->send_P(200, txt_plain, PSTR("previous")); previous(); }); server.on("/playbyid", HTTP_GET, id_song_action); @@ -1434,14 +1442,14 @@ void setup() if (continuePlaying) { - Serial.print("deleting "); + Serial.print(F("deleting ")); Serial.println(progressPath); SD.remove(progressPath); } deactivateSD(); activateRFID(); - Serial.println("RFID"); + Serial.println(F("RFID")); // Init MFRC522 // Init SPI bus @@ -1466,7 +1474,7 @@ void setup() // Optimize audio buffer size to save memory (ESP32-audioI2S optimization) audio.setBufferSize(8192); // Reduced from default large buffer (saves 40-600KB!) - Serial.println("Audio init"); + Serial.println(F("Audio init")); lastVoltage = getBatteryVoltageMv(); @@ -1475,7 +1483,7 @@ void setup() AsyncWiFiManager wifiManager(&server, &dns); // Memory optimizations for WiFiManager - wifiManager.setDebugOutput(true); // Disable debug strings + wifiManager.setDebugOutput(false); // Disable debug strings // Reduce timeouts to free memory faster wifiManager.setTimeout(180); // Reduced from 180 @@ -1493,7 +1501,7 @@ void setup() { init_webserver(); server.begin(); - Serial.println("Wifi init"); + Serial.println(F("Wifi init")); } else { @@ -1513,7 +1521,7 @@ void setup() 0); /* Core where the task should run */ lastInteraction = millis(); - Serial.println("Init done."); + Serial.println(F("Init done.")); } void id_song_action(AsyncWebServerRequest *request) @@ -1530,7 +1538,7 @@ void id_song_action(AsyncWebServerRequest *request) } } lastInteraction = millis(); - request->send_P(200, txt_plain, "ok"); + request->send_P(200, txt_plain, PSTR("ok")); } void progress_action(AsyncWebServerRequest *request) @@ -1548,7 +1556,7 @@ void progress_action(AsyncWebServerRequest *request) } } lastInteraction = millis(); - request->send_P(200, txt_plain, "ok"); + request->send_P(200, txt_plain, PSTR("ok")); } void volume_action(AsyncWebServerRequest *request) @@ -1566,7 +1574,7 @@ void volume_action(AsyncWebServerRequest *request) } } lastInteraction = millis(); - request->send_P(200, txt_plain, "ok"); + request->send_P(200, txt_plain, PSTR("ok")); } const String getSysDir(const String filename) @@ -1760,9 +1768,9 @@ void loop() { if (voltage_threshold_counter > 3) { - Serial.print("deep sleep due to low volts ("); + Serial.print(F("deep sleep due to low volts (")); Serial.print(lastVoltage); - Serial.print(") min: "); + Serial.print(F(") min: ")); Serial.println(config.minVoltage); lastInteraction = millis() - config.sleepMessageDelay; @@ -1833,7 +1841,7 @@ void loop2(void *parameter) } if (!loggingDone) { - Serial.println("loop2 started"); + Serial.println(F("loop2 started")); loggingDone = true; } vTaskDelay(1);