// ============================================================ // ESP32 Serial Logger // Hardware: ESP-WROOM-32D DevKitC V4 + DS3231 RTC // // 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. WebSockets by Links2004 // 2. ArduinoJson v6.x // 3. SoftWire by Steve Marple (+ AsyncDelay) // 4. SD, SPI (built-in) // // Pin Assignments: // UART2 TX: GPIO17 | UART2 RX: GPIO16 // SD MISO: GPIO19 | SD MOSI: GPIO23 | SD SCLK: GPIO18 | SD CS: GPIO5 (VSPI) // RTC SDA: GPIO25 | RTC SCL: GPIO26 (DS3231 @ 0x68, SoftWire) // // WiFi: // Boot → AP mode (always available) // Settings → STA mode enable (AP+STA dual mode) // ============================================================ #include #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" #include "rtc_task.h" // --- WiFi STA state (controlled from web UI) --- volatile bool staEnabled = false; volatile bool staConnected = false; char staSSID[64] = ""; char staPW[64] = ""; // ============================================================ // Enable STA mode (AP+STA dual) - called from web_task // ============================================================ bool wifiEnableSTA(const char *ssid, const char *password) { if (!ssid || strlen(ssid) == 0) return false; strncpy(staSSID, ssid, sizeof(staSSID) - 1); strncpy(staPW, password, sizeof(staPW) - 1); Serial.printf("[WiFi] Enabling STA: SSID='%s'\n", staSSID); // Switch to AP+STA dual mode WiFi.mode(WIFI_AP_STA); WiFi.softAP(WIFI_AP_SSID, WIFI_AP_PASSWORD); WiFi.begin(staSSID, staPW); WiFi.setSleep(false); staEnabled = true; // Wait for connection (non-blocking, with timeout) unsigned long start = millis(); while (WiFi.status() != WL_CONNECTED && millis() - start < 10000) { delay(500); Serial.print("."); } Serial.println(); if (WiFi.status() == WL_CONNECTED) { staConnected = true; Serial.printf("[WiFi] STA connected! IP: %s (RSSI: %d)\n", WiFi.localIP().toString().c_str(), WiFi.RSSI()); Serial.printf("[WiFi] AP still active: %s (%s)\n", WIFI_AP_SSID, WiFi.softAPIP().toString().c_str()); // STA 연결 성공 → NTP 시도 configTime(NTP_GMT_OFFSET, NTP_DAYLIGHT_OFFSET, NTP_SERVER); return true; } else { staConnected = false; Serial.println("[WiFi] STA connection failed (will keep retrying)"); return false; } } // ============================================================ // Disable STA mode - return to AP only // ============================================================ void wifiDisableSTA() { Serial.println("[WiFi] Disabling STA, AP-only mode"); WiFi.disconnect(true); delay(100); WiFi.mode(WIFI_AP); WiFi.softAP(WIFI_AP_SSID, WIFI_AP_PASSWORD); staEnabled = false; staConnected = false; staSSID[0] = '\0'; staPW[0] = '\0'; Serial.printf("[WiFi] AP mode: %s (%s)\n", WIFI_AP_SSID, WiFi.softAPIP().toString().c_str()); } // ============================================================ // NTP + STA Monitor Task // Periodically checks STA status, retries connection, syncs NTP // ============================================================ void ntpSyncTask(void *param) { Serial.println("[Task] NTP/WiFi monitor started"); vTaskDelay(pdMS_TO_TICKS(3000)); while (true) { // If STA is enabled, monitor connection if (staEnabled) { if (WiFi.status() == WL_CONNECTED) { if (!staConnected) { staConnected = true; Serial.printf("[WiFi] STA reconnected: %s\n", WiFi.localIP().toString().c_str()); } // Try NTP sync struct tm timeinfo; configTime(NTP_GMT_OFFSET, NTP_DAYLIGHT_OFFSET, NTP_SERVER); if (getLocalTime(&timeinfo, 3000)) { rtcSyncFromSystem(); static uint32_t ntpCount = 0; if (++ntpCount % 60 == 1) { // Log every ~60 cycles 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 { staConnected = false; // Auto-retry connection if (strlen(staSSID) > 0) { WiFi.begin(staSSID, staPW); } } } vTaskDelay(pdMS_TO_TICKS(60000)); // Check every 60s } } // ============================================================ // Setup // ============================================================ void setup() { Serial.begin(115200); delay(1000); Serial.println("============================================"); Serial.println(" ESP32 Serial Logger v2.1"); Serial.println(" ESP-WROOM-32D DevKitC V4 + DS3231"); Serial.println("============================================"); Serial.printf(" Free Heap: %d bytes\n", ESP.getFreeHeap()); Serial.printf(" CPU Freq: %d MHz\n", getCpuFrequencyMhz()); Serial.println("============================================\n"); // 1. DS3231 RTC Serial.println("--- Step 1: RTC ---"); bool hasRTC = rtcInit(); // 2. WiFi - Always start AP mode (immediate access) Serial.println("\n--- Step 2: WiFi (AP mode) ---"); WiFi.mode(WIFI_AP); WiFi.softAP(WIFI_AP_SSID, WIFI_AP_PASSWORD); Serial.printf("[WiFi] AP SSID: %s\n", WIFI_AP_SSID); Serial.printf("[WiFi] AP IP: %s\n", WiFi.softAPIP().toString().c_str()); Serial.println("[WiFi] STA: disabled (enable via Settings page)"); // 3. Time status Serial.println("\n--- Step 3: Time ---"); if (hasRTC && rtcStatus.timeSynced) { Serial.println("[Time] Using RTC time"); } else { Serial.println("[Time] Waiting for browser sync"); } // 4. SD Card Serial.println("\n--- Step 4: SD Card ---"); sdTaskInit(); // 5. Serial2 UART Serial.println("\n--- Step 5: Serial2 ---"); serialTaskInit(); // 6. Web Server + WebSocket Serial.println("\n--- Step 6: Web Server ---"); webTaskInit(); // 7. RTC periodic sync task rtcTaskInit(); // 8. NTP + WiFi monitor task (always running) xTaskCreatePinnedToCore(ntpSyncTask, "NTP", TASK_STACK_NTP, NULL, TASK_PRIORITY_NTP, NULL, 0); // Status summary Serial.println("\n============================================"); Serial.println(" All systems initialized!"); Serial.printf(" Free Heap: %d bytes\n", ESP.getFreeHeap()); Serial.printf(" AP URL: http://%s\n", WiFi.softAPIP().toString().c_str()); Serial.println("============================================"); Serial.println(" Core 1: SerialRX(5), SerialTX(4)"); Serial.println(" Core 0: SDLog(3), WebBC(2), RTC(1), NTP(1)"); Serial.println(" Loop : server.handleClient()"); Serial.printf(" RTC: %s | Time: %s\n", rtcStatus.available ? "OK" : "NO", rtcStatus.timeSynced ? "Valid" : "Pending"); Serial.printf(" AutoStart: %s\n", sdAutoStart ? "ON (logging started)" : "OFF"); Serial.println("============================================\n"); } // ============================================================ // Loop // ============================================================ void loop() { server.handleClient(); vTaskDelay(pdMS_TO_TICKS(2)); static unsigned long lastReport = 0; if (millis() - lastReport > 30000) { lastReport = millis(); char timeBuf[24]; getTimestamp(timeBuf, sizeof(timeBuf)); Serial.printf("[SYS] %s | Heap:%d | SD:%s | WS:%d | RTC:%s | STA:%s | Auto:%s\n", timeBuf, ESP.getFreeHeap(), sdCardPresent() ? "OK" : "FAIL", webSocket.connectedClients(), rtcStatus.available ? "OK" : "NO", staConnected ? "ON" : "OFF", sdAutoStart ? "ON" : "OFF"); } }