00004 monitor 창 graph연결 버튼 추가, 핸드폰 자동화면 변경

여전히 메인 웹페이지에서 monitor, Transmit 밖에 선택지가 없는데 다시한번 누락되어 있는 부분 확인해줘, 그리고 핸드폰 에서 웹페이지 접속시 가로창 넘게 표시되어 이미지가 짤리는데 핸드폰 화면에 맞추어 글자와 이미지들이 들어갈 수 있게 자동 조절하게 해줄 수 있어? 특히 전송 웹페이지에 data byte(hex) 입력 칸들이 화면에 다 안들어가서 짤린상태로 전체 값들을 넣을 수 가 없어
This commit is contained in:
2025-10-05 16:36:18 +00:00
parent c5b940318f
commit 2dd7f9177f
4 changed files with 724 additions and 145 deletions

View File

@@ -6,7 +6,7 @@ const char transmit_html[] PROGMEM = R"rawliteral(
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<title>CAN Transmitter</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
@@ -14,7 +14,7 @@ const char transmit_html[] PROGMEM = R"rawliteral(
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
min-height: 100vh;
padding: 20px;
padding: 10px;
}
.container {
max-width: 1200px;
@@ -27,39 +27,42 @@ const char transmit_html[] PROGMEM = R"rawliteral(
.header {
background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
color: white;
padding: 30px;
padding: 20px;
text-align: center;
}
.header h1 { font-size: 2.5em; margin-bottom: 10px; }
.header p { opacity: 0.9; font-size: 1.1em; }
.header h1 { font-size: 1.8em; margin-bottom: 5px; }
.header p { opacity: 0.9; font-size: 0.9em; }
.nav {
background: #2c3e50;
padding: 15px 30px;
padding: 10px;
display: flex;
gap: 20px;
gap: 10px;
flex-wrap: wrap;
justify-content: center;
}
.nav a {
color: white;
text-decoration: none;
padding: 10px 20px;
padding: 10px 15px;
border-radius: 5px;
transition: all 0.3s;
font-size: 0.9em;
}
.nav a:hover { background: #34495e; }
.nav a.active { background: #3498db; }
.content { padding: 30px; }
.content { padding: 15px; }
.message-form {
background: #f8f9fa;
padding: 25px;
padding: 20px;
border-radius: 10px;
margin-bottom: 20px;
}
.form-row {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 15px;
margin-bottom: 15px;
gap: 12px;
margin-bottom: 12px;
}
.form-group {
display: flex;
@@ -69,12 +72,13 @@ const char transmit_html[] PROGMEM = R"rawliteral(
font-weight: 600;
margin-bottom: 5px;
color: #333;
font-size: 0.9em;
}
.form-group input, .form-group select {
padding: 10px;
border: 2px solid #ddd;
border-radius: 5px;
font-size: 1em;
font-size: 0.95em;
font-family: 'Courier New', monospace;
}
.form-group input:focus, .form-group select:focus {
@@ -83,18 +87,22 @@ const char transmit_html[] PROGMEM = R"rawliteral(
}
.data-bytes {
display: grid;
grid-template-columns: repeat(8, 1fr);
gap: 10px;
grid-template-columns: repeat(auto-fit, minmax(45px, 1fr));
gap: 8px;
max-width: 100%;
}
.data-bytes input {
text-align: center;
text-transform: uppercase;
width: 100%;
min-width: 45px;
padding: 10px 5px;
}
.btn {
padding: 12px 25px;
padding: 12px 20px;
border: none;
border-radius: 5px;
font-size: 1em;
font-size: 0.95em;
font-weight: 600;
cursor: pointer;
transition: all 0.3s;
@@ -116,16 +124,16 @@ const char transmit_html[] PROGMEM = R"rawliteral(
.message-list {
background: #f8f9fa;
border-radius: 10px;
padding: 20px;
padding: 15px;
}
.message-item {
background: white;
padding: 15px;
padding: 12px;
margin-bottom: 10px;
border-radius: 8px;
display: grid;
grid-template-columns: 100px 80px 1fr 150px 120px;
gap: 15px;
grid-template-columns: 90px 70px 1fr auto;
gap: 10px;
align-items: center;
border-left: 4px solid #f093fb;
}
@@ -133,66 +141,129 @@ const char transmit_html[] PROGMEM = R"rawliteral(
border-left-color: #38ef7d;
background: #f0fff4;
}
.message-id { font-weight: 700; color: #f5576c; font-family: 'Courier New', monospace; }
.message-id {
font-weight: 700;
color: #f5576c;
font-family: 'Courier New', monospace;
font-size: 0.9em;
}
.message-type {
padding: 5px 10px;
padding: 4px 8px;
border-radius: 5px;
font-size: 0.85em;
font-size: 0.75em;
font-weight: 600;
text-align: center;
}
.type-std { background: #3498db; color: white; }
.type-ext { background: #9b59b6; color: white; }
.message-data { font-family: 'Courier New', monospace; color: #666; }
.message-interval { color: #888; font-size: 0.9em; }
.message-data {
font-family: 'Courier New', monospace;
color: #666;
font-size: 0.85em;
word-break: break-all;
}
.message-interval {
color: #888;
font-size: 0.8em;
}
.message-controls {
display: flex;
gap: 5px;
flex-wrap: wrap;
}
.btn-small {
padding: 5px 12px;
font-size: 0.85em;
padding: 6px 12px;
font-size: 0.8em;
}
h2 {
color: #333;
margin: 30px 0 20px 0;
padding-bottom: 10px;
margin: 20px 0 15px 0;
padding-bottom: 8px;
border-bottom: 3px solid #f093fb;
font-size: 1.3em;
}
.status-bar {
background: #2c3e50;
color: white;
padding: 10px 20px;
padding: 10px 15px;
border-radius: 5px;
margin-bottom: 20px;
margin-bottom: 15px;
display: flex;
justify-content: space-between;
align-items: center;
font-size: 0.9em;
}
.status-bar span { font-weight: 600; }
@media (max-width: 768px) {
body { padding: 5px; }
.header h1 { font-size: 1.5em; }
.content { padding: 10px; }
.form-row { grid-template-columns: 1fr; }
.data-bytes {
grid-template-columns: repeat(4, 1fr);
gap: 6px;
}
.data-bytes input {
min-width: 40px;
font-size: 0.9em;
padding: 8px 4px;
}
.message-item {
grid-template-columns: 1fr;
gap: 8px;
}
.message-controls {
justify-content: flex-start;
}
.control-row {
flex-direction: column;
align-items: stretch;
}
.control-row > * {
width: 100%;
}
.nav {
padding: 8px;
gap: 5px;
}
.nav a {
padding: 8px 10px;
font-size: 0.85em;
}
h2 {
font-size: 1.1em;
}
.status-bar {
flex-direction: column;
gap: 8px;
text-align: center;
}
}
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>📡 CAN Transmitter</h1>
<h1>CAN Transmitter</h1>
<p>Send CAN Messages</p>
</div>
<div class="nav">
<a href="/">📊 Monitor</a>
<a href="/transmit" class="active">📡 Transmit</a>
<a href="/">Monitor</a>
<a href="/transmit" class="active">Transmit</a>
<a href="/graph">Graph</a>
</div>
<div class="content">
<div class="status-bar">
<span id="connection-status">🔴 Disconnected</span>
<span id="tx-count">Sent: 0 messages</span>
<span id="connection-status">Disconnected</span>
<span id="tx-count">Sent: 0</span>
</div>
<h2> Add CAN Message</h2>
<h2>Add CAN Message</h2>
<div class="message-form">
<div class="form-row">
<div class="form-group">
@@ -200,10 +271,10 @@ const char transmit_html[] PROGMEM = R"rawliteral(
<input type="text" id="can-id" placeholder="123" maxlength="8">
</div>
<div class="form-group">
<label>Message Type</label>
<label>Type</label>
<select id="msg-type">
<option value="std">Standard (11-bit)</option>
<option value="ext">Extended (29-bit)</option>
<option value="std">Standard</option>
<option value="ext">Extended</option>
</select>
</div>
<div class="form-group">
@@ -240,21 +311,21 @@ const char transmit_html[] PROGMEM = R"rawliteral(
</div>
</div>
<div style="display: flex; gap: 10px;">
<button class="btn btn-primary" onclick="addMessage()"> Add to List</button>
<button class="btn btn-success" onclick="sendOnce()">📤 Send Once</button>
<div style="display: flex; gap: 10px; flex-wrap: wrap;">
<button class="btn btn-primary" onclick="addMessage()">Add to List</button>
<button class="btn btn-success" onclick="sendOnce()">Send Once</button>
</div>
</div>
<h2>📋 Message List</h2>
<div style="margin-bottom: 15px; display: flex; gap: 10px;">
<button class="btn btn-success" onclick="startAll()"> Start All</button>
<button class="btn btn-danger" onclick="stopAll()"> Stop All</button>
<button class="btn btn-danger" onclick="clearAll()">🗑 Clear All</button>
<h2>Message List</h2>
<div style="margin-bottom: 15px; display: flex; gap: 10px; flex-wrap: wrap;">
<button class="btn btn-success" onclick="startAll()">Start All</button>
<button class="btn btn-danger" onclick="stopAll()">Stop All</button>
<button class="btn btn-danger" onclick="clearAll()">Clear All</button>
</div>
<div class="message-list" id="message-list">
<p style="text-align: center; color: #666;">No messages added yet</p>
<p style="text-align: center; color: #666; font-size: 0.9em;">No messages added yet</p>
</div>
</div>
</div>
@@ -269,12 +340,12 @@ const char transmit_html[] PROGMEM = R"rawliteral(
ws.onopen = function() {
console.log('WebSocket connected');
document.getElementById('connection-status').innerHTML = '🟢 Connected';
document.getElementById('connection-status').innerHTML = 'Connected';
};
ws.onclose = function() {
console.log('WebSocket disconnected');
document.getElementById('connection-status').innerHTML = '🔴 Disconnected';
document.getElementById('connection-status').innerHTML = 'Disconnected';
setTimeout(initWebSocket, 3000);
};
@@ -282,7 +353,7 @@ const char transmit_html[] PROGMEM = R"rawliteral(
const data = JSON.parse(event.data);
if (data.type === 'txStatus') {
txCount = data.count;
document.getElementById('tx-count').textContent = 'Sent: ' + txCount + ' messages';
document.getElementById('tx-count').textContent = 'Sent: ' + txCount;
}
};
}
@@ -294,7 +365,7 @@ const char transmit_html[] PROGMEM = R"rawliteral(
const interval = parseInt(document.getElementById('interval').value);
if (!id || !/^[0-9A-F]+$/.test(id)) {
alert('Invalid CAN ID! Use hex format (e.g., 123, 1A2)');
alert('Invalid CAN ID!');
return;
}
@@ -302,7 +373,7 @@ const char transmit_html[] PROGMEM = R"rawliteral(
for (let i = 0; i < 8; i++) {
const val = document.getElementById('d' + i).value.toUpperCase();
if (!/^[0-9A-F]{0,2}$/.test(val)) {
alert('Invalid data byte D' + i + '! Use hex format (00-FF)');
alert('Invalid data byte D' + i + '!');
return;
}
data.push(val.padStart(2, '0'));
@@ -343,14 +414,13 @@ const char transmit_html[] PROGMEM = R"rawliteral(
data: data.join('')
};
ws.send(JSON.stringify(cmd));
console.log('Sent:', cmd);
}
function updateMessageList() {
const list = document.getElementById('message-list');
if (messages.length === 0) {
list.innerHTML = '<p style="text-align: center; color: #666;">No messages added yet</p>';
list.innerHTML = '<p style="text-align: center; color: #666; font-size: 0.9em;">No messages</p>';
return;
}
@@ -359,21 +429,16 @@ const char transmit_html[] PROGMEM = R"rawliteral(
const item = document.createElement('div');
item.className = 'message-item' + (msg.active ? ' active' : '');
item.innerHTML = `
<div class="message-id">0x${msg.id}</div>
<div class="message-type type-${msg.type}">${msg.type.toUpperCase()}</div>
<div class="message-data">${msg.data.slice(0, msg.dlc).join(' ')}</div>
<div class="message-interval">Every ${msg.interval}ms</div>
<div class="message-controls">
<button class="btn ${msg.active ? 'btn-danger' : 'btn-success'} btn-small"
onclick="toggleMessage(${index})">
${msg.active ? ' Stop' : ' Start'}
</button>
<button class="btn btn-danger btn-small" onclick="deleteMessage(${index})">
🗑
</button>
</div>
`;
item.innerHTML =
'<div class="message-id">0x' + msg.id + '</div>' +
'<div class="message-type type-' + msg.type + '">' + msg.type.toUpperCase() + '</div>' +
'<div class="message-data">' + msg.data.slice(0, msg.dlc).join(' ') + '</div>' +
'<div class="message-controls">' +
'<button class="btn ' + (msg.active ? 'btn-danger' : 'btn-success') + ' btn-small" onclick="toggleMessage(' + index + ')">' +
(msg.active ? 'Stop' : 'Start') +
'</button>' +
'<button class="btn btn-danger btn-small" onclick="deleteMessage(' + index + ')">Delete</button>' +
'</div>';
list.appendChild(item);
});
@@ -426,7 +491,6 @@ const char transmit_html[] PROGMEM = R"rawliteral(
}
}
// Hex input validation
document.querySelectorAll('input[id^="d"]').forEach(input => {
input.addEventListener('input', function() {
this.value = this.value.toUpperCase().replace(/[^0-9A-F]/g, '');