hannabox/MEMORY_OPTIMIZATIONS_WEB.md

7.7 KiB

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:

// 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

// 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

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.

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)

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)

// 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)

// 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("<style>body{font-size:14px;}</style>"));
    
    if (wifiManager.autoConnect("HannaBox")) {
        // ... existing server setup ...
    }
}

Additional Compiler & Build Optimizations

7. Enhanced Build Flags (MEDIUM IMPACT: 5-10KB)

Add to platformio.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