diff --git a/src/CrossPointWebServer.cpp b/src/CrossPointWebServer.cpp index a4e70bba..9bc0bcd7 100644 --- a/src/CrossPointWebServer.cpp +++ b/src/CrossPointWebServer.cpp @@ -107,19 +107,30 @@ void CrossPointWebServer::begin() { void CrossPointWebServer::stop() { if (!running || !server) { + Serial.printf("[%lu] [WEB] stop() called but already stopped (running=%d, server=%p)\n", millis(), running, server); return; } + Serial.printf("[%lu] [WEB] STOP INITIATED - setting running=false first\n", millis()); + running = false; // Set this FIRST to prevent handleClient from using server + Serial.printf("[%lu] [WEB] [MEM] Free heap before stop: %d bytes\n", millis(), ESP.getFreeHeap()); + // Add delay to allow any in-flight handleClient() calls to complete + delay(100); + Serial.printf("[%lu] [WEB] Waited 100ms for handleClient to finish\n", millis()); + server->stop(); Serial.printf("[%lu] [WEB] [MEM] Free heap after server->stop(): %d bytes\n", millis(), ESP.getFreeHeap()); + // Add another delay before deletion to ensure server->stop() completes + delay(50); + Serial.printf("[%lu] [WEB] Waited 50ms before deleting server\n", millis()); + delete server; server = nullptr; - running = false; - Serial.printf("[%lu] [WEB] Web server stopped\n", millis()); + Serial.printf("[%lu] [WEB] Web server stopped and deleted\n", millis()); Serial.printf("[%lu] [WEB] [MEM] Free heap after delete server: %d bytes\n", millis(), ESP.getFreeHeap()); // Note: Static upload variables (uploadFileName, uploadPath, uploadError) are declared @@ -129,14 +140,25 @@ void CrossPointWebServer::stop() { void CrossPointWebServer::handleClient() { static unsigned long lastDebugPrint = 0; - if (running && server) { - // Print debug every 10 seconds to confirm handleClient is being called - if (millis() - lastDebugPrint > 10000) { - Serial.printf("[%lu] [WEB] handleClient active, server running on port %d\n", millis(), port); - lastDebugPrint = millis(); - } - server->handleClient(); + + // Check running flag FIRST before accessing server + if (!running) { + return; } + + // Double-check server pointer is valid + if (!server) { + Serial.printf("[%lu] [WEB] WARNING: handleClient called with null server!\n", millis()); + return; + } + + // Print debug every 10 seconds to confirm handleClient is being called + if (millis() - lastDebugPrint > 10000) { + Serial.printf("[%lu] [WEB] handleClient active, server running on port %d\n", millis(), port); + lastDebugPrint = millis(); + } + + server->handleClient(); } void CrossPointWebServer::handleRoot() { @@ -453,6 +475,12 @@ void CrossPointWebServer::handleUpload() { static unsigned long uploadStartTime = 0; static size_t lastLoggedSize = 0; + // Safety check: ensure server is still valid + if (!running || !server) { + Serial.printf("[%lu] [WEB] [UPLOAD] ERROR: handleUpload called but server not running!\n", millis()); + return; + } + HTTPUpload& upload = server->upload(); if (upload.status == UPLOAD_FILE_START) { diff --git a/src/activities/network/WifiScreen.cpp b/src/activities/network/WifiScreen.cpp index 77c3fc6e..d3a2efd2 100644 --- a/src/activities/network/WifiScreen.cpp +++ b/src/activities/network/WifiScreen.cpp @@ -48,37 +48,59 @@ void WifiScreen::onEnter() { } void WifiScreen::onExit() { + Serial.printf("[%lu] [WIFI] ========== onExit START ==========\n", millis()); Serial.printf("[%lu] [WIFI] [MEM] Free heap at onExit start: %d bytes\n", millis(), ESP.getFreeHeap()); // Stop any ongoing WiFi scan + Serial.printf("[%lu] [WIFI] Deleting WiFi scan...\n", millis()); WiFi.scanDelete(); Serial.printf("[%lu] [WIFI] [MEM] Free heap after scanDelete: %d bytes\n", millis(), ESP.getFreeHeap()); - // Stop the web server to free memory + // CRITICAL: Stop the web server FIRST to prevent new packets from being queued + Serial.printf("[%lu] [WIFI] Stopping web server...\n", millis()); crossPointWebServer.stop(); - Serial.printf("[%lu] [WIFI] [MEM] Free heap after webserver stop: %d bytes\n", millis(), ESP.getFreeHeap()); + Serial.printf("[%lu] [WIFI] Web server stopped successfully\n", millis()); + Serial.printf("[%lu] [WIFI] [MEM] Free heap after webserver stop: %d bytes\n", millis()); - // Disconnect WiFi to free memory - WiFi.disconnect(true); + // CRITICAL: Wait for LWIP stack to flush any pending packets + // The crash occurs because WiFi.disconnect() tears down the interface while + // packets are still queued in the LWIP stack (ethernet.c, etharp.c, wlanif.c) + Serial.printf("[%lu] [WIFI] Waiting 500ms for network stack to flush pending packets...\n", millis()); + delay(500); + + // Disconnect WiFi gracefully - use disconnect(false) first to send disconnect frame + Serial.printf("[%lu] [WIFI] Disconnecting WiFi (graceful)...\n", millis()); + WiFi.disconnect(false); // false = don't erase credentials, send disconnect frame + delay(100); // Allow disconnect frame to be sent + + Serial.printf("[%lu] [WIFI] Setting WiFi mode OFF...\n", millis()); WiFi.mode(WIFI_OFF); + delay(100); // Allow WiFi hardware to fully power down + Serial.printf("[%lu] [WIFI] [MEM] Free heap after WiFi disconnect: %d bytes\n", millis(), ESP.getFreeHeap()); // Delete the display task + Serial.printf("[%lu] [WIFI] Deleting display task...\n", millis()); if (displayTaskHandle) { vTaskDelete(displayTaskHandle); displayTaskHandle = nullptr; + Serial.printf("[%lu] [WIFI] Display task deleted\n", millis()); } // Small delay to ensure task is fully deleted before cleaning up mutex + Serial.printf("[%lu] [WIFI] Waiting for task cleanup...\n", millis()); vTaskDelay(10 / portTICK_PERIOD_MS); // Now safe to delete the mutex + Serial.printf("[%lu] [WIFI] Deleting mutex...\n", millis()); if (renderingMutex) { vSemaphoreDelete(renderingMutex); renderingMutex = nullptr; + Serial.printf("[%lu] [WIFI] Mutex deleted\n", millis()); } Serial.printf("[%lu] [WIFI] [MEM] Free heap at onExit end: %d bytes\n", millis(), ESP.getFreeHeap()); + Serial.printf("[%lu] [WIFI] ========== onExit COMPLETE ==========\n", millis()); } void WifiScreen::startWifiScan() {