saltylab-firmware/ui/vesc_panel.html
sl-webui f71dad5344 feat(arch): migrate all STM32/Mamba/BlackPill refs to ESP32 BALANCE/IO + fix roslib@1.4.0
Architecture change (2026-04-03): Mamba F722S (STM32F722) and BlackPill
replaced by ESP32 BALANCE (PID loop) and ESP32 IO (motors/sensors/comms).

- Update CLAUDE.md, docs, chassis BOM/ASSEMBLY, pinout, power-budget,
  wiring-diagram, TEAM.md, AUTONOMOUS_ARMING.md, docker-compose
- Update all ROS2 package comments, config labels, launch args
  (stm32_port→esp32_port, /dev/stm32-bridge→/dev/esp32-bridge)
- Update WebUI: stm32Mode→esp32Mode, stm32Version→esp32Version,
  "STM32 State/Mode" labels → "ESP32 State/Mode" (ControlMode, SettingsPanel)
- Add TODO(esp32-migration) markers on stm32_protocol.py and mamba_protocol.py
  binary frame layouts — pending ESP32 protocol spec from max
- Fix roslib CDN 1.3.0→1.4.0 in all 11 HTML panels (fixes ROS2 Humble
  rosbridge "Received a message without an op" incompatibility)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-04 08:25:24 -04:00

230 lines
8.4 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0">
<title>Saltybot — VESC Motor Dashboard</title>
<link rel="stylesheet" href="vesc_panel.css">
<script src="https://cdn.jsdelivr.net/npm/roslib@1.4.0/build/roslib.min.js"></script>
</head>
<body>
<!-- ── Header ── -->
<div id="header">
<div class="logo">⚡ SALTYBOT — VESC MOTORS</div>
<div id="conn-bar">
<div id="conn-dot"></div>
<input id="ws-input" type="text" value="ws://localhost:9090" placeholder="ws://robot-ip:9090" />
<button id="btn-connect" class="hdr-btn">CONNECT</button>
<span id="conn-label" style="color:#4b5563;font-size:10px">Not connected</span>
</div>
<div id="header-right">
<span id="hz-label" class="meta-label">— Hz</span>
<span style="color:#374151">|</span>
<span id="stamp-label" class="meta-label">No data</span>
</div>
</div>
<!-- ── Status bar ── -->
<div id="status-bar">
<span style="color:#6b7280;font-size:10px">LEFT</span>
<span class="sys-badge badge-stale" id="badge-left">OFFLINE</span>
<span style="color:#4b5563"></span>
<span style="color:#6b7280;font-size:10px">RIGHT</span>
<span class="sys-badge badge-stale" id="badge-right">OFFLINE</span>
<span style="color:#4b5563"></span>
<span style="color:#6b7280;font-size:10px">BATTERY</span>
<span class="sys-badge badge-stale" id="badge-batt"></span>
<span style="color:#4b5563"></span>
<span style="color:#6b7280;font-size:10px">TOTAL DRAW</span>
<span class="sys-badge badge-stale" id="badge-total"></span>
<button id="btn-estop" class="estop-btn">⛔ E-STOP</button>
</div>
<!-- ── Dashboard ── -->
<div id="dashboard">
<!-- ╔═════════ LEFT MOTOR ═════════╗ -->
<div class="card motor-card" id="card-left">
<div class="card-title">
LEFT MOTOR
<span class="fault-badge" id="fault-left">OK</span>
</div>
<!-- Arc gauge + direction -->
<div class="gauge-row-top">
<div class="arc-wrap">
<canvas id="rpm-arc-left" width="140" height="100"></canvas>
<div class="arc-dir" id="dir-left"></div>
</div>
<div class="motor-stats">
<div class="stat-row">
<span class="stat-label">CURRENT (MTR)</span>
<span class="stat-val" id="cur-left"></span>
</div>
<div class="bar-track"><div class="bar-fill" id="cur-bar-left"></div></div>
<div class="stat-row" style="margin-top:6px">
<span class="stat-label">CURRENT (IN)</span>
<span class="stat-val" id="curin-left"></span>
</div>
<div class="bar-track"><div class="bar-fill" id="curin-bar-left"></div></div>
<div class="stat-row" style="margin-top:6px">
<span class="stat-label">DUTY CYCLE</span>
<span class="stat-val" id="duty-left"></span>
</div>
<div class="bar-track"><div class="bar-fill" id="duty-bar-left"></div></div>
</div>
</div>
<!-- Temperatures -->
<div class="temp-row">
<div class="temp-box" id="tbox-fet-left">
<div class="temp-label">FET TEMP</div>
<div class="temp-val" id="tfet-left"></div>
<div class="bar-track mini"><div class="bar-fill" id="tfet-bar-left"></div></div>
</div>
<div class="temp-box" id="tbox-mot-left">
<div class="temp-label">MOTOR TEMP</div>
<div class="temp-val" id="tmot-left"></div>
<div class="bar-track mini"><div class="bar-fill" id="tmot-bar-left"></div></div>
</div>
</div>
<!-- Sparklines -->
<div class="spark-section">
<div class="spark-label">RPM · 60s</div>
<canvas class="sparkline" id="spark-rpm-left" height="40"></canvas>
<div class="spark-label" style="margin-top:4px">CURRENT · 60s</div>
<canvas class="sparkline" id="spark-cur-left" height="40"></canvas>
</div>
</div>
<!-- ╔═════════ RIGHT MOTOR ═════════╗ -->
<div class="card motor-card" id="card-right">
<div class="card-title">
RIGHT MOTOR
<span class="fault-badge" id="fault-right">OK</span>
</div>
<div class="gauge-row-top">
<div class="arc-wrap">
<canvas id="rpm-arc-right" width="140" height="100"></canvas>
<div class="arc-dir" id="dir-right"></div>
</div>
<div class="motor-stats">
<div class="stat-row">
<span class="stat-label">CURRENT (MTR)</span>
<span class="stat-val" id="cur-right"></span>
</div>
<div class="bar-track"><div class="bar-fill" id="cur-bar-right"></div></div>
<div class="stat-row" style="margin-top:6px">
<span class="stat-label">CURRENT (IN)</span>
<span class="stat-val" id="curin-right"></span>
</div>
<div class="bar-track"><div class="bar-fill" id="curin-bar-right"></div></div>
<div class="stat-row" style="margin-top:6px">
<span class="stat-label">DUTY CYCLE</span>
<span class="stat-val" id="duty-right"></span>
</div>
<div class="bar-track"><div class="bar-fill" id="duty-bar-right"></div></div>
</div>
</div>
<div class="temp-row">
<div class="temp-box" id="tbox-fet-right">
<div class="temp-label">FET TEMP</div>
<div class="temp-val" id="tfet-right"></div>
<div class="bar-track mini"><div class="bar-fill" id="tfet-bar-right"></div></div>
</div>
<div class="temp-box" id="tbox-mot-right">
<div class="temp-label">MOTOR TEMP</div>
<div class="temp-val" id="tmot-right"></div>
<div class="bar-track mini"><div class="bar-fill" id="tmot-bar-right"></div></div>
</div>
</div>
<div class="spark-section">
<div class="spark-label">RPM · 60s</div>
<canvas class="sparkline" id="spark-rpm-right" height="40"></canvas>
<div class="spark-label" style="margin-top:4px">CURRENT · 60s</div>
<canvas class="sparkline" id="spark-cur-right" height="40"></canvas>
</div>
</div>
<!-- ╔═════════ BATTERY / COMBINED ═════════╗ -->
<div class="card" id="card-batt">
<div class="card-title">
BATTERY — 4S LiPo (12.016.8 V)
<span class="meta-label" id="batt-stamp"></span>
</div>
<div class="batt-row">
<!-- Big voltage -->
<div class="batt-metric">
<div class="batt-metric-label">VOLTAGE</div>
<div class="big-num" id="batt-voltage"></div>
<div class="batt-unit">V</div>
</div>
<div class="batt-metric">
<div class="batt-metric-label">TOTAL DRAW</div>
<div class="big-num" id="batt-total-cur" style="color:#06b6d4"></div>
<div class="batt-unit">A</div>
</div>
<div class="batt-metric">
<div class="batt-metric-label">LEFT RPM</div>
<div class="big-num" id="batt-rpm-l" style="color:#a855f7;font-size:20px"></div>
</div>
<div class="batt-metric">
<div class="batt-metric-label">RIGHT RPM</div>
<div class="big-num" id="batt-rpm-r" style="color:#a855f7;font-size:20px"></div>
</div>
</div>
<!-- Voltage bar -->
<div>
<div class="stat-row">
<span class="stat-label">Voltage (12.016.8 V)</span>
<span class="stat-val" id="batt-volt-pct"></span>
</div>
<div class="bar-track"><div class="bar-fill" id="batt-volt-bar"></div></div>
</div>
<!-- Total current bar -->
<div>
<div class="stat-row">
<span class="stat-label">Total Current (0120 A)</span>
<span class="stat-val" id="batt-cur-pct"></span>
</div>
<div class="bar-track"><div class="bar-fill" id="batt-cur-bar"></div></div>
</div>
<div style="font-size:9px;color:#374151">
Voltage zones: &lt;13.2V warn · &lt;12.4V critical · FET &gt;70°C warn · Motor &gt;85°C warn
</div>
</div>
</div>
<!-- ── Footer ── -->
<div id="footer">
<span>rosbridge: <code id="footer-ws">ws://localhost:9090</code></span>
<span>topics: /vesc/left/state · /vesc/right/state · /vesc/combined (std_msgs/String JSON)</span>
<span>vesc motor dashboard — issue #653</span>
</div>
<script src="vesc_panel.js"></script>
<script>
document.getElementById('ws-input').addEventListener('input', (e) => {
document.getElementById('footer-ws').textContent = e.target.value;
});
</script>
</body>
</html>