diff --git a/ESP32_CAN_Logger-a.ino b/ESP32_CAN_Logger-a.ino index 21c3c55..97b6921 100644 --- a/ESP32_CAN_Logger-a.ino +++ b/ESP32_CAN_Logger-a.ino @@ -2960,6 +2960,27 @@ void webUpdateTask(void *parameter) { doc["sdReady"] = sdCardReady; doc["totalMsg"] = totalMsgCount; doc["msgPerSec"] = msgPerSecond; + + // πŸ†• CAN λ²„μŠ€ λΆ€ν•˜μœ¨ 계산 + // CAN 속도별 이둠적 μ΅œλŒ€ λ©”μ‹œμ§€/초 (8λ°”μ΄νŠΈ 데이터 κΈ°μ€€) + // 125 Kbps: ~1,000 msg/s + // 250 Kbps: ~2,000 msg/s + // 500 Kbps: ~4,000 msg/s + // 1 Mbps: ~8,000 msg/s + uint32_t maxMsgPerSec; + switch(currentCanSpeed) { + case CAN_125KBPS: maxMsgPerSec = 1000; break; + case CAN_250KBPS: maxMsgPerSec = 2000; break; + case CAN_500KBPS: maxMsgPerSec = 4000; break; + case CAN_1000KBPS: + default: maxMsgPerSec = 8000; break; + } + + // λΆ€ν•˜μœ¨ 계산 (0~100%) + float busLoad = (msgPerSecond * 100.0) / maxMsgPerSec; + if (busLoad > 100.0) busLoad = 100.0; + doc["busLoad"] = (int)busLoad; // μ •μˆ˜λ‘œ 전솑 + doc["totalTx"] = totalTxCount; doc["totalSerialRx"] = totalSerialRxCount; doc["totalSerialTx"] = totalSerialTxCount; diff --git a/index.h b/index.h index 336c7a6..517e617 100644 --- a/index.h +++ b/index.h @@ -252,14 +252,14 @@ const char index_html[] PROGMEM = R"rawliteral( .status-grid { display: grid; - grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); - gap: 10px; + grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); + gap: 12px; margin-bottom: 20px; } .status-card { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; - padding: 15px; + padding: 18px 12px; border-radius: 10px; text-align: center; box-shadow: 0 4px 15px rgba(0,0,0,0.2); @@ -270,15 +270,17 @@ const char index_html[] PROGMEM = R"rawliteral( box-shadow: 0 6px 20px rgba(0,0,0,0.3); } .status-card h3 { - font-size: 0.75em; - opacity: 0.9; - margin-bottom: 8px; - letter-spacing: 1px; + font-size: 0.8em; + opacity: 0.95; + margin-bottom: 10px; + letter-spacing: 0.5px; + font-weight: 600; } .status-card .value { - font-size: 1.5em; + font-size: 1.6em; font-weight: 700; font-family: 'Courier New', monospace; + line-height: 1.2; } .status-card.status-on { background: linear-gradient(135deg, #11998e 0%, #38ef7d 100%); @@ -288,6 +290,41 @@ const char index_html[] PROGMEM = R"rawliteral( opacity: 0.7; } + /* λ²„μŠ€ λΆ€ν•˜μœ¨ 색상 */ + .status-card.bus-load-low { + background: linear-gradient(135deg, #11998e 0%, #38ef7d 100%); + } + .status-card.bus-load-medium { + background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%); + } + .status-card.bus-load-high { + background: linear-gradient(135deg, #eb3349 0%, #f45c43 100%); + } + + /* λ°˜μ‘ν˜•: λͺ¨λ°”일 */ + @media (max-width: 768px) { + .status-grid { + grid-template-columns: repeat(2, 1fr); + gap: 10px; + } + .status-card { + padding: 15px 10px; + } + .status-card h3 { + font-size: 0.75em; + } + .status-card .value { + font-size: 1.4em; + } + } + + /* λ°˜μ‘ν˜•: λŒ€ν˜• ν™”λ©΄ */ + @media (min-width: 1200px) { + .status-grid { + grid-template-columns: repeat(5, 1fr); + } + } + h2 { color: #333; margin: 20px 0 10px 0; @@ -339,7 +376,7 @@ const char index_html[] PROGMEM = R"rawliteral( cursor: pointer; transition: all 0.3s; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); - color: white; /* ⭐ #666 β†’ white */ + color: white; } .control-row button:hover { transform: translateY(-2px); @@ -855,7 +892,7 @@ const char index_html[] PROGMEM = R"rawliteral(
CONNECTION
-
μ—°κ²° 쀑...
+
Connecting...
@@ -906,6 +943,10 @@ const char index_html[] PROGMEM = R"rawliteral(

SPEED

0/s
+
+

BUS LOAD

+
0%
+

TIME SYNC

0
@@ -971,14 +1012,14 @@ const char index_html[] PROGMEM = R"rawliteral( (Text - Excel Ready)
- +
- - - - + + + +
@@ -1105,7 +1146,7 @@ const char index_html[] PROGMEM = R"rawliteral( DLC Data Count - Time(ms) + Time @@ -1164,8 +1205,24 @@ const char index_html[] PROGMEM = R"rawliteral( function updateCurrentTime() { const now = new Date(); - const timeStr = now.toLocaleTimeString('ko-KR', {hour12: false}); - document.getElementById('current-time').textContent = timeStr; + const h = String(now.getHours()).padStart(2, '0'); + const m = String(now.getMinutes()).padStart(2, '0'); + const s = String(now.getSeconds()).padStart(2, '0'); + document.getElementById('current-time').textContent = `${h}h ${m}m ${s}s`; + } + + // CAN λ©”μ‹œμ§€ μ‹œκ°„ 포맷 ν•¨μˆ˜ (초.λ°€λ¦¬μ΄ˆ ν˜•μ‹μœΌλ‘œ κ°„κ²°ν•˜κ²Œ) + function formatTimestamp(ms) { + const totalSeconds = ms / 1000; + + // 1초 미만: 0.xxx s + if (ms < 1000) { + return `${totalSeconds.toFixed(3)} s`; + } + // 1초 이상: xxx.xxx s (μ†Œμˆ˜μ  3자리) + else { + return `${totalSeconds.toFixed(3)} s`; + } } setInterval(updateCurrentTime, 1000); updateCurrentTime(); @@ -1217,7 +1274,7 @@ const char index_html[] PROGMEM = R"rawliteral( ws.onopen = function() { console.log('WebSocket connected'); - document.getElementById('sync-status').textContent = '연결됨'; + document.getElementById('sync-status').textContent = 'Connected'; document.getElementById('sync-status').style.color = '#38ef7d'; // ⭐⭐⭐ μžλ™ μ‹œκ°„ 동기화 (νŽ˜μ΄μ§€ λ‘œλ“œ μ‹œ 항상 μ‹€ν–‰) @@ -1238,7 +1295,7 @@ const char index_html[] PROGMEM = R"rawliteral( ws.onclose = function() { console.log('WebSocket disconnected'); - document.getElementById('sync-status').textContent = 'μ—°κ²° λŠκΉ€'; + document.getElementById('sync-status').textContent = 'Disconnected'; document.getElementById('sync-status').style.color = '#f45c43'; mcpModeSynced = false; // πŸ†• μž¬μ—°κ²° μ‹œ λ‹€μ‹œ λ™κΈ°ν™”ν•˜λ„λ‘ ν”Œλž˜κ·Έ 리셋 window.canFormatSynced = false; // πŸ†• File Format도 μž¬λ™κΈ°ν™” @@ -1422,6 +1479,23 @@ const char index_html[] PROGMEM = R"rawliteral( document.getElementById('msg-speed').textContent = data.msgPerSec + '/s'; } + // πŸ†• λ²„μŠ€ λΆ€ν•˜μœ¨ μ—…λ°μ΄νŠΈ + if (data.busLoad !== undefined) { + const busLoadCard = document.getElementById('bus-load-card'); + const busLoadValue = document.getElementById('bus-load'); + busLoadValue.textContent = data.busLoad + '%'; + + // 색상 λ³€κ²½ (λΆ€ν•˜μœ¨μ— 따라) + busLoadCard.classList.remove('bus-load-low', 'bus-load-medium', 'bus-load-high'); + if (data.busLoad < 50) { + busLoadCard.classList.add('bus-load-low'); + } else if (data.busLoad < 80) { + busLoadCard.classList.add('bus-load-medium'); + } else { + busLoadCard.classList.add('bus-load-high'); + } + } + // μ‹œκ°„ 동기화 - β˜… μˆ˜μ •: timeSync λ˜λŠ” timeSynced λ‘˜ λ‹€ 지원 const timeSyncCard = document.getElementById('time-sync-card'); const timeSynced = data.timeSynced !== undefined ? data.timeSynced : data.timeSync; @@ -1612,7 +1686,7 @@ const char index_html[] PROGMEM = R"rawliteral( 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.cells[4].textContent = formatTimestamp(msg.timestamp); if (hasChanged) { row.classList.add('flash-row'); @@ -1626,7 +1700,7 @@ const char index_html[] PROGMEM = R"rawliteral( '' + msg.dlc + '' + '' + msg.data + '' + '' + msg.updateCount + '' + - '' + msg.timestamp + ''; + '' + formatTimestamp(msg.timestamp) + ''; row.classList.add('flash-row'); setTimeout(() => row.classList.remove('flash-row'), 300); }