Fix watchdog crashes during file uploads

- Add esp_task_wdt_reset() calls throughout upload path:
  - Before/after SD card file creation (FAT allocation can be slow)
  - Before/after buffer flush writes
  - Every 64 iterations in handleClient loop

- Reduce upload buffer from 8KB to 4KB
  - Keeps individual write times shorter
  - Reduces watchdog risk while maintaining batching benefit

- Switch from time-based to iteration-based handleClient loop
  - More predictable behavior
  - 200 iterations with watchdog resets every 64
This commit is contained in:
Claude 2026-01-12 23:12:56 +00:00
parent 9d7d209d84
commit 4767be7c01
No known key found for this signature in database
2 changed files with 19 additions and 15 deletions

View File

@ -4,6 +4,7 @@
#include <ESPmDNS.h> #include <ESPmDNS.h>
#include <GfxRenderer.h> #include <GfxRenderer.h>
#include <WiFi.h> #include <WiFi.h>
#include <esp_task_wdt.h>
#include <qrcode.h> #include <qrcode.h>
#include <cstddef> #include <cstddef>
@ -316,20 +317,18 @@ void CrossPointWebServerActivity::loop() {
timeSinceLastHandleClient); timeSinceLastHandleClient);
} }
// Time-based processing: handle requests for up to 50ms per loop iteration // Process HTTP requests with watchdog safety
// This is more efficient than a fixed iteration count because: // Use iteration-based approach with watchdog resets to prevent crashes
// 1. Processes more data when available (during uploads) // Higher iteration count improves throughput during uploads
// 2. Returns quickly when idle (no wasted spinning) constexpr int MAX_ITERATIONS = 200;
// 3. yield() between calls lets WiFi stack receive more data for (int i = 0; i < MAX_ITERATIONS && webServer->isRunning(); i++) {
constexpr unsigned long TIME_BUDGET_MS = 50;
const unsigned long handleStart = millis();
while (webServer->isRunning() && (millis() - handleStart) < TIME_BUDGET_MS) {
webServer->handleClient(); webServer->handleClient();
// Yield between calls to let WiFi stack process incoming packets // Yield every iteration to let WiFi stack receive more packets
// This is critical for throughput - without it, TCP flow control
// throttles the sender because our receive buffer fills up
yield(); yield();
// Reset watchdog every 50 iterations to prevent timeout during uploads
if ((i & 0x3F) == 0) { // Every 64 iterations
esp_task_wdt_reset();
}
} }
lastHandleClientTime = millis(); lastHandleClientTime = millis();
} }

View File

@ -304,8 +304,9 @@ static bool uploadSuccess = false;
static String uploadError = ""; static String uploadError = "";
// Upload write buffer - batches small writes into larger SD card operations // Upload write buffer - batches small writes into larger SD card operations
// This improves throughput by reducing the number of SD write syscalls // 4KB is a good balance: large enough to reduce syscall overhead, small enough
constexpr size_t UPLOAD_BUFFER_SIZE = 8192; // 8KB buffer // 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 uint8_t uploadBuffer[UPLOAD_BUFFER_SIZE];
static size_t uploadBufferPos = 0; static size_t uploadBufferPos = 0;
@ -316,10 +317,12 @@ static size_t writeCount = 0;
static bool flushUploadBuffer() { static bool flushUploadBuffer() {
if (uploadBufferPos > 0 && uploadFile) { if (uploadBufferPos > 0 && uploadFile) {
esp_task_wdt_reset(); // Reset watchdog before potentially slow SD write
const unsigned long writeStart = millis(); const unsigned long writeStart = millis();
const size_t written = uploadFile.write(uploadBuffer, uploadBufferPos); const size_t written = uploadFile.write(uploadBuffer, uploadBufferPos);
totalWriteTime += millis() - writeStart; totalWriteTime += millis() - writeStart;
writeCount++; writeCount++;
esp_task_wdt_reset(); // Reset watchdog after SD write
if (written != uploadBufferPos) { if (written != uploadBufferPos) {
Serial.printf("[%lu] [WEB] [UPLOAD] Buffer flush failed: expected %d, wrote %d\n", millis(), 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()); 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)) { if (!SdMan.openFileForWrite("WEB", filePath, uploadFile)) {
uploadError = "Failed to create file on SD card"; uploadError = "Failed to create file on SD card";
Serial.printf("[%lu] [WEB] [UPLOAD] FAILED to create file: %s\n", millis(), filePath.c_str()); Serial.printf("[%lu] [WEB] [UPLOAD] FAILED to create file: %s\n", millis(), filePath.c_str());
return; return;
} }
esp_task_wdt_reset();
Serial.printf("[%lu] [WEB] [UPLOAD] File created successfully: %s\n", millis(), filePath.c_str()); Serial.printf("[%lu] [WEB] [UPLOAD] File created successfully: %s\n", millis(), filePath.c_str());
} else if (upload.status == UPLOAD_FILE_WRITE) { } else if (upload.status == UPLOAD_FILE_WRITE) {