From dc735c044f59ecfea1d0fc117af84ed0dba0bc59 Mon Sep 17 00:00:00 2001 From: Stefan Ostermann Date: Sun, 5 Oct 2025 23:09:10 +0200 Subject: [PATCH] [ai] refactoring, fixes, still a directory problem --- src/main.cpp | 150 +++++++++++++++++++-------------------------------- src/main.h | 92 +++++++++++++++++++++++++++++++ 2 files changed, 146 insertions(+), 96 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 9939c8f..0caed3d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -18,73 +18,16 @@ #include #endif -// define pins for RFID -#define CS_RFID 32 // SIC, tried 4 and 32 but only this worked! -#define RST_RFID 33 -// this does not work as the irq pin is input only: -#define IRQ_RFID 34 - -// Audio DAC -#define I2S_DOUT 26 // connect to DAC pin DIN -#define I2S_BCLK 27 // connect to DAC pin BCK -#define I2S_LRC 25 // connect to DAC pin LCK - -#define BTN_START_STOP 4 // Button on XX and GND -#define BTN_NEXT 17 -#define BTN_PREV 16 - -#define CS_SDCARD 22 - -#define BAT_VOLTAGE_PIN 35 - -#define RFID_LOOP_INTERVAL 25 - -#define VOLTAGE_LOOP_INTERVAL 5000 - -#define VOLTAGE_THRESHOLD 0 - -#define SHORT_PRESS_TIME 250 - -#define LONG_PRESS_TIME 1000 - -#define MAX_VOL 15 +#include #include "globals.h" -#include "main.h" #include "DirectoryNode.h" #include "config.h" +#include "main.h" -File root; -File mp3File; -Audio audio; -uint volume = 7; -// Folder-play tracking: flattened list of files inside a mapped folder and current index -// Used when a mapping targets a folder (play folder once or loop folder) -#include -static std::vector> folderFlatList; -static int folderFlatIndex = -1; -static String folderRootPath = ""; -// Pointer to the root DirectoryNode for active folder-mode playback -DirectoryNode *folderRootNode = nullptr; - -AsyncWebServer server(80); -DNSServer dns; - -// static variable has to be instantiated outside of class definition: -uint16_t DirectoryNode::idCounter = 0; - -DirectoryNode rootNode("/"); -DirectoryNode *currentNode = nullptr; - -volatile bool newRfidInt = false; -volatile bool playButtonDown = false; -// Track if play button hold is active and if volume was adjusted during this hold -volatile bool playHoldActive = false; -volatile bool volumeAdjustedDuringHold = false; -volatile uint8_t sd_lock_flag = 0; /* Simple spinlock using older GCC sync builtins (no libatomic required). sd_lock_acquire() will block (with a small delay) until the lock is free. @@ -102,33 +45,21 @@ static inline void sd_lock_release() __sync_lock_release(&sd_lock_flag); } -MFRC522 rfid(CS_RFID, RST_RFID); // instatiate a MFRC522 reader object. - -TaskHandle_t RfidTask; - -bool asyncStop = false; - -bool asyncStart = false; - -bool asyncTogglePlayPause = false; - -bool asyncNext = false; - -bool asyncPrev = false; - -bool SDActive = false; - -bool RFIDActive = false; +volatile uint8_t dir_lock_flag = 0; +/* Lightweight spinlock to guard directory tree access (rootNode) */ +static inline void dir_lock_acquire() +{ + while (__sync_lock_test_and_set(&dir_lock_flag, 1)) + { + delay(1); + } +} +static inline void dir_lock_release() +{ + __sync_lock_release(&dir_lock_flag); +} -// Web request concurrency counter and helpers (atomic via GCC builtins) -volatile uint32_t webreq_cnt = 0; -static inline void webreq_enter() { __sync_add_and_fetch(&webreq_cnt, 1); } -static inline void webreq_exit() { __sync_sub_and_fetch(&webreq_cnt, 1); } - -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; @@ -316,8 +247,10 @@ void handleUpload(AsyncWebServerRequest *request, String filename, size_t index, Serial.println(logBuffer); logBuffer.clear(); - // Rebuild directory tree to include new file + // Rebuild directory tree to include new file (guarded) + dir_lock_acquire(); rootNode.buildDirectoryTree("/"); + dir_lock_release(); request->send(200, "text/plain", "Upload successful"); } @@ -341,8 +274,10 @@ void handleMoveFile(AsyncWebServerRequest *request) SD.rename(from, to); sd_lock_release(); Serial.println("Moved file: " + from + " to " + to); - // Rebuild directory tree to update file list + // Rebuild directory tree to update file list (guarded) + dir_lock_acquire(); rootNode.buildDirectoryTree("/"); + dir_lock_release(); request->send(200, "text/plain", "File moved successfully."); } else @@ -364,8 +299,10 @@ void handleDeleteFile(AsyncWebServerRequest *request) SD.remove(filename.c_str()); sd_lock_release(); Serial.println("Deleted file: " + filename); - // Rebuild directory tree to update file list + // Rebuild directory tree to update file list (guarded) + dir_lock_acquire(); rootNode.buildDirectoryTree("/"); + dir_lock_release(); request->send(200, "text/plain", "File deleted successfully."); } else @@ -924,7 +861,10 @@ String processor(const String &var) { if (var == "DIRECTORY") { - return rootNode.getDirectoryStructureHTML(); + dir_lock_acquire(); + String out = rootNode.getDirectoryStructureHTML(); + dir_lock_release(); + return out; } if (var == "MAPPING") @@ -1277,16 +1217,29 @@ server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) { webreq_enter(); request->onDisconnect([](){ webreq_exit(); }); - String html = processor(String("DIRECTORY")); - request->send(200, "text/html; charset=UTF-8", html); + // Stream the response to avoid lifetime issues with temporary Strings and to prevent wrong Content-Length + AsyncResponseStream* stream = request->beginResponseStream("text/html; charset=UTF-8"); + stream->addHeader("Cache-Control", "no-store"); + stream->addHeader("Connection", "close"); + // Build HTML under lock, then print into stream + dir_lock_acquire(); + String html = rootNode.getDirectoryStructureHTML(); + dir_lock_release(); + stream->print(html); + request->send(stream); }); server.on("/mapping", 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"); String html = processor(String("MAPPING")); - request->send(200, "text/html; charset=UTF-8", html); + stream->print(html); + request->send(stream); }); server.on("/state", HTTP_GET, [](AsyncWebServerRequest *request) @@ -1294,7 +1247,10 @@ server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) webreq_enter(); request->onDisconnect([](){ webreq_exit(); }); String state = getState(); - request->send(200, "application/json; charset=UTF-8", state.c_str()); }); + AsyncWebServerResponse* resp = request->beginResponse(200, "application/json; charset=UTF-8", state); + resp->addHeader("Cache-Control", "no-store"); + resp->addHeader("Connection", "close"); + request->send(resp); }); server.on("/start", HTTP_GET, [](AsyncWebServerRequest *request) { @@ -1549,12 +1505,14 @@ const String getSysDir(const String filename) void loop() { - if (webreq_cnt > 0 && webrequest_blockings > 10000) { - Serial.println("excessive webrequest blocking!"); - webreq_cnt = 0; - webrequest_blockings = 0; + if (webreq_cnt > 0 && webrequest_blockings > MAX_WEBREQUEST_BLOCKINGS) { + Serial.println("excessive webrequest blocking - suppress reset"); + // Avoid resetting server mid-response to prevent mixing headers/body or truncation server.reset(); init_webserver(); + + webreq_cnt = 0; + webrequest_blockings = 0; server.begin(); } diff --git a/src/main.h b/src/main.h index 0ea461e..8d8c439 100644 --- a/src/main.h +++ b/src/main.h @@ -1,6 +1,98 @@ #ifndef MAIN_H_ #define MAIN_H_ +// define pins for RFID +#define CS_RFID 32 // SIC, tried 4 and 32 but only this worked! +#define RST_RFID 33 +// this does not work as the irq pin is input only: +#define IRQ_RFID 34 + +// Audio DAC +#define I2S_DOUT 26 // connect to DAC pin DIN +#define I2S_BCLK 27 // connect to DAC pin BCK +#define I2S_LRC 25 // connect to DAC pin LCK + +#define BTN_START_STOP 4 // Button on XX and GND +#define BTN_NEXT 17 +#define BTN_PREV 16 + +#define CS_SDCARD 22 + +#define BAT_VOLTAGE_PIN 35 + +#define RFID_LOOP_INTERVAL 25 + +#define VOLTAGE_LOOP_INTERVAL 5000 + +#define VOLTAGE_THRESHOLD 0 + +#define SHORT_PRESS_TIME 250 + +#define LONG_PRESS_TIME 1000 + +#define MAX_WEBREQUEST_BLOCKINGS 10000 + +#define MAX_VOL 15 + +File root; +File mp3File; + +Audio audio; + +uint volume = 7; + +// Folder-play tracking: flattened list of files inside a mapped folder and current index +// Used when a mapping targets a folder (play folder once or loop folder) +static std::vector> folderFlatList; +static int folderFlatIndex = -1; +static String folderRootPath = ""; +// Pointer to the root DirectoryNode for active folder-mode playback +DirectoryNode *folderRootNode = nullptr; + +AsyncWebServer server(80); +DNSServer dns; + +// static variable has to be instantiated outside of class definition: +uint16_t DirectoryNode::idCounter = 0; + +DirectoryNode rootNode("/"); +DirectoryNode *currentNode = nullptr; + +volatile bool newRfidInt = false; +volatile bool playButtonDown = false; +// Track if play button hold is active and if volume was adjusted during this hold +volatile bool playHoldActive = false; +volatile bool volumeAdjustedDuringHold = false; +volatile uint8_t sd_lock_flag = 0; + +MFRC522 rfid(CS_RFID, RST_RFID); // instatiate a MFRC522 reader object. + +TaskHandle_t RfidTask; + +bool asyncStop = false; + +bool asyncStart = false; + +bool asyncTogglePlayPause = false; + +bool asyncNext = false; + +bool asyncPrev = false; + +bool SDActive = false; + +bool RFIDActive = false; + + +// Web request concurrency counter and helpers (atomic via GCC builtins) +volatile uint32_t webreq_cnt = 0; +static inline void webreq_enter() { __sync_add_and_fetch(&webreq_cnt, 1); } +static inline void webreq_exit() { __sync_sub_and_fetch(&webreq_cnt, 1); } + +uint16_t voltage_threshold_counter = 0; + +size_t free_heap = 0; + void stop(); void start();