diff --git a/ESP32_CAN_Logger-a.ino b/ESP32_CAN_Logger-a.ino index 906ca4a..f52336c 100644 --- a/ESP32_CAN_Logger-a.ino +++ b/ESP32_CAN_Logger-a.ino @@ -220,6 +220,23 @@ SerialSettings serial2Settings = {115200, 8, 0, 1}; // ⭐ Serial2 추가 // 전역 객체 (내부 SRAM) SPIClass hspi(HSPI); // SPIClass vspi(FSPI); // ⭐ SDIO 사용으로 제거 +// ======================================== +// MCP2515 크리스탈 설정 +// ======================================== +// 8MHz 크리스탈: 500 Kbps까지 안정적 +// 16MHz 크리스탈: 1 Mbps 고속 통신 권장 (고부하 대응) +// +// 사용법: +// - 하드웨어에 16MHz 크리스탈 장착 시: MCP_16MHZ +// - 하드웨어에 8MHz 크리스탈 장착 시: MCP_8MHZ +// +#define MCP_CRYSTAL MCP_16MHZ // ⭐ 16MHz 크리스탈 (1 Mbps 권장) +// #define MCP_CRYSTAL MCP_8MHZ // 8MHz 크리스탈 (500 Kbps까지) + +// SPI 속도: 20MHz (MCP2515 최대 속도와 균형) +// 10MHz: 안정적이지만 느림 +// 20MHz: 권장 (고속 + 안정성) +// 25MHz: 불안정할 수 있음 MCP2515 mcp2515(HSPI_CS, 20000000, &hspi); HardwareSerial SerialComm(1); // UART1 HardwareSerial Serial2Comm(2); // ⭐ UART2 추가 @@ -370,7 +387,7 @@ void resetMCP2515() { Serial.printf(" 3. Speed=%d, Mode=%d\n", (int)currentCanSpeed, (int)currentMcpMode); // 5. Bitrate 설정 (Configuration 모드에서) - mcp2515.setBitrate(currentCanSpeed, MCP_8MHZ); + mcp2515.setBitrate(currentCanSpeed, MCP_CRYSTAL); delay(10); // 6. 필터/마스크 설정 (모든 메시지 수신) @@ -427,7 +444,7 @@ void resetMCP2515() { // 에러가 있으면 완전 리셋 mcp2515.reset(); delay(50); - mcp2515.setBitrate(currentCanSpeed, MCP_8MHZ); + mcp2515.setBitrate(currentCanSpeed, MCP_CRYSTAL); delay(10); if (currentMcpMode == MCP_MODE_NORMAL) { mcp2515.setNormalMode(); @@ -1386,7 +1403,7 @@ void canRxTask(void *parameter) { delay(100); // 2. Bitrate 재설정 - mcp2515.setBitrate(currentCanSpeed, MCP_8MHZ); + mcp2515.setBitrate(currentCanSpeed, MCP_CRYSTAL); delay(10); // 3. 모든 에러 플래그 클리어 @@ -1807,10 +1824,11 @@ void webSocketEvent(uint8_t num, WStype_t type, uint8_t *payload, size_t length) if (type == WStype_CONNECTED) { Serial.printf("[%u] ✅ WebSocket 연결됨\n", num); - // 기본 설정 전송 - DynamicJsonDocument settings(512); - settings["type"] = "currentSettings"; + // ⭐ 모든 데이터를 하나의 JSON으로 통합 전송 (빠른 연결) + DynamicJsonDocument allData(3072); // 통합 JSON + allData["type"] = "initialData"; + // 기본 설정 int speedIndex = 3; for (int i = 0; i < 4; i++) { if (canSpeedValues[i] == currentCanSpeed) { @@ -1818,29 +1836,20 @@ void webSocketEvent(uint8_t num, WStype_t type, uint8_t *payload, size_t length) break; } } - settings["canSpeed"] = speedIndex; - settings["mcpMode"] = (int)currentMcpMode; - settings["autoTriggerEnabled"] = autoTriggerEnabled; - settings["autoTriggerLogCSV"] = autoTriggerLogCSV; + allData["canSpeed"] = speedIndex; + allData["mcpMode"] = (int)currentMcpMode; + allData["autoTriggerEnabled"] = autoTriggerEnabled; + allData["autoTriggerLogCSV"] = autoTriggerLogCSV; - String json; - serializeJson(settings, json); - webSocket.sendTXT(num, json); - - // Auto Trigger 설정도 전송 + // Auto Trigger 설정도 포함 if (autoTriggerEnabled) { - delay(50); // 메시지 간격 + allData["logFormat"] = autoTriggerLogCSV ? "csv" : "bin"; + allData["startLogic"] = startLogicOp; + allData["stopLogic"] = stopLogicOp; + allData["startFormula"] = startFormula; + allData["stopFormula"] = stopFormula; - DynamicJsonDocument autoTrigger(2048); - autoTrigger["type"] = "autoTriggers"; - autoTrigger["enabled"] = autoTriggerEnabled; - autoTrigger["logFormat"] = autoTriggerLogCSV ? "csv" : "bin"; - autoTrigger["startLogic"] = startLogicOp; - autoTrigger["stopLogic"] = stopLogicOp; - autoTrigger["startFormula"] = startFormula; - autoTrigger["stopFormula"] = stopFormula; - - JsonArray startArray = autoTrigger.createNestedArray("startTriggers"); + JsonArray startArray = allData.createNestedArray("startTriggers"); for (int i = 0; i < startTriggerCount; i++) { JsonObject t = startArray.createNestedObject(); char idStr[10]; @@ -1853,7 +1862,7 @@ void webSocketEvent(uint8_t num, WStype_t type, uint8_t *payload, size_t length) t["enabled"] = startTriggers[i].enabled; } - JsonArray stopArray = autoTrigger.createNestedArray("stopTriggers"); + JsonArray stopArray = allData.createNestedArray("stopTriggers"); for (int i = 0; i < stopTriggerCount; i++) { JsonObject t = stopArray.createNestedObject(); char idStr[10]; @@ -1865,11 +1874,12 @@ void webSocketEvent(uint8_t num, WStype_t type, uint8_t *payload, size_t length) t["value"] = (long)stopTriggers[i].value; t["enabled"] = stopTriggers[i].enabled; } - - String autoJson; - serializeJson(autoTrigger, autoJson); - webSocket.sendTXT(num, autoJson); } + + // ⭐ 하나의 메시지로 전송 (delay 제거) + String json; + serializeJson(allData, json); + webSocket.sendTXT(num, json); } else if (type == WStype_TEXT) { DynamicJsonDocument doc(44384); @@ -2138,7 +2148,7 @@ void webSocketEvent(uint8_t num, WStype_t type, uint8_t *payload, size_t length) // ⭐⭐⭐ MCP2515 리셋 제거! (CAN 수신 중단 방지) // 속도 변경은 로깅 중지 후 수동으로만 가능하도록 변경 // mcp2515.reset(); - // mcp2515.setBitrate(currentCanSpeed, MCP_8MHZ); + // mcp2515.setBitrate(currentCanSpeed, MCP_CRYSTAL); // setMCP2515Mode(currentMcpMode); saveSettings(); @@ -2263,7 +2273,7 @@ void webSocketEvent(uint8_t num, WStype_t type, uint8_t *payload, size_t length) // ⭐⭐⭐ MCP2515 리셋 제거! (CAN 수신 중단 방지) // 속도 변경은 로깅 중지 후 수동으로만 가능하도록 변경 // mcp2515.reset(); - // mcp2515.setBitrate(currentCanSpeed, MCP_8MHZ); + // mcp2515.setBitrate(currentCanSpeed, MCP_CRYSTAL); // setMCP2515Mode(currentMcpMode); saveSettings(); @@ -2875,10 +2885,10 @@ void webSocketEvent(uint8_t num, WStype_t type, uint8_t *payload, size_t length) // Web Update Task // ======================================== void webUpdateTask(void *parameter) { - const TickType_t xDelay = pdMS_TO_TICKS(200); // ⭐ 100ms → 200ms (WiFi 안정성 향상) + const TickType_t xDelay = pdMS_TO_TICKS(100); // ⭐ 200ms → 100ms (더 빠른 업데이트) - // 🆕 초기화 대기 (부팅 직후 안정화) - vTaskDelay(pdMS_TO_TICKS(2000)); + // 🆕 초기화 대기 단축 (부팅 직후 안정화) + vTaskDelay(pdMS_TO_TICKS(500)); // ⭐ 2000ms → 500ms (빠른 연결) while (1) { webSocket.loop(); @@ -3136,7 +3146,33 @@ void webUpdateTask(void *parameter) { void setup() { Serial.begin(115200); delay(1000); + + // ⭐ 리셋 원인 확인 (전원 부족 감지) + esp_reset_reason_t reset_reason = esp_reset_reason(); + if (reset_reason == ESP_RST_BROWNOUT) { + Serial.println(""); + Serial.println("╔════════════════════════════════════════════╗"); + Serial.println("║ 🚨 브라운아웃 리셋 감지! ║"); + Serial.println("║ ║"); + Serial.println("║ 원인: 전원 공급 부족 ║"); + Serial.println("║ ║"); + Serial.println("║ 필요 전류: ║"); + Serial.println("║ - 평균: 300 mA ║"); + Serial.println("║ - 피크: 550 mA (WiFi TX + SD Write) ║"); + Serial.println("║ ║"); + Serial.println("║ 해결 방법: ║"); + Serial.println("║ 1. 5V 1A USB 어댑터 사용 (권장) ║"); + Serial.println("║ 2. USB 케이블 교체 (짧고 굵은 케이블) ║"); + Serial.println("║ 3. USB 3.0 포트 사용 (900mA 지원) ║"); + Serial.println("╚════════════════════════════════════════════╝"); + Serial.println(""); + delay(5000); // 경고 메시지 읽을 시간 + } + + // ⭐ WiFi 전력 최적화 (전류 소비 감소) WiFi.setSleep(false); + WiFi.setTxPower(WIFI_POWER_15dBm); // 19.5dBm → 15dBm (전류 절감) + Serial.println("\n========================================"); Serial.println(" Byun CAN Logger + Serial Terminal"); Serial.println(" Version 2.3 - PSRAM Optimized"); @@ -3198,7 +3234,7 @@ void setup() { // 4. Configuration 모드에서 설정 (중요: 이 순서대로!) Serial.println(" 3. Bitrate 설정..."); - mcp2515.setBitrate(currentCanSpeed, MCP_8MHZ); + mcp2515.setBitrate(currentCanSpeed, MCP_CRYSTAL); delay(10); // 5. 필터/마스크 설정 (모든 메시지 수신) @@ -3275,7 +3311,7 @@ void setup() { // 에러가 있으면 완전 리셋 재시도 mcp2515.reset(); delay(50); - mcp2515.setBitrate(currentCanSpeed, MCP_8MHZ); + mcp2515.setBitrate(currentCanSpeed, MCP_CRYSTAL); delay(10); mcp2515.clearRXnOVRFlags(); mcp2515.clearInterrupts(); @@ -3658,8 +3694,8 @@ void setup() { // - TX: CAN 전송 // - SEQ: 시퀀스 재생 // - MONITOR: 상태 모니터링 - xTaskCreatePinnedToCore(canRxTask, "CAN_RX", 12288, NULL, 24, &canRxTaskHandle, 1); // ⭐ 8KB → 12KB, Pri 24 (최고) - xTaskCreatePinnedToCore(webUpdateTask, "WEB_UPDATE", 12288, NULL, 4, &webTaskHandle, 1); // ⭐ Core 0 → 1, Pri 4 (SD와 분리) + xTaskCreatePinnedToCore(canRxTask, "CAN_RX", 12288, NULL, 20, &canRxTaskHandle, 1); // ⭐ 8KB → 12KB, Pri 24 (최고) + xTaskCreatePinnedToCore(webUpdateTask, "WEB_UPDATE", 12288, NULL, 16, &webTaskHandle, 1); // ⭐ Core 0 → 1, Pri 4 (SD와 분리) xTaskCreatePinnedToCore(txTask, "TX", 4096, NULL, 3, NULL, 1); // Pri 3 xTaskCreatePinnedToCore(sequenceTask, "SEQ", 4096, NULL, 2, NULL, 1); // Pri 2 xTaskCreatePinnedToCore(sdMonitorTask, "SD_MONITOR", 4096, NULL, 1, NULL, 1); // Pri 1