Files
esp32-Serial-Logger/ESP32_SerialLogger.ino

259 lines
8.1 KiB
C++

// ============================================================
// 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 <Arduino.h>
#include <WiFi.h>
#include <esp_wifi.h>
#include <WebServer.h>
#include <WebSocketsServer.h>
#include <ArduinoJson.h>
#include <SPI.h>
#include <SD.h>
#include <SoftWire.h>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include <freertos/queue.h>
#include <freertos/semphr.h>
#include <sys/time.h>
#include <time.h>
#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");
}
}