시리얼, seting수정

This commit is contained in:
2026-03-27 21:21:38 +00:00
parent 5616158cc7
commit 9432ea6a40
3 changed files with 659 additions and 383 deletions

View File

@@ -255,6 +255,8 @@ const char serial2_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,23 @@ const char serial2_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>
</div>
<div style="font-size:.76em;color:var(--muted);line-height:1.7;">
<strong style="color:var(--text)">Loopback:</strong> GPIO6(TX)GPIO7(RX) TX/RX = UART
</div>
</div>
<div class="stats">
<h3>📊 Statistics</h3>
<div class="stat-grid">
@@ -410,6 +424,8 @@ const char serial2_terminal_html[] PROGMEM = R"rawliteral(
<script>
let ws;
let pingTimer = null;
let webLogEnabled = false;
let autoScroll = true;
let rxCount = 0;
let txCount = 0;
@@ -514,22 +530,24 @@ const char serial2_terminal_html[] PROGMEM = R"rawliteral(
ws.onopen = function() {
console.log('WebSocket Connected');
addToTerminal('System', '🟢 Connected to Serial2 Terminal');
// 시간 동기화
setTimeout(function() {
syncTimeFromPhone();
}, 500);
addToTerminal('System', '🟢 Serial2 | GPIO6=TX / GPIO7=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 === 'serial2') {
handleSerialData(data);
if (data.type === 's2') {
// ★ 펌웨어 직접 전송 시리얼 패킷 {tx:bool, d:hex, ts:ms}
handlePacket(data.tx, data.d, data.ts);
} else if (data.type === 'serial2Status' || data.type === 'update') {
updateStatus(data);
} else if (data.type === 'pong') {
// keepalive
}
} catch (e) {
console.error('Parse error:', e);
@@ -537,8 +555,8 @@ const char serial2_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 +565,56 @@ const char serial2_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 +1041,55 @@ const char serial2_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예) FF1, FF012'); return; }
if (ws && ws.readyState === WebSocket.OPEN) {
ws.send(JSON.stringify({
cmd: 'sendSerial2',
data: hexData
}));
ws.send(JSON.stringify({ cmd: 'sendSerial2', 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: 'sendSerial2', data: hex }));
input.value = '';
addToTerminal('System', `Sent ${hexData.length / 2} bytes`);
}
}
function sendLoopbackTest() {
if (!ws || ws.readyState !== WebSocket.OPEN) return;
const s = '>>>SER2_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: 'sendSerial2', data: hex }));
addToTerminal('System', '📤 Loopback TX GPIO6GPIO7 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 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 = '';