// ============================================================ // ESP32 Serial Logger // Hardware: ESP-WROOM-32D DevKitC V4 // // Arduino IDE Settings: // Board: "ESP32 Dev Module" // Upload Speed: 921600 // CPU Frequency: 240MHz (WiFi/BT) // Flash Frequency: 80MHz // Flash Mode: QIO // Flash Size: 4MB (32Mb) // Partition Scheme: "Huge APP (3MB No OTA/1MB SPIFFS)" // PSRAM: Disabled // // Required Libraries: // 1. WebSocketsServer by Links2004 // (Install: Library Manager -> "WebSockets" by Markus Sattler) // 2. ArduinoJson by Benoit Blanchon v6.x // (Install: Library Manager -> "ArduinoJson") // 3. SD (built-in) // 4. SPI (built-in) // // Pin Assignments: // UART2 TX: GPIO17 | UART2 RX: GPIO16 // SD CLK: GPIO14 | SD MISO: GPIO26 | SD MOSI: GPIO13 | SD CS: GPIO15 // ============================================================ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "config.h" #include "serial_task.h" #include "sdcard_task.h" #include "web_task.h" // ============================================================ // NTP Time Sync Task // Only works in STA mode (internet required) // In AP mode, time is synced from browser via WebSocket // ============================================================ void ntpSyncTask(void *param) { Serial.println("[Task] NTP Sync started"); // Only attempt NTP if connected to external WiFi (STA mode) if (WiFi.getMode() == WIFI_AP) { Serial.println("[NTP] AP mode - skipping NTP, waiting for browser time sync"); // In AP mode, just keep task alive for potential future use while (true) { vTaskDelay(pdMS_TO_TICKS(60000)); } } // STA mode - try NTP configTime(NTP_GMT_OFFSET, NTP_DAYLIGHT_OFFSET, NTP_SERVER); struct tm timeinfo; int retries = 0; while (!getLocalTime(&timeinfo) && retries < 5) { Serial.println("[NTP] Waiting for time sync..."); vTaskDelay(pdMS_TO_TICKS(1000)); retries++; } if (getLocalTime(&timeinfo)) { Serial.printf("[NTP] Synced: %04d-%02d-%02d %02d:%02d:%02d\n", timeinfo.tm_year + 1900, timeinfo.tm_mon + 1, timeinfo.tm_mday, timeinfo.tm_hour, timeinfo.tm_min, timeinfo.tm_sec); } else { Serial.println("[NTP] Failed - will use browser time when client connects"); } // Periodic re-sync every 1 hour while (true) { vTaskDelay(pdMS_TO_TICKS(3600000)); configTime(NTP_GMT_OFFSET, NTP_DAYLIGHT_OFFSET, NTP_SERVER); Serial.println("[NTP] Time re-synced"); } } // ============================================================ // WiFi Connection (STA + fallback AP mode) // ============================================================ void wifiConnect() { Serial.println("[WiFi] Connecting to: " + String(WIFI_SSID)); WiFi.mode(WIFI_STA); WiFi.begin(WIFI_SSID, WIFI_PASSWORD); WiFi.setSleep(false); unsigned long startTime = millis(); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); if (millis() - startTime > WIFI_CONNECT_TIMEOUT) { Serial.println("\n[WiFi] STA failed! Starting AP mode..."); WiFi.mode(WIFI_AP); WiFi.softAP(WIFI_AP_SSID, WIFI_AP_PASSWORD); Serial.println("[WiFi] AP SSID: " + String(WIFI_AP_SSID)); Serial.println("[WiFi] AP IP: " + WiFi.softAPIP().toString()); return; } } Serial.println(); Serial.println("[WiFi] Connected! IP: " + WiFi.localIP().toString()); Serial.println("[WiFi] RSSI: " + String(WiFi.RSSI()) + " dBm"); } // ============================================================ // Setup // ============================================================ void setup() { Serial.begin(115200); delay(1000); Serial.println("============================================"); Serial.println(" ESP32 Serial Logger v1.0"); Serial.println(" ESP-WROOM-32D DevKitC V4"); Serial.println("============================================"); Serial.printf(" Free Heap: %d bytes\n", ESP.getFreeHeap()); Serial.printf(" CPU Freq: %d MHz\n", getCpuFrequencyMhz()); Serial.println("============================================"); // 1. WiFi wifiConnect(); // 2. NTP (STA mode only) + Browser time sync (AP mode) xTaskCreatePinnedToCore(ntpSyncTask, "NTP", TASK_STACK_NTP, NULL, TASK_PRIORITY_NTP, NULL, 0); if (WiFi.getMode() != WIFI_AP) { delay(2000); // Brief wait for NTP only in STA mode } else { Serial.println("[Time] AP mode: time will sync from browser on first connect"); } // 3. SD Card sdTaskInit(); // 4. Serial2 UART serialTaskInit(); // 5. Web Server + WebSocket webTaskInit(); Serial.println("============================================"); Serial.println(" All tasks started!"); Serial.printf(" Free Heap: %d bytes\n", ESP.getFreeHeap()); String ip = (WiFi.getMode() == WIFI_AP) ? WiFi.softAPIP().toString() : WiFi.localIP().toString(); Serial.println(" URL: http://" + ip); Serial.println("============================================"); Serial.println("\n Task Layout:"); Serial.println(" Core 1: SerialRX(5), SerialTX(4)"); Serial.println(" Core 0: SDLog(3), WebBC(2), NTP(1)"); Serial.println(" Loop : server.handleClient()"); Serial.println(" Time : NTP(STA) or Browser sync(AP)"); Serial.println("============================================\n"); } // ============================================================ // Loop - handles HTTP requests (must be on main loop) // ============================================================ void loop() { server.handleClient(); vTaskDelay(pdMS_TO_TICKS(2)); // Periodic status report (30s) static unsigned long lastReport = 0; if (millis() - lastReport > 30000) { lastReport = millis(); Serial.printf("[SYS] Heap: %d | MinHeap: %d | SD: %s | WS: %d clients\n", ESP.getFreeHeap(), ESP.getMinFreeHeap(), sdCardPresent() ? "OK" : "FAIL", webSocket.connectedClients()); } }