sd카드 플러쉬 관련

This commit is contained in:
2026-03-06 17:04:05 +00:00
parent d33cdb8e82
commit e251acd6e3

View File

@@ -862,20 +862,17 @@ void checkAutoTriggers(struct can_frame &frame) {
}
}
if (anyMatch && result) {
loggingEnabled = false; // 먼저 중단 → sdFlushTask 새 flush 안 함
int waitCount = 0;
while (flushInProgress && waitCount++ < 500) vTaskDelay(10);
if (xSemaphoreTake(sdMutex, pdMS_TO_TICKS(2000)) == pdTRUE) {
if (writeBufferIndex > 0 && logFile) {
logFile.write(currentWriteBuffer, writeBufferIndex);
logFile.flush();
writeBufferIndex = 0;
}
int waitCount = 0;
while (flushInProgress && waitCount++ < 500) vTaskDelay(10);
if ((canLogFormatCSV || canLogFormatPCAP) && logFile) logFile.flush();
if (logFile) { logFile.close(); }
loggingEnabled = false;
if (logFile) { logFile.flush(); logFile.close(); }
autoTriggerActive = false;
currentFilename[0]= '\0';
writeBufferIndex = 0;
flushBufferSize = 0;
Serial.println("🎯 Auto Trigger 중지");
xSemaphoreGive(sdMutex);
@@ -1232,19 +1229,26 @@ void serial2RxTask(void *parameter) {
// ========================================
void sdFlushTask(void *parameter) {
Serial.println("✓ sdFlushTask 시작 (더블 버퍼링 + RingBuffer)");
uint32_t flushCount = 0; // flush 횟수 카운터 (flush() 호출 빈도 조절용)
while (1) {
uint32_t flushSize;
if (xTaskNotifyWait(0, 0xFFFFFFFF, &flushSize, portMAX_DELAY) == pdTRUE) {
if (flushSize > 0 && flushSize <= FILE_BUFFER_SIZE) {
flushInProgress = true;
uint32_t t0 = millis();
if (xSemaphoreTake(sdMutex, pdMS_TO_TICKS(5000)) == pdTRUE) {
if (logFile && sdCardReady && loggingEnabled) {
size_t written = logFile.write(currentFlushBuffer, flushSize);
logFile.flush();
uint32_t t0 = millis();
logFile.write(currentFlushBuffer, flushSize);
flushCount++;
// ★ logFile.flush()는 FAT 디렉토리 엔트리 2회 섹터 쓰기 유발
// 매번 호출하면 300ms+ 지연 → 4번(~256KB)마다 1회로 줄임
// 전원 차단 시 최대 256KB 손실 가능하나, 로깅 완료 후 stopLogging에서 반드시 flush
if (flushCount % 4 == 0) {
logFile.flush();
}
uint32_t elapsed = millis() - t0;
if (elapsed > 100)
Serial.printf("⚠️ SD Flush 지연: %d ms (%d bytes)\n", elapsed, written);
if (elapsed > 150)
Serial.printf("⚠️ SD Flush 지연: %d ms (%d bytes)\n", elapsed, (int)flushSize);
}
xSemaphoreGive(sdMutex);
} else {
@@ -1320,13 +1324,13 @@ void sdWriteTask(void *parameter) {
} else if (canLogFormatPCAP) {
// ── PCAP: 32bytes/record → 더블 버퍼 (BIN과 동일 구조) ──
if (writeBufferIndex + sizeof(PcapRecord) > FILE_BUFFER_SIZE) {
int waitCount = 0;
while (flushInProgress && waitCount++ < 100) vTaskDelay(1);
uint8_t* tmp = currentWriteBuffer;
// ★ flush 완전 완료까지 대기 (타임아웃 없음 - 데이터 손상 방지)
while (flushInProgress) vTaskDelay(1);
uint8_t* tmp = currentWriteBuffer;
currentWriteBuffer = currentFlushBuffer;
currentFlushBuffer = tmp;
flushBufferSize = writeBufferIndex;
writeBufferIndex = 0;
flushBufferSize = writeBufferIndex;
writeBufferIndex = 0;
if (sdFlushTaskHandle)
xTaskNotify(sdFlushTaskHandle, flushBufferSize, eSetValueWithOverwrite);
}
@@ -1362,18 +1366,18 @@ void sdWriteTask(void *parameter) {
// writeBuffer는 sdWriteTask만 쓰므로 mutex 불필요
if (writeBufferIndex + sizeof(CANMessage) > FILE_BUFFER_SIZE) {
// 버퍼 스왑 - flushTask 완료 대기 (최대 100ms)
int waitCount = 0;
while (flushInProgress && waitCount++ < 100) vTaskDelay(1);
// flush 완전 완료까지 대기 (타임아웃 없음)
// 이유: 100ms 타임아웃 후 강제 스왑하면 sdFlushTask가 읽는 중인
// 버퍼에 sdWriteTask가 덮어써서 SD 파일 데이터 손상 발생
// 안전성: RingBuffer 8192슬롯(~1초@8kfps)이 대기 중 메시지 흡수
while (flushInProgress) vTaskDelay(1);
// ★ 버퍼 포인터 스왑 (즉시, 블로킹 없음)
uint8_t* tmp = currentWriteBuffer;
uint8_t* tmp = currentWriteBuffer;
currentWriteBuffer = currentFlushBuffer;
currentFlushBuffer = tmp;
flushBufferSize = writeBufferIndex;
writeBufferIndex = 0;
flushBufferSize = writeBufferIndex;
writeBufferIndex = 0;
// sdFlushTask에 비동기 알림
if (sdFlushTaskHandle)
xTaskNotify(sdFlushTaskHandle, flushBufferSize, eSetValueWithOverwrite);
}
@@ -1672,20 +1676,23 @@ void webSocketEvent(uint8_t num, WStype_t type, uint8_t *payload, size_t length)
}
else if (strcmp(cmd, "stopLogging") == 0) {
if (loggingEnabled) {
// ① 로깅 중단 신호 → sdWriteTask/sdFlushTask가 새 데이터 쓰기 중단
loggingEnabled = false;
// ② sdFlushTask가 진행 중인 flush 완료 대기 (뮤텍스 잡기 전에!)
// sdMutex를 먼저 잡으면 sdFlushTask가 뮤텍스 못 얻어 데드락
int wc = 0;
while (flushInProgress && wc++ < 500) vTaskDelay(10);
// ③ 이제 sdMutex 안전하게 획득
if (xSemaphoreTake(sdMutex, pdMS_TO_TICKS(2000)) == pdTRUE) {
// ④ writeBuffer에 남은 데이터 마지막으로 쓰기
if (writeBufferIndex > 0 && logFile) {
logFile.write(currentWriteBuffer, writeBufferIndex);
logFile.flush();
writeBufferIndex = 0;
}
int wc = 0;
while (flushInProgress && wc++ < 500) vTaskDelay(10);
// CSV/PCAP 모두 최종 flush
if ((canLogFormatCSV || canLogFormatPCAP) && logFile) logFile.flush();
if (logFile) { logFile.close(); }
loggingEnabled = false;
// ⑤ 최종 flush (4번에 1번 규칙 무관하게 반드시 호출)
if (logFile) { logFile.flush(); logFile.close(); }
currentFilename[0] = '\0';
writeBufferIndex = 0; flushBufferSize = 0;
flushBufferSize = 0;
xSemaphoreGive(sdMutex);
}
}
@@ -2442,13 +2449,15 @@ void setup() {
}
String filename = "/" + server.arg("file");
// 로깅 중이면 잠시 중단 (sdWriteTask와 SD 버스 충돌 방지)
// 로깅 중이면 완전 중단 SD 버스 정리
bool wasLogging = loggingEnabled;
if (wasLogging) {
loggingEnabled = false;
// sdWriteTask가 현재 flush 중이면 완료 대기 (최대 500ms)
// sdWriteTask가 버퍼에 쓰던 것 완료 대기
// ② sdFlushTask가 SD에 flush 완료 대기 (여기가 핵심 - 안 기다리면 SD 버스 충돌)
int waitMs = 0;
while (flushInProgress && waitMs < 500) { delay(10); waitMs += 10; }
while (flushInProgress && waitMs < 2000) { delay(10); waitMs += 10; }
delay(50); // SD 버스 안정화
}
// sdMutex 획득 (sdFlushTask와 동시 접근 차단)
@@ -2486,7 +2495,6 @@ void setup() {
if (buf) {
size_t total = 0;
WiFiClient client = server.client();
uint32_t lastWdtFeed = millis();
while (file.available() && total < fileSize && client.connected()) {
size_t r = file.read(buf, CHUNK);
@@ -2505,13 +2513,7 @@ void setup() {
}
}
total += r;
// ★ Watchdog 피드 (매 500ms마다)
if (millis() - lastWdtFeed > 500) {
esp_task_wdt_reset();
lastWdtFeed = millis();
}
yield();
yield(); // WiFi 스택에 처리 시간 양보
}
free(buf);
Serial.printf("Download OK: %s (%u bytes)\n", filename.c_str(), total);