[ai] memory optimizations
This commit is contained in:
parent
fd40b663a0
commit
b97eb79b91
136
src/main.cpp
136
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);
|
||||
|
|
|
|||
Loading…
Reference in New Issue