diff --git a/ESP32_CAN_Logger.ino b/ESP32_CAN_Logger.ino index 0c632f7..7877ff2 100644 --- a/ESP32_CAN_Logger.ino +++ b/ESP32_CAN_Logger.ino @@ -16,6 +16,7 @@ #include #include "index.h" #include "transmit.h" +#include "graph.h" // 그래프 페이지 추가 // GPIO 핀 정의 #define CAN_INT_PIN 27 @@ -738,6 +739,10 @@ void setup() { server.send_P(200, "text/html", transmit_html); }); + server.on("/graph", HTTP_GET, []() { + server.send_P(200, "text/html", graph_html); + }); + server.on("/download", HTTP_GET, []() { if (server.hasArg("file")) { String filename = "/" + server.arg("file"); @@ -783,8 +788,10 @@ void setup() { Serial.println(" 1. WiFi: ESP32_CAN_Logger (12345678)"); Serial.print(" 2. http://"); Serial.println(WiFi.softAPIP()); - Serial.println(" 3. Monitor: /"); - Serial.println(" 4. Transmit: /transmit"); + Serial.println(" 3. Pages:"); + Serial.println(" - Monitor: /"); + Serial.println(" - Transmit: /transmit"); + Serial.println(" - Graph: /graph"); Serial.println("========================================\n"); } diff --git a/graph.h b/graph.h new file mode 100644 index 0000000..2258b34 --- /dev/null +++ b/graph.h @@ -0,0 +1,495 @@ +#ifndef GRAPH_H +#define GRAPH_H + +const char graph_html[] PROGMEM = R"rawliteral( + + + + + + CAN Signal Graph + + + + +
+
+

📈 CAN Signal Graph

+

Real-time Signal Visualization

+
+ + + +
+ + +

📁 Upload DBC File

+
+
+ +

🗂️ Click to upload DBC file

+

or drag and drop here

+
+
+ + +
+
+ + + + +)rawliteral"; + +#endif \ No newline at end of file diff --git a/index.h b/index.h index 14be40b..ddffeeb 100644 --- a/index.h +++ b/index.h @@ -6,7 +6,7 @@ const char index_html[] PROGMEM = R"rawliteral( - + ESP32 CAN Logger
-

🚗 ESP32 CAN Logger

+

ESP32 CAN Logger

Real-time CAN Bus Monitor & Data Logger

-

LOGGING STATUS

+

LOGGING

OFF
@@ -192,16 +219,16 @@ const char index_html[] PROGMEM = R"rawliteral(

SPEED

-
0 msg/s
+
0/s

CURRENT FILE

-
-
+
-
-

⚙️ Control Panel

+

Control Panel

- +
- - + +
-

📊 Real-time CAN Messages (by ID)

+

CAN Messages (by ID)

- + - +
CAN IDID DLC Data CountLast Time (ms)Time(ms)
-

💾 Log Files

+

Log Files

-

Loading files...

+

Loading...

@@ -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 = - '

Error: ' + data.error + '

'; + '

Error: ' + data.error + '

'; } 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 = '

No log files found

'; + fileList.innerHTML = '

No log files

'; return; } @@ -402,15 +427,13 @@ const char index_html[] PROGMEM = R"rawliteral( const fileItem = document.createElement('div'); fileItem.className = 'file-item'; fileItem.innerHTML = - '
' + - '📄 ' + file.name + '' + + '
' + + '' + file.name + '' + '(' + formatBytes(file.size) + ')' + '
' + - ''; + ''; 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 = - '

WebSocket not connected. Reconnecting...

'; } } @@ -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); diff --git a/transmit.h b/transmit.h index bb1659a..c8600bd 100644 --- a/transmit.h +++ b/transmit.h @@ -6,7 +6,7 @@ const char transmit_html[] PROGMEM = R"rawliteral( - + CAN Transmitter
-

📡 CAN Transmitter

+

CAN Transmitter

Send CAN Messages

- 🔴 Disconnected - Sent: 0 messages + Disconnected + Sent: 0
-

➕ Add CAN Message

+

Add CAN Message

@@ -200,10 +271,10 @@ const char transmit_html[] PROGMEM = R"rawliteral(
- +
@@ -240,21 +311,21 @@ const char transmit_html[] PROGMEM = R"rawliteral(
-
- - +
+ +
-

📋 Message List

-
- - - +

Message List

+
+ + +
-

No messages added yet

+

No messages added yet

@@ -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 = '

No messages added yet

'; + list.innerHTML = '

No messages

'; 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 = ` -
0x${msg.id}
-
${msg.type.toUpperCase()}
-
${msg.data.slice(0, msg.dlc).join(' ')}
-
Every ${msg.interval}ms
-
- - -
- `; + item.innerHTML = + '
0x' + msg.id + '
' + + '
' + msg.type.toUpperCase() + '
' + + '
' + msg.data.slice(0, msg.dlc).join(' ') + '
' + + '
' + + '' + + '' + + '
'; 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, '');