can logging 멈춤 해결, 파일 정상 로깅
This commit is contained in:
@@ -250,9 +250,9 @@ File serial2LogFile; // ⭐ Serial2 추가
|
||||
char currentFilename[MAX_FILENAME_LEN];
|
||||
char currentSerialFilename[MAX_FILENAME_LEN];
|
||||
char currentSerial2Filename[MAX_FILENAME_LEN]; // ⭐ Serial2 추가
|
||||
uint16_t bufferIndex = 0;
|
||||
uint16_t serialCsvIndex = 0;
|
||||
uint16_t serial2CsvIndex = 0; // ⭐ Serial2 추가
|
||||
uint32_t bufferIndex = 0; // ⭐ uint16_t → uint32_t (오버플로우 방지)
|
||||
uint32_t serialCsvIndex = 0; // ⭐ uint16_t → uint32_t
|
||||
uint32_t serial2CsvIndex = 0; // ⭐ uint16_t → uint32_t // ⭐ Serial2 추가
|
||||
volatile uint32_t currentFileSize = 0;
|
||||
volatile uint32_t currentSerialFileSize = 0;
|
||||
volatile uint32_t currentSerial2FileSize = 0; // ⭐ Serial2 추가
|
||||
@@ -837,6 +837,28 @@ void canRxTask(void *parameter) {
|
||||
struct can_frame frame;
|
||||
CANMessage msg;
|
||||
|
||||
Serial.println("✓ CAN RX Task 시작 (Core 0, Priority 24 - 절대 최고!)");
|
||||
|
||||
// ⭐⭐⭐ 초기 버퍼 확인
|
||||
if (digitalRead(CAN_INT_PIN) == LOW) {
|
||||
Serial.println("⚠️ 초기 CAN 인터럽트 핀 LOW - 버퍼 클리어");
|
||||
int readCount = 0;
|
||||
while (mcp2515.readMessage(&frame) == MCP2515::ERROR_OK && readCount < 100) {
|
||||
struct timeval tv;
|
||||
gettimeofday(&tv, NULL);
|
||||
msg.timestamp_us = (uint64_t)tv.tv_sec * 1000000ULL + tv.tv_usec;
|
||||
msg.id = frame.can_id & 0x1FFFFFFF;
|
||||
msg.dlc = frame.can_dlc;
|
||||
memcpy(msg.data, frame.data, 8);
|
||||
|
||||
if (xQueueSend(canQueue, &msg, 0) == pdTRUE) {
|
||||
totalMsgCount++;
|
||||
readCount++;
|
||||
}
|
||||
}
|
||||
Serial.printf("✓ 초기 버퍼에서 %d개 읽음\n", readCount);
|
||||
}
|
||||
|
||||
while (1) {
|
||||
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
|
||||
|
||||
@@ -848,7 +870,7 @@ void canRxTask(void *parameter) {
|
||||
msg.dlc = frame.can_dlc;
|
||||
memcpy(msg.data, frame.data, 8);
|
||||
|
||||
if (xQueueSend(canQueue, &msg, 0) == pdTRUE) {
|
||||
if (xQueueSend(canQueue, &msg, pdMS_TO_TICKS(10)) == pdTRUE) {
|
||||
totalMsgCount++;
|
||||
}
|
||||
}
|
||||
@@ -892,7 +914,7 @@ void sdWriteTask(void *parameter) {
|
||||
|
||||
// CAN 로깅
|
||||
if (loggingEnabled && sdCardReady) {
|
||||
if (xSemaphoreTake(sdMutex, pdMS_TO_TICKS(100)) == pdTRUE) {
|
||||
if (xSemaphoreTake(sdMutex, pdMS_TO_TICKS(50)) == pdTRUE) {
|
||||
if (canLogFormatCSV) {
|
||||
char csvLine[128];
|
||||
uint64_t relativeTime = canMsg.timestamp_us - canLogStartTime;
|
||||
@@ -913,26 +935,27 @@ void sdWriteTask(void *parameter) {
|
||||
currentFileSize += lineLen;
|
||||
|
||||
static int csvFlushCounter = 0;
|
||||
if (++csvFlushCounter >= 100) {
|
||||
if (++csvFlushCounter >= 20) { // 50 → 20으로 더 자주 플러시
|
||||
logFile.flush();
|
||||
csvFlushCounter = 0;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// BIN 형식
|
||||
if (bufferIndex + sizeof(CANMessage) <= FILE_BUFFER_SIZE) {
|
||||
memcpy(&fileBuffer[bufferIndex], &canMsg, sizeof(CANMessage));
|
||||
bufferIndex += sizeof(CANMessage);
|
||||
currentFileSize += sizeof(CANMessage);
|
||||
}
|
||||
|
||||
if (bufferIndex >= FILE_BUFFER_SIZE - sizeof(CANMessage)) {
|
||||
// ⭐⭐⭐ 1단계: 버퍼 가득 찼으면 먼저 플러시
|
||||
if (bufferIndex + sizeof(CANMessage) > FILE_BUFFER_SIZE) {
|
||||
if (logFile) {
|
||||
logFile.write(fileBuffer, bufferIndex);
|
||||
size_t written = logFile.write(fileBuffer, bufferIndex);
|
||||
logFile.flush();
|
||||
Serial.printf("✓ BIN 버퍼 플러시: %d bytes written\n", written);
|
||||
bufferIndex = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// ⭐⭐⭐ 2단계: 이제 공간 확보됨, 데이터 추가
|
||||
memcpy(&fileBuffer[bufferIndex], &canMsg, sizeof(CANMessage));
|
||||
bufferIndex += sizeof(CANMessage);
|
||||
currentFileSize += sizeof(CANMessage);
|
||||
}
|
||||
xSemaphoreGive(sdMutex);
|
||||
}
|
||||
@@ -946,7 +969,7 @@ void sdWriteTask(void *parameter) {
|
||||
}
|
||||
|
||||
void sdMonitorTask(void *parameter) {
|
||||
const TickType_t xDelay = pdMS_TO_TICKS(1000);
|
||||
const TickType_t xDelay = pdMS_TO_TICKS(500);
|
||||
|
||||
while (1) {
|
||||
uint32_t currentTime = millis();
|
||||
@@ -1182,7 +1205,7 @@ void sequenceTask(void *parameter) {
|
||||
// ========================================
|
||||
void webSocketEvent(uint8_t num, WStype_t type, uint8_t *payload, size_t length) {
|
||||
if (type == WStype_TEXT) {
|
||||
DynamicJsonDocument doc(2048);
|
||||
DynamicJsonDocument doc(44384);
|
||||
DeserializationError error = deserializeJson(doc, payload);
|
||||
|
||||
if (error) return;
|
||||
@@ -1268,16 +1291,29 @@ void webSocketEvent(uint8_t num, WStype_t type, uint8_t *payload, size_t length)
|
||||
timeinfo.tm_year + 1900, timeinfo.tm_mon + 1, timeinfo.tm_mday,
|
||||
timeinfo.tm_hour, timeinfo.tm_min, timeinfo.tm_sec, ext);
|
||||
|
||||
// ⭐⭐⭐ 파일 생성 (헤더 쓰기)
|
||||
logFile = SD.open(currentFilename, FILE_WRITE);
|
||||
|
||||
if (logFile) {
|
||||
if (canLogFormatCSV) {
|
||||
logFile.println("Time_us,CAN_ID,DLC,Data");
|
||||
logFile.flush();
|
||||
}
|
||||
logFile.close(); // ⭐ 헤더 쓰고 닫기
|
||||
|
||||
loggingEnabled = true;
|
||||
bufferIndex = 0;
|
||||
currentFileSize = logFile.size();
|
||||
// ⭐⭐⭐ APPEND 모드로 다시 열기
|
||||
logFile = SD.open(currentFilename, FILE_APPEND);
|
||||
|
||||
if (logFile) {
|
||||
loggingEnabled = true;
|
||||
bufferIndex = 0;
|
||||
currentFileSize = logFile.size();
|
||||
Serial.printf("✓ 로깅 파일 열림 (APPEND): %s\n", currentFilename);
|
||||
} else {
|
||||
Serial.println("✗ APPEND 모드로 파일 열기 실패");
|
||||
}
|
||||
} else {
|
||||
Serial.println("✗ 파일 생성 실패");
|
||||
}
|
||||
|
||||
xSemaphoreGive(sdMutex);
|
||||
@@ -1410,10 +1446,20 @@ void webSocketEvent(uint8_t num, WStype_t type, uint8_t *payload, size_t length)
|
||||
int speedIndex = doc["speed"];
|
||||
if (speedIndex >= 0 && speedIndex < 4) {
|
||||
currentCanSpeed = canSpeedValues[speedIndex];
|
||||
mcp2515.reset();
|
||||
mcp2515.setBitrate(currentCanSpeed, MCP_8MHZ);
|
||||
setMCP2515Mode(currentMcpMode);
|
||||
// ⭐⭐⭐ MCP2515 리셋 제거! (CAN 수신 중단 방지)
|
||||
// 속도 변경은 로깅 중지 후 수동으로만 가능하도록 변경
|
||||
// mcp2515.reset();
|
||||
// mcp2515.setBitrate(currentCanSpeed, MCP_8MHZ);
|
||||
// setMCP2515Mode(currentMcpMode);
|
||||
saveSettings();
|
||||
|
||||
// 사용자에게 안내 메시지
|
||||
StaticJsonDocument<256> response;
|
||||
response["type"] = "info";
|
||||
response["message"] = "CAN speed saved. Stop logging and restart to apply.";
|
||||
String json;
|
||||
serializeJson(response, json);
|
||||
webSocket.sendTXT(num, json);
|
||||
}
|
||||
}
|
||||
else if (strcmp(cmd, "startSerial2Logging") == 0) {
|
||||
@@ -1525,10 +1571,20 @@ void webSocketEvent(uint8_t num, WStype_t type, uint8_t *payload, size_t length)
|
||||
int speedIndex = doc["speed"];
|
||||
if (speedIndex >= 0 && speedIndex < 4) {
|
||||
currentCanSpeed = canSpeedValues[speedIndex];
|
||||
mcp2515.reset();
|
||||
mcp2515.setBitrate(currentCanSpeed, MCP_8MHZ);
|
||||
setMCP2515Mode(currentMcpMode);
|
||||
// ⭐⭐⭐ MCP2515 리셋 제거! (CAN 수신 중단 방지)
|
||||
// 속도 변경은 로깅 중지 후 수동으로만 가능하도록 변경
|
||||
// mcp2515.reset();
|
||||
// mcp2515.setBitrate(currentCanSpeed, MCP_8MHZ);
|
||||
// setMCP2515Mode(currentMcpMode);
|
||||
saveSettings();
|
||||
|
||||
// 사용자에게 안내 메시지
|
||||
StaticJsonDocument<256> response;
|
||||
response["type"] = "info";
|
||||
response["message"] = "CAN speed saved. Stop logging and restart to apply.";
|
||||
String json;
|
||||
serializeJson(response, json);
|
||||
webSocket.sendTXT(num, json);
|
||||
}
|
||||
}
|
||||
else if (strcmp(cmd, "setMcpMode") == 0) {
|
||||
@@ -1735,7 +1791,7 @@ void webUpdateTask(void *parameter) {
|
||||
webSocket.loop();
|
||||
|
||||
if (webSocket.connectedClients() > 0) {
|
||||
DynamicJsonDocument doc(8192); // ⭐ 4096 → 8192로 증가
|
||||
DynamicJsonDocument doc(16384); // ⭐ 4096 → 8192로 증가
|
||||
doc["type"] = "update";
|
||||
doc["logging"] = loggingEnabled;
|
||||
doc["serialLogging"] = serialLoggingEnabled;
|
||||
@@ -1837,7 +1893,7 @@ void webUpdateTask(void *parameter) {
|
||||
serialMsg.isTx ? "TX" : "RX",
|
||||
dataStr);
|
||||
|
||||
if (serialCsvIndex + lineLen < SERIAL_CSV_BUFFER_SIZE) {
|
||||
if (serialCsvIndex + lineLen <= SERIAL_CSV_BUFFER_SIZE) { // < → <=
|
||||
memcpy(&serialCsvBuffer[serialCsvIndex], csvLine, lineLen);
|
||||
serialCsvIndex += lineLen;
|
||||
currentSerialFileSize += lineLen;
|
||||
@@ -1898,7 +1954,7 @@ void webUpdateTask(void *parameter) {
|
||||
serial2Msg.isTx ? "TX" : "RX",
|
||||
dataStr);
|
||||
|
||||
if (serial2CsvIndex + lineLen < SERIAL2_CSV_BUFFER_SIZE) {
|
||||
if (serial2CsvIndex + lineLen <= SERIAL2_CSV_BUFFER_SIZE) { // < → <=
|
||||
memcpy(&serial2CsvBuffer[serial2CsvIndex], csvLine, lineLen);
|
||||
serial2CsvIndex += lineLen;
|
||||
currentSerial2FileSize += lineLen;
|
||||
@@ -1951,7 +2007,7 @@ void webUpdateTask(void *parameter) {
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
delay(1000);
|
||||
|
||||
WiFi.setSleep(false);
|
||||
Serial.println("\n========================================");
|
||||
Serial.println(" Byun CAN Logger + Serial Terminal");
|
||||
Serial.println(" Version 2.3 - PSRAM Optimized");
|
||||
@@ -1993,10 +2049,33 @@ void setup() {
|
||||
|
||||
// MCP2515 초기화
|
||||
Serial.println("MCP2515 초기화...");
|
||||
mcp2515.reset();
|
||||
|
||||
// ⭐⭐⭐ 하드 리셋
|
||||
digitalWrite(HSPI_CS, LOW);
|
||||
delay(10);
|
||||
digitalWrite(HSPI_CS, HIGH);
|
||||
delay(50);
|
||||
|
||||
mcp2515.reset();
|
||||
delay(100);
|
||||
mcp2515.setBitrate(currentCanSpeed, MCP_8MHZ);
|
||||
delay(10);
|
||||
mcp2515.setNormalMode();
|
||||
delay(50);
|
||||
|
||||
// 버퍼 클리어
|
||||
Serial.println("CAN 버퍼 클리어...");
|
||||
struct can_frame dummyFrame;
|
||||
int clearCount = 0;
|
||||
while (mcp2515.readMessage(&dummyFrame) == MCP2515::ERROR_OK) {
|
||||
clearCount++;
|
||||
if (clearCount > 100) break;
|
||||
}
|
||||
if (clearCount > 0) {
|
||||
Serial.printf("✓ 버퍼 클리어 완료 (%d개)\n", clearCount);
|
||||
}
|
||||
mcp2515.clearRXnOVRFlags();
|
||||
|
||||
Serial.println("✓ MCP2515 초기화 완료");
|
||||
|
||||
// Serial 통신 초기화
|
||||
@@ -2132,13 +2211,13 @@ void setup() {
|
||||
attachInterrupt(digitalPinToInterrupt(CAN_INT_PIN), canISR, FALLING);
|
||||
|
||||
// Task 생성
|
||||
xTaskCreatePinnedToCore(canRxTask, "CAN_RX", 4096, NULL, 6, &canRxTaskHandle, 1);
|
||||
xTaskCreatePinnedToCore(sdWriteTask, "SD_WRITE", 24576, NULL, 4, &sdWriteTaskHandle, 1);
|
||||
xTaskCreatePinnedToCore(canRxTask, "CAN_RX", 8192, NULL, configMAX_PRIORITIES - 1, &canRxTaskHandle, 1); // Core 0, Pri 24
|
||||
xTaskCreatePinnedToCore(sdWriteTask, "SD_WRITE", 12576, NULL, 6, &sdWriteTaskHandle, 0);
|
||||
xTaskCreatePinnedToCore(sequenceTask, "SEQ", 4096, NULL, 2, NULL, 1);
|
||||
xTaskCreatePinnedToCore(serialRxTask, "SERIAL_RX", 6144, NULL, 5, &serialRxTaskHandle, 0);
|
||||
xTaskCreatePinnedToCore(serial2RxTask, "SERIAL2_RX", 6144, NULL, 5, &serial2RxTaskHandle, 0); // ⭐ Serial2
|
||||
xTaskCreatePinnedToCore(txTask, "TX", 4096, NULL, 3, NULL, 0);
|
||||
xTaskCreatePinnedToCore(webUpdateTask, "WEB_UPDATE", 16384, NULL, 2, &webTaskHandle, 0); // ⭐ 10240 → 16384
|
||||
xTaskCreatePinnedToCore(txTask, "TX", 4096, NULL, 3, NULL, 1);
|
||||
xTaskCreatePinnedToCore(webUpdateTask, "WEB_UPDATE", 16384, NULL, 5, &webTaskHandle, 0); // ⭐ 10240 → 16384
|
||||
xTaskCreatePinnedToCore(sdMonitorTask, "SD_MONITOR", 4096, NULL, 1, NULL, 0);
|
||||
|
||||
if (timeSyncStatus.rtcAvailable) {
|
||||
|
||||
17
index.h
17
index.h
@@ -26,7 +26,7 @@ const char index_html[] PROGMEM = R"rawliteral(
|
||||
}
|
||||
.header {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
color: #666;
|
||||
color: white;
|
||||
padding: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
@@ -883,7 +883,7 @@ const char index_html[] PROGMEM = R"rawliteral(
|
||||
const modeNames = {0: 'NORMAL (TX/RX+ACK)', 1: 'LISTEN-ONLY (RX)', 2: 'LOOPBACK', 3: 'TRANSMIT-ONLY (TX)'};
|
||||
let currentLoggingFile = '';
|
||||
let commentingFile = '';
|
||||
let hasInitialSync = false;
|
||||
// hasInitialSync 제거 - 매번 자동 동기화
|
||||
|
||||
function updateCurrentTime() {
|
||||
const now = new Date();
|
||||
@@ -943,11 +943,18 @@ const char index_html[] PROGMEM = R"rawliteral(
|
||||
document.getElementById('sync-status').textContent = '연결됨';
|
||||
document.getElementById('sync-status').style.color = '#38ef7d';
|
||||
|
||||
if (!hasInitialSync) {
|
||||
// ⭐⭐⭐ 자동 시간 동기화 (1분에 1회로 제한)
|
||||
const now = Date.now();
|
||||
const lastSync = parseInt(localStorage.getItem('lastTimeSync') || '0');
|
||||
|
||||
if (now - lastSync > 60000) { // 1분 이상 경과
|
||||
setTimeout(function() {
|
||||
syncTimeFromPhone();
|
||||
hasInitialSync = true;
|
||||
}, 500);
|
||||
localStorage.setItem('lastTimeSync', now.toString());
|
||||
console.log('Auto time sync on connect');
|
||||
}, 100);
|
||||
} else {
|
||||
console.log('Skipping time sync (last sync was recent)');
|
||||
}
|
||||
|
||||
// ⭐ WebSocket 연결되면 즉시 파일 목록 요청
|
||||
|
||||
Reference in New Issue
Block a user