메모리 rtos psram 효율화

ESP32-S3 성능 최적화 완료!
 주요 개선 사항
1. Queue 크기 대폭 증가 (PSRAM 활용)

CAN Queue: 2,000 → 6,000개 (+200%)
Serial Queue: 200 → 1,200개 (+500%)
File Buffer: 16 KB → 32 KB (+100%)
Serial CSV Buffer: 8 KB → 20 KB (+150%)

2. Task 우선순위 최적화
Core 1 (고성능 작업 전용):

CAN_RX: Priority 6 (최고) - 인터럽트 직후 즉시 처리
SD_WRITE: Priority 4 (높음), Stack 24 KB - 버퍼 넘침 방지
SEQ_TASK: Priority 2

Core 0 (WiFi/시스템 공유):

SERIAL_RX: Priority 5 (높음), Stack 6 KB - 921600 bps 고속 처리
TX_TASK: Priority 3 (보통-높음)
WEB_UPDATE: Priority 2, Stack 10 KB - 큰 JSON 버퍼
SD_MONITOR: Priority 1
RTC_SYNC: Priority 0
This commit is contained in:
2025-11-29 19:34:42 +00:00
parent 4b3b2702ca
commit fd797791d3

View File

@@ -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 시작");
}