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