자동로깅 추가, usb port추가
This commit is contained in:
@@ -229,6 +229,8 @@ void setup() {
|
||||
Serial.printf(" RTC: %s | Time: %s\n",
|
||||
rtcStatus.available ? "OK" : "NO",
|
||||
rtcStatus.timeSynced ? "Valid" : "Pending");
|
||||
Serial.printf(" AutoStart: %s\n",
|
||||
sdAutoStart ? "ON (logging started)" : "OFF");
|
||||
Serial.println("============================================\n");
|
||||
}
|
||||
|
||||
@@ -244,12 +246,13 @@ void loop() {
|
||||
lastReport = millis();
|
||||
char timeBuf[24];
|
||||
getTimestamp(timeBuf, sizeof(timeBuf));
|
||||
Serial.printf("[SYS] %s | Heap:%d | SD:%s | WS:%d | RTC:%s | STA:%s\n",
|
||||
Serial.printf("[SYS] %s | Heap:%d | SD:%s | WS:%d | RTC:%s | STA:%s | Auto:%s\n",
|
||||
timeBuf,
|
||||
ESP.getFreeHeap(),
|
||||
sdCardPresent() ? "OK" : "FAIL",
|
||||
webSocket.connectedClients(),
|
||||
rtcStatus.available ? "OK" : "NO",
|
||||
staConnected ? "ON" : "OFF");
|
||||
staConnected ? "ON" : "OFF",
|
||||
sdAutoStart ? "ON" : "OFF");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,11 +4,15 @@
|
||||
|
||||
static SPIClass vspi(VSPI);
|
||||
volatile bool sdLoggingActive = false;
|
||||
volatile bool sdAutoStart = false;
|
||||
char currentLogFileName[64] = "";
|
||||
static File logFile;
|
||||
static bool sdReady = false;
|
||||
static SemaphoreHandle_t sdMutex = NULL;
|
||||
|
||||
// --- Persistent autostart flag ---
|
||||
#define AUTOSTART_FLAG "/logs/.autostart"
|
||||
|
||||
// --- File rotation tracking ---
|
||||
static int logStartDay = -1; // Day-of-year when current log started
|
||||
static size_t logFileSize = 0; // Running byte count of current file
|
||||
@@ -70,8 +74,19 @@ void sdTaskInit() {
|
||||
SD.mkdir(LOG_DIR);
|
||||
Serial.println("[SD] Created /logs directory");
|
||||
}
|
||||
sdLoggingActive = false;
|
||||
Serial.println("[SD] Ready (logging OFF - start via web UI)");
|
||||
|
||||
// Check persistent autostart flag
|
||||
sdAutoStart = SD.exists(AUTOSTART_FLAG);
|
||||
|
||||
if (sdAutoStart) {
|
||||
// Auto-start logging immediately (field deployment mode)
|
||||
Serial.println("[SD] *** AUTOSTART MODE - starting logging immediately ***");
|
||||
// sdCreateNewLogFile takes mutex, so don't hold it here
|
||||
sdLoggingActive = false; // Will be set true after file creation
|
||||
} else {
|
||||
sdLoggingActive = false;
|
||||
Serial.println("[SD] Ready (logging OFF - start via web UI)");
|
||||
}
|
||||
Serial.printf("[SD] Auto-rotate: midnight=%s, maxSize=%luMB\n",
|
||||
LOG_ROTATE_MIDNIGHT ? "ON" : "OFF",
|
||||
(unsigned long)(LOG_MAX_FILE_SIZE / (1024 * 1024)));
|
||||
@@ -80,6 +95,13 @@ void sdTaskInit() {
|
||||
|
||||
xTaskCreatePinnedToCore(sdLoggingTask, "SDLog", TASK_STACK_SD_LOG,
|
||||
NULL, TASK_PRIORITY_SD_LOG, NULL, 0);
|
||||
|
||||
// Deferred autostart (after task is running)
|
||||
if (sdReady && sdAutoStart) {
|
||||
sdCreateNewLogFile();
|
||||
sdLoggingActive = true;
|
||||
Serial.printf("[SD] Autostart logging: %s\n", currentLogFileName);
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
@@ -403,3 +425,36 @@ bool sdDeleteFile(const char *filename) {
|
||||
}
|
||||
|
||||
bool sdCardPresent() { return sdReady; }
|
||||
|
||||
// ============================================================
|
||||
// Autostart - persistent flag on SD card
|
||||
// ============================================================
|
||||
bool sdGetAutoStart() {
|
||||
if (!sdReady) return false;
|
||||
return SD.exists(AUTOSTART_FLAG);
|
||||
}
|
||||
|
||||
void sdSetAutoStart(bool enable) {
|
||||
if (!sdReady) return;
|
||||
xSemaphoreTake(sdMutex, portMAX_DELAY);
|
||||
|
||||
if (enable) {
|
||||
// Create flag file
|
||||
File f = SD.open(AUTOSTART_FLAG, FILE_WRITE);
|
||||
if (f) {
|
||||
f.println("autostart=1");
|
||||
f.close();
|
||||
}
|
||||
sdAutoStart = true;
|
||||
Serial.println("[SD] Autostart ENABLED (will log on next boot)");
|
||||
} else {
|
||||
// Remove flag file
|
||||
if (SD.exists(AUTOSTART_FLAG)) {
|
||||
SD.remove(AUTOSTART_FLAG);
|
||||
}
|
||||
sdAutoStart = false;
|
||||
Serial.println("[SD] Autostart DISABLED");
|
||||
}
|
||||
|
||||
xSemaphoreGive(sdMutex);
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "config.h"
|
||||
|
||||
extern volatile bool sdLoggingActive;
|
||||
extern volatile bool sdAutoStart;
|
||||
extern char currentLogFileName[64];
|
||||
|
||||
void sdTaskInit();
|
||||
@@ -21,5 +22,7 @@ bool sdDeleteFile(const char *filename);
|
||||
bool sdCardPresent();
|
||||
String sdGetComment(const char *filename);
|
||||
bool sdSetComment(const char *filename, const char *comment);
|
||||
bool sdGetAutoStart();
|
||||
void sdSetAutoStart(bool enable);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -10,6 +10,10 @@ volatile uint32_t serialBaud = DEFAULT_BAUD_RATE;
|
||||
volatile uint8_t serialDataBits = 8;
|
||||
volatile char serialParity = 'N';
|
||||
volatile uint8_t serialStopBits = 1;
|
||||
volatile uint8_t serialPort = 0; // 0=UART2, 1=UART0(USB)
|
||||
|
||||
// Pointer to the active monitoring serial port
|
||||
HardwareSerial* monSerial = &Serial2;
|
||||
|
||||
static SemaphoreHandle_t serialMutex = NULL;
|
||||
|
||||
@@ -25,7 +29,6 @@ void getTimestamp(char *buf, size_t len) {
|
||||
}
|
||||
|
||||
static uint32_t getSerialConfig(uint8_t dataBits, char parity, uint8_t stopBits) {
|
||||
// Use standard Arduino serial config constants
|
||||
if (dataBits == 8 && parity == 'N' && stopBits == 1) return SERIAL_8N1;
|
||||
if (dataBits == 8 && parity == 'E' && stopBits == 1) return SERIAL_8E1;
|
||||
if (dataBits == 8 && parity == 'O' && stopBits == 1) return SERIAL_8O1;
|
||||
@@ -41,30 +44,80 @@ static uint32_t getSerialConfig(uint8_t dataBits, char parity, uint8_t stopBits)
|
||||
if (dataBits == 5 && parity == 'N' && stopBits == 1) return SERIAL_5N1;
|
||||
if (dataBits == 5 && parity == 'E' && stopBits == 1) return SERIAL_5E1;
|
||||
if (dataBits == 5 && parity == 'O' && stopBits == 1) return SERIAL_5O1;
|
||||
return SERIAL_8N1; // default
|
||||
return SERIAL_8N1;
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// Switch serial port: 0=UART2(GPIO16/17), 1=UART0(USB)
|
||||
// ============================================================
|
||||
void switchSerialPort(uint8_t port) {
|
||||
if (serialMutex) xSemaphoreTake(serialMutex, portMAX_DELAY);
|
||||
|
||||
if (port == 1) {
|
||||
// Switch to UART0 (USB) for testing
|
||||
// Note: UART0 is already running (Serial.begin in setup)
|
||||
// Just point monSerial to it. Debug output still goes to Serial TX,
|
||||
// but RX task only reads what PC sends (RX buffer), so no conflict.
|
||||
Serial2.flush();
|
||||
Serial2.end();
|
||||
monSerial = &Serial;
|
||||
serialPort = 1;
|
||||
Serial.println("[Serial] Switched to UART0 (USB) - test mode");
|
||||
Serial.println("[Serial] Send data from PC terminal to test");
|
||||
} else {
|
||||
// Switch to UART2 (GPIO16/17) for field use
|
||||
uint32_t config = getSerialConfig(serialDataBits, serialParity, serialStopBits);
|
||||
Serial2.begin(serialBaud, config, SERIAL2_RX_PIN, SERIAL2_TX_PIN);
|
||||
Serial2.setRxBufferSize(DEFAULT_RX_BUFFER);
|
||||
monSerial = &Serial2;
|
||||
serialPort = 0;
|
||||
Serial.printf("[Serial] Switched to UART2 (TX=%d, RX=%d)\n",
|
||||
SERIAL2_TX_PIN, SERIAL2_RX_PIN);
|
||||
}
|
||||
|
||||
// Drain any old data from new port's RX buffer
|
||||
while (monSerial->available()) monSerial->read();
|
||||
|
||||
if (serialMutex) xSemaphoreGive(serialMutex);
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// Reconfigure baud/parity/etc (applies to current port)
|
||||
// ============================================================
|
||||
void reconfigureSerial(uint32_t baud, uint8_t dataBits, char parity, uint8_t stopBits) {
|
||||
if (serialMutex) xSemaphoreTake(serialMutex, portMAX_DELAY);
|
||||
|
||||
Serial2.flush();
|
||||
Serial2.end();
|
||||
delay(50);
|
||||
monSerial->flush();
|
||||
|
||||
uint32_t config = getSerialConfig(dataBits, parity, stopBits);
|
||||
Serial2.begin(baud, config, SERIAL2_RX_PIN, SERIAL2_TX_PIN);
|
||||
Serial2.setRxBufferSize(DEFAULT_RX_BUFFER);
|
||||
if (serialPort == 0) {
|
||||
// UART2 - full reconfigure
|
||||
Serial2.end();
|
||||
delay(50);
|
||||
uint32_t config = getSerialConfig(dataBits, parity, stopBits);
|
||||
Serial2.begin(baud, config, SERIAL2_RX_PIN, SERIAL2_TX_PIN);
|
||||
Serial2.setRxBufferSize(DEFAULT_RX_BUFFER);
|
||||
} else {
|
||||
// UART0 - reconfigure USB serial baud
|
||||
Serial.end();
|
||||
delay(50);
|
||||
Serial.begin(baud);
|
||||
}
|
||||
|
||||
serialBaud = baud;
|
||||
serialDataBits = dataBits;
|
||||
serialParity = parity;
|
||||
serialStopBits = stopBits;
|
||||
|
||||
Serial.printf("[Serial] Reconfigured: %lu %d%c%d\n", baud, dataBits, parity, stopBits);
|
||||
Serial.printf("[Serial] Reconfigured: %lu %d%c%d (port=%s)\n",
|
||||
baud, dataBits, parity, stopBits,
|
||||
serialPort == 0 ? "UART2" : "USB");
|
||||
|
||||
if (serialMutex) xSemaphoreGive(serialMutex);
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// Init
|
||||
// ============================================================
|
||||
void serialTaskInit() {
|
||||
queueSD = xQueueCreate(QUEUE_SD_SIZE, sizeof(LogEntry*));
|
||||
queueWeb = xQueueCreate(QUEUE_WEB_SIZE, sizeof(LogEntry*));
|
||||
@@ -72,13 +125,17 @@ void serialTaskInit() {
|
||||
|
||||
serialMutex = xSemaphoreCreateMutex();
|
||||
|
||||
// Default: UART2
|
||||
uint32_t config = getSerialConfig(serialDataBits, serialParity, serialStopBits);
|
||||
Serial2.begin(serialBaud, config, SERIAL2_RX_PIN, SERIAL2_TX_PIN);
|
||||
Serial2.setRxBufferSize(DEFAULT_RX_BUFFER);
|
||||
monSerial = &Serial2;
|
||||
serialPort = 0;
|
||||
|
||||
Serial.printf("[Serial] Init: %lu %d%c%d on TX=%d RX=%d\n",
|
||||
Serial.printf("[Serial] Init: %lu %d%c%d on UART2 (TX=%d, RX=%d)\n",
|
||||
serialBaud, serialDataBits, (char)serialParity, serialStopBits,
|
||||
SERIAL2_TX_PIN, SERIAL2_RX_PIN);
|
||||
Serial.println("[Serial] Port switchable via Settings (UART2/USB)");
|
||||
|
||||
xTaskCreatePinnedToCore(serialRxTask, "SerialRX", TASK_STACK_SERIAL,
|
||||
NULL, TASK_PRIORITY_SERIAL, NULL, 1);
|
||||
@@ -87,7 +144,9 @@ void serialTaskInit() {
|
||||
NULL, TASK_PRIORITY_SERIAL - 1, NULL, 1);
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// Flush assembled line to SD and Web queues
|
||||
// ============================================================
|
||||
static void flushLineToQueues(char *lineBuf, int &linePos) {
|
||||
if (linePos <= 0) return;
|
||||
lineBuf[linePos] = '\0';
|
||||
@@ -114,6 +173,9 @@ static void flushLineToQueues(char *lineBuf, int &linePos) {
|
||||
linePos = 0;
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// RX Task - reads from monSerial (UART2 or UART0)
|
||||
// ============================================================
|
||||
void serialRxTask(void *param) {
|
||||
static char lineBuf[LOG_LINE_MAX_LEN];
|
||||
int linePos = 0;
|
||||
@@ -122,7 +184,8 @@ void serialRxTask(void *param) {
|
||||
Serial.println("[Task] SerialRX started on core " + String(xPortGetCoreID()));
|
||||
|
||||
while (true) {
|
||||
int available = Serial2.available();
|
||||
HardwareSerial* port = monSerial; // Local copy for thread safety
|
||||
int available = port->available();
|
||||
|
||||
if (available > 0) {
|
||||
int space = (int)(LOG_LINE_MAX_LEN - linePos - 1);
|
||||
@@ -130,13 +193,12 @@ void serialRxTask(void *param) {
|
||||
|
||||
int toRead = (available < space) ? available : space;
|
||||
for (int i = 0; i < toRead; i++) {
|
||||
char c = Serial2.read();
|
||||
char c = port->read();
|
||||
if (c == '\n' || c == '\r') {
|
||||
if (linePos > 0) flushLineToQueues(lineBuf, linePos);
|
||||
continue;
|
||||
}
|
||||
// Only accept printable ASCII (0x20~0x7E) and TAB (0x09)
|
||||
// Skip null bytes and noise (0x00, 0xFF, etc.)
|
||||
if ((c >= 0x20 && c <= 0x7E) || c == '\t') {
|
||||
lineBuf[linePos++] = c;
|
||||
}
|
||||
@@ -151,15 +213,20 @@ void serialRxTask(void *param) {
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// TX Task - writes to monSerial (UART2 or UART0)
|
||||
// ============================================================
|
||||
void serialTxTask(void *param) {
|
||||
Serial.println("[Task] SerialTX started on core " + String(xPortGetCoreID()));
|
||||
|
||||
while (true) {
|
||||
LogEntry *entry;
|
||||
if (xQueueReceive(queueTX, &entry, pdMS_TO_TICKS(50)) == pdTRUE) {
|
||||
HardwareSerial* port = monSerial;
|
||||
|
||||
if (serialMutex) xSemaphoreTake(serialMutex, portMAX_DELAY);
|
||||
Serial2.write((uint8_t*)entry->data, entry->dataLen);
|
||||
Serial2.flush();
|
||||
port->write((uint8_t*)entry->data, entry->dataLen);
|
||||
port->flush();
|
||||
if (serialMutex) xSemaphoreGive(serialMutex);
|
||||
|
||||
LogEntry *sdEntry = (LogEntry*)pvPortMalloc(sizeof(LogEntry));
|
||||
|
||||
@@ -15,11 +15,15 @@ extern volatile uint32_t serialBaud;
|
||||
extern volatile uint8_t serialDataBits;
|
||||
extern volatile char serialParity;
|
||||
extern volatile uint8_t serialStopBits;
|
||||
extern volatile uint8_t serialPort; // 0=UART2(GPIO16/17), 1=UART0(USB)
|
||||
|
||||
extern HardwareSerial* monSerial; // Active monitoring serial port
|
||||
|
||||
void serialTaskInit();
|
||||
void serialRxTask(void *param);
|
||||
void serialTxTask(void *param);
|
||||
void reconfigureSerial(uint32_t baud, uint8_t dataBits, char parity, uint8_t stopBits);
|
||||
void switchSerialPort(uint8_t port);
|
||||
void getTimestamp(char *buf, size_t len);
|
||||
|
||||
#endif
|
||||
|
||||
88
web_html.h
88
web_html.h
@@ -186,6 +186,15 @@ tr:active{background:rgba(233,69,96,0.1);}
|
||||
<div class="spage">
|
||||
<div class="sgrp">
|
||||
<h3>Serial Port</h3>
|
||||
<div class="fr"><label>Monitor Port</label>
|
||||
<select id="sPort" onchange="switchPort()">
|
||||
<option value="0" selected>UART2 (GPIO16/17) - 현장용</option>
|
||||
<option value="1">UART0 (USB) - 테스트용</option>
|
||||
</select>
|
||||
</div>
|
||||
<div id="portInfo" style="font-size:10px;color:#888;margin:-4px 0 4px 0;">
|
||||
UART2: TX=GPIO17, RX=GPIO16 (외부 장비 연결)
|
||||
</div>
|
||||
<div class="fr"><label>Baud Rate</label>
|
||||
<select id="baud"><option value="1200">1200</option><option value="2400">2400</option><option value="4800">4800</option><option value="9600">9600</option><option value="19200">19200</option><option value="38400">38400</option><option value="57600">57600</option><option value="115200" selected>115200</option><option value="230400">230400</option><option value="460800">460800</option><option value="921600">921600</option><option value="1000000">1M</option><option value="2000000">2M</option></select>
|
||||
</div>
|
||||
@@ -240,6 +249,20 @@ tr:active{background:rgba(233,69,96,0.1);}
|
||||
<button class="abtn" onclick="sendC('new_log')" style="background:var(--warn);color:#000;">New File</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sgrp" id="autoGrp" style="border:1px solid var(--border);">
|
||||
<h3>상시로깅 (Auto-Start)</h3>
|
||||
<p style="font-size:11px;color:#999;margin:0 0 8px 0;">전원 OFF→ON 시 자동으로 로깅 시작. 현장 장기 설치용.</p>
|
||||
<div class="fr" style="flex-direction:row;align-items:center;gap:10px;">
|
||||
<label>상시로깅</label>
|
||||
<span id="autoSt" style="font-size:13px;color:#888;">OFF</span>
|
||||
</div>
|
||||
<button class="abtn" id="autoBtn" onclick="toggleAutoStart()" style="margin-top:8px;background:var(--border);color:var(--text);width:100%;">
|
||||
상시로깅 활성화
|
||||
</button>
|
||||
<p id="autoDesc" style="font-size:10px;color:#666;margin:6px 0 0 0;display:none;">
|
||||
⚡ 활성화 상태: 전원이 다시 들어오면 RTC 시간 로드 후 자동 로깅 시작
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -266,7 +289,7 @@ tr:active{background:rgba(233,69,96,0.1);}
|
||||
<div class="sbar">
|
||||
<span class="ld" id="lDot"></span>
|
||||
<span id="lInfo">Log OFF</span>
|
||||
<span id="serInfo">115200 8N1</span>
|
||||
<span id="serInfo">UART2 115200 8N1</span>
|
||||
<span id="rxC">RX:0</span>
|
||||
<span id="txC">TX:0</span>
|
||||
<span style="margin-left:auto;" id="tInfo">--</span>
|
||||
@@ -304,6 +327,7 @@ function wsConn(){
|
||||
sendC('sysinfo');
|
||||
sendC('get_serial_config');
|
||||
sendC('get_wifi');
|
||||
sendC('get_autostart');
|
||||
};
|
||||
ws.onmessage=function(e){
|
||||
try{
|
||||
@@ -313,6 +337,7 @@ function wsConn(){
|
||||
else if(m.type==='serial_config') updSer(m);
|
||||
else if(m.type==='log_status') updLog(m);
|
||||
else if(m.type==='wifi_status') updWifi(m);
|
||||
else if(m.type==='autostart_status') updAuto(m);
|
||||
}catch(x){}
|
||||
};
|
||||
ws.onclose=function(){
|
||||
@@ -432,7 +457,37 @@ function updSer(m){
|
||||
if(m.dataBits) document.getElementById('dBits').value=m.dataBits;
|
||||
if(m.parity) document.getElementById('par').value=m.parity;
|
||||
if(m.stopBits) document.getElementById('sBits').value=m.stopBits;
|
||||
document.getElementById('serInfo').textContent=m.baud+' '+m.dataBits+m.parity+m.stopBits;
|
||||
// Port selector
|
||||
if(m.port!==undefined){
|
||||
document.getElementById('sPort').value=m.port;
|
||||
updPortInfo(m.port);
|
||||
}
|
||||
// Status bar: show port + config
|
||||
let portLabel=((m.port||0)==1)?'USB':'UART2';
|
||||
document.getElementById('serInfo').textContent=portLabel+' '+m.baud+' '+m.dataBits+m.parity+m.stopBits;
|
||||
}
|
||||
|
||||
function updPortInfo(p){
|
||||
let info=document.getElementById('portInfo');
|
||||
if(p==1){
|
||||
info.textContent='USB: PC 시리얼 터미널에서 데이터 송수신 테스트';
|
||||
info.style.color='var(--ok)';
|
||||
}else{
|
||||
info.textContent='UART2: TX=GPIO17, RX=GPIO16 (외부 장비 연결)';
|
||||
info.style.color='#888';
|
||||
}
|
||||
}
|
||||
|
||||
function switchPort(){
|
||||
let port=parseInt(document.getElementById('sPort').value);
|
||||
if(port===1){
|
||||
if(!confirm('USB 테스트 모드로 전환하시겠습니까?\n\nPC의 시리얼 터미널(PuTTY 등)에서\nESP32 COM 포트로 데이터를 보내면\n웹 터미널에 표시됩니다.')) {
|
||||
document.getElementById('sPort').value='0';
|
||||
return;
|
||||
}
|
||||
}
|
||||
updPortInfo(port);
|
||||
if(ws&&ws.readyState===1) ws.send(JSON.stringify({cmd:'switch_port',port:port}));
|
||||
}
|
||||
|
||||
// ===== System Info =====
|
||||
@@ -533,6 +588,35 @@ function updLog(m){
|
||||
document.getElementById('logFile').value=m.file||'';
|
||||
}
|
||||
|
||||
// ===== Autostart (Persistent Logging) =====
|
||||
function updAuto(m){
|
||||
let st=document.getElementById('autoSt');
|
||||
let btn=document.getElementById('autoBtn');
|
||||
let grp=document.getElementById('autoGrp');
|
||||
let desc=document.getElementById('autoDesc');
|
||||
if(m.enabled){
|
||||
st.textContent='ON';st.style.color='var(--ok)';
|
||||
btn.textContent='상시로깅 비활성화';
|
||||
btn.style.background='var(--btn)';btn.style.color='#fff';
|
||||
grp.style.borderColor='var(--ok)';
|
||||
desc.style.display='block';
|
||||
}else{
|
||||
st.textContent='OFF';st.style.color='#888';
|
||||
btn.textContent='상시로깅 활성화';
|
||||
btn.style.background='var(--border)';btn.style.color='var(--text)';
|
||||
grp.style.borderColor='var(--border)';
|
||||
desc.style.display='none';
|
||||
}
|
||||
}
|
||||
|
||||
function toggleAutoStart(){
|
||||
let cur=document.getElementById('autoSt').textContent==='ON';
|
||||
if(!cur){
|
||||
if(!confirm('상시로깅을 활성화하시겠습니까?\n\n전원이 꺼졌다 켜져도 자동으로 로깅이 시작됩니다.\n(현장 장기 설치용)')) return;
|
||||
}
|
||||
sendC('toggle_autostart');
|
||||
}
|
||||
|
||||
// ===== File Manager =====
|
||||
function loadFiles(){
|
||||
fetch('/api/files').then(r=>r.json()).then(d=>{
|
||||
|
||||
54
web_task.cpp
54
web_task.cpp
@@ -172,11 +172,29 @@ void handleWsMessage(uint8_t num, const char *message) {
|
||||
resp["dataBits"] = serialDataBits;
|
||||
resp["parity"] = String((char)serialParity);
|
||||
resp["stopBits"] = serialStopBits;
|
||||
resp["port"] = serialPort; // 0=UART2, 1=USB
|
||||
String json;
|
||||
serializeJson(resp, json);
|
||||
webSocket.sendTXT(num, json);
|
||||
}
|
||||
|
||||
// --- Switch Serial Port ---
|
||||
else if (strcmp(cmd, "switch_port") == 0) {
|
||||
uint8_t port = doc["port"] | 0;
|
||||
switchSerialPort(port);
|
||||
|
||||
StaticJsonDocument<256> resp;
|
||||
resp["type"] = "serial_config";
|
||||
resp["baud"] = (uint32_t)serialBaud;
|
||||
resp["dataBits"] = serialDataBits;
|
||||
resp["parity"] = String((char)serialParity);
|
||||
resp["stopBits"] = serialStopBits;
|
||||
resp["port"] = serialPort;
|
||||
String json;
|
||||
serializeJson(resp, json);
|
||||
webSocket.broadcastTXT(json);
|
||||
}
|
||||
|
||||
// --- System Info (includes RTC + WiFi status) ---
|
||||
else if (strcmp(cmd, "sysinfo") == 0) {
|
||||
StaticJsonDocument<768> resp;
|
||||
@@ -291,6 +309,42 @@ void handleWsMessage(uint8_t num, const char *message) {
|
||||
serializeJson(resp, json);
|
||||
webSocket.sendTXT(num, json);
|
||||
}
|
||||
|
||||
// --- Toggle Autostart (persistent logging) ---
|
||||
else if (strcmp(cmd, "toggle_autostart") == 0) {
|
||||
bool newState = !sdAutoStart;
|
||||
sdSetAutoStart(newState);
|
||||
|
||||
// If just enabled and not already logging, start now
|
||||
if (newState && !sdLoggingActive) {
|
||||
sdStartLogging();
|
||||
// Also send log_status update
|
||||
StaticJsonDocument<256> logResp;
|
||||
logResp["type"] = "log_status";
|
||||
logResp["active"] = sdLoggingActive;
|
||||
logResp["file"] = currentLogFileName;
|
||||
String logJson;
|
||||
serializeJson(logResp, logJson);
|
||||
webSocket.broadcastTXT(logJson);
|
||||
}
|
||||
|
||||
StaticJsonDocument<128> resp;
|
||||
resp["type"] = "autostart_status";
|
||||
resp["enabled"] = (bool)sdAutoStart;
|
||||
String json;
|
||||
serializeJson(resp, json);
|
||||
webSocket.broadcastTXT(json);
|
||||
}
|
||||
|
||||
// --- Get Autostart Status ---
|
||||
else if (strcmp(cmd, "get_autostart") == 0) {
|
||||
StaticJsonDocument<128> resp;
|
||||
resp["type"] = "autostart_status";
|
||||
resp["enabled"] = (bool)sdAutoStart;
|
||||
String json;
|
||||
serializeJson(resp, json);
|
||||
webSocket.sendTXT(num, json);
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
|
||||
Reference in New Issue
Block a user