시리얼, seting수정
This commit is contained in:
@@ -255,6 +255,8 @@ const char serial_terminal_html[] PROGMEM = R"rawliteral(
|
||||
.modbus-error { color:var(--red); }
|
||||
/* Send / Stats */
|
||||
.send-panel { margin-top:10px; }
|
||||
.tog-on{border-color:var(--accent)!important;color:var(--accent)!important;background:rgba(67,206,162,.10)!important;}
|
||||
.tog-off{border-color:var(--muted)!important;color:var(--muted)!important;}
|
||||
.send-panel h3, .stats h3 { color:var(--accent); border-color:var(--border); }
|
||||
.send-controls { display:flex; gap:8px; align-items:center; }
|
||||
.send-controls input { flex:1; }
|
||||
@@ -379,11 +381,24 @@ const char serial_terminal_html[] PROGMEM = R"rawliteral(
|
||||
<div class="send-panel">
|
||||
<h3>📤 Send Data</h3>
|
||||
<div class="send-controls">
|
||||
<input type="text" id="sendInput" placeholder="Enter HEX data (e.g., 01 03 00 00 00 0A)" />
|
||||
<button class="btn btn-primary" onclick="sendData()">Send</button>
|
||||
<input type="text" id="sendInput" placeholder="HEX 입력 (예: FF A0 01 또는 FFA001)" />
|
||||
<button class="btn btn-primary" onclick="sendData()">Send HEX</button>
|
||||
</div>
|
||||
<div class="send-controls" style="margin-top:7px;">
|
||||
<input type="text" id="sendTextInput" placeholder="ASCII 텍스트 전송 (CRLF 자동 추가)" />
|
||||
<button class="btn btn-secondary btn-small" onclick="sendText()">Send Text</button>
|
||||
</div>
|
||||
</div>
|
||||
<div style="background:var(--panel);border:1px solid var(--border);border-left:3px solid var(--yellow);border-radius:var(--r);padding:11px 13px;margin-bottom:10px;">
|
||||
<div style="display:flex;gap:8px;flex-wrap:wrap;margin-bottom:8px;">
|
||||
<button class="btn btn-warning" onclick="sendLoopbackTest()">📤 Loopback Test</button>
|
||||
<button class="btn tog-off" id="btn-weblog" onclick="toggleWebLog()">🖥️ WebLog: OFF</button>
|
||||
<button class="btn tog-off" id="btn-usbmirror" onclick="toggleUsbMirror()">🔌 USB Mirror: OFF</button>
|
||||
</div>
|
||||
<div style="font-size:.76em;color:var(--muted);line-height:1.7;">
|
||||
<strong style="color:var(--text)">Loopback:</strong> GPIO17(TX)↔GPIO18(RX) 단락 후 버튼 클릭 → TX/RX 동시 수신 = UART 정상
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="stats">
|
||||
<h3>📊 Statistics</h3>
|
||||
<div class="stat-grid">
|
||||
@@ -410,6 +425,9 @@ const char serial_terminal_html[] PROGMEM = R"rawliteral(
|
||||
|
||||
<script>
|
||||
let ws;
|
||||
let pingTimer = null;
|
||||
let webLogEnabled = false;
|
||||
let usbMirrorEnabled = false;
|
||||
let autoScroll = true;
|
||||
let rxCount = 0;
|
||||
let txCount = 0;
|
||||
@@ -514,22 +532,26 @@ const char serial_terminal_html[] PROGMEM = R"rawliteral(
|
||||
|
||||
ws.onopen = function() {
|
||||
console.log('WebSocket Connected');
|
||||
addToTerminal('System', '🟢 Connected to Serial1 Terminal');
|
||||
|
||||
// 시간 동기화
|
||||
setTimeout(function() {
|
||||
syncTimeFromPhone();
|
||||
}, 500);
|
||||
addToTerminal('System', '🟢 Serial1 연결됨 | GPIO17=TX / GPIO18=RX');
|
||||
setTimeout(syncTimeFromPhone, 500);
|
||||
if (pingTimer) clearInterval(pingTimer);
|
||||
pingTimer = setInterval(function(){
|
||||
if (ws && ws.readyState === WebSocket.OPEN) ws.send(JSON.stringify({cmd:'ping'}));
|
||||
}, 20000);
|
||||
};
|
||||
|
||||
ws.onmessage = function(event) {
|
||||
try {
|
||||
const data = JSON.parse(event.data);
|
||||
|
||||
if (data.type === 'serial') {
|
||||
handleSerialData(data);
|
||||
if (data.type === 's1') {
|
||||
// ★ 펌웨어 직접 전송 시리얼 패킷 {tx:bool, d:hex, ts:ms}
|
||||
handlePacket(data.tx, data.d, data.ts);
|
||||
} else if (data.type === 'serialStatus' || data.type === 'update') {
|
||||
updateStatus(data);
|
||||
} else if (data.type === 'pong') {
|
||||
// keepalive } else if (data.type === 'usbMirrorState') {
|
||||
usbMirrorEnabled = data.enabled;
|
||||
updateToggle('btn-usbmirror', usbMirrorEnabled, '🔌 USB Mirror');
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Parse error:', e);
|
||||
@@ -537,8 +559,8 @@ const char serial_terminal_html[] PROGMEM = R"rawliteral(
|
||||
};
|
||||
|
||||
ws.onclose = function() {
|
||||
console.log('WebSocket Disconnected');
|
||||
addToTerminal('System', '🔴 Disconnected from server. Reconnecting...');
|
||||
if (pingTimer) { clearInterval(pingTimer); pingTimer = null; }
|
||||
addToTerminal('System', '🔴 연결 끊김. 재연결 중...');
|
||||
setTimeout(initWebSocket, 3000);
|
||||
};
|
||||
|
||||
@@ -547,7 +569,56 @@ const char serial_terminal_html[] PROGMEM = R"rawliteral(
|
||||
};
|
||||
}
|
||||
|
||||
// ★ 핵심 처리 함수: 펌웨어 s1/s2 패킷 → 터미널 표시
|
||||
// isTx: bool, hexData: hex string (예 "48656C"), tsMs: ms timestamp
|
||||
function handlePacket(isTx, hexData, tsMs) {
|
||||
if (!hexData || hexData.length === 0) return;
|
||||
const dir = isTx ? 'TX' : 'RX';
|
||||
const byteLen = Math.floor(hexData.length / 2);
|
||||
if (dir === 'RX') {
|
||||
rxCount += byteLen;
|
||||
document.getElementById('rxCount').textContent = rxCount;
|
||||
} else {
|
||||
txCount += byteLen;
|
||||
document.getElementById('txCount').textContent = txCount;
|
||||
}
|
||||
frameCount++;
|
||||
document.getElementById('frameCount').textContent = frameCount;
|
||||
if (!teratermMode && modbusParseEnabled && dir === 'RX') {
|
||||
const mf = parseModbus(hexData);
|
||||
if (mf) {
|
||||
modbusCount++;
|
||||
document.getElementById('modbusCount').textContent = modbusCount;
|
||||
displayModbusFrame(dir, mf, tsMs);
|
||||
return;
|
||||
}
|
||||
}
|
||||
displaySerialData(dir, hexData, tsMs);
|
||||
}
|
||||
// 하위 호환용 (기존 'serial' 타입 포맷)
|
||||
function handleSerialData(data) {
|
||||
if (data.direction === 'RX') {
|
||||
rxCount += data.data.length / 2;
|
||||
document.getElementById('rxCount').textContent = rxCount;
|
||||
} else if (data.direction === 'TX') {
|
||||
txCount += data.data.length / 2;
|
||||
document.getElementById('txCount').textContent = txCount;
|
||||
}
|
||||
frameCount++;
|
||||
document.getElementById('frameCount').textContent = frameCount;
|
||||
if (!teratermMode && modbusParseEnabled && data.direction === 'RX') {
|
||||
const mf = parseModbus(data.data);
|
||||
if (mf) {
|
||||
modbusCount++;
|
||||
document.getElementById('modbusCount').textContent = modbusCount;
|
||||
displayModbusFrame(data.direction, mf, data.timestamp);
|
||||
return;
|
||||
}
|
||||
}
|
||||
displaySerialData(data.direction, data.data, data.timestamp);
|
||||
}
|
||||
// ── 구버전 handleSerialData 원본 (아래는 새 것으로 대체됨) ──
|
||||
function _handleSerialData_orig(data) {
|
||||
if (data.direction === 'RX') {
|
||||
rxCount += data.data.length;
|
||||
document.getElementById('rxCount').textContent = rxCount;
|
||||
@@ -974,28 +1045,61 @@ const char serial_terminal_html[] PROGMEM = R"rawliteral(
|
||||
|
||||
function sendData() {
|
||||
const input = document.getElementById('sendInput');
|
||||
const hexData = input.value.replace(/[^0-9A-Fa-f]/g, '');
|
||||
|
||||
if (hexData.length === 0) {
|
||||
alert('Please enter valid HEX data');
|
||||
return;
|
||||
}
|
||||
|
||||
if (hexData.length % 2 !== 0) {
|
||||
alert('HEX data must have even number of characters');
|
||||
return;
|
||||
}
|
||||
|
||||
const hexData = input.value.replace(/[^0-9A-Fa-f \-]/g,'').replace(/[\s\-]+/g,'');
|
||||
if (!hexData) { alert('HEX 데이터 입력\n예) 48 65 6C 또는 48656C'); return; }
|
||||
if (hexData.length % 2 !== 0) { alert('HEX는 짝수 자리 (1바이트=2자리)\n예) FF→1바이트, FF01→2바이트'); return; }
|
||||
if (ws && ws.readyState === WebSocket.OPEN) {
|
||||
ws.send(JSON.stringify({
|
||||
cmd: 'sendSerial',
|
||||
data: hexData
|
||||
}));
|
||||
|
||||
ws.send(JSON.stringify({ cmd: 'sendSerial', data: hexData }));
|
||||
input.value = '';
|
||||
} else { alert('WebSocket 연결 끊김'); }
|
||||
}
|
||||
function sendText() {
|
||||
const input = document.getElementById('sendTextInput');
|
||||
const text = input.value;
|
||||
if (!text) return;
|
||||
let hex = '';
|
||||
for (let i = 0; i < text.length; i++) hex += text.charCodeAt(i).toString(16).padStart(2,'0');
|
||||
hex += '0d0a';
|
||||
if (ws && ws.readyState === WebSocket.OPEN) {
|
||||
ws.send(JSON.stringify({ cmd: 'sendSerial', data: hex }));
|
||||
input.value = '';
|
||||
addToTerminal('System', `Sent ${hexData.length / 2} bytes`);
|
||||
}
|
||||
}
|
||||
function sendLoopbackTest() {
|
||||
if (!ws || ws.readyState !== WebSocket.OPEN) return;
|
||||
const s = '>>>SER1_LOOPBACK_TEST<<<\r\n';
|
||||
let hex = '';
|
||||
for (let i = 0; i < s.length; i++) hex += s.charCodeAt(i).toString(16).padStart(2,'0');
|
||||
ws.send(JSON.stringify({ cmd: 'sendSerial', data: hex }));
|
||||
addToTerminal('System', '📤 Loopback TX → GPIO17↔GPIO18 단락 시 RX 수신 확인');
|
||||
}
|
||||
function toggleWebLog() {
|
||||
if (!ws || ws.readyState !== WebSocket.OPEN) return;
|
||||
webLogEnabled = !webLogEnabled;
|
||||
ws.send(JSON.stringify({ cmd: webLogEnabled ? 'enableWebLog' : 'disableWebLog' }));
|
||||
updateToggle('btn-weblog', webLogEnabled, '🖥️ WebLog');
|
||||
}
|
||||
function toggleUsbMirror() {
|
||||
if (!ws || ws.readyState !== WebSocket.OPEN) return;
|
||||
usbMirrorEnabled = !usbMirrorEnabled;
|
||||
ws.send(JSON.stringify({ cmd: usbMirrorEnabled ? 'enableUsbMirror' : 'disableUsbMirror' }));
|
||||
updateToggle('btn-usbmirror', usbMirrorEnabled, '🔌 USB Mirror');
|
||||
}
|
||||
function updateToggle(id, state, label) {
|
||||
const btn = document.getElementById(id);
|
||||
if (!btn) return;
|
||||
btn.textContent = label + ': ' + (state ? 'ON' : 'OFF');
|
||||
btn.className = 'btn ' + (state ? 'tog-on' : 'tog-off');
|
||||
}
|
||||
function showWebLog(msg) {
|
||||
const t = document.getElementById('terminal');
|
||||
const d = document.createElement('div');
|
||||
d.className = 'line';
|
||||
const ts = new Date().toLocaleTimeString('ko-KR',{hour12:false});
|
||||
d.innerHTML = '<span class="timestamp">['+ts+']</span><span style="color:#ffa657">[DBG] </span>'+msg;
|
||||
t.appendChild(d);
|
||||
if (autoScroll) t.scrollTop = t.scrollHeight;
|
||||
}
|
||||
|
||||
function clearTerminal() {
|
||||
document.getElementById('terminal').innerHTML = '';
|
||||
|
||||
Reference in New Issue
Block a user