파일 라이팅 페일 조치
This commit is contained in:
@@ -1506,14 +1506,27 @@ void sdWriteTask(void *parameter) {
|
|||||||
csvFlushCounter = 0;
|
csvFlushCounter = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ⭐⭐⭐ 500개마다 파일 재오픈 (핵심!)
|
// ⭐⭐⭐ 2000개마다 파일 재오픈 (500 → 2000 변경, SDIO 안정성)
|
||||||
if (++csvReopenCounter >= 500) {
|
if (++csvReopenCounter >= 2000) {
|
||||||
logFile.close();
|
logFile.close();
|
||||||
|
delay(50); // SD 카드 안정화 대기
|
||||||
|
|
||||||
|
// 파일 재오픈 (재시도 로직)
|
||||||
|
bool reopenSuccess = false;
|
||||||
|
for (int retry = 0; retry < 3; retry++) {
|
||||||
logFile = SD_MMC.open(currentFilename, FILE_APPEND);
|
logFile = SD_MMC.open(currentFilename, FILE_APPEND);
|
||||||
if (logFile) {
|
if (logFile) {
|
||||||
Serial.printf("✓ CSV 파일 재오픈: %s (%lu bytes)\n", currentFilename, currentFileSize);
|
Serial.printf("✓ CSV 파일 재오픈: %s (%lu bytes)\n", currentFilename, currentFileSize);
|
||||||
|
reopenSuccess = true;
|
||||||
|
break;
|
||||||
} else {
|
} else {
|
||||||
Serial.println("✗ CSV 파일 재오픈 실패!");
|
Serial.printf("⚠ CSV 파일 재오픈 실패, 재시도 %d/3\n", retry + 1);
|
||||||
|
delay(100); // 재시도 전 대기
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!reopenSuccess) {
|
||||||
|
Serial.println("✗ CSV 파일 재오픈 완전 실패! 로깅 중지");
|
||||||
loggingEnabled = false;
|
loggingEnabled = false;
|
||||||
}
|
}
|
||||||
csvReopenCounter = 0;
|
csvReopenCounter = 0;
|
||||||
@@ -1552,8 +1565,8 @@ void sdWriteTask(void *parameter) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ⭐⭐⭐ 4단계: 500개마다 파일 재오픈 (핵심!)
|
// ⭐⭐⭐ 4단계: 2000개마다 파일 재오픈 (500 → 2000 변경, SDIO 안정성)
|
||||||
if (binReopenCounter >= 500) {
|
if (binReopenCounter >= 2000) {
|
||||||
// 버퍼 먼저 플러시
|
// 버퍼 먼저 플러시
|
||||||
if (logFile && bufferIndex > 0) {
|
if (logFile && bufferIndex > 0) {
|
||||||
logFile.write(fileBuffer, bufferIndex);
|
logFile.write(fileBuffer, bufferIndex);
|
||||||
@@ -1561,15 +1574,29 @@ void sdWriteTask(void *parameter) {
|
|||||||
bufferIndex = 0;
|
bufferIndex = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 파일 닫고 다시 열기
|
// 파일 닫기
|
||||||
logFile.close();
|
logFile.close();
|
||||||
|
delay(50); // SD 카드 안정화 대기
|
||||||
|
|
||||||
|
// 파일 재오픈 (재시도 로직)
|
||||||
|
bool reopenSuccess = false;
|
||||||
|
for (int retry = 0; retry < 3; retry++) {
|
||||||
logFile = SD_MMC.open(currentFilename, FILE_APPEND);
|
logFile = SD_MMC.open(currentFilename, FILE_APPEND);
|
||||||
if (logFile) {
|
if (logFile) {
|
||||||
Serial.printf("✓ BIN 파일 재오픈: %s (%lu bytes)\n", currentFilename, currentFileSize);
|
Serial.printf("✓ BIN 파일 재오픈: %s (%lu bytes)\n", currentFilename, currentFileSize);
|
||||||
|
reopenSuccess = true;
|
||||||
|
break;
|
||||||
} else {
|
} else {
|
||||||
Serial.println("✗ BIN 파일 재오픈 실패!");
|
Serial.printf("⚠ BIN 파일 재오픈 실패, 재시도 %d/3\n", retry + 1);
|
||||||
|
delay(100); // 재시도 전 대기
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!reopenSuccess) {
|
||||||
|
Serial.println("✗ BIN 파일 재오픈 완전 실패! 로깅 중지");
|
||||||
loggingEnabled = false;
|
loggingEnabled = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
binReopenCounter = 0;
|
binReopenCounter = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3368,7 +3395,6 @@ void setup() {
|
|||||||
// WebSocket 시작
|
// WebSocket 시작
|
||||||
webSocket.begin();
|
webSocket.begin();
|
||||||
webSocket.onEvent(webSocketEvent);
|
webSocket.onEvent(webSocketEvent);
|
||||||
webSocket.enableHeartbeat(15000, 3000, 2); // ping 15초, pong 3초, 재시도 2회
|
|
||||||
|
|
||||||
// ★★★ 웹 서버 라우팅 (중요!) ★★★
|
// ★★★ 웹 서버 라우팅 (중요!) ★★★
|
||||||
server.on("/", HTTP_GET, []() {
|
server.on("/", HTTP_GET, []() {
|
||||||
@@ -3644,22 +3670,55 @@ void setup() {
|
|||||||
// CAN 인터럽트 활성화
|
// CAN 인터럽트 활성화
|
||||||
attachInterrupt(digitalPinToInterrupt(CAN_INT_PIN), canISR, FALLING);
|
attachInterrupt(digitalPinToInterrupt(CAN_INT_PIN), canISR, FALLING);
|
||||||
|
|
||||||
// Task 생성
|
// ========================================
|
||||||
xTaskCreatePinnedToCore(canRxTask, "CAN_RX", 8192, NULL, configMAX_PRIORITIES - 1, &canRxTaskHandle, 1); // Core 0, Pri 24
|
// Task 생성 (최적화 버전)
|
||||||
xTaskCreatePinnedToCore(sdWriteTask, "SD_WRITE", 18576, NULL, 6, &sdWriteTaskHandle, 0);
|
// ========================================
|
||||||
xTaskCreatePinnedToCore(sequenceTask, "SEQ", 4096, NULL, 2, NULL, 1);
|
Serial.println("\nTask 생성 중...");
|
||||||
xTaskCreatePinnedToCore(serialRxTask, "SERIAL_RX", 6144, NULL, 5, &serialRxTaskHandle, 0);
|
|
||||||
xTaskCreatePinnedToCore(serial2RxTask, "SERIAL2_RX", 6144, NULL, 5, &serial2RxTaskHandle, 0); // ⭐ Serial2
|
// Core 1: 실시간 처리 + 웹 서비스
|
||||||
xTaskCreatePinnedToCore(txTask, "TX", 4096, NULL, 3, NULL, 1);
|
// - CAN RX: 최고 우선순위로 메시지 수신
|
||||||
xTaskCreatePinnedToCore(webUpdateTask, "WEB_UPDATE", 18384, NULL, 2, &webTaskHandle, 1); // ⭐ 10240 → 16384
|
// - WEB: Core 0의 SD 쓰기와 분리하여 응답성 확보
|
||||||
xTaskCreatePinnedToCore(sdMonitorTask, "SD_MONITOR", 4096, NULL, 1, NULL, 1);
|
// - 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(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
|
||||||
|
|
||||||
|
// Core 0: I/O 전용 (SD, Serial, RTC)
|
||||||
|
// - SD 쓰기와 Serial 수신만 처리
|
||||||
|
// - 웹은 Core 1에서 처리하므로 SD 지연 영향 없음
|
||||||
|
xTaskCreatePinnedToCore(sdWriteTask, "SD_WRITE", 18576, NULL, 8, &sdWriteTaskHandle, 0); // Pri 8 (최우선)
|
||||||
|
xTaskCreatePinnedToCore(serialRxTask, "SERIAL_RX", 6144, NULL, 6, &serialRxTaskHandle, 0); // Pri 6
|
||||||
|
xTaskCreatePinnedToCore(serial2RxTask, "SERIAL2_RX", 6144, NULL, 6, &serial2RxTaskHandle,0); // Pri 6
|
||||||
|
|
||||||
if (timeSyncStatus.rtcAvailable) {
|
if (timeSyncStatus.rtcAvailable) {
|
||||||
xTaskCreatePinnedToCore(rtcSyncTask, "RTC_SYNC", 3072, NULL, 0, &rtcTaskHandle, 0);
|
xTaskCreatePinnedToCore(rtcSyncTask, "RTC_SYNC", 3072, NULL, 0, &rtcTaskHandle, 0); // Pri 0 (최저)
|
||||||
}
|
}
|
||||||
|
|
||||||
Serial.println("✓ 모든 Task 시작 완료");
|
Serial.println("✓ 모든 Task 시작 완료");
|
||||||
Serial.println("\n========================================");
|
Serial.println("\n========================================");
|
||||||
|
Serial.println(" Task 구성 (Core 분리 전략)");
|
||||||
|
Serial.println("========================================");
|
||||||
|
Serial.println("Core 1 (실시간 + 웹 서비스):");
|
||||||
|
Serial.println(" - CAN_RX: 12KB, Pri 24 (최고)");
|
||||||
|
Serial.println(" - WEB_UPDATE: 12KB, Pri 4 ⭐ SD와 분리");
|
||||||
|
Serial.println(" - TX: 4KB, Pri 3");
|
||||||
|
Serial.println(" - SEQ: 4KB, Pri 2");
|
||||||
|
Serial.println(" - SD_MONITOR: 4KB, Pri 1");
|
||||||
|
Serial.println("Core 0 (I/O 전용):");
|
||||||
|
Serial.println(" - SD_WRITE: 18KB, Pri 8 (최우선)");
|
||||||
|
Serial.println(" - SERIAL_RX: 6KB, Pri 6");
|
||||||
|
Serial.println(" - SERIAL2_RX: 6KB, Pri 6");
|
||||||
|
if (timeSyncStatus.rtcAvailable) {
|
||||||
|
Serial.println(" - RTC_SYNC: 3KB, Pri 0");
|
||||||
|
}
|
||||||
|
Serial.println("========================================");
|
||||||
|
Serial.println("📌 웹을 Core 1로 배치 → SD 쓰기 지연 영향 없음");
|
||||||
|
Serial.println("========================================");
|
||||||
|
Serial.println("\n========================================");
|
||||||
Serial.println(" 접속 방법");
|
Serial.println(" 접속 방법");
|
||||||
Serial.println("========================================");
|
Serial.println("========================================");
|
||||||
Serial.printf(" WiFi SSID: %s\n", wifiSSID);
|
Serial.printf(" WiFi SSID: %s\n", wifiSSID);
|
||||||
@@ -3678,6 +3737,7 @@ void loop() {
|
|||||||
server.handleClient();
|
server.handleClient();
|
||||||
vTaskDelay(pdMS_TO_TICKS(10));
|
vTaskDelay(pdMS_TO_TICKS(10));
|
||||||
|
|
||||||
|
// 주기적 상태 출력 (30초)
|
||||||
static uint32_t lastPrint = 0;
|
static uint32_t lastPrint = 0;
|
||||||
if (millis() - lastPrint > 30000) {
|
if (millis() - lastPrint > 30000) {
|
||||||
Serial.printf("[상태] CAN: %d/%d | S1: %d/%d | S2: %d/%d | PSRAM: %d KB\n",
|
Serial.printf("[상태] CAN: %d/%d | S1: %d/%d | S2: %d/%d | PSRAM: %d KB\n",
|
||||||
@@ -3687,4 +3747,44 @@ void loop() {
|
|||||||
ESP.getFreePsram() / 1024);
|
ESP.getFreePsram() / 1024);
|
||||||
lastPrint = millis();
|
lastPrint = millis();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 스택 사용량 모니터링 (5분마다)
|
||||||
|
static uint32_t lastStackCheck = 0;
|
||||||
|
if (millis() - lastStackCheck > 300000) {
|
||||||
|
Serial.println("\n========== Task Stack Usage ==========");
|
||||||
|
|
||||||
|
if (canRxTaskHandle) {
|
||||||
|
UBaseType_t stackLeft = uxTaskGetStackHighWaterMark(canRxTaskHandle);
|
||||||
|
Serial.printf("CAN_RX: %5u bytes free (alloc: 12288)\n", stackLeft * 4);
|
||||||
|
if (stackLeft * 4 < 2000) Serial.println(" ⚠️ 스택 부족 위험!");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sdWriteTaskHandle) {
|
||||||
|
UBaseType_t stackLeft = uxTaskGetStackHighWaterMark(sdWriteTaskHandle);
|
||||||
|
Serial.printf("SD_WRITE: %5u bytes free (alloc: 18576)\n", stackLeft * 4);
|
||||||
|
if (stackLeft * 4 < 3000) Serial.println(" ⚠️ 스택 부족 위험!");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (webTaskHandle) {
|
||||||
|
UBaseType_t stackLeft = uxTaskGetStackHighWaterMark(webTaskHandle);
|
||||||
|
Serial.printf("WEB_UPDATE: %5u bytes free (alloc: 12288)\n", stackLeft * 4);
|
||||||
|
if (stackLeft * 4 < 2000) Serial.println(" ⚠️ 스택 부족 위험!");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (serialRxTaskHandle) {
|
||||||
|
UBaseType_t stackLeft = uxTaskGetStackHighWaterMark(serialRxTaskHandle);
|
||||||
|
Serial.printf("SERIAL_RX: %5u bytes free (alloc: 6144)\n", stackLeft * 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (serial2RxTaskHandle) {
|
||||||
|
UBaseType_t stackLeft = uxTaskGetStackHighWaterMark(serial2RxTaskHandle);
|
||||||
|
Serial.printf("SERIAL2_RX: %5u bytes free (alloc: 6144)\n", stackLeft * 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
Serial.printf("Free Heap: %u bytes\n", ESP.getFreeHeap());
|
||||||
|
Serial.printf("Free PSRAM: %u KB\n", ESP.getFreePsram() / 1024);
|
||||||
|
Serial.println("======================================\n");
|
||||||
|
|
||||||
|
lastStackCheck = millis();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user