[ai] refactoring, fixes, still a directory problem

This commit is contained in:
Stefan Ostermann 2025-10-05 23:09:10 +02:00
parent 33701636af
commit dc735c044f
2 changed files with 146 additions and 96 deletions

View File

@ -18,73 +18,16 @@
#include <esp_system.h> #include <esp_system.h>
#endif #endif
// define pins for RFID #include <vector>
#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 "globals.h" #include "globals.h"
#include "main.h"
#include "DirectoryNode.h" #include "DirectoryNode.h"
#include "config.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 <vector>
static std::vector<std::pair<DirectoryNode *, int>> 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). /* Simple spinlock using older GCC sync builtins (no libatomic required).
sd_lock_acquire() will block (with a small delay) until the lock is free. 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); __sync_lock_release(&sd_lock_flag);
} }
MFRC522 rfid(CS_RFID, RST_RFID); // instatiate a MFRC522 reader object. volatile uint8_t dir_lock_flag = 0;
/* Lightweight spinlock to guard directory tree access (rootNode) */
TaskHandle_t RfidTask; static inline void dir_lock_acquire()
{
bool asyncStop = false; while (__sync_lock_test_and_set(&dir_lock_flag, 1))
{
bool asyncStart = false; delay(1);
}
bool asyncTogglePlayPause = false; }
static inline void dir_lock_release()
bool asyncNext = false; {
__sync_lock_release(&dir_lock_flag);
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;
// 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. // 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; int webrequest_blockings = 0;
@ -316,8 +247,10 @@ void handleUpload(AsyncWebServerRequest *request, String filename, size_t index,
Serial.println(logBuffer); Serial.println(logBuffer);
logBuffer.clear(); logBuffer.clear();
// Rebuild directory tree to include new file // Rebuild directory tree to include new file (guarded)
dir_lock_acquire();
rootNode.buildDirectoryTree("/"); rootNode.buildDirectoryTree("/");
dir_lock_release();
request->send(200, "text/plain", "Upload successful"); request->send(200, "text/plain", "Upload successful");
} }
@ -341,8 +274,10 @@ void handleMoveFile(AsyncWebServerRequest *request)
SD.rename(from, to); SD.rename(from, to);
sd_lock_release(); sd_lock_release();
Serial.println("Moved file: " + from + " to " + to); 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("/"); rootNode.buildDirectoryTree("/");
dir_lock_release();
request->send(200, "text/plain", "File moved successfully."); request->send(200, "text/plain", "File moved successfully.");
} }
else else
@ -364,8 +299,10 @@ void handleDeleteFile(AsyncWebServerRequest *request)
SD.remove(filename.c_str()); SD.remove(filename.c_str());
sd_lock_release(); sd_lock_release();
Serial.println("Deleted file: " + filename); 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("/"); rootNode.buildDirectoryTree("/");
dir_lock_release();
request->send(200, "text/plain", "File deleted successfully."); request->send(200, "text/plain", "File deleted successfully.");
} }
else else
@ -924,7 +861,10 @@ String processor(const String &var)
{ {
if (var == "DIRECTORY") if (var == "DIRECTORY")
{ {
return rootNode.getDirectoryStructureHTML(); dir_lock_acquire();
String out = rootNode.getDirectoryStructureHTML();
dir_lock_release();
return out;
} }
if (var == "MAPPING") if (var == "MAPPING")
@ -1277,16 +1217,29 @@ server.on("/", HTTP_GET, [](AsyncWebServerRequest *request)
{ {
webreq_enter(); webreq_enter();
request->onDisconnect([](){ webreq_exit(); }); request->onDisconnect([](){ webreq_exit(); });
String html = processor(String("DIRECTORY")); // Stream the response to avoid lifetime issues with temporary Strings and to prevent wrong Content-Length
request->send(200, "text/html; charset=UTF-8", html); 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) server.on("/mapping", HTTP_GET, [](AsyncWebServerRequest *request)
{ {
webreq_enter(); webreq_enter();
request->onDisconnect([](){ webreq_exit(); }); 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")); 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) server.on("/state", HTTP_GET, [](AsyncWebServerRequest *request)
@ -1294,7 +1247,10 @@ server.on("/", HTTP_GET, [](AsyncWebServerRequest *request)
webreq_enter(); webreq_enter();
request->onDisconnect([](){ webreq_exit(); }); request->onDisconnect([](){ webreq_exit(); });
String state = getState(); 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) server.on("/start", HTTP_GET, [](AsyncWebServerRequest *request)
{ {
@ -1549,12 +1505,14 @@ const String getSysDir(const String filename)
void loop() void loop()
{ {
if (webreq_cnt > 0 && webrequest_blockings > 10000) { if (webreq_cnt > 0 && webrequest_blockings > MAX_WEBREQUEST_BLOCKINGS) {
Serial.println("excessive webrequest blocking!"); Serial.println("excessive webrequest blocking - suppress reset");
webreq_cnt = 0; // Avoid resetting server mid-response to prevent mixing headers/body or truncation
webrequest_blockings = 0;
server.reset(); server.reset();
init_webserver(); init_webserver();
webreq_cnt = 0;
webrequest_blockings = 0;
server.begin(); server.begin();
} }

View File

@ -1,6 +1,98 @@
#ifndef MAIN_H_ #ifndef MAIN_H_
#define 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<std::pair<DirectoryNode *, int>> 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 stop();
void start(); void start();