transmit 재 작성_can mode 추가
This commit is contained in:
@@ -85,6 +85,33 @@ struct TxMessage {
|
||||
bool active;
|
||||
};
|
||||
|
||||
// CAN 시퀀스 스텝 구조체
|
||||
struct SequenceStep {
|
||||
uint32_t canId;
|
||||
bool extended;
|
||||
uint8_t dlc;
|
||||
uint8_t data[8];
|
||||
uint32_t delayMs; // 이 스텝 실행 후 대기 시간 (ms)
|
||||
};
|
||||
|
||||
// CAN 시퀀스 구조체
|
||||
struct CANSequence {
|
||||
char name[32];
|
||||
SequenceStep steps[20]; // 최대 20개 스텝
|
||||
uint8_t stepCount;
|
||||
uint8_t repeatMode; // 0=한번, 1=특정횟수, 2=무한
|
||||
uint32_t repeatCount; // repeatMode=1일 때 반복 횟수
|
||||
};
|
||||
|
||||
// 시퀀스 실행 상태
|
||||
struct SequenceRuntime {
|
||||
bool running;
|
||||
uint8_t currentStep;
|
||||
uint32_t currentRepeat;
|
||||
uint32_t lastStepTime;
|
||||
int8_t activeSequenceIndex; // 실행 중인 시퀀스 인덱스
|
||||
};
|
||||
|
||||
// 파일 커멘트 구조체
|
||||
struct FileComment {
|
||||
char filename[MAX_FILENAME_LEN];
|
||||
@@ -110,11 +137,21 @@ struct PowerStatus {
|
||||
uint32_t lastMinReset; // 최소값 리셋 시간
|
||||
} powerStatus = {0.0, 999.9, false, 0, 0};
|
||||
|
||||
// MCP2515 레지스터 주소 정의 (라이브러리에 없는 경우)
|
||||
#ifndef MCP_CANCTRL
|
||||
#define MCP_CANCTRL 0x0F
|
||||
#endif
|
||||
|
||||
#ifndef MCP_CANSTAT
|
||||
#define MCP_CANSTAT 0x0E
|
||||
#endif
|
||||
|
||||
// MCP2515 모드 정의
|
||||
enum MCP2515Mode {
|
||||
MCP_MODE_NORMAL = 0,
|
||||
MCP_MODE_LISTEN_ONLY = 1,
|
||||
MCP_MODE_LOOPBACK = 2
|
||||
MCP_MODE_LOOPBACK = 2,
|
||||
MCP_MODE_TRANSMIT = 3 // 송신만 (ACK 없음)
|
||||
};
|
||||
|
||||
// WiFi AP 기본 설정
|
||||
@@ -174,6 +211,12 @@ uint32_t lastMsgCount = 0;
|
||||
TxMessage txMessages[MAX_TX_MESSAGES];
|
||||
uint32_t totalTxCount = 0;
|
||||
|
||||
// CAN 시퀀스 (최대 10개 저장 가능)
|
||||
#define MAX_SEQUENCES 10
|
||||
CANSequence sequences[MAX_SEQUENCES];
|
||||
uint8_t sequenceCount = 0;
|
||||
SequenceRuntime seqRuntime = {false, 0, 0, 0, -1};
|
||||
|
||||
// 파일 커멘트 저장 (최대 50개)
|
||||
#define MAX_FILE_COMMENTS 50
|
||||
FileComment fileComments[MAX_FILE_COMMENTS];
|
||||
@@ -216,6 +259,92 @@ void saveSettings() {
|
||||
Serial.println("⚠️ 재부팅 후 WiFi 설정이 적용됩니다.");
|
||||
}
|
||||
|
||||
// ========================================
|
||||
// 시퀀스 관리 함수
|
||||
// ========================================
|
||||
|
||||
void loadSequences() {
|
||||
if (!sdCardReady) return;
|
||||
|
||||
if (xSemaphoreTake(sdMutex, pdMS_TO_TICKS(1000)) == pdTRUE) {
|
||||
File seqFile = SD.open("/sequences.bin", FILE_READ);
|
||||
if (seqFile) {
|
||||
sequenceCount = 0;
|
||||
while (seqFile.available() && sequenceCount < MAX_SEQUENCES) {
|
||||
seqFile.read((uint8_t*)&sequences[sequenceCount], sizeof(CANSequence));
|
||||
sequenceCount++;
|
||||
}
|
||||
seqFile.close();
|
||||
Serial.printf("✓ 시퀀스 로드: %d개\n", sequenceCount);
|
||||
}
|
||||
xSemaphoreGive(sdMutex);
|
||||
}
|
||||
}
|
||||
|
||||
void saveSequences() {
|
||||
if (!sdCardReady) return;
|
||||
|
||||
if (xSemaphoreTake(sdMutex, pdMS_TO_TICKS(1000)) == pdTRUE) {
|
||||
// 기존 파일 삭제
|
||||
if (SD.exists("/sequences.bin")) {
|
||||
SD.remove("/sequences.bin");
|
||||
}
|
||||
|
||||
File seqFile = SD.open("/sequences.bin", FILE_WRITE);
|
||||
if (seqFile) {
|
||||
for (int i = 0; i < sequenceCount; i++) {
|
||||
seqFile.write((uint8_t*)&sequences[i], sizeof(CANSequence));
|
||||
}
|
||||
seqFile.close();
|
||||
Serial.println("✓ 시퀀스 저장 완료");
|
||||
}
|
||||
xSemaphoreGive(sdMutex);
|
||||
}
|
||||
}
|
||||
|
||||
int addSequence(const CANSequence& seq) {
|
||||
if (sequenceCount >= MAX_SEQUENCES) {
|
||||
return -1; // 가득 참
|
||||
}
|
||||
|
||||
sequences[sequenceCount] = seq;
|
||||
sequenceCount++;
|
||||
saveSequences();
|
||||
return sequenceCount - 1;
|
||||
}
|
||||
|
||||
bool deleteSequence(uint8_t index) {
|
||||
if (index >= sequenceCount) return false;
|
||||
|
||||
// 배열 왼쪽으로 시프트
|
||||
for (int i = index; i < sequenceCount - 1; i++) {
|
||||
sequences[i] = sequences[i + 1];
|
||||
}
|
||||
sequenceCount--;
|
||||
saveSequences();
|
||||
return true;
|
||||
}
|
||||
|
||||
void stopSequence() {
|
||||
seqRuntime.running = false;
|
||||
seqRuntime.currentStep = 0;
|
||||
seqRuntime.currentRepeat = 0;
|
||||
seqRuntime.activeSequenceIndex = -1;
|
||||
Serial.println("✓ 시퀀스 실행 중지");
|
||||
}
|
||||
|
||||
void startSequence(uint8_t index) {
|
||||
if (index >= sequenceCount) return;
|
||||
|
||||
seqRuntime.running = true;
|
||||
seqRuntime.currentStep = 0;
|
||||
seqRuntime.currentRepeat = 0;
|
||||
seqRuntime.lastStepTime = millis();
|
||||
seqRuntime.activeSequenceIndex = index;
|
||||
|
||||
Serial.printf("✓ 시퀀스 실행 시작: %s\n", sequences[index].name);
|
||||
}
|
||||
|
||||
// ========================================
|
||||
// 파일 커멘트 관리 함수
|
||||
// ========================================
|
||||
@@ -510,6 +639,19 @@ bool setMCP2515Mode(MCP2515Mode mode) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
|
||||
case MCP_MODE_TRANSMIT:
|
||||
// Transmit mode: Normal 모드와 동일하게 동작
|
||||
// (라이브러리 제약으로 인해 레지스터 직접 접근 불가)
|
||||
// 송신 위주로 사용하되, 수신도 가능한 모드
|
||||
result = mcp2515.setNormalMode();
|
||||
if (result == MCP2515::ERROR_OK) {
|
||||
currentMcpMode = MCP_MODE_TRANSMIT;
|
||||
Serial.println("✓ MCP2515 모드: TRANSMIT (Normal mode base)");
|
||||
Serial.println(" ※ 송신 위주 사용, 수신 데이터는 로깅하지 않음");
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
Serial.println("✗ MCP2515 모드 변경 실패");
|
||||
@@ -535,6 +677,15 @@ void canRxTask(void* parameter) {
|
||||
for (;;) {
|
||||
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
|
||||
|
||||
// Transmit 모드일 때는 수신 데이터를 처리하지 않음
|
||||
if (currentMcpMode == MCP_MODE_TRANSMIT) {
|
||||
// MCP2515 버퍼만 비우고 처리는 건너뜀
|
||||
while (mcp2515.readMessage(&frame) == MCP2515::ERROR_OK) {
|
||||
// 버퍼만 비움
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// 한 번에 여러 메시지를 읽어서 처리 속도 향상
|
||||
int readCount = 0;
|
||||
while (mcp2515.readMessage(&frame) == MCP2515::ERROR_OK && readCount < 20) {
|
||||
@@ -547,10 +698,12 @@ void canRxTask(void* parameter) {
|
||||
canMsg.dlc = frame.can_dlc;
|
||||
memcpy(canMsg.data, frame.data, 8);
|
||||
|
||||
// 큐에 추가 (블로킹 없이)
|
||||
xQueueSend(canQueue, &canMsg, 0);
|
||||
// 로깅 중일 때만 큐에 추가
|
||||
if (loggingEnabled) {
|
||||
xQueueSend(canQueue, &canMsg, 0);
|
||||
}
|
||||
|
||||
// 실시간 데이터 업데이트
|
||||
// 실시간 데이터 업데이트 (웹 표시용)
|
||||
bool found = false;
|
||||
for (int i = 0; i < RECENT_MSG_COUNT; i++) {
|
||||
if (recentData[i].count > 0 && recentData[i].msg.id == canMsg.id) {
|
||||
@@ -685,9 +838,16 @@ void stopLogging() {
|
||||
|
||||
bufferIndex = 0;
|
||||
|
||||
// 큐 비우기 (로깅 중이 아닐 때는 큐가 불필요)
|
||||
CANMessage dummyMsg;
|
||||
while (xQueueReceive(canQueue, &dummyMsg, 0) == pdTRUE) {
|
||||
// 큐의 모든 메시지 제거
|
||||
}
|
||||
|
||||
Serial.print("✓ 로깅 종료: ");
|
||||
Serial.println(currentFilename);
|
||||
Serial.printf(" 파일 크기: %u bytes\n", currentFileSize);
|
||||
Serial.println("✓ 큐 비움 완료");
|
||||
|
||||
// 현재 파일명 초기화
|
||||
currentFilename[0] = '\0';
|
||||
@@ -725,6 +885,7 @@ void txTask(void* parameter) {
|
||||
for (;;) {
|
||||
uint32_t now = millis();
|
||||
|
||||
// 주기적 송신 (기존)
|
||||
for (int i = 0; i < MAX_TX_MESSAGES; i++) {
|
||||
if (txMessages[i].active && txMessages[i].interval > 0) {
|
||||
if (now - txMessages[i].lastSent >= txMessages[i].interval) {
|
||||
@@ -747,6 +908,81 @@ void txTask(void* parameter) {
|
||||
}
|
||||
}
|
||||
|
||||
// ========================================
|
||||
// 시퀀스 실행 태스크
|
||||
// ========================================
|
||||
|
||||
void sequenceTask(void* parameter) {
|
||||
struct can_frame frame;
|
||||
|
||||
for (;;) {
|
||||
if (seqRuntime.running && seqRuntime.activeSequenceIndex >= 0) {
|
||||
CANSequence* seq = &sequences[seqRuntime.activeSequenceIndex];
|
||||
uint32_t now = millis();
|
||||
|
||||
if (seqRuntime.currentStep < seq->stepCount) {
|
||||
SequenceStep* step = &seq->steps[seqRuntime.currentStep];
|
||||
|
||||
// 첫 번째 스텝이거나 딜레이 시간이 지났으면 실행
|
||||
if (seqRuntime.currentStep == 0 || (now - seqRuntime.lastStepTime >= step->delayMs)) {
|
||||
// CAN 메시지 전송
|
||||
frame.can_id = step->canId;
|
||||
if (step->extended) {
|
||||
frame.can_id |= CAN_EFF_FLAG;
|
||||
}
|
||||
frame.can_dlc = step->dlc;
|
||||
memcpy(frame.data, step->data, 8);
|
||||
|
||||
MCP2515::ERROR result = mcp2515.sendMessage(&frame);
|
||||
if (result == MCP2515::ERROR_OK) {
|
||||
totalTxCount++;
|
||||
Serial.printf(" [Seq] Step %d/%d: ID=0x%X, DLC=%d, Delay=%dms - OK\n",
|
||||
seqRuntime.currentStep + 1, seq->stepCount,
|
||||
step->canId, step->dlc, step->delayMs);
|
||||
} else {
|
||||
Serial.printf(" [Seq] Step %d/%d: ID=0x%X - FAIL (Error %d)\n",
|
||||
seqRuntime.currentStep + 1, seq->stepCount, step->canId, result);
|
||||
}
|
||||
|
||||
seqRuntime.currentStep++;
|
||||
seqRuntime.lastStepTime = now;
|
||||
}
|
||||
} else {
|
||||
// 모든 스텝 완료
|
||||
seqRuntime.currentRepeat++;
|
||||
|
||||
// 반복 체크
|
||||
bool shouldContinue = false;
|
||||
|
||||
if (seq->repeatMode == 0) {
|
||||
// 한 번만
|
||||
shouldContinue = false;
|
||||
} else if (seq->repeatMode == 1) {
|
||||
// 특정 횟수
|
||||
if (seqRuntime.currentRepeat < seq->repeatCount) {
|
||||
shouldContinue = true;
|
||||
}
|
||||
} else if (seq->repeatMode == 2) {
|
||||
// 무한 반복
|
||||
shouldContinue = true;
|
||||
}
|
||||
|
||||
if (shouldContinue) {
|
||||
seqRuntime.currentStep = 0;
|
||||
seqRuntime.lastStepTime = now;
|
||||
Serial.printf(" [Seq] 반복 %d회 시작\n", seqRuntime.currentRepeat + 1);
|
||||
} else {
|
||||
Serial.printf("✓ 시퀀스 실행 완료: %s (총 %d회 반복)\n",
|
||||
seq->name, seqRuntime.currentRepeat);
|
||||
stopSequence();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
vTaskDelay(pdMS_TO_TICKS(10));
|
||||
}
|
||||
}
|
||||
|
||||
// ========================================
|
||||
// 파일 리스트 전송 함수
|
||||
// ========================================
|
||||
@@ -974,6 +1210,179 @@ void webSocketEvent(uint8_t num, WStype_t type, uint8_t* payload, size_t length)
|
||||
saveSettings();
|
||||
|
||||
webSocket.sendTXT(num, "{\"type\":\"settingsSaved\",\"success\":true}");
|
||||
|
||||
} else if (message.indexOf("\"cmd\":\"getSequences\"") >= 0) {
|
||||
// 시퀀스 리스트 전송
|
||||
String seqList = "{\"type\":\"sequences\",\"sequences\":[";
|
||||
for (int i = 0; i < sequenceCount; i++) {
|
||||
if (i > 0) seqList += ",";
|
||||
seqList += "{\"index\":" + String(i);
|
||||
seqList += ",\"name\":\"" + String(sequences[i].name) + "\"";
|
||||
seqList += ",\"steps\":" + String(sequences[i].stepCount);
|
||||
seqList += ",\"mode\":" + String(sequences[i].repeatMode);
|
||||
seqList += ",\"count\":" + String(sequences[i].repeatCount) + "}";
|
||||
}
|
||||
seqList += "]}";
|
||||
webSocket.sendTXT(num, seqList);
|
||||
|
||||
} else if (message.indexOf("\"cmd\":\"getSequence\"") >= 0) {
|
||||
// 특정 시퀀스 상세 정보 전송
|
||||
int indexStart = message.indexOf("\"index\":") + 8;
|
||||
int indexEnd = message.indexOf(",", indexStart);
|
||||
if (indexEnd < 0) indexEnd = message.indexOf("}", indexStart);
|
||||
int index = message.substring(indexStart, indexEnd).toInt();
|
||||
|
||||
if (index >= 0 && index < sequenceCount) {
|
||||
String seqData = "{\"type\":\"sequenceDetail\",\"sequence\":{";
|
||||
seqData += "\"name\":\"" + String(sequences[index].name) + "\",";
|
||||
seqData += "\"mode\":" + String(sequences[index].repeatMode) + ",";
|
||||
seqData += "\"count\":" + String(sequences[index].repeatCount) + ",";
|
||||
seqData += "\"steps\":[";
|
||||
|
||||
for (int i = 0; i < sequences[index].stepCount; i++) {
|
||||
if (i > 0) seqData += ",";
|
||||
SequenceStep* step = &sequences[index].steps[i];
|
||||
seqData += "{\"id\":" + String(step->canId);
|
||||
seqData += ",\"ext\":" + String(step->extended ? "true" : "false");
|
||||
seqData += ",\"dlc\":" + String(step->dlc);
|
||||
seqData += ",\"data\":\"";
|
||||
for (int j = 0; j < 8; j++) {
|
||||
if (step->data[j] < 0x10) seqData += "0";
|
||||
seqData += String(step->data[j], HEX);
|
||||
if (j < 7) seqData += " ";
|
||||
}
|
||||
seqData += "\",\"delay\":" + String(step->delayMs) + "}";
|
||||
}
|
||||
seqData += "]}}";
|
||||
webSocket.sendTXT(num, seqData);
|
||||
}
|
||||
|
||||
} else if (message.indexOf("\"cmd\":\"saveSequence\"") >= 0) {
|
||||
// 새 시퀀스 저장 (JSON 파싱)
|
||||
CANSequence newSeq;
|
||||
memset(&newSeq, 0, sizeof(CANSequence));
|
||||
|
||||
// 이름 추출
|
||||
int nameStart = message.indexOf("\"name\":\"") + 8;
|
||||
int nameEnd = message.indexOf("\"", nameStart);
|
||||
String name = message.substring(nameStart, nameEnd);
|
||||
strncpy(newSeq.name, name.c_str(), sizeof(newSeq.name) - 1);
|
||||
|
||||
// 모드 추출
|
||||
int modeStart = message.indexOf("\"mode\":") + 7;
|
||||
int modeEnd = message.indexOf(",", modeStart);
|
||||
newSeq.repeatMode = message.substring(modeStart, modeEnd).toInt();
|
||||
|
||||
// 반복 횟수 추출
|
||||
int countStart = message.indexOf("\"repeatCount\":") + 14;
|
||||
int countEnd = message.indexOf(",", countStart);
|
||||
if (countEnd < 0) countEnd = message.indexOf("}", countStart);
|
||||
newSeq.repeatCount = message.substring(countStart, countEnd).toInt();
|
||||
|
||||
// 스텝 배열 파싱
|
||||
int stepsStart = message.indexOf("\"steps\":[");
|
||||
if (stepsStart >= 0) {
|
||||
stepsStart += 9; // "steps":[ 길이
|
||||
int stepsEnd = message.indexOf("]}", stepsStart);
|
||||
String stepsJson = message.substring(stepsStart, stepsEnd);
|
||||
|
||||
// 각 스텝 파싱
|
||||
newSeq.stepCount = 0;
|
||||
int pos = 0;
|
||||
|
||||
while (pos < stepsJson.length() && newSeq.stepCount < 20) {
|
||||
int stepStart = stepsJson.indexOf("{", pos);
|
||||
if (stepStart < 0) break;
|
||||
|
||||
int stepEnd = stepsJson.indexOf("}", stepStart);
|
||||
if (stepEnd < 0) break;
|
||||
|
||||
String stepJson = stepsJson.substring(stepStart, stepEnd + 1);
|
||||
|
||||
// canId 추출
|
||||
int idStart = stepJson.indexOf("\"canId\":") + 8;
|
||||
int idEnd = stepJson.indexOf(",", idStart);
|
||||
if (idEnd < 0) idEnd = stepJson.indexOf("}", idStart);
|
||||
newSeq.steps[newSeq.stepCount].canId = stepJson.substring(idStart, idEnd).toInt();
|
||||
|
||||
// extended 추출
|
||||
int extStart = stepJson.indexOf("\"extended\":") + 11;
|
||||
String extStr = stepJson.substring(extStart, extStart + 5);
|
||||
newSeq.steps[newSeq.stepCount].extended = (extStr.indexOf("true") >= 0);
|
||||
|
||||
// dlc 추출
|
||||
int dlcStart = stepJson.indexOf("\"dlc\":") + 6;
|
||||
int dlcEnd = stepJson.indexOf(",", dlcStart);
|
||||
newSeq.steps[newSeq.stepCount].dlc = stepJson.substring(dlcStart, dlcEnd).toInt();
|
||||
|
||||
// data 배열 추출
|
||||
int dataStart = stepJson.indexOf("\"data\":[") + 8;
|
||||
int dataEnd = stepJson.indexOf("]", dataStart);
|
||||
String dataStr = stepJson.substring(dataStart, dataEnd);
|
||||
|
||||
// data 바이트 파싱
|
||||
int bytePos = 0;
|
||||
int byteIdx = 0;
|
||||
while (bytePos < dataStr.length() && byteIdx < 8) {
|
||||
int commaPos = dataStr.indexOf(",", bytePos);
|
||||
if (commaPos < 0) commaPos = dataStr.length();
|
||||
|
||||
String byteStr = dataStr.substring(bytePos, commaPos);
|
||||
byteStr.trim();
|
||||
newSeq.steps[newSeq.stepCount].data[byteIdx] = byteStr.toInt();
|
||||
|
||||
byteIdx++;
|
||||
bytePos = commaPos + 1;
|
||||
}
|
||||
|
||||
// delay 추출
|
||||
int delayStart = stepJson.indexOf("\"delayMs\":") + 10;
|
||||
int delayEnd = stepJson.indexOf(",", delayStart);
|
||||
if (delayEnd < 0) delayEnd = stepJson.indexOf("}", delayStart);
|
||||
newSeq.steps[newSeq.stepCount].delayMs = stepJson.substring(delayStart, delayEnd).toInt();
|
||||
|
||||
newSeq.stepCount++;
|
||||
pos = stepEnd + 1;
|
||||
}
|
||||
}
|
||||
|
||||
Serial.printf("📝 시퀀스 저장: %s (%d 스텝)\n", newSeq.name, newSeq.stepCount);
|
||||
for (int i = 0; i < newSeq.stepCount; i++) {
|
||||
Serial.printf(" Step %d: ID=0x%X, DLC=%d, Delay=%dms\n",
|
||||
i, newSeq.steps[i].canId, newSeq.steps[i].dlc, newSeq.steps[i].delayMs);
|
||||
}
|
||||
|
||||
int result = addSequence(newSeq);
|
||||
if (result >= 0) {
|
||||
webSocket.sendTXT(num, "{\"type\":\"sequenceSaved\",\"success\":true,\"index\":" + String(result) + "}");
|
||||
} else {
|
||||
webSocket.sendTXT(num, "{\"type\":\"sequenceSaved\",\"success\":false}");
|
||||
}
|
||||
|
||||
} else if (message.indexOf("\"cmd\":\"deleteSequence\"") >= 0) {
|
||||
// 시퀀스 삭제
|
||||
int indexStart = message.indexOf("\"index\":") + 8;
|
||||
int indexEnd = message.indexOf(",", indexStart);
|
||||
if (indexEnd < 0) indexEnd = message.indexOf("}", indexStart);
|
||||
int index = message.substring(indexStart, indexEnd).toInt();
|
||||
|
||||
bool success = deleteSequence(index);
|
||||
webSocket.sendTXT(num, "{\"type\":\"sequenceDeleted\",\"success\":" + String(success ? "true" : "false") + "}");
|
||||
|
||||
} else if (message.indexOf("\"cmd\":\"startSequence\"") >= 0) {
|
||||
// 시퀀스 실행
|
||||
int indexStart = message.indexOf("\"index\":") + 8;
|
||||
int indexEnd = message.indexOf(",", indexStart);
|
||||
if (indexEnd < 0) indexEnd = message.indexOf("}", indexStart);
|
||||
int index = message.substring(indexStart, indexEnd).toInt();
|
||||
|
||||
startSequence(index);
|
||||
webSocket.sendTXT(num, "{\"type\":\"sequenceStarted\",\"success\":true}");
|
||||
|
||||
} else if (message.indexOf("\"cmd\":\"stopSequence\"") >= 0) {
|
||||
// 시퀀스 중지
|
||||
stopSequence();
|
||||
webSocket.sendTXT(num, "{\"type\":\"sequenceStopped\",\"success\":true}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1247,6 +1656,7 @@ void setup() {
|
||||
xTaskCreatePinnedToCore(sdMonitorTask, "SD_MONITOR", 4096, NULL, 1, NULL, 0);
|
||||
xTaskCreatePinnedToCore(webUpdateTask, "WEB_UPDATE", 8192, NULL, 2, &webTaskHandle, 0);
|
||||
xTaskCreatePinnedToCore(txTask, "TX_TASK", 4096, NULL, 2, NULL, 1);
|
||||
xTaskCreatePinnedToCore(sequenceTask, "SEQ_TASK", 4096, NULL, 2, NULL, 1); // 시퀀스 Task 추가
|
||||
|
||||
// RTC 동기화 Task
|
||||
if (timeSyncStatus.rtcAvailable) {
|
||||
@@ -1254,6 +1664,9 @@ void setup() {
|
||||
Serial.println("✓ RTC 자동 동기화 Task 시작");
|
||||
}
|
||||
|
||||
// 시퀀스 로드
|
||||
loadSequences();
|
||||
|
||||
Serial.println("✓ 모든 태스크 시작 완료");
|
||||
Serial.println("\n========================================");
|
||||
Serial.println(" 웹 인터페이스 접속 방법");
|
||||
|
||||
3
index.h
3
index.h
@@ -694,6 +694,7 @@ const char index_html[] PROGMEM = R"rawliteral(
|
||||
<option value="0" selected>Normal</option>
|
||||
<option value="1">Listen-Only</option>
|
||||
<option value="2">Loopback</option>
|
||||
<option value="3">Transmit (TX Focus)</option>
|
||||
</select>
|
||||
<button onclick="setMcpMode()">Apply</button>
|
||||
<span id="mode-status" style="color: #11998e; font-size: 0.85em; font-weight: 600;"></span>
|
||||
@@ -751,7 +752,7 @@ const char index_html[] PROGMEM = R"rawliteral(
|
||||
let messageOrder = [];
|
||||
let lastMessageData = {};
|
||||
const speedNames = {0: '125K', 1: '250K', 2: '500K', 3: '1M'};
|
||||
const modeNames = {0: 'NORMAL', 1: 'LISTEN-ONLY', 2: 'LOOPBACK'};
|
||||
const modeNames = {0: 'NORMAL', 1: 'LISTEN-ONLY', 2: 'LOOPBACK', 3: 'TRANSMIT'};
|
||||
let currentLoggingFile = '';
|
||||
let commentingFile = '';
|
||||
let hasInitialSync = false; // 초기 동기화 완료 여부
|
||||
|
||||
1477
transmit.h
1477
transmit.h
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user