diff --git a/ESP32_CAN_Logger-a.ino b/ESP32_CAN_Logger-a.ino index 31e9763..4c5ad73 100644 --- a/ESP32_CAN_Logger-a.ino +++ b/ESP32_CAN_Logger-a.ino @@ -1736,16 +1736,26 @@ void webSocketEvent(uint8_t num, WStype_t type, uint8_t *payload, size_t length) } } else if (strcmp(cmd, "sendSerial") == 0) { - const char* data = doc["data"]; - if (data && strlen(data) > 0) { - SerialComm.println(data); - SerialMessage msg; + const char* hexStr = doc["data"]; + if (hexStr && strlen(hexStr) > 0) { + uint8_t rawBuf[MAX_SERIAL_LINE_LEN]; int rawLen = 0; + for (int i = 0; hexStr[i] && hexStr[i+1] && rawLen < MAX_SERIAL_LINE_LEN; i += 2) { + while (hexStr[i] == ' ') i++; + if (!hexStr[i] || !hexStr[i+1]) break; + char h[3] = {hexStr[i], hexStr[i+1], 0}; + rawBuf[rawLen++] = (uint8_t)strtoul(h, NULL, 16); + } + SerialComm.write(rawBuf, rawLen); + totalSerialTxCount++; + // ★ TX echo: s1 패킷으로 즉시 전송 → JS handlePacket(true, hex, ts) + char txHex[MAX_SERIAL_LINE_LEN*2+1]; + for (int i=0; i 0) { - Serial2Comm.println(data); - SerialMessage msg; + const char* hexStr = doc["data"]; + if (hexStr && strlen(hexStr) > 0) { + uint8_t rawBuf[MAX_SERIAL_LINE_LEN]; int rawLen = 0; + for (int i = 0; hexStr[i] && hexStr[i+1] && rawLen < MAX_SERIAL_LINE_LEN; i += 2) { + while (hexStr[i] == ' ') i++; + if (!hexStr[i] || !hexStr[i+1]) break; + char h[3] = {hexStr[i], hexStr[i+1], 0}; + rawBuf[rawLen++] = (uint8_t)strtoul(h, NULL, 16); + } + Serial2Comm.write(rawBuf, rawLen); + totalSerial2TxCount++; + char txHex[MAX_SERIAL_LINE_LEN*2+1]; + for (int i=0; i= 0 && si < 4) { @@ -2244,89 +2266,87 @@ void webUpdateTask(void *parameter) { } } - // ★ Serial RingBuffer pop (최대 10개) - SerialMessage serialMsg; - JsonArray serialMessages = doc.createNestedArray("serialMessages"); - int serialCnt = 0; - while (serialCnt < 10 && ring_serial_pop(&serialRing, &serialMsg)) { - JsonObject so = serialMessages.createNestedObject(); - so["timestamp"] = serialMsg.timestamp_us; - so["isTx"] = serialMsg.isTx; - char ds[MAX_SERIAL_LINE_LEN+1]; - memcpy(ds, serialMsg.data, serialMsg.length); - ds[serialMsg.length] = '\0'; - so["data"] = ds; - serialCnt++; - - // Serial SD 로깅 - if (serialLoggingEnabled && sdCardReady) { - if (xSemaphoreTake(sdMutex, pdMS_TO_TICKS(10)) == pdTRUE) { - if (serialLogFormatCSV) { - uint64_t rt = serialMsg.timestamp_us - serialLogStartTime; - char csvLine[256]; - int ll = snprintf(csvLine, sizeof(csvLine), - "%llu,%s,\"%s\"\n", rt, serialMsg.isTx?"TX":"RX", ds); - if (serialCsvIndex + ll <= SERIAL_CSV_BUFFER_SIZE) { - memcpy(&serialCsvBuffer[serialCsvIndex], csvLine, ll); - serialCsvIndex += ll; - currentSerialFileSize += ll; - } - if (serialCsvIndex >= SERIAL_CSV_BUFFER_SIZE - 256) { - if (serialLogFile) { serialLogFile.write((uint8_t*)serialCsvBuffer, serialCsvIndex); serialLogFile.flush(); serialCsvIndex=0; } - } - } else { - if (serialLogFile) { - serialLogFile.write((uint8_t*)&serialMsg, sizeof(SerialMessage)); - currentSerialFileSize += sizeof(SerialMessage); - static int bfc=0; if (++bfc>=50){serialLogFile.flush();bfc=0;} + // ★★★ Serial1/2 데이터: update 패킷 분리 → 별도 소형 s1/s2 패킷으로 즉시 전송 + // 이유: update 패킷 JSON 크기 초과 시 전체 드롭됨 (8192 또는 12000 limit) + { + SerialMessage sMsg; + while (ring_serial_pop(&serialRing, &sMsg)) { + // raw bytes → hex string (0x80+ UTF-8 안전) + char hexBuf[MAX_SERIAL_LINE_LEN*2+1]; + for (int hi=0; hi= SERIAL_CSV_BUFFER_SIZE-256 && serialLogFile) + { serialLogFile.write((uint8_t*)serialCsvBuffer, serialCsvIndex); serialLogFile.flush(); serialCsvIndex=0; } + } else { + if (serialLogFile) { + serialLogFile.write((uint8_t*)&sMsg, sizeof(SerialMessage)); + currentSerialFileSize += sizeof(SerialMessage); + static int bfc=0; if (++bfc>=50){serialLogFile.flush();bfc=0;} + } } + xSemaphoreGive(sdMutex); } - xSemaphoreGive(sdMutex); } } } - - // ★ Serial2 RingBuffer pop - SerialMessage serial2Msg; - JsonArray serial2Messages = doc.createNestedArray("serial2Messages"); - int s2Cnt = 0; - while (s2Cnt < 10 && ring_serial_pop(&serial2Ring, &serial2Msg)) { - JsonObject so = serial2Messages.createNestedObject(); - so["timestamp"] = serial2Msg.timestamp_us; - so["isTx"] = serial2Msg.isTx; - char ds[MAX_SERIAL_LINE_LEN+1]; - memcpy(ds, serial2Msg.data, serial2Msg.length); - ds[serial2Msg.length] = '\0'; - so["data"] = ds; - s2Cnt++; - - if (serial2LoggingEnabled && sdCardReady) { - if (xSemaphoreTake(sdMutex, pdMS_TO_TICKS(10)) == pdTRUE) { - if (serial2LogFormatCSV) { - uint64_t rt = serial2Msg.timestamp_us - serial2LogStartTime; - char csvLine[256]; - int ll = snprintf(csvLine, sizeof(csvLine), - "%llu,%s,\"%s\"\n", rt, serial2Msg.isTx?"TX":"RX", ds); - if (serial2CsvIndex + ll <= SERIAL2_CSV_BUFFER_SIZE) { - memcpy(&serial2CsvBuffer[serial2CsvIndex], csvLine, ll); - serial2CsvIndex += ll; - currentSerial2FileSize += ll; - } - if (serial2CsvIndex >= SERIAL2_CSV_BUFFER_SIZE - 256) { - if (serial2LogFile) { serial2LogFile.write((uint8_t*)serial2CsvBuffer, serial2CsvIndex); serial2LogFile.flush(); serial2CsvIndex=0; } - } - } else { - if (serial2LogFile) { - serial2LogFile.write((uint8_t*)&serial2Msg, sizeof(SerialMessage)); - currentSerial2FileSize += sizeof(SerialMessage); - static int bfc2=0; if (++bfc2>=50){serial2LogFile.flush();bfc2=0;} + { + SerialMessage sMsg2; + while (ring_serial_pop(&serial2Ring, &sMsg2)) { + char hexBuf2[MAX_SERIAL_LINE_LEN*2+1]; + for (int hi=0; hi= SERIAL2_CSV_BUFFER_SIZE-256 && serial2LogFile) + { serial2LogFile.write((uint8_t*)serial2CsvBuffer, serial2CsvIndex); serial2LogFile.flush(); serial2CsvIndex=0; } + } else { + if (serial2LogFile) { + serial2LogFile.write((uint8_t*)&sMsg2, sizeof(SerialMessage)); + currentSerial2FileSize += sizeof(SerialMessage); + static int bfc2=0; if (++bfc2>=50){serial2LogFile.flush();bfc2=0;} + } } + xSemaphoreGive(sdMutex); } - xSemaphoreGive(sdMutex); } } } - String json; size_t jsonSize = serializeJson(doc, json); if (jsonSize > 0 && jsonSize < 8192) webSocket.broadcastTXT(json); @@ -2655,8 +2675,8 @@ void setup() { xTaskCreatePinnedToCore(sdMonitorTask, "SD_MONITOR", 4096, NULL, 1, NULL, 1); xTaskCreatePinnedToCore(sdFlushTask, "SD_FLUSH", 4096, NULL, 9, &sdFlushTaskHandle, 0); xTaskCreatePinnedToCore(sdWriteTask, "SD_WRITE", 18576, NULL, 8, &sdWriteTaskHandle, 0); - xTaskCreatePinnedToCore(serialRxTask, "SERIAL_RX", 6144, NULL, 6, &serialRxTaskHandle, 0); - xTaskCreatePinnedToCore(serial2RxTask, "SERIAL2_RX", 6144, NULL, 6, &serial2RxTaskHandle, 0); + xTaskCreatePinnedToCore(serialRxTask, "SERIAL_RX", 6144, NULL, 6, &serialRxTaskHandle, 1); + xTaskCreatePinnedToCore(serial2RxTask, "SERIAL2_RX", 6144, NULL, 6, &serial2RxTaskHandle, 1); if (timeSyncStatus.rtcAvailable) xTaskCreatePinnedToCore(rtcSyncTask,"RTC_SYNC", 3072, NULL, 0, &rtcTaskHandle, 0); // ★ canRxTask 마지막 생성 (Core 1 Pri 24 → 이 시점엔 setup() 완료 직전)