From d0c9a7e482a87e3613ea0599d98352e021bb442b Mon Sep 17 00:00:00 2001 From: Stefan Ostermann Date: Sun, 5 Oct 2025 18:10:50 +0200 Subject: [PATCH] [ai] random mode --- USER_MANUAL.md | 1 + src/main.cpp | 38 +++++++++++++++++++++++++++++++++++--- src/main.h | 10 ++++++---- web/index.html | 1 + 4 files changed, 43 insertions(+), 7 deletions(-) diff --git a/USER_MANUAL.md b/USER_MANUAL.md index 6379127..c980ba6 100644 --- a/USER_MANUAL.md +++ b/USER_MANUAL.md @@ -133,6 +133,7 @@ Option A: Map through the web app (recommended) 5) Choose Mode: - Single (s): Play just the selected song/file once. - Folder (f): Play all files in the selected folder once, then stop. + - Random (r): Play all files in the selected folder in random order once, then stop. - Continuous (c): Loop through the selected folder continuously. 6) Click “Update Mapping”. diff --git a/src/main.cpp b/src/main.cpp index 3b8e845..109cdb6 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -14,6 +14,9 @@ #include #include //RFID Reader #include +#if defined(ESP32) +#include +#endif // define pins for RFID #define CS_RFID 32 // SIC, tried 4 and 32 but only this worked! @@ -127,6 +130,7 @@ uint16_t voltage_threshold_counter = 0; size_t free_heap = 0; +// webrequest_blockings is a simple watchdog counter that tracks how long at least one HTTP request has been “active” (not yet disconnected) according to the AsyncWebServer. int webrequest_blockings = 0; void activateSD() @@ -534,8 +538,8 @@ void playSongByRFID(String id) targetIsFolder = true; } - // If the mapping targets a folder (or explicitly 'f' mode), activate folder tracking - if (targetIsFolder || entry.mode == 'f') + // If the mapping targets a folder (or explicitly 'f' or 'r' mode), activate folder tracking + if (targetIsFolder || entry.mode == 'f' || entry.mode == 'r') { folderModeActive = true; folderRootNode = currentNode; @@ -543,6 +547,19 @@ void playSongByRFID(String id) folderFlatList.clear(); folderRootNode->buildFlatMP3List(folderFlatList); + // If random-folder mode requested, shuffle the flat list once + if (entry.mode == 'r' && folderFlatList.size() > 1) + { + // Fisher-Yates shuffle using Arduino random() + for (int i = (int)folderFlatList.size() - 1; i > 0; --i) + { + int j = (int)random((long)(i + 1)); // 0..i + auto tmp = folderFlatList[i]; + folderFlatList[i] = folderFlatList[j]; + folderFlatList[j] = tmp; + } + } + // Find index of current playing file within the folder list for (size_t i = 0; i < folderFlatList.size(); i++) { @@ -554,6 +571,14 @@ void playSongByRFID(String id) break; } } + // If random mode, make sure the current file is at the start so next advance is random + if (entry.mode == 'r' && folderFlatIndex > 0 && folderFlatIndex < (int)folderFlatList.size()) + { + auto tmp = folderFlatList[0]; + folderFlatList[0] = folderFlatList[folderFlatIndex]; + folderFlatList[folderFlatIndex] = tmp; + folderFlatIndex = 0; + } // Compute root path for safety checks (path up to last '/') int lastSlash = mp3File.lastIndexOf('/'); @@ -1337,6 +1362,13 @@ void setup() activateSD(); Serial.println("SD initialization done."); + // Seed RNG for shuffle mode +#if defined(ESP32) + randomSeed(esp_random()); +#else + randomSeed((uint32_t)micros()); +#endif + // Load configuration from SD card Serial.println("Loading configuration..."); loadConfig(); @@ -1503,7 +1535,7 @@ const String getSysDir(const String filename) void loop() { - if (webreq_cnt > 0 && webrequest_blockings > 5000) { + if (webreq_cnt > 0 && webrequest_blockings > 10000) { Serial.println("excessive webrequest blocking!"); webreq_cnt = 0; webrequest_blockings = 0; diff --git a/src/main.h b/src/main.h index 54051cf..0ea461e 100644 --- a/src/main.h +++ b/src/main.h @@ -84,10 +84,12 @@ boolean prepareSleepMode = false; class DirectoryNode; -// Mapping entry that stores target (file or folder) and playback mode: -// 's' = single (default) - play only the selected song (or single file in folder) -// 'f' = folder - play files inside the selected folder, then stop -// 'c' = continuous - continuously play (like previous continuousMode) +/* Mapping entry that stores target (file or folder) and playback mode: + * 's' = single (default) - play only the selected song (or single file in folder) + * 'f' = folder - play files inside the selected folder, then stop + * 'r' = random-folder - play files inside the selected folder in random order, then stop + * 'c' = continuous - continuously play (like previous continuousMode) + */ struct MappingEntry { String target; char mode; diff --git a/web/index.html b/web/index.html index 1e07812..68bc744 100644 --- a/web/index.html +++ b/web/index.html @@ -120,6 +120,7 @@