diff --git a/src/activities/network/CrossPointWebServerActivity.cpp b/src/activities/network/CrossPointWebServerActivity.cpp index 228f84d4..f22fd9b2 100644 --- a/src/activities/network/CrossPointWebServerActivity.cpp +++ b/src/activities/network/CrossPointWebServerActivity.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include @@ -316,20 +317,18 @@ void CrossPointWebServerActivity::loop() { timeSinceLastHandleClient); } - // Time-based processing: handle requests for up to 50ms per loop iteration - // This is more efficient than a fixed iteration count because: - // 1. Processes more data when available (during uploads) - // 2. Returns quickly when idle (no wasted spinning) - // 3. yield() between calls lets WiFi stack receive more data - constexpr unsigned long TIME_BUDGET_MS = 50; - const unsigned long handleStart = millis(); - - while (webServer->isRunning() && (millis() - handleStart) < TIME_BUDGET_MS) { + // Process HTTP requests with watchdog safety + // Use iteration-based approach with watchdog resets to prevent crashes + // Higher iteration count improves throughput during uploads + constexpr int MAX_ITERATIONS = 200; + for (int i = 0; i < MAX_ITERATIONS && webServer->isRunning(); i++) { webServer->handleClient(); - // Yield between calls to let WiFi stack process incoming packets - // This is critical for throughput - without it, TCP flow control - // throttles the sender because our receive buffer fills up + // Yield every iteration to let WiFi stack receive more packets yield(); + // Reset watchdog every 50 iterations to prevent timeout during uploads + if ((i & 0x3F) == 0) { // Every 64 iterations + esp_task_wdt_reset(); + } } lastHandleClientTime = millis(); } diff --git a/src/network/CrossPointWebServer.cpp b/src/network/CrossPointWebServer.cpp index 68db44f0..530c7c1c 100644 --- a/src/network/CrossPointWebServer.cpp +++ b/src/network/CrossPointWebServer.cpp @@ -304,8 +304,9 @@ static bool uploadSuccess = false; static String uploadError = ""; // Upload write buffer - batches small writes into larger SD card operations -// This improves throughput by reducing the number of SD write syscalls -constexpr size_t UPLOAD_BUFFER_SIZE = 8192; // 8KB buffer +// 4KB is a good balance: large enough to reduce syscall overhead, small enough +// to keep individual write times short and avoid watchdog issues +constexpr size_t UPLOAD_BUFFER_SIZE = 4096; // 4KB buffer static uint8_t uploadBuffer[UPLOAD_BUFFER_SIZE]; static size_t uploadBufferPos = 0; @@ -316,10 +317,12 @@ static size_t writeCount = 0; static bool flushUploadBuffer() { if (uploadBufferPos > 0 && uploadFile) { + esp_task_wdt_reset(); // Reset watchdog before potentially slow SD write const unsigned long writeStart = millis(); const size_t written = uploadFile.write(uploadBuffer, uploadBufferPos); totalWriteTime += millis() - writeStart; writeCount++; + esp_task_wdt_reset(); // Reset watchdog after SD write if (written != uploadBufferPos) { Serial.printf("[%lu] [WEB] [UPLOAD] Buffer flush failed: expected %d, wrote %d\n", millis(), uploadBufferPos, @@ -385,12 +388,14 @@ void CrossPointWebServer::handleUpload() const { SdMan.remove(filePath.c_str()); } - // Open file for writing + // Open file for writing - this can be slow due to FAT cluster allocation + esp_task_wdt_reset(); if (!SdMan.openFileForWrite("WEB", filePath, uploadFile)) { uploadError = "Failed to create file on SD card"; Serial.printf("[%lu] [WEB] [UPLOAD] FAILED to create file: %s\n", millis(), filePath.c_str()); return; } + esp_task_wdt_reset(); Serial.printf("[%lu] [WEB] [UPLOAD] File created successfully: %s\n", millis(), filePath.c_str()); } else if (upload.status == UPLOAD_FILE_WRITE) {