00003 전송웹추가

웹페이지 하나 더 추가 하고 싶은데 (포트생성) 이 신규 페이지는 can data를 송신 하는 목적의 페이지로 can 신호id, messagy type, message data(hex) 를 설정할 수 있게 하고 여러 신호들을 조합으로 송신할 수 있게, 그리고 특정 시간마다 송신할 수 있게 기능을 추가 할 수 있어? 해당페이지는 차후 수정이 용이하게 파일도 추가했으면 좋겠어
This commit is contained in:
2025-10-05 07:11:05 +00:00
parent e26240f84f
commit c5b940318f

92
index.h
View File

@@ -32,6 +32,21 @@ const char index_html[] PROGMEM = R"rawliteral(
}
.header h1 { font-size: 2.5em; margin-bottom: 10px; }
.header p { opacity: 0.9; font-size: 1.1em; }
.nav {
background: #2c3e50;
padding: 15px 30px;
display: flex;
gap: 20px;
}
.nav a {
color: white;
text-decoration: none;
padding: 10px 20px;
border-radius: 5px;
transition: all 0.3s;
}
.nav a:hover { background: #34495e; }
.nav a.active { background: #3498db; }
.content { padding: 30px; }
.status-grid {
display: grid;
@@ -155,6 +170,12 @@ const char index_html[] PROGMEM = R"rawliteral(
<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>
</div>
<div class="content">
<div class="status-grid">
<div class="status-card" id="logging-status">
@@ -223,8 +244,8 @@ const char index_html[] PROGMEM = R"rawliteral(
<script>
let ws;
let reconnectInterval;
let canMessages = {}; // CAN ID별 메시지 저장 객체
let messageOrder = []; // 메시지 표시 순서
let canMessages = {};
let messageOrder = [];
function initWebSocket() {
ws = new WebSocket('ws://' + window.location.hostname + ':81');
@@ -232,11 +253,7 @@ const char index_html[] PROGMEM = R"rawliteral(
ws.onopen = function() {
console.log('WebSocket connected');
clearInterval(reconnectInterval);
// 연결 직후 파일 목록 요청
setTimeout(() => {
refreshFiles();
}, 500);
setTimeout(() => { refreshFiles(); }, 500);
};
ws.onclose = function() {
@@ -253,13 +270,12 @@ const char index_html[] PROGMEM = R"rawliteral(
} else if (data.type === 'can') {
addCanMessage(data);
} 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;">Error: ' + data.error + '</p>';
} else {
updateFileList(data.files);
}
@@ -292,7 +308,6 @@ const char index_html[] PROGMEM = R"rawliteral(
sdCard.querySelector('.value').textContent = 'NOT READY';
}
// 현재 파일명 표시
if (data.currentFile && data.currentFile !== '') {
fileCard.classList.add('status-on');
fileCard.classList.remove('status-off');
@@ -308,80 +323,60 @@ const char index_html[] PROGMEM = R"rawliteral(
function addCanMessage(data) {
const canId = data.id;
// 새로운 CAN ID인 경우 순서에 추가
if (!canMessages[canId]) {
messageOrder.push(canId);
}
// CAN ID별로 최신 메시지 저장
canMessages[canId] = {
timestamp: data.timestamp,
dlc: data.dlc,
data: data.data,
updateCount: (canMessages[canId]?.updateCount || 0) + 1
updateCount: data.count
};
}
function updateCanBatch(messages) {
// 여러 메시지를 한 번에 처리
messages.forEach(msg => {
const canId = msg.id;
if (!canMessages[canId]) {
messageOrder.push(canId);
}
// ESP32에서 받은 실제 count 값 사용
canMessages[canId] = {
timestamp: msg.timestamp,
dlc: msg.dlc,
data: msg.data,
updateCount: msg.count // 실제 수신 횟수
updateCount: msg.count
};
});
// 한 번만 테이블 업데이트
updateCanTable();
}
function updateCanTable() {
const tbody = document.getElementById('can-messages');
// 기존 행들의 ID를 Map으로 저장
const existingRows = new Map();
Array.from(tbody.rows).forEach(row => {
existingRows.set(row.dataset.canId, row);
});
// CAN ID 순서대로 업데이트
messageOrder.forEach(canId => {
const msg = canMessages[canId];
let row = existingRows.get(canId);
if (row) {
// 기존 행 업데이트
row.cells[1].textContent = msg.dlc;
row.cells[2].textContent = msg.data;
row.cells[3].textContent = msg.updateCount;
row.cells[4].textContent = msg.timestamp;
// 업데이트 강조
row.classList.add('flash-row');
setTimeout(() => row.classList.remove('flash-row'), 300);
} else {
// 새 행 추가
row = tbody.insertRow();
row.dataset.canId = canId;
row.innerHTML = `
<td class="mono">0x${canId}</td>
<td>${msg.dlc}</td>
<td class="mono">${msg.data}</td>
<td>${msg.updateCount}</td>
<td>${msg.timestamp}</td>
`;
row.innerHTML =
'<td class="mono">0x' + canId + '</td>' +
'<td>' + msg.dlc + '</td>' +
'<td class="mono">' + msg.data + '</td>' +
'<td>' + msg.updateCount + '</td>' +
'<td>' + msg.timestamp + '</td>';
row.classList.add('flash-row');
setTimeout(() => row.classList.remove('flash-row'), 300);
}
@@ -396,29 +391,26 @@ const char index_html[] PROGMEM = R"rawliteral(
return;
}
// 파일명 기준으로 내림차순 정렬 (최신 파일이 위로)
files.sort((a, b) => {
// canlog_XXXXX.bin에서 숫자 부분 추출
const numA = parseInt(a.name.match(/\d+/)?.[0] || '0');
const numB = parseInt(b.name.match(/\d+/)?.[0] || '0');
return numB - numA; // 내림차순
return numB - numA;
});
fileList.innerHTML = '';
files.forEach(file => {
const fileItem = document.createElement('div');
fileItem.className = 'file-item';
fileItem.innerHTML = `
<div>
<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>
`;
fileItem.innerHTML =
'<div>' +
'<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>';
fileList.appendChild(fileItem);
});
console.log(` : ${files.length} ()`);
console.log(' : ' + files.length + ' ()');
}
function formatBytes(bytes) {
@@ -454,8 +446,6 @@ const char index_html[] PROGMEM = R"rawliteral(
}
initWebSocket();
// 페이지 로드 시 파일 목록 요청 (2초 후 재시도)
setTimeout(() => {
if (!document.getElementById('file-list').querySelector('.file-item')) {
console.log(' ');