From 4f0588c7d83f88b88d91b93d629bb54645171e26 Mon Sep 17 00:00:00 2001 From: byun Date: Sat, 4 Oct 2025 17:26:09 +0000 Subject: [PATCH] =?UTF-8?q?00001=20=EC=97=90=EB=9F=AC=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Z:\byun_work\01.codings\250928_esp32_spi_sdcard_ads8688_mcp\03.wifi_can\ESP32_CAN_Logger\ESP32_CAN_Logger.ino: In function 'void canRxTask(void*)': Z:\byun_work\01.codings\250928_esp32_spi_sdcard_ads8688_mcp\03.wifi_can\ESP32_CAN_Logger\ESP32_CAN_Logger.ino:261:13: error: 'recentMessages' was not declared in this scope 261 | recentMessages[recentMsgIndex] = msg; | ^~~~~~~~~~~~~~ Z:\byun_work\01.codings\250928_esp32_spi_sdcard_ads8688_mcp\03.wifi_can\ESP32_CAN_Logger\ESP32_CAN_Logger.ino:261:28: error: 'recentMsgIndex' was not declared in this scope; did you mean 'recentDataIndex'? 261 | recentMessages[recentMsgIndex] = msg; | ^~~~~~~~~~~~~~ | recentDataIndex Z:\byun_work\01.codings\250928_esp32_spi_sdcard_ads8688_mcp\03.wifi_can\ESP32_CAN_Logger\ESP32_CAN_Logger.ino: In function 'void webUpdateTask(void*)': Z:\byun_work\01.codings\250928_esp32_spi_sdcard_ads8688_mcp\03.wifi_can\ESP32_CAN_Logger\ESP32_CAN_Logger.ino:491:21: error: 'recentMessages' was not declared in this scope 491 | if (recentMessages[i].timestamp > 0) { | ^~~~~~~~~~~~~~ "mcp2515.h"를 위한 복수개의 라이브러리가 발견되었습니다 사용됨: C:\Users\sinla\OneDrive\문서\Arduino\libraries\autowp-mcp2515 사용되지 않음: C:\Users\sinla\OneDrive\문서\Arduino\libraries\esp32-mcp2515-master "SD.h"를 위한 복수개의 라이브러리가 발견되었습니다 사용됨: C:\Users\sinla\AppData\Local\Arduino15\packages\esp32\hardware\esp32\3.3.0\libraries\SD 사용되지 않음: C:\Users\sinla\AppData\Local\Arduino15\libraries\SD exit status 1 Compilation error: 'recentMessages' was not declared in this scope --- ESP32_CAN_Logger.ino | 236 ++++++++++++++++++++++--------------------- 1 file changed, 120 insertions(+), 116 deletions(-) diff --git a/ESP32_CAN_Logger.ino b/ESP32_CAN_Logger.ino index 5c76891..534324c 100644 --- a/ESP32_CAN_Logger.ino +++ b/ESP32_CAN_Logger.ino @@ -8,6 +8,8 @@ * - CAN speed adjustment (125K - 1M) * - Log file download * - Batch update every 0.5s for real-time performance + * - Accurate message count per CAN ID + * - Current logging file display * * Hardware: * - ESP32 WROVER Module (ESP32-WROOM-32D) @@ -20,9 +22,6 @@ * Required Libraries: * - WebSockets by Markus Sattler * - mcp2515 by autowp - * - * Author: ESP32 CAN Logger Project - * Version: 1.0 */ #include @@ -36,9 +35,9 @@ #include #include #include -#include "index.h" // HTML 페이지 +#include "index.h" -// ==================== GPIO 핀 정의 ==================== +// GPIO 핀 정의 #define CAN_INT_PIN 27 #define LOGGING_CONTROL_PIN 17 #define LOGGING_STATUS_LED 16 @@ -56,13 +55,13 @@ #define VSPI_SCLK 18 #define VSPI_CS 5 -// ==================== 버퍼 설정 ==================== +// 버퍼 설정 #define CAN_QUEUE_SIZE 1000 #define FILE_BUFFER_SIZE 8192 #define MAX_FILENAME_LEN 32 -#define WEB_UPDATE_INTERVAL 100 +#define RECENT_MSG_COUNT 100 -// ==================== CAN 메시지 구조체 ==================== +// CAN 메시지 구조체 struct CANMessage { uint32_t timestamp; uint32_t id; @@ -70,11 +69,17 @@ struct CANMessage { uint8_t data[8]; } __attribute__((packed)); -// ==================== WiFi AP 설정 ==================== +// 실시간 모니터링용 구조체 +struct RecentCANData { + CANMessage msg; + uint32_t count; +}; + +// WiFi AP 설정 const char* ssid = "ESP32_CAN_Logger"; const char* password = "12345678"; -// ==================== 전역 변수 ==================== +// 전역 변수 SPIClass hspi(HSPI); SPIClass vspi(VSPI); MCP2515 mcp2515(HSPI_CS, 10000000, &hspi); @@ -103,19 +108,13 @@ const char* canSpeedNames[] = {"125K", "250K", "500K", "1M"}; CAN_SPEED canSpeedValues[] = {CAN_125KBPS, CAN_250KBPS, CAN_500KBPS, CAN_1000KBPS}; // 실시간 모니터링용 -#define RECENT_MSG_COUNT 100 -struct RecentCANData { - CANMessage msg; - uint32_t count; // 실제 수신 횟수 -} recentData[RECENT_MSG_COUNT]; - -int recentDataIndex = 0; +RecentCANData recentData[RECENT_MSG_COUNT]; uint32_t totalMsgCount = 0; uint32_t msgPerSecond = 0; uint32_t lastMsgCountTime = 0; uint32_t lastMsgCount = 0; -// ==================== CAN 인터럽트 핸들러 ==================== +// CAN 인터럽트 핸들러 void IRAM_ATTR canISR() { BaseType_t xHigherPriorityTaskWoken = pdFALSE; if (canRxTaskHandle != NULL) { @@ -124,7 +123,7 @@ void IRAM_ATTR canISR() { } } -// ==================== CAN 속도 변경 ==================== +// CAN 속도 변경 void changeCanSpeed(CAN_SPEED newSpeed) { detachInterrupt(digitalPinToInterrupt(CAN_INT_PIN)); mcp2515.reset(); @@ -135,7 +134,7 @@ void changeCanSpeed(CAN_SPEED newSpeed) { Serial.printf("CAN 속도 변경: %s\n", canSpeedNames[newSpeed]); } -// ==================== 기존 파일 번호 스캔 ==================== +// 기존 파일 번호 스캔 void scanExistingFiles() { if (!sdCardReady) { fileCounter = 0; @@ -159,10 +158,8 @@ void scanExistingFiles() { String name = file.name(); if (name.startsWith("/")) name = name.substring(1); - // canlog_XXXXX.bin 형식 확인 if (name.startsWith("canlog_") && (name.endsWith(".bin") || name.endsWith(".BIN"))) { - // 숫자 부분 추출 (canlog_00005.bin -> 5) - int startIdx = 7; // "canlog_" 길이 + int startIdx = 7; int endIdx = name.lastIndexOf('.'); if (endIdx > startIdx) { @@ -190,6 +187,8 @@ void scanExistingFiles() { Serial.println("기존 파일 없음 - 파일 카운터 0으로 시작"); } } + +// 새 로그 파일 생성 bool createNewLogFile() { if (logFile) { logFile.flush(); @@ -212,7 +211,7 @@ bool createNewLogFile() { return true; } -// ==================== 버퍼 플러시 ==================== +// 버퍼 플러시 bool flushBuffer() { if (bufferIndex == 0) return true; @@ -234,7 +233,7 @@ bool flushBuffer() { return false; } -// ==================== CAN 수신 태스크 ==================== +// CAN 수신 태스크 void canRxTask(void *pvParameters) { struct can_frame frame; CANMessage msg; @@ -258,14 +257,43 @@ void canRxTask(void *pvParameters) { } } - recentMessages[recentMsgIndex] = msg; - recentMsgIndex = (recentMsgIndex + 1) % RECENT_MSG_COUNT; + // 최근 메시지 저장 및 카운트 증가 + bool found = false; + for (int i = 0; i < RECENT_MSG_COUNT; i++) { + if (recentData[i].msg.id == msg.id && recentData[i].msg.timestamp > 0) { + recentData[i].msg = msg; + recentData[i].count++; + found = true; + break; + } + } + + if (!found) { + // 새 ID - 빈 슬롯 찾기 + for (int i = 0; i < RECENT_MSG_COUNT; i++) { + if (recentData[i].msg.timestamp == 0) { + recentData[i].msg = msg; + recentData[i].count = 1; + found = true; + break; + } + } + + // 빈 슬롯 없으면 가장 오래된 것 교체 + if (!found) { + static int replaceIndex = 0; + recentData[replaceIndex].msg = msg; + recentData[replaceIndex].count = 1; + replaceIndex = (replaceIndex + 1) % RECENT_MSG_COUNT; + } + } + totalMsgCount++; } } } -// ==================== SD 쓰기 태스크 ==================== +// SD 쓰기 태스크 void sdWriteTask(void *pvParameters) { CANMessage msg; @@ -295,7 +323,7 @@ void sdWriteTask(void *pvParameters) { } } -// ==================== 제어 태스크 ==================== +// 제어 태스크 void controlTask(void *pvParameters) { bool lastLoggingState = false; @@ -338,7 +366,7 @@ void controlTask(void *pvParameters) { } } -// ==================== SD 모니터 태스크 ==================== +// SD 모니터 태스크 void sdMonitorTask(void *pvParameters) { Serial.println("SD 모니터 태스크 시작"); @@ -351,7 +379,6 @@ void sdMonitorTask(void *pvParameters) { if (sdCardReady) { Serial.println("SD 카드 준비됨"); - // SD 카드가 새로 삽입되면 파일 번호 다시 스캔 scanExistingFiles(); } else { Serial.println("SD 카드 없음"); @@ -366,45 +393,7 @@ void sdMonitorTask(void *pvParameters) { } } -// ==================== 웹소켓 이벤트 핸들러 ==================== -void webSocketEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t length) { - switch(type) { - case WStype_DISCONNECTED: - Serial.printf("WebSocket 클라이언트 #%u 연결 해제\n", num); - break; - - case WStype_CONNECTED: - { - IPAddress ip = webSocket.remoteIP(num); - Serial.printf("WebSocket 클라이언트 #%u 연결: %d.%d.%d.%d\n", - num, ip[0], ip[1], ip[2], ip[3]); - - // 연결 시 즉시 파일 목록 전송 - sendFileList(num); - } - break; - - case WStype_TEXT: - { - String msg = String((char*)payload); - - if (msg.indexOf("\"cmd\":\"setSpeed\"") >= 0) { - int speedIdx = msg.indexOf("\"speed\":") + 8; - int speed = msg.substring(speedIdx, speedIdx + 1).toInt(); - - if (speed >= 0 && speed < 4) { - changeCanSpeed(canSpeedValues[speed]); - } - } else if (msg.indexOf("\"cmd\":\"getFiles\"") >= 0) { - // 파일 목록 전송 - sendFileList(num); - } - } - break; - } -} - -// ==================== 파일 목록 전송 함수 ==================== +// 파일 목록 전송 void sendFileList(uint8_t clientNum) { String fileList = "{\"type\":\"files\",\"files\":["; @@ -450,11 +439,46 @@ void sendFileList(uint8_t clientNum) { Serial.printf("파일 목록 전송 완료: %d개 파일\n", fileCount); } -// ==================== 웹 업데이트 태스크 (0.5초 일괄 전송) ==================== +// 웹소켓 이벤트 핸들러 +void webSocketEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t length) { + switch(type) { + case WStype_DISCONNECTED: + Serial.printf("WebSocket 클라이언트 #%u 연결 해제\n", num); + break; + + case WStype_CONNECTED: + { + IPAddress ip = webSocket.remoteIP(num); + Serial.printf("WebSocket 클라이언트 #%u 연결: %d.%d.%d.%d\n", + num, ip[0], ip[1], ip[2], ip[3]); + sendFileList(num); + } + break; + + case WStype_TEXT: + { + String msg = String((char*)payload); + + if (msg.indexOf("\"cmd\":\"setSpeed\"") >= 0) { + int speedIdx = msg.indexOf("\"speed\":") + 8; + int speed = msg.substring(speedIdx, speedIdx + 1).toInt(); + + if (speed >= 0 && speed < 4) { + changeCanSpeed(canSpeedValues[speed]); + } + } else if (msg.indexOf("\"cmd\":\"getFiles\"") >= 0) { + sendFileList(num); + } + } + break; + } +} + +// 웹 업데이트 태스크 void webUpdateTask(void *pvParameters) { uint32_t lastStatusUpdate = 0; uint32_t lastCanUpdate = 0; - const uint32_t CAN_UPDATE_INTERVAL = 500; // 0.5초마다 CAN 데이터 전송 + const uint32_t CAN_UPDATE_INTERVAL = 500; Serial.println("웹 업데이트 태스크 시작"); @@ -475,7 +499,14 @@ void webUpdateTask(void *pvParameters) { status += "\"logging\":" + String(loggingEnabled ? "true" : "false") + ","; status += "\"sdReady\":" + String(sdCardReady ? "true" : "false") + ","; status += "\"msgCount\":" + String(totalMsgCount) + ","; - status += "\"msgSpeed\":" + String(msgPerSecond) + "}"; + status += "\"msgSpeed\":" + String(msgPerSecond) + ","; + + if (loggingEnabled && logFile) { + status += "\"currentFile\":\"" + String(currentFilename) + "\""; + } else { + status += "\"currentFile\":\"\""; + } + status += "}"; webSocket.broadcastTXT(status); lastStatusUpdate = now; @@ -486,10 +517,9 @@ void webUpdateTask(void *pvParameters) { String canBatch = "{\"type\":\"canBatch\",\"messages\":["; bool first = true; - // 모든 최근 메시지를 JSON 배열로 구성 for (int i = 0; i < RECENT_MSG_COUNT; i++) { - if (recentMessages[i].timestamp > 0) { - CANMessage* msg = &recentMessages[i]; + if (recentData[i].msg.timestamp > 0) { + CANMessage* msg = &recentData[i].msg; if (!first) canBatch += ","; first = false; @@ -508,13 +538,14 @@ void webUpdateTask(void *pvParameters) { if (j < msg->dlc - 1) canBatch += " "; } - canBatch += "\",\"timestamp\":" + String(msg->timestamp) + "}"; + canBatch += "\",\"timestamp\":" + String(msg->timestamp); + canBatch += ",\"count\":" + String(recentData[i].count) + "}"; } } canBatch += "]}"; - if (!first) { // 메시지가 있을 때만 전송 + if (!first) { webSocket.broadcastTXT(canBatch); } @@ -525,16 +556,16 @@ void webUpdateTask(void *pvParameters) { } } -// ==================== SETUP ==================== void setup() { Serial.begin(115200); delay(1000); - Serial.println("\n\n"); - Serial.println("========================================"); + Serial.println("\n\n========================================"); Serial.println(" ESP32 CAN Logger with Web Interface "); Serial.println("========================================"); - // GPIO 초기화 + // 배열 초기화 + memset(recentData, 0, sizeof(recentData)); + pinMode(LOGGING_CONTROL_PIN, INPUT); pinMode(LOGGING_STATUS_LED, OUTPUT); pinMode(SD_READY_LED, OUTPUT); @@ -543,46 +574,37 @@ void setup() { digitalWrite(LOGGING_STATUS_LED, LOW); digitalWrite(SD_READY_LED, LOW); - // SPI 초기화 hspi.begin(HSPI_SCLK, HSPI_MISO, HSPI_MOSI, HSPI_CS); vspi.begin(VSPI_SCLK, VSPI_MISO, VSPI_MOSI, VSPI_CS); - // MCP2515 초기화 mcp2515.reset(); mcp2515.setBitrate(CAN_1000KBPS, MCP_8MHZ); mcp2515.setNormalMode(); Serial.println("✓ MCP2515 초기화 완료 (1Mbps)"); - // SD 카드 초기화 if (SD.begin(VSPI_CS, vspi)) { sdCardReady = true; digitalWrite(SD_READY_LED, HIGH); Serial.println("✓ SD 카드 초기화 완료"); - - // 기존 파일 번호 스캔 scanExistingFiles(); } else { Serial.println("✗ SD 카드 초기화 실패"); fileCounter = 0; } - // WiFi AP 시작 Serial.println("\nWiFi AP 시작..."); WiFi.softAP(ssid, password); Serial.print("✓ AP IP 주소: "); Serial.println(WiFi.softAPIP()); - // 웹소켓 설정 webSocket.begin(); webSocket.onEvent(webSocketEvent); Serial.println("✓ WebSocket 서버 시작 (포트 81)"); - // HTTP 핸들러 server.on("/", HTTP_GET, []() { server.send_P(200, "text/html", index_html); }); - // 파일 다운로드 핸들러 server.on("/download", HTTP_GET, []() { if (server.hasArg("file")) { String filename = "/" + server.arg("file"); @@ -606,7 +628,6 @@ void setup() { server.begin(); Serial.println("✓ 웹 서버 시작 (포트 80)"); - // RTOS 객체 생성 canQueue = xQueueCreate(CAN_QUEUE_SIZE, sizeof(CANMessage)); sdMutex = xSemaphoreCreateMutex(); @@ -616,10 +637,8 @@ void setup() { } Serial.println("✓ RTOS 객체 생성 완료"); - // 인터럽트 설정 attachInterrupt(digitalPinToInterrupt(CAN_INT_PIN), canISR, FALLING); - // 태스크 생성 xTaskCreatePinnedToCore(canRxTask, "CAN_RX", 4096, NULL, 4, &canRxTaskHandle, 1); xTaskCreatePinnedToCore(sdWriteTask, "SD_WRITE", 12288, NULL, 3, &sdWriteTaskHandle, 1); xTaskCreatePinnedToCore(controlTask, "CONTROL", 8192, NULL, 2, &controlTaskHandle, 0); @@ -627,41 +646,26 @@ void setup() { xTaskCreatePinnedToCore(webUpdateTask, "WEB_UPDATE", 8192, NULL, 2, &webTaskHandle, 0); Serial.println("✓ 모든 RTOS 태스크 시작 완료"); - Serial.println("\n========================================"); - Serial.println(" GPIO 핀 설정"); + Serial.println(" 웹 인터페이스 접속"); Serial.println("========================================"); - Serial.println(" GPIO17: 로깅 제어 (HIGH=시작, LOW=정지)"); - Serial.println(" GPIO16: 로깅 상태 LED"); - Serial.println(" GPIO26: SD 카드 준비 LED"); - Serial.println(" GPIO27: CAN 인터럽트"); - - Serial.println("\n========================================"); - Serial.println(" 웹 인터페이스 접속 방법"); - Serial.println("========================================"); - Serial.println(" 1. WiFi 연결: ESP32_CAN_Logger"); - Serial.println(" 2. 비밀번호: 12345678"); - Serial.print(" 3. 브라우저: http://"); + Serial.println(" 1. WiFi: ESP32_CAN_Logger (12345678)"); + Serial.print(" 2. http://"); Serial.println(WiFi.softAPIP()); Serial.println("========================================\n"); } -// ==================== LOOP ==================== void loop() { server.handleClient(); vTaskDelay(pdMS_TO_TICKS(10)); - // 디버그 정보 (10초마다) static uint32_t lastPrint = 0; if (millis() - lastPrint > 10000) { - Serial.printf("[상태] 큐: %d/%d | 로깅: %s | SD: %s | 총: %lu msg | 속도: %lu msg/s\n", - uxQueueMessagesWaiting(canQueue), - CAN_QUEUE_SIZE, + Serial.printf("[상태] 큐: %d/%d | 로깅: %s | SD: %s | 총: %lu | 속도: %lu msg/s\n", + uxQueueMessagesWaiting(canQueue), CAN_QUEUE_SIZE, loggingEnabled ? "ON " : "OFF", - sdCardReady ? "Ready " : "Not Ready", - totalMsgCount, - msgPerSecond - ); + sdCardReady ? "Ready" : "No ", + totalMsgCount, msgPerSecond); lastPrint = millis(); } } \ No newline at end of file