Adds standalone vanilla JS/HTML/CSS panel for live CAN sensor monitoring:
- can_monitor_panel.html: 5-card dashboard grid with VESC L/R, Balance,
IMU Attitude (span-2), and Barometer cards
- can_monitor_panel.css: dark-theme styles matching existing panel suite;
bidirectional bars, live-dot flash, stat-grid, responsive layout
- can_monitor_panel.js: rosbridge subscriptions to
/vesc/left/state + /vesc/right/state (RPM bidir bar, current gauge,
voltage/duty/temp stats, fault badge, stale detection)
/saltybot/imu (quaternion→Euler, angular vel, lin accel, cal badge
from orientation_covariance[0], canvas artificial horizon + compass)
/saltybot/balance_state (state badge, motor_cmd bidir bar, PID grid)
/saltybot/barometer (altitude, pressure, temp)
Auto-connect from localStorage, 1 Hz stale checker, msg rate display
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
304 lines
11 KiB
HTML
304 lines
11 KiB
HTML
<!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 — CAN Monitor</title>
|
|
<link rel="stylesheet" href="can_monitor_panel.css">
|
|
<script src="https://cdn.jsdelivr.net/npm/roslib@1.3.0/build/roslib.min.js"></script>
|
|
</head>
|
|
<body>
|
|
|
|
<!-- ── Header ── -->
|
|
<div id="header">
|
|
<div class="logo">⚡ <a href="index.html">SALTYBOT</a> — CAN MONITOR</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="hz-display">— msg/s</div>
|
|
</div>
|
|
|
|
<!-- ── System status bar ── -->
|
|
<div id="status-bar">
|
|
<span style="color:#6b7280;font-size:10px">VESC L</span>
|
|
<span class="sys-badge badge-stale" id="badge-vesc-l">STALE</span>
|
|
<span style="color:#4b5563">│</span>
|
|
<span style="color:#6b7280;font-size:10px">VESC R</span>
|
|
<span class="sys-badge badge-stale" id="badge-vesc-r">STALE</span>
|
|
<span style="color:#4b5563">│</span>
|
|
<span style="color:#6b7280;font-size:10px">IMU</span>
|
|
<span class="sys-badge badge-stale" id="badge-imu">STALE</span>
|
|
<span style="color:#4b5563">│</span>
|
|
<span style="color:#6b7280;font-size:10px">BALANCE</span>
|
|
<span class="sys-badge badge-stale" id="badge-balance">STALE</span>
|
|
<span style="color:#4b5563">│</span>
|
|
<span style="color:#6b7280;font-size:10px">BARO</span>
|
|
<span class="sys-badge badge-stale" id="badge-baro">STALE</span>
|
|
<span id="last-update">Awaiting data…</span>
|
|
</div>
|
|
|
|
<!-- ── Dashboard grid ── -->
|
|
<div id="dashboard">
|
|
|
|
<!-- ╔═══════════════════ VESC LEFT ═══════════════════╗ -->
|
|
<div class="card" id="card-vesc-l">
|
|
<div class="card-title">
|
|
VESC LEFT — /vesc/left/state
|
|
<span class="badge badge-stale" id="vesc-l-fault-badge">STALE</span>
|
|
</div>
|
|
|
|
<div class="big-row">
|
|
<div class="big-metric">
|
|
<div class="big-label">RPM</div>
|
|
<div class="big-num" id="vesc-l-rpm" style="color:#f97316">—</div>
|
|
</div>
|
|
<div class="big-metric">
|
|
<div class="big-label">VOLTAGE</div>
|
|
<div><span class="big-num" id="vesc-l-voltage" style="color:#22c55e">—</span><span class="big-unit"> V</span></div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="bidir-row">
|
|
<div class="bidir-label-row">
|
|
<span class="gauge-label">RPM</span>
|
|
<span id="vesc-l-rpm-val">—</span>
|
|
</div>
|
|
<div class="bidir-track"><div class="bidir-fill" id="vesc-l-rpm-bar"></div></div>
|
|
</div>
|
|
|
|
<div class="gauge-row">
|
|
<div class="gauge-label-row">
|
|
<span class="gauge-label">CURRENT</span>
|
|
<span id="vesc-l-current-val">—</span>
|
|
</div>
|
|
<div class="gauge-track"><div class="gauge-fill" id="vesc-l-current-bar"></div></div>
|
|
</div>
|
|
|
|
<div class="stat-grid">
|
|
<div class="stat-cell">
|
|
<div class="stat-lbl">IN CURR</div>
|
|
<div class="stat-val" id="vesc-l-current-in">—</div>
|
|
</div>
|
|
<div class="stat-cell">
|
|
<div class="stat-lbl">DUTY</div>
|
|
<div class="stat-val" id="vesc-l-duty">—</div>
|
|
</div>
|
|
<div class="stat-cell">
|
|
<div class="stat-lbl">FET °C</div>
|
|
<div class="stat-val" id="vesc-l-temp-fet">—</div>
|
|
</div>
|
|
<div class="stat-cell">
|
|
<div class="stat-lbl">MTR °C</div>
|
|
<div class="stat-val" id="vesc-l-temp-motor">—</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="ts-row">
|
|
<div class="live-dot" id="dot-vesc-l"></div>
|
|
<span id="ts-vesc-l">—</span>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- ╔═══════════════════ VESC RIGHT ══════════════════╗ -->
|
|
<div class="card" id="card-vesc-r">
|
|
<div class="card-title">
|
|
VESC RIGHT — /vesc/right/state
|
|
<span class="badge badge-stale" id="vesc-r-fault-badge">STALE</span>
|
|
</div>
|
|
|
|
<div class="big-row">
|
|
<div class="big-metric">
|
|
<div class="big-label">RPM</div>
|
|
<div class="big-num" id="vesc-r-rpm" style="color:#f97316">—</div>
|
|
</div>
|
|
<div class="big-metric">
|
|
<div class="big-label">VOLTAGE</div>
|
|
<div><span class="big-num" id="vesc-r-voltage" style="color:#22c55e">—</span><span class="big-unit"> V</span></div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="bidir-row">
|
|
<div class="bidir-label-row">
|
|
<span class="gauge-label">RPM</span>
|
|
<span id="vesc-r-rpm-val">—</span>
|
|
</div>
|
|
<div class="bidir-track"><div class="bidir-fill" id="vesc-r-rpm-bar"></div></div>
|
|
</div>
|
|
|
|
<div class="gauge-row">
|
|
<div class="gauge-label-row">
|
|
<span class="gauge-label">CURRENT</span>
|
|
<span id="vesc-r-current-val">—</span>
|
|
</div>
|
|
<div class="gauge-track"><div class="gauge-fill" id="vesc-r-current-bar"></div></div>
|
|
</div>
|
|
|
|
<div class="stat-grid">
|
|
<div class="stat-cell">
|
|
<div class="stat-lbl">IN CURR</div>
|
|
<div class="stat-val" id="vesc-r-current-in">—</div>
|
|
</div>
|
|
<div class="stat-cell">
|
|
<div class="stat-lbl">DUTY</div>
|
|
<div class="stat-val" id="vesc-r-duty">—</div>
|
|
</div>
|
|
<div class="stat-cell">
|
|
<div class="stat-lbl">FET °C</div>
|
|
<div class="stat-val" id="vesc-r-temp-fet">—</div>
|
|
</div>
|
|
<div class="stat-cell">
|
|
<div class="stat-lbl">MTR °C</div>
|
|
<div class="stat-val" id="vesc-r-temp-motor">—</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="ts-row">
|
|
<div class="live-dot" id="dot-vesc-r"></div>
|
|
<span id="ts-vesc-r">—</span>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- ╔═══════════════════ BALANCE STATE ═══════════════╗ -->
|
|
<div class="card">
|
|
<div class="card-title">
|
|
BALANCE STATE — /saltybot/balance_state
|
|
<span class="badge badge-stale" id="balance-state-badge">—</span>
|
|
</div>
|
|
|
|
<div class="bidir-row">
|
|
<div class="bidir-label-row">
|
|
<span class="gauge-label">MOTOR CMD</span>
|
|
<span id="balance-cmd-val">—</span>
|
|
</div>
|
|
<div class="bidir-track"><div class="bidir-fill" id="balance-cmd-bar"></div></div>
|
|
</div>
|
|
|
|
<div class="stat-grid">
|
|
<div class="stat-cell">
|
|
<div class="stat-lbl">PID ERR</div>
|
|
<div class="stat-val" id="balance-pid-error" style="color:#f87171">—</div>
|
|
</div>
|
|
<div class="stat-cell">
|
|
<div class="stat-lbl">INTEGRAL</div>
|
|
<div class="stat-val" id="balance-integral">—</div>
|
|
</div>
|
|
<div class="stat-cell">
|
|
<div class="stat-lbl">PITCH</div>
|
|
<div class="stat-val" id="balance-pitch">—</div>
|
|
</div>
|
|
<div class="stat-cell">
|
|
<div class="stat-lbl">FRAMES</div>
|
|
<div class="stat-val" id="balance-frames">—</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="ts-row">
|
|
<div class="live-dot" id="dot-balance"></div>
|
|
<span id="ts-balance">—</span>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- ╔═══════════════════ IMU ATTITUDE ════════════════╗ -->
|
|
<div class="card span2">
|
|
<div class="card-title">
|
|
IMU ATTITUDE — /saltybot/imu
|
|
<span class="badge badge-stale" id="imu-cal-badge">CAL: ?</span>
|
|
</div>
|
|
|
|
<div style="display:flex;gap:12px;flex-wrap:wrap">
|
|
<div style="flex:1;min-width:200px;display:flex;flex-direction:column;gap:6px">
|
|
<div class="stat-grid-3">
|
|
<div class="stat-cell">
|
|
<div class="stat-lbl">PITCH °</div>
|
|
<div class="stat-val" id="imu-pitch" style="color:#06b6d4">—</div>
|
|
</div>
|
|
<div class="stat-cell">
|
|
<div class="stat-lbl">ROLL °</div>
|
|
<div class="stat-val" id="imu-roll" style="color:#06b6d4">—</div>
|
|
</div>
|
|
<div class="stat-cell">
|
|
<div class="stat-lbl">YAW °</div>
|
|
<div class="stat-val" id="imu-yaw">—</div>
|
|
</div>
|
|
</div>
|
|
<div class="stat-grid-3">
|
|
<div class="stat-cell">
|
|
<div class="stat-lbl">ωP °/s</div>
|
|
<div class="stat-val" id="imu-wp">—</div>
|
|
</div>
|
|
<div class="stat-cell">
|
|
<div class="stat-lbl">ωR °/s</div>
|
|
<div class="stat-val" id="imu-wr">—</div>
|
|
</div>
|
|
<div class="stat-cell">
|
|
<div class="stat-lbl">ωY °/s</div>
|
|
<div class="stat-val" id="imu-wy">—</div>
|
|
</div>
|
|
</div>
|
|
<div class="stat-grid-3">
|
|
<div class="stat-cell">
|
|
<div class="stat-lbl">aX m/s²</div>
|
|
<div class="stat-val" id="imu-ax">—</div>
|
|
</div>
|
|
<div class="stat-cell">
|
|
<div class="stat-lbl">aY m/s²</div>
|
|
<div class="stat-val" id="imu-ay">—</div>
|
|
</div>
|
|
<div class="stat-cell">
|
|
<div class="stat-lbl">aZ m/s²</div>
|
|
<div class="stat-val" id="imu-az">—</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div style="display:flex;flex-direction:column;gap:6px">
|
|
<canvas id="horizon-canvas" width="320" height="120"></canvas>
|
|
<canvas id="compass-canvas" width="320" height="44"></canvas>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="ts-row">
|
|
<div class="live-dot" id="dot-imu"></div>
|
|
<span id="ts-imu">—</span>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- ╔═══════════════════ BAROMETER ═══════════════════╗ -->
|
|
<div class="card">
|
|
<div class="card-title">BAROMETER — /saltybot/barometer</div>
|
|
|
|
<div class="big-row">
|
|
<div class="big-metric">
|
|
<div class="big-label">ALTITUDE</div>
|
|
<div><span class="big-num" id="baro-alt" style="color:#a78bfa">—</span><span class="big-unit"> m</span></div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="stat-grid" style="grid-template-columns:repeat(2,1fr)">
|
|
<div class="stat-cell">
|
|
<div class="stat-lbl">PRESSURE Pa</div>
|
|
<div class="stat-val" id="baro-pressure">—</div>
|
|
</div>
|
|
<div class="stat-cell">
|
|
<div class="stat-lbl">TEMP °C</div>
|
|
<div class="stat-val" id="baro-temp">—</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="ts-row">
|
|
<div class="live-dot" id="dot-baro"></div>
|
|
<span id="ts-baro">—</span>
|
|
</div>
|
|
</div>
|
|
|
|
</div><!-- /#dashboard -->
|
|
|
|
<script src="can_monitor_panel.js"></script>
|
|
</body>
|
|
</html>
|