diff --git a/src/CrossPointWebServer.cpp b/src/CrossPointWebServer.cpp index a87ffac..252649f 100644 --- a/src/CrossPointWebServer.cpp +++ b/src/CrossPointWebServer.cpp @@ -449,6 +449,10 @@ static bool uploadSuccess = false; static String uploadError = ""; void CrossPointWebServer::handleUpload() { + static unsigned long lastWriteTime = 0; + static unsigned long uploadStartTime = 0; + static size_t lastLoggedSize = 0; + HTTPUpload& upload = server->upload(); if (upload.status == UPLOAD_FILE_START) { @@ -456,6 +460,9 @@ void CrossPointWebServer::handleUpload() { uploadSize = 0; uploadSuccess = false; uploadError = ""; + uploadStartTime = millis(); + lastWriteTime = millis(); + lastLoggedSize = 0; // Get upload path from query parameter (defaults to root if not specified) // Note: We use query parameter instead of form data because multipart form @@ -474,12 +481,13 @@ void CrossPointWebServer::handleUpload() { uploadPath = "/"; } - Serial.printf("[%lu] [WEB] Upload start: %s to path: %s\n", millis(), uploadFileName.c_str(), uploadPath.c_str()); + Serial.printf("[%lu] [WEB] [UPLOAD] START: %s to path: %s\n", millis(), uploadFileName.c_str(), uploadPath.c_str()); + Serial.printf("[%lu] [WEB] [UPLOAD] Free heap: %d bytes\n", millis(), ESP.getFreeHeap()); // Validate file extension if (!isEpubFile(uploadFileName)) { uploadError = "Only .epub files are allowed"; - Serial.printf("[%lu] [WEB] Upload rejected - not an epub file\n", millis()); + Serial.printf("[%lu] [WEB] [UPLOAD] REJECTED - not an epub file\n", millis()); return; } @@ -490,7 +498,7 @@ void CrossPointWebServer::handleUpload() { // Check if file already exists if (SD.exists(filePath.c_str())) { - Serial.printf("[%lu] [WEB] Overwriting existing file: %s\n", millis(), filePath.c_str()); + Serial.printf("[%lu] [WEB] [UPLOAD] Overwriting existing file: %s\n", millis(), filePath.c_str()); SD.remove(filePath.c_str()); } @@ -498,20 +506,36 @@ void CrossPointWebServer::handleUpload() { uploadFile = SD.open(filePath.c_str(), FILE_WRITE); if (!uploadFile) { uploadError = "Failed to create file on SD card"; - Serial.printf("[%lu] [WEB] 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; } - Serial.printf("[%lu] [WEB] File created: %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) { if (uploadFile && uploadError.isEmpty()) { + unsigned long writeStartTime = millis(); size_t written = uploadFile.write(upload.buf, upload.currentSize); + unsigned long writeEndTime = millis(); + unsigned long writeDuration = writeEndTime - writeStartTime; + if (written != upload.currentSize) { uploadError = "Failed to write to SD card - disk may be full"; uploadFile.close(); - Serial.printf("[%lu] [WEB] Write error - expected %d, wrote %d\n", millis(), upload.currentSize, written); + Serial.printf("[%lu] [WEB] [UPLOAD] WRITE ERROR - expected %d, wrote %d\n", millis(), upload.currentSize, written); } else { uploadSize += written; + + // Log progress every 50KB or if write took >100ms + if (uploadSize - lastLoggedSize >= 51200 || writeDuration > 100) { + unsigned long timeSinceStart = millis() - uploadStartTime; + unsigned long timeSinceLastWrite = millis() - lastWriteTime; + float kbps = (uploadSize / 1024.0) / (timeSinceStart / 1000.0); + + Serial.printf("[%lu] [WEB] [UPLOAD] Progress: %d bytes (%.1f KB), %.1f KB/s, write took %lu ms, gap since last: %lu ms\n", + millis(), uploadSize, uploadSize / 1024.0, kbps, writeDuration, timeSinceLastWrite); + lastLoggedSize = uploadSize; + } + lastWriteTime = millis(); } } } else if (upload.status == UPLOAD_FILE_END) { diff --git a/src/main.cpp b/src/main.cpp index 5303ade..134538a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -202,7 +202,20 @@ void setup() { } void loop() { - delay(10); + static unsigned long lastLoopTime = 0; + static unsigned long maxLoopDuration = 0; + static unsigned long lastHandleClientTime = 0; + + unsigned long loopStartTime = millis(); + unsigned long timeSinceLastLoop = loopStartTime - lastLoopTime; + + // Reduce delay when webserver is running to allow faster handleClient() calls + // This is critical for upload performance and preventing TCP timeouts + if (crossPointWebServer.isRunning()) { + delay(1); // Minimal delay to prevent tight loop + } else { + delay(10); // Normal delay when webserver not active + } static unsigned long lastMemPrint = 0; if (Serial && millis() - lastMemPrint >= 10000) { @@ -232,12 +245,34 @@ void loop() { return; } + unsigned long activityStartTime = millis(); if (currentActivity) { currentActivity->loop(); } + unsigned long activityDuration = millis() - activityStartTime; // Handle web server requests if running if (crossPointWebServer.isRunning()) { + unsigned long timeSinceLastHandleClient = millis() - lastHandleClientTime; + + // Log if there's a significant gap between handleClient calls (>100ms) + if (lastHandleClientTime > 0 && timeSinceLastHandleClient > 100) { + Serial.printf("[%lu] [LOOP] WARNING: %lu ms gap since last handleClient (activity took %lu ms)\n", + millis(), timeSinceLastHandleClient, activityDuration); + } + crossPointWebServer.handleClient(); + lastHandleClientTime = millis(); } + + unsigned long loopDuration = millis() - loopStartTime; + if (loopDuration > maxLoopDuration) { + maxLoopDuration = loopDuration; + if (maxLoopDuration > 50) { + Serial.printf("[%lu] [LOOP] New max loop duration: %lu ms (activity: %lu ms)\n", + millis(), maxLoopDuration, activityDuration); + } + } + + lastLoopTime = loopStartTime; }