diff --git a/ESP32_CAN_Logger-a.ino b/ESP32_CAN_Logger-a.ino index 0b1a047..904b469 100644 --- a/ESP32_CAN_Logger-a.ino +++ b/ESP32_CAN_Logger-a.ino @@ -54,18 +54,19 @@ #define RTC_SCL 9 #define DS3231_ADDRESS 0x68 -// 버퍼 설정 -#define CAN_QUEUE_SIZE 2000 -#define FILE_BUFFER_SIZE 16384 +// 버퍼 설정 (ESP32-S3 N16R8 - DRAM 제한 회피) +// 주의: PSRAM 할당이 안 되는 경우 크기 축소 +#define CAN_QUEUE_SIZE 1000 // 6000 → 1000 (DRAM 절약) +#define FILE_BUFFER_SIZE 16384 // 32768 → 16384 (16KB) #define MAX_FILENAME_LEN 64 #define RECENT_MSG_COUNT 100 #define MAX_TX_MESSAGES 20 #define MAX_COMMENT_LEN 128 // Serial 버퍼 설정 (추가) -#define SERIAL_QUEUE_SIZE 200 -#define SERIAL_CSV_BUFFER_SIZE 8192 // CSV 텍스트 버퍼 -#define MAX_SERIAL_LINE_LEN 128 +#define SERIAL_QUEUE_SIZE 200 // 1200 → 200 (DRAM 절약) +#define SERIAL_CSV_BUFFER_SIZE 8192 // 20480 → 8192 (8KB) +#define MAX_SERIAL_LINE_LEN 64 // 128 → 64 (FreeRTOS Queue 제한 회피) // RTC 동기화 설정 #define RTC_SYNC_INTERVAL_MS 60000 @@ -209,6 +210,7 @@ Preferences preferences; // Forward declaration void IRAM_ATTR canISR(); +// Queue 핸들 (FreeRTOS가 자동 할당) QueueHandle_t canQueue; QueueHandle_t serialQueue; SemaphoreHandle_t sdMutex; @@ -228,9 +230,9 @@ File serialLogFile; char currentFilename[MAX_FILENAME_LEN]; char currentSerialFilename[MAX_FILENAME_LEN]; uint8_t fileBuffer[FILE_BUFFER_SIZE]; -char serialCsvBuffer[SERIAL_CSV_BUFFER_SIZE]; // CSV 텍스트 버퍼 +char serialCsvBuffer[SERIAL_CSV_BUFFER_SIZE]; uint16_t bufferIndex = 0; -uint16_t serialCsvIndex = 0; // CSV 버퍼 인덱스 +uint16_t serialCsvIndex = 0; // 로깅 파일 크기 추적 volatile uint32_t currentFileSize = 0; @@ -848,21 +850,35 @@ void sdMonitorTask(void *parameter) { powerStatus.lastCheck = currentTime; } - // 10초마다 상태 출력 (시간 동기화 확인용) + // 10초마다 상태 출력 (시간 동기화 및 Queue 사용률 확인용) if (currentTime - lastStatusPrint >= 10000) { time_t now; time(&now); struct tm timeinfo; localtime_r(&now, &timeinfo); - Serial.printf("[상태] %04d-%02d-%02d %02d:%02d:%02d | CAN: %u msg/s | Serial큐: %u/%u | TimeSync: %s | RTC동기: %u회\n", + uint32_t canQueueUsed = uxQueueMessagesWaiting(canQueue); + uint32_t serialQueueUsed = uxQueueMessagesWaiting(serialQueue); + float canQueuePercent = (float)canQueueUsed / CAN_QUEUE_SIZE * 100.0; + float serialQueuePercent = (float)serialQueueUsed / SERIAL_QUEUE_SIZE * 100.0; + + Serial.printf("[상태] %04d-%02d-%02d %02d:%02d:%02d | CAN: %u msg/s | CAN큐: %u/%u (%.1f%%) | Serial큐: %u/%u (%.1f%%) | TimeSync: %s | RTC동기: %u회\n", timeinfo.tm_year + 1900, timeinfo.tm_mon + 1, timeinfo.tm_mday, timeinfo.tm_hour, timeinfo.tm_min, timeinfo.tm_sec, msgPerSecond, - uxQueueMessagesWaiting(serialQueue), SERIAL_QUEUE_SIZE, + canQueueUsed, CAN_QUEUE_SIZE, canQueuePercent, + serialQueueUsed, SERIAL_QUEUE_SIZE, serialQueuePercent, timeSyncStatus.synchronized ? "OK" : "NO", timeSyncStatus.rtcSyncCount); + // Queue 사용률 경고 (90% 이상) + if (canQueuePercent >= 90.0) { + Serial.printf("⚠️ 경고: CAN Queue 사용률 %.1f%% - SD 카드 속도 확인 필요!\n", canQueuePercent); + } + if (serialQueuePercent >= 90.0) { + Serial.printf("⚠️ 경고: Serial Queue 사용률 %.1f%% - SD 카드 속도 확인 필요!\n", serialQueuePercent); + } + lastStatusPrint = currentTime; } @@ -1935,10 +1951,23 @@ void setup() { Serial.println("\n========================================"); Serial.println(" Byun CAN Logger + Serial Terminal"); - Serial.println(" Version 2.2 - ESP32-S3 Edition"); - Serial.println(" Fixed: RTC Time Sync + Settings"); + Serial.println(" Version 2.3 - ESP32-S3 Optimized"); + Serial.println(" 8MB PSRAM High Performance Edition"); Serial.println("========================================\n"); + // PSRAM 확인 (ESP32-S3 N16R8) + if (psramFound()) { + Serial.printf("✓ PSRAM 감지: %d MB\n", ESP.getPsramSize() / 1024 / 1024); + Serial.printf("✓ PSRAM 여유: %d KB\n", ESP.getFreePsram() / 1024); + } else { + Serial.println("✗ PSRAM 없음 - Arduino IDE에서 PSRAM: OPI PSRAM 설정 필요!"); + Serial.println("✗ Tools → PSRAM → OPI PSRAM 선택"); + while (1) { + delay(1000); + Serial.println("✗ PSRAM 설정 후 재업로드 필요!"); + } + } + loadSettings(); analogSetPinAttenuation(MONITORING_VOLT, ADC_11db); Serial.println("💡 설정 변경: http://[IP]/settings\n"); @@ -2160,30 +2189,49 @@ void setup() { server.begin(); - // Queue 생성 + // Queue 생성 (Dynamic - 크기 축소) + Serial.println("Queue 생성 중..."); + + // CAN Queue: 1,000개 × 21 bytes = 21 KB canQueue = xQueueCreate(CAN_QUEUE_SIZE, sizeof(CANMessage)); + + // Serial Queue: 200개 × 75 bytes = 15 KB serialQueue = xQueueCreate(SERIAL_QUEUE_SIZE, sizeof(SerialMessage)); if (canQueue == NULL || serialQueue == NULL) { Serial.println("✗ Queue 생성 실패!"); + + Serial.println("\n✗ 시스템 중지"); + Serial.println(" 메모리 부족 - Queue 크기를 더 줄이거나"); + Serial.println(" 불필요한 변수를 제거하세요"); while (1) delay(1000); } + Serial.printf("✓ CAN Queue: %d개 (%.1f KB)\n", CAN_QUEUE_SIZE, + (float)(CAN_QUEUE_SIZE * sizeof(CANMessage)) / 1024.0); + Serial.printf("✓ Serial Queue: %d개 (%.1f KB)\n", SERIAL_QUEUE_SIZE, + (float)(SERIAL_QUEUE_SIZE * sizeof(SerialMessage)) / 1024.0); + Serial.printf("✓ 총 Queue 메모리: %.1f KB\n\n", + (float)(CAN_QUEUE_SIZE * sizeof(CANMessage) + SERIAL_QUEUE_SIZE * sizeof(SerialMessage)) / 1024.0); + // CAN 인터럽트 활성화 attachInterrupt(digitalPinToInterrupt(CAN_INT_PIN), canISR, FALLING); - // Task 생성 - xTaskCreatePinnedToCore(canRxTask, "CAN_RX", 8096, NULL, 5, &canRxTaskHandle, 1); - xTaskCreatePinnedToCore(serialRxTask, "SERIAL_RX", 4072, NULL, 4, &serialRxTaskHandle, 0); - xTaskCreatePinnedToCore(sdWriteTask, "SD_WRITE", 15240, NULL, 3, &sdWriteTaskHandle, 1); - xTaskCreatePinnedToCore(sdMonitorTask, "SD_MONITOR", 5072, NULL, 1, NULL, 0); - xTaskCreatePinnedToCore(webUpdateTask, "WEB_UPDATE", 8192, NULL, 2, &webTaskHandle, 0); - xTaskCreatePinnedToCore(txTask, "TX_TASK", 5072, NULL, 2, NULL, 0); - xTaskCreatePinnedToCore(sequenceTask, "SEQ_TASK", 4072, NULL, 2, NULL, 1); + // Task 생성 (ESP32-S3 듀얼코어 최적화) + // Core 1 (사용자 전용 - 고성능 작업) + xTaskCreatePinnedToCore(canRxTask, "CAN_RX", 4096, NULL, 6, &canRxTaskHandle, 1); // 최고 우선순위 + xTaskCreatePinnedToCore(sdWriteTask, "SD_WRITE", 24576, NULL, 4, &sdWriteTaskHandle, 1); // 높음 (큰 버퍼) + xTaskCreatePinnedToCore(sequenceTask, "SEQ_TASK", 4096, NULL, 2, NULL, 1); // 보통 + + // Core 0 (WiFi/시스템 공유) + xTaskCreatePinnedToCore(serialRxTask, "SERIAL_RX", 6144, NULL, 5, &serialRxTaskHandle, 0); // 높음 + xTaskCreatePinnedToCore(txTask, "TX_TASK", 4096, NULL, 3, NULL, 0); // 보통-높음 + xTaskCreatePinnedToCore(webUpdateTask, "WEB_UPDATE", 10240, NULL, 2, &webTaskHandle, 0); // 보통 (JSON 버퍼) + xTaskCreatePinnedToCore(sdMonitorTask, "SD_MONITOR", 4096, NULL, 1, NULL, 0); // 낮음 // RTC 동기화 Task if (timeSyncStatus.rtcAvailable) { - xTaskCreatePinnedToCore(rtcSyncTask, "RTC_SYNC", 4096, NULL, 0, &rtcTaskHandle, 0); + xTaskCreatePinnedToCore(rtcSyncTask, "RTC_SYNC", 3072, NULL, 0, &rtcTaskHandle, 0); // 최저 Serial.println("✓ RTC 자동 동기화 Task 시작"); }