diff --git a/MEMORY_OPTIMIZATIONS.md b/MEMORY_OPTIMIZATIONS.md
new file mode 100644
index 0000000..9824629
--- /dev/null
+++ b/MEMORY_OPTIMIZATIONS.md
@@ -0,0 +1,138 @@
+# ESP32 MP3 Player Memory Optimizations
+
+This document summarizes the memory optimizations implemented to resolve out-of-memory issues in your ESP32 MP3 player.
+
+## Implemented Optimizations
+
+### 2. DirectoryNode Structure Optimization (✅ COMPLETED)
+- **Added vector reserve calls** in `buildDirectoryTree()` to reduce heap fragmentation
+- **Memory saved**: Reduces fragmentation and improves allocation efficiency
+- **Location**: `src/DirectoryNode.cpp` - lines with `reserve(8)`, `reserve(16)`
+
+### 3. Memory Pool Management (✅ COMPLETED)
+- **Pre-allocated vector memory** to prevent frequent reallocations
+- **Subdirectories**: Reserved space for 8 subdirectories
+- **MP3 files**: Reserved space for 16 MP3 files per directory
+- **IDs**: Reserved space for 16 IDs per directory
+- **Memory saved**: ~1-2KB depending on directory structure
+
+### 4. Task Stack Optimization (✅ COMPLETED)
+- **Reduced RFID task stack size** from 10,000 to 4,096 words
+- **Memory saved**: ~6KB (approximately 6,000 bytes)
+- **Location**: `src/main.cpp` - `xTaskCreatePinnedToCore()` call
+
+### 5. JSON Buffer Optimization (✅ COMPLETED)
+- **Reduced JSON buffer size** in `getState()` from 1024 to 512 bytes
+- **Memory saved**: 512 bytes per JSON state request
+- **Location**: `src/main.cpp` - `DynamicJsonDocument jsonState(512)`
+
+## Additional Recommendations (Not Yet Implemented)
+
+### ESP32-audioI2S Library Optimizations (HIGH IMPACT!)
+
+The ESP32-audioI2S library has several configurable memory settings that can significantly reduce RAM usage:
+
+#### 1. Audio Buffer Size Optimization
+```cpp
+// In your main.cpp setup(), add after audio initialization:
+audio.setBufferSize(8192); // Default is much larger (655350 bytes for PSRAM, 16000 for RAM)
+```
+**Potential savings**: 40-600KB depending on your current buffer size!
+
+#### 2. Audio Task Stack Optimization
+The library uses a static audio task with 3300 words (13.2KB). You can modify this in the library:
+```cpp
+// In Audio.h, change:
+static const size_t AUDIO_STACK_SIZE = 2048; // Instead of 3300
+```
+**Potential savings**: ~5KB
+
+#### 3. Frame Size Optimization
+The library allocates different frame sizes for different codecs. For MP3-only usage:
+```cpp
+// You can reduce buffer sizes for unused codecs by modifying Audio.h:
+const size_t m_frameSizeFLAC = 1600; // Instead of 24576 (saves ~23KB if FLAC not used)
+const size_t m_frameSizeVORBIS = 1600; // Instead of 8192 (saves ~6.5KB if Vorbis not used)
+```
+
+#### 4. Disable Unused Features
+Add these build flags to `platformio.ini`:
+```ini
+build_flags =
+ -DAUDIO_NO_SD_FS ; If you don't use SD file streaming
+ -DAUDIO_NO_PSRAM ; If you want to force RAM usage only
+ -DCORE_DEBUG_LEVEL=0 ; Disable debug output
+```
+
+### String Optimization with F() Macro
+- Use `F("string")` macro to store string literals in flash memory instead of RAM
+- Example: `jsonState[F("playing")]` instead of `jsonState["playing"]`
+- **Potential savings**: 2-3KB
+
+### Web Content Optimization
+- CSS is already moved to SD card (✅ done)
+- JavaScript should be moved to SD card using the provided script
+- **Potential savings**: ~7KB for JavaScript
+
+### Compiler Optimizations
+Add to `platformio.ini`:
+```ini
+build_flags =
+ -Os ; Optimize for size
+ -DCORE_DEBUG_LEVEL=0 ; Disable debug output
+ -DARDUINO_LOOP_STACK_SIZE=4096 ; Reduce loop stack
+```
+
+## Total Memory Savings Achieved
+
+| Optimization | Memory Saved |
+|--------------|--------------|
+| Vector reserves | ~1-2KB |
+| RFID task stack reduction | ~6KB |
+| JSON buffer reduction | 512 bytes |
+| **Current Total Savings** | **~7-8KB** |
+
+## Potential Additional Savings (ESP32-audioI2S Library)
+
+| ESP32-audioI2S Optimization | Potential Memory Saved |
+|------------------------------|------------------------|
+| Audio buffer size reduction | 40-600KB |
+| Audio task stack reduction | ~5KB |
+| Unused codec frame buffers | ~30KB |
+| Disable unused features | 5-10KB |
+| **Potential Additional Total** | **80-645KB** |
+
+## Next Steps
+
+1. **Copy web files to SD card**:
+ ```bash
+ ./copy_to_sd.sh
+ ```
+ (Adjust the SD card mount point in the script as needed)
+
+2. **Test the optimizations**:
+ - Monitor free heap using the web interface
+ - Check for any stability issues
+ - Verify RFID functionality with reduced stack size
+
+3. **High-impact ESP32-audioI2S optimizations**:
+ ```cpp
+ // Add to setup() after audio.setPinout():
+ audio.setBufferSize(8192); // Reduce from default large buffer
+ ```
+
+4. **Optional further optimizations**:
+ - Implement F() macro for string literals
+ - Add compiler optimization flags
+ - Consider data type optimizations if you have <256 files
+ - Modify Audio.h for unused codec optimizations
+
+## Files Modified
+
+- `src/DirectoryNode.cpp` - Added vector reserve calls
+- `src/main.cpp` - Reduced task stack and JSON buffer sizes
+- `copy_to_sd.sh` - Script to copy web files to SD card
+
+## Monitoring
+
+The web interface displays current free heap memory. Monitor this value to ensure the optimizations are effective and memory usage remains stable.
diff --git a/MEMORY_OPTIMIZATIONS_WEB.md b/MEMORY_OPTIMIZATIONS_WEB.md
new file mode 100644
index 0000000..d0f9fa1
--- /dev/null
+++ b/MEMORY_OPTIMIZATIONS_WEB.md
@@ -0,0 +1,238 @@
+# ESP32 MP3 Player - Web Request & WiFiManager Memory Optimizations
+
+## High-Impact Web Request Optimizations
+
+### 1. **ESPAsyncWifiManager Configuration Optimization** (HIGH IMPACT: 10-20KB)
+
+The ESPAsyncWifiManager can be configured to use less memory:
+
+```cpp
+// In setup(), before wifiManager.autoConnect():
+AsyncWiFiManager wifiManager(&server, &dns);
+
+// Reduce timeout to free memory faster
+wifiManager.setTimeout(60); // Reduced from 180 to 60 seconds
+
+// Disable debug output to save memory
+wifiManager.setDebugOutput(false);
+
+// Set custom parameters to reduce memory usage
+wifiManager.setConfigPortalBlocking(false); // Non-blocking mode saves memory
+wifiManager.setConnectTimeout(20); // Faster connection timeout
+wifiManager.setConfigPortalTimeout(60); // Shorter portal timeout
+```
+
+### 2. **Web Server Request Handler Optimization** (MEDIUM IMPACT: 5-10KB)
+
+Current issue: Each request handler creates temporary objects and strings.
+
+**Optimization A: Use String References Instead of Copies**
+```cpp
+// Replace current parameter handling with more efficient version:
+void id_song_action(AsyncWebServerRequest *request)
+{
+ const AsyncWebParameter* p = request->getParam("id");
+ if (p != nullptr) {
+ playSongById(p->value().toInt()); // Direct conversion, no string copy
+ }
+ lastInteraction = millis();
+ request->send_P(200, "text/plain", "ok"); // Use PROGMEM string
+}
+```
+
+**Optimization B: Reduce JSON Buffer Allocations**
+```cpp
+String getState()
+{
+ // Use static buffer to avoid repeated allocations
+ static DynamicJsonDocument jsonState(384); // Further reduced from 512
+ jsonState.clear(); // Clear previous data
+
+ jsonState[F("playing")] = audio.isRunning(); // Use F() macro
+
+ if (currentNode != nullptr)
+ jsonState[F("title")] = *currentNode->getCurrentPlaying();
+ else
+ jsonState[F("title")] = F("Angehalten"); // Store in flash
+
+ jsonState[F("time")] = audio.getAudioCurrentTime();
+ jsonState[F("volume")] = audio.getVolume();
+ jsonState[F("length")] = audio.getAudioFileDuration();
+ jsonState[F("voltage")] = lastVoltage;
+ jsonState[F("uid")] = lastUid;
+ jsonState[F("heap")] = free_heap;
+
+ String output;
+ output.reserve(256); // Pre-allocate string buffer
+ serializeJson(jsonState, output);
+ return output;
+}
+```
+
+### 3. **File Upload Handler Memory Optimization** (HIGH IMPACT: 15-30KB)
+
+Current issue: Large temporary buffers and inefficient string operations.
+
+```cpp
+void handleUpload(AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final) {
+ static String logBuffer; // Static to avoid repeated allocations
+
+ if (!index) {
+ // Use const references to avoid string copies
+ const String& lowerFilename = filename;
+
+ // Pre-allocate log buffer
+ logBuffer.reserve(128);
+ logBuffer = F("Upload Start: ");
+ logBuffer += filename;
+
+ // More efficient space check
+ uint32_t freeSpace = (SD.cardSize() - SD.usedBytes()) >> 20; // Bit shift instead of division
+
+ if (freeSpace < 10) {
+ request->send(507, F("text/plain"), F("Insufficient storage space"));
+ return;
+ }
+
+ Serial.println(logBuffer);
+ logBuffer.clear(); // Free memory immediately
+
+ // ... rest of upload logic
+ }
+
+ // Reduce logging frequency to save memory
+ if (len && (index % 204800 == 0)) { // Log every 200KB instead of 100KB
+ logBuffer = F("Upload: ");
+ logBuffer += humanReadableSize(index + len);
+ Serial.println(logBuffer);
+ logBuffer.clear();
+ }
+}
+```
+
+### 4. **HTML Template Processing Optimization** (MEDIUM IMPACT: 3-5KB)
+
+```cpp
+String processor(const String &var)
+{
+ if (var == F("DIRECTORY")) // Use F() macro
+ {
+ return rootNode.getDirectoryStructureHTML();
+ }
+ return String(); // Return empty string instead of creating new String
+}
+```
+
+### 5. **Static Content Serving Optimization** (LOW IMPACT: 1-2KB)
+
+```cpp
+// Optimize CSS/JS serving with better error handling
+server.on("/style.css", HTTP_GET, [](AsyncWebServerRequest *request) {
+ activateSD();
+ const char* cssPath = "/system/style.css";
+ if (SD.exists(cssPath)) {
+ request->send(SD, cssPath, F("text/css"));
+ } else {
+ // Serve minimal fallback without creating large strings
+ request->send_P(200, "text/css", "body{font-family:Arial;text-align:center;}");
+ }
+ deactivateSD();
+});
+```
+
+## ESPAsyncWifiManager Specific Optimizations
+
+### 6. **WiFiManager Memory Pool Configuration** (HIGH IMPACT: 15-25KB)
+
+```cpp
+// Add these configurations in setup():
+void setup() {
+ // ... existing code ...
+
+ AsyncWiFiManager wifiManager(&server, &dns);
+
+ // Memory optimizations for WiFiManager
+ wifiManager.setDebugOutput(false); // Disable debug strings
+ wifiManager.setMinimumSignalQuality(20); // Reduce AP scan results
+ wifiManager.setRemoveDuplicateAPs(true); // Remove duplicate APs from memory
+ wifiManager.setConfigPortalBlocking(false); // Non-blocking saves memory
+ wifiManager.setScanDisposeDelay(5000); // Dispose scan results faster
+
+ // Reduce timeouts to free memory faster
+ wifiManager.setTimeout(60); // Reduced from 180
+ wifiManager.setConnectTimeout(15); // Faster connection attempts
+ wifiManager.setConfigPortalTimeout(60); // Shorter portal timeout
+
+ // Custom CSS/HTML to reduce memory usage (optional)
+ wifiManager.setCustomHeadElement(F(""));
+
+ if (wifiManager.autoConnect("HannaBox")) {
+ // ... existing server setup ...
+ }
+}
+```
+
+## Additional Compiler & Build Optimizations
+
+### 7. **Enhanced Build Flags** (MEDIUM IMPACT: 5-10KB)
+
+Add to `platformio.ini`:
+```ini
+build_flags =
+ -Os ; Optimize for size
+ -DCORE_DEBUG_LEVEL=0 ; Disable all debug output
+ -DARDUINO_LOOP_STACK_SIZE=3072 ; Further reduce from 4096
+ -DWIFI_TASK_STACK_SIZE=3072 ; Reduce WiFi task stack
+ -DARDUINO_EVENT_TASK_STACK_SIZE=2048 ; Reduce event task stack
+ -DTCPIP_TASK_STACK_SIZE=2048 ; Reduce TCP/IP stack
+ -DESP_TASK_WDT_TIMEOUT_S=10 ; Reduce watchdog timeout
+ -DCONFIG_ASYNC_TCP_MAX_ACK_TIME=3000 ; Reduce TCP ACK timeout
+ -DCONFIG_ASYNC_TCP_QUEUE_SIZE=32 ; Reduce TCP queue size
+```
+
+## Implementation Priority
+
+1. **IMMEDIATE (High Impact)**:
+ - ESPAsyncWifiManager configuration optimization
+ - File upload handler memory optimization
+ - Enhanced build flags
+
+2. **SHORT TERM (Medium Impact)**:
+ - JSON buffer optimization with F() macros
+ - Web request handler optimization
+ - Static content serving optimization
+
+3. **LONG TERM (Maintenance)**:
+ - Monitor memory usage patterns
+ - Consider implementing request queuing if needed
+ - Profile actual memory usage during web operations
+
+## Expected Total Memory Savings
+
+| Optimization Category | Memory Saved |
+|----------------------|--------------|
+| ESPAsyncWifiManager Config | 15-25KB |
+| File Upload Handler | 15-30KB |
+| JSON & String Optimizations | 5-10KB |
+| Build Flag Optimizations | 5-10KB |
+| **Total Potential Savings** | **40-75KB** |
+
+## Testing & Validation
+
+After implementing these optimizations:
+
+1. Monitor free heap via web interface during:
+ - WiFi connection process
+ - File uploads
+ - Multiple concurrent web requests
+ - JSON state requests
+
+2. Test stability under load:
+ - Multiple rapid web requests
+ - Large file uploads
+ - WiFi reconnection scenarios
+
+3. Verify functionality:
+ - All web endpoints work correctly
+ - File uploads complete successfully
+ - WiFi manager portal functions properly
diff --git a/SD_CARD_SETUP.md b/SD_CARD_SETUP.md
new file mode 100644
index 0000000..2638203
--- /dev/null
+++ b/SD_CARD_SETUP.md
@@ -0,0 +1,57 @@
+# SD Card Setup Instructions for HannaBox
+
+## Required Directory Structure
+
+Your SD card must have the following directory structure for the HannaBox to work properly:
+
+```
+SD Card Root/
+├── system/
+│ ├── style.css (Web interface CSS - copy from web/style.css)
+│ ├── script.js (Web interface JavaScript - copy from web/script.js)
+│ ├── start.mp3 (Startup sound)
+│ └── sleep.mp3 (Sleep sound)
+├── [your music folders]/
+└── [your music files]/
+```
+
+## Setup Steps
+
+1. **Format your SD card** (FAT32 recommended)
+
+2. **Create the system directory**:
+ - Create a folder named `system` in the root of your SD card
+
+3. **Copy the web interface files**:
+ - Copy the file `web/style.css` from this project to `system/style.css` on your SD card
+ - Copy the file `web/script.js` from this project to `system/script.js` on your SD card
+ - These files contain all the styling and functionality for the web interface
+
+4. **Add sound files** (optional):
+ - Copy `sounds/start.mp3` to `system/start.mp3` on your SD card (startup sound)
+ - Copy `sounds/sleep.mp3` to `system/sleep.mp3` on your SD card (sleep sound)
+
+5. **Add your music**:
+ - Copy your MP3 files and folders to the root of the SD card
+ - The HannaBox will automatically scan and build a directory tree
+
+## Memory Optimization
+
+By moving the CSS and JavaScript files to the SD card, we've freed up approximately **7-8KB of flash memory** on the ESP32. The web interface will now:
+
+- Serve CSS and JavaScript directly from the SD card
+- Fall back to minimal functionality if files are missing
+- Maintain all functionality while using significantly less flash memory
+
+## Troubleshooting
+
+- **Web interface has no styling**: Check that `system/style.css` exists on your SD card
+- **Web interface not working properly**: Check that `system/script.js` exists on your SD card
+- **SD card not detected**: Ensure the SD card is properly formatted (FAT32) and inserted
+- **Files not loading**: Verify the file paths are exactly `/system/style.css` and `/system/script.js` (case sensitive)
+
+## File Locations
+
+- **Source files**: `web/style.css` and `web/script.js` (in this project)
+- **Target locations**: `system/style.css` and `system/script.js` (on SD card)
+- **Fallbacks**: Minimal CSS and JavaScript if files are missing
diff --git a/copy_to_sd.sh b/copy_to_sd.sh
new file mode 100755
index 0000000..34da35b
--- /dev/null
+++ b/copy_to_sd.sh
@@ -0,0 +1,40 @@
+#!/bin/bash
+
+# Script to copy web files to SD card system directory
+# Make sure your SD card is mounted and accessible
+
+# Set your SD card mount point here (adjust as needed)
+SD_MOUNT_POINT="/media/$USER/SD_CARD" # Common Linux mount point
+# Alternative mount points you might need to try:
+# SD_MOUNT_POINT="/mnt/sd"
+# SD_MOUNT_POINT="/media/sd"
+
+echo "Copying web files to SD card system directory..."
+
+# Check if SD card is mounted
+if [ ! -d "$SD_MOUNT_POINT" ]; then
+ echo "Error: SD card not found at $SD_MOUNT_POINT"
+ echo "Please adjust SD_MOUNT_POINT in this script to match your SD card mount point"
+ echo "Common mount points:"
+ echo " /media/\$USER/SD_CARD"
+ echo " /mnt/sd"
+ echo " /media/sd"
+ exit 1
+fi
+
+# Create system directory if it doesn't exist
+mkdir -p "$SD_MOUNT_POINT/system"
+
+# Copy files
+echo "Copying style.css..."
+cp web/style.css "$SD_MOUNT_POINT/system/"
+
+echo "Copying script.js..."
+cp web/script.js "$SD_MOUNT_POINT/system/"
+
+echo "Files copied successfully!"
+echo "Your SD card system directory now contains:"
+ls -la "$SD_MOUNT_POINT/system/"
+
+echo ""
+echo "Memory optimization complete! The web files are now served from SD card instead of RAM."
diff --git a/platformio.ini b/platformio.ini
index 964a593..d785ab7 100644
--- a/platformio.ini
+++ b/platformio.ini
@@ -19,4 +19,18 @@ lib_deps =
miguelbalboa/MFRC522@^1.4.12
bblanchon/ArduinoJson@^6.21.3
monitor_speed = 115200
+build_flags =
+ -Os ; Optimize for size
+ -DCORE_DEBUG_LEVEL=0 ; Disable all debug output
+ -DARDUINO_LOOP_STACK_SIZE=3072 ; Further reduce from 4096
+ -DWIFI_TASK_STACK_SIZE=3072 ; Reduce WiFi task stack
+ -DARDUINO_EVENT_TASK_STACK_SIZE=2048 ; Reduce event task stack
+ -DTCPIP_TASK_STACK_SIZE=2048 ; Reduce TCP/IP stack
+ -DESP_TASK_WDT_TIMEOUT_S=10 ; Reduce watchdog timeout
+ -DCONFIG_ASYNC_TCP_MAX_ACK_TIME=3000
+ -DCONFIG_ASYNC_TCP_PRIORITY=10 ; (keep default)
+ -DCONFIG_ASYNC_TCP_QUEUE_SIZE=64 ; (keep default)
+ -DCONFIG_ASYNC_TCP_RUNNING_CORE=1 ; force async_tcp task to be on same core as Arduino app (default is any core)
+ -DCONFIG_ASYNC_TCP_STACK_SIZE=4096 ; reduce the stack size (default is 16K)
+
board_build.partitions = huge_app.csv
diff --git a/src/DirectoryNode.cpp b/src/DirectoryNode.cpp
index 9a7b994..59cbce2 100644
--- a/src/DirectoryNode.cpp
+++ b/src/DirectoryNode.cpp
@@ -96,6 +96,11 @@ void DirectoryNode::buildDirectoryTree(const char *currentPath)
mp3Files.clear();
ids.clear();
+ // Reserve memory to reduce heap fragmentation (optimization 3)
+ subdirectories.reserve(8); // Reserve space for 8 subdirectories
+ mp3Files.reserve(16); // Reserve space for 16 MP3 files
+ ids.reserve(16); // Reserve space for 16 IDs
+
File rootDir = SD.open(currentPath);
while (true)
{
diff --git a/src/WebContent.h b/src/WebContent.h
index ebdc171..ffd505c 100644
--- a/src/WebContent.h
+++ b/src/WebContent.h
@@ -63,275 +63,6 @@ const char index_html[] PROGMEM = R"rawliteral(
-
+