sd카드 플러쉬 관련
This commit is contained in:
@@ -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);
|
||||
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,8 +1324,8 @@ 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);
|
||||
// ★ flush 완전 완료까지 대기 (타임아웃 없음 - 데이터 손상 방지)
|
||||
while (flushInProgress) vTaskDelay(1);
|
||||
uint8_t* tmp = currentWriteBuffer;
|
||||
currentWriteBuffer = currentFlushBuffer;
|
||||
currentFlushBuffer = tmp;
|
||||
@@ -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;
|
||||
currentWriteBuffer = currentFlushBuffer;
|
||||
currentFlushBuffer = tmp;
|
||||
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) {
|
||||
if (xSemaphoreTake(sdMutex, pdMS_TO_TICKS(2000)) == pdTRUE) {
|
||||
if (writeBufferIndex > 0 && logFile) {
|
||||
logFile.write(currentWriteBuffer, writeBufferIndex);
|
||||
logFile.flush();
|
||||
writeBufferIndex = 0;
|
||||
}
|
||||
// ① 로깅 중단 신호 → sdWriteTask/sdFlushTask가 새 데이터 쓰기 중단
|
||||
loggingEnabled = false;
|
||||
// ② sdFlushTask가 진행 중인 flush 완료 대기 (뮤텍스 잡기 전에!)
|
||||
// sdMutex를 먼저 잡으면 sdFlushTask가 뮤텍스 못 얻어 데드락
|
||||
int wc = 0;
|
||||
while (flushInProgress && wc++ < 500) vTaskDelay(10);
|
||||
// CSV/PCAP 모두 최종 flush
|
||||
if ((canLogFormatCSV || canLogFormatPCAP) && logFile) logFile.flush();
|
||||
if (logFile) { logFile.close(); }
|
||||
loggingEnabled = false;
|
||||
// ③ 이제 sdMutex 안전하게 획득
|
||||
if (xSemaphoreTake(sdMutex, pdMS_TO_TICKS(2000)) == pdTRUE) {
|
||||
// ④ writeBuffer에 남은 데이터 마지막으로 쓰기
|
||||
if (writeBufferIndex > 0 && logFile) {
|
||||
logFile.write(currentWriteBuffer, writeBufferIndex);
|
||||
writeBufferIndex = 0;
|
||||
}
|
||||
// ⑤ 최종 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);
|
||||
|
||||
Reference in New Issue
Block a user