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

153
index.h
View File

@@ -6,7 +6,7 @@ const char index_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>ESP32 CAN Logger</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
@@ -14,7 +14,7 @@ const char index_html[] PROGMEM = R"rawliteral(
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
padding: 20px;
padding: 10px;
}
.container {
max-width: 1400px;
@@ -27,64 +27,68 @@ const char index_html[] PROGMEM = R"rawliteral(
.header {
background: linear-gradient(135deg, #667eea 0%, #764ba2 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;
white-space: nowrap;
}
.nav a:hover { background: #34495e; }
.nav a.active { background: #3498db; }
.content { padding: 30px; }
.content { padding: 15px; }
.status-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 20px;
margin-bottom: 30px;
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
gap: 10px;
margin-bottom: 20px;
}
.status-card {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
padding: 20px;
padding: 15px;
border-radius: 10px;
text-align: center;
}
.status-card h3 { font-size: 0.9em; opacity: 0.9; margin-bottom: 10px; }
.status-card .value { font-size: 2em; font-weight: bold; }
.status-card h3 { font-size: 0.75em; opacity: 0.9; margin-bottom: 8px; }
.status-card .value { font-size: 1.5em; font-weight: bold; word-break: break-all; }
.status-on { background: linear-gradient(135deg, #11998e 0%, #38ef7d 100%) !important; }
.status-off { background: linear-gradient(135deg, #eb3349 0%, #f45c43 100%) !important; }
.control-panel {
background: #f8f9fa;
padding: 20px;
padding: 15px;
border-radius: 10px;
margin-bottom: 30px;
margin-bottom: 20px;
}
.control-row {
display: flex;
gap: 15px;
gap: 10px;
align-items: center;
flex-wrap: wrap;
margin-bottom: 15px;
margin-bottom: 10px;
}
.control-row:last-child { margin-bottom: 0; }
label { font-weight: 600; color: #333; }
label { font-weight: 600; color: #333; font-size: 0.9em; }
select, button {
padding: 10px 20px;
padding: 8px 15px;
border: none;
border-radius: 5px;
font-size: 1em;
font-size: 0.9em;
cursor: pointer;
transition: all 0.3s;
}
@@ -103,11 +107,13 @@ const char index_html[] PROGMEM = R"rawliteral(
.can-table-container {
background: #f8f9fa;
border-radius: 10px;
padding: 20px;
padding: 10px;
overflow-x: auto;
-webkit-overflow-scrolling: touch;
}
table {
width: 100%;
min-width: 500px;
border-collapse: collapse;
background: white;
border-radius: 8px;
@@ -116,14 +122,16 @@ const char index_html[] PROGMEM = R"rawliteral(
th {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
padding: 15px;
padding: 10px 8px;
text-align: left;
font-weight: 600;
font-size: 0.85em;
}
td {
padding: 12px 15px;
padding: 8px;
border-bottom: 1px solid #e9ecef;
font-family: 'Courier New', monospace;
font-size: 0.8em;
}
tr:hover { background: #f8f9fa; }
.flash-row {
@@ -136,50 +144,69 @@ const char index_html[] PROGMEM = R"rawliteral(
.mono { font-family: 'Courier New', monospace; }
h2 {
color: #333;
margin: 30px 0 20px 0;
padding-bottom: 10px;
margin: 20px 0 15px 0;
padding-bottom: 8px;
border-bottom: 3px solid #667eea;
font-size: 1.3em;
}
.file-list {
background: #f8f9fa;
border-radius: 10px;
padding: 20px;
padding: 15px;
}
.file-item {
background: white;
padding: 15px;
margin-bottom: 10px;
padding: 12px;
margin-bottom: 8px;
border-radius: 8px;
display: flex;
justify-content: space-between;
align-items: center;
transition: all 0.3s;
flex-wrap: wrap;
gap: 10px;
}
.file-item:hover { transform: translateX(5px); box-shadow: 0 3px 10px rgba(0,0,0,0.1); }
.file-name { font-weight: 600; color: #333; }
.file-size { color: #666; margin-left: 15px; }
.file-name { font-weight: 600; color: #333; font-size: 0.9em; }
.file-size { color: #666; margin-left: 10px; font-size: 0.85em; }
.download-btn {
padding: 8px 16px;
font-size: 0.9em;
padding: 6px 12px;
font-size: 0.85em;
}
@media (max-width: 768px) {
body { padding: 5px; }
.header h1 { font-size: 1.5em; }
.header p { font-size: 0.85em; }
.content { padding: 10px; }
.status-grid { grid-template-columns: repeat(2, 1fr); gap: 8px; }
.status-card { padding: 10px; }
.status-card h3 { font-size: 0.7em; }
.status-card .value { font-size: 1.2em; }
h2 { font-size: 1.1em; }
.nav a { padding: 8px 12px; font-size: 0.85em; }
table { min-width: 400px; }
th, td { padding: 6px 4px; font-size: 0.75em; }
}
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>🚗 ESP32 CAN Logger</h1>
<h1>ESP32 CAN Logger</h1>
<p>Real-time CAN Bus Monitor & Data Logger</p>
</div>
<div class="nav">
<a href="/" class="active">📊 Monitor</a>
<a href="/transmit">📡 Transmit</a>
<a href="/" class="active">Monitor</a>
<a href="/transmit">Transmit</a>
<a href="/graph">Graph</a>
</div>
<div class="content">
<div class="status-grid">
<div class="status-card" id="logging-status">
<h3>LOGGING STATUS</h3>
<h3>LOGGING</h3>
<div class="value">OFF</div>
</div>
<div class="status-card" id="sd-status">
@@ -192,16 +219,16 @@ const char index_html[] PROGMEM = R"rawliteral(
</div>
<div class="status-card">
<h3>SPEED</h3>
<div class="value" id="msg-speed">0 msg/s</div>
<div class="value" id="msg-speed">0/s</div>
</div>
<div class="status-card" id="file-status" style="grid-column: span 2;">
<h3>CURRENT FILE</h3>
<div class="value" id="current-file" style="font-size: 1.3em;">-</div>
<div class="value" id="current-file" style="font-size: 1em;">-</div>
</div>
</div>
<div class="control-panel">
<h2> Control Panel</h2>
<h2>Control Panel</h2>
<div class="control-row">
<label for="can-speed">CAN Speed:</label>
<select id="can-speed">
@@ -210,33 +237,33 @@ const char index_html[] PROGMEM = R"rawliteral(
<option value="2">500 Kbps</option>
<option value="3" selected>1 Mbps</option>
</select>
<button onclick="setCanSpeed()">Apply Speed</button>
<button onclick="setCanSpeed()">Apply</button>
</div>
<div class="control-row">
<button onclick="refreshFiles()">🔄 Refresh Files</button>
<button onclick="clearMessages()">🗑 Clear Display</button>
<button onclick="refreshFiles()">Refresh Files</button>
<button onclick="clearMessages()">Clear Display</button>
</div>
</div>
<h2>📊 Real-time CAN Messages (by ID)</h2>
<h2>CAN Messages (by ID)</h2>
<div class="can-table-container">
<table>
<thead>
<tr>
<th>CAN ID</th>
<th>ID</th>
<th>DLC</th>
<th>Data</th>
<th>Count</th>
<th>Last Time (ms)</th>
<th>Time(ms)</th>
</tr>
</thead>
<tbody id="can-messages"></tbody>
</table>
</div>
<h2>💾 Log Files</h2>
<h2>Log Files</h2>
<div class="file-list" id="file-list">
<p style="text-align: center; color: #666;">Loading files...</p>
<p style="text-align: center; color: #666; font-size: 0.9em;">Loading...</p>
</div>
</div>
</div>
@@ -263,7 +290,6 @@ const char index_html[] PROGMEM = R"rawliteral(
ws.onmessage = function(event) {
const data = JSON.parse(event.data);
console.log(':', data.type);
if (data.type === 'status') {
updateStatus(data);
@@ -272,10 +298,9 @@ const char index_html[] PROGMEM = R"rawliteral(
} else if (data.type === 'canBatch') {
updateCanBatch(data.messages);
} else if (data.type === 'files') {
console.log(' :', data.files ? data.files.length : 0);
if (data.error) {
document.getElementById('file-list').innerHTML =
'<p style="text-align: center; color: #e74c3c;">Error: ' + data.error + '</p>';
'<p style="text-align: center; color: #e74c3c; font-size: 0.9em;">Error: ' + data.error + '</p>';
} else {
updateFileList(data.files);
}
@@ -318,7 +343,7 @@ const char index_html[] PROGMEM = R"rawliteral(
}
document.getElementById('msg-count').textContent = data.msgCount.toLocaleString();
document.getElementById('msg-speed').textContent = data.msgSpeed + ' msg/s';
document.getElementById('msg-speed').textContent = data.msgSpeed + '/s';
}
function addCanMessage(data) {
@@ -387,7 +412,7 @@ const char index_html[] PROGMEM = R"rawliteral(
const fileList = document.getElementById('file-list');
if (!files || files.length === 0) {
fileList.innerHTML = '<p style="text-align: center; color: #666;">No log files found</p>';
fileList.innerHTML = '<p style="text-align: center; color: #666; font-size: 0.9em;">No log files</p>';
return;
}
@@ -402,15 +427,13 @@ const char index_html[] PROGMEM = R"rawliteral(
const fileItem = document.createElement('div');
fileItem.className = 'file-item';
fileItem.innerHTML =
'<div>' +
'<span class="file-name">📄 ' + file.name + '</span>' +
'<div style="flex: 1; min-width: 0;">' +
'<span class="file-name">' + file.name + '</span>' +
'<span class="file-size">(' + formatBytes(file.size) + ')</span>' +
'</div>' +
'<button class="download-btn" onclick="downloadFile(\'' + file.name + '\')"> Download</button>';
'<button class="download-btn" onclick="downloadFile(\'' + file.name + '\')">Download</button>';
fileList.appendChild(fileItem);
});
console.log(' : ' + files.length + ' ()');
}
function formatBytes(bytes) {
@@ -427,11 +450,6 @@ const char index_html[] PROGMEM = R"rawliteral(
function refreshFiles() {
if (ws && ws.readyState === WebSocket.OPEN) {
ws.send(JSON.stringify({cmd: 'getFiles'}));
console.log(' ');
} else {
console.log('WebSocket ');
document.getElementById('file-list').innerHTML =
'<p style="text-align: center; color: #e74c3c;">WebSocket not connected. Reconnecting...</p>';
}
}
@@ -446,12 +464,7 @@ const char index_html[] PROGMEM = R"rawliteral(
}
initWebSocket();
setTimeout(() => {
if (!document.getElementById('file-list').querySelector('.file-item')) {
console.log(' ');
refreshFiles();
}
}, 2000);
setTimeout(() => { refreshFiles(); }, 2000);
</script>
</body>
</html>