55 Commits

Author SHA1 Message Date
5dac6337e6 feat: WebUI map view (Issue #587)
Standalone 3-file 2D map panel (ui/map_panel.{html,js,css}).
No build step. Open directly or serve ui/ directory.

Canvas layers (drawn every animation frame):
  - Grid lines (1m spacing) + world-origin axis cross + 1m scale bar
  - RPLIDAR scan dots (/scan, green, cbor-compressed, 100ms throttle)
  - Safety zone rings: danger 0.30m (red dashed) + warn 1.00m (amber dashed)
  - 100-position breadcrumb trail with fading cyan polyline + dots every 5 pts
  - UWB anchor markers (amber diamond + label, user-configured)
  - Robot marker: circle + forward arrow, red when e-stopped

Interactions:
  - Mouse wheel zoom (zooms around cursor)
  - Click+drag pan
  - Pinch-to-zoom (touch, two-finger)
  - Auto-center toggle (robot stays centered when on)
  - Zoom +/- buttons, Reset view button
  - Clear trail button
  - Mouse hover shows world coords (m) in bottom-left HUD

ROS topics:
  SUB /saltybot/pose/fused        geometry_msgs/PoseStamped   50ms throttle
  SUB /scan                       sensor_msgs/LaserScan       100ms + cbor
  SUB /saltybot/safety_zone/status std_msgs/String (JSON)     200ms throttle

Sidebar:
  - Robot position (x, y m) + heading (°)
  - Safety zone: forward zone (CLEAR/WARN/DANGER), closest obstacle (m), e-stop
  - UWB anchor manager: add/remove anchors with x/y/label, persisted localStorage
  - Topic reference

E-stop banner: pulsing red overlay when /saltybot/safety_zone/status estop_active=true

Mobile-responsive: sidebar hidden on <700px, canvas fills viewport.
WS URL persisted in localStorage.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-14 12:20:04 -04:00
44691742c8 feat: WebUI event log panel (Issue #576)
Standalone 3-file filterable real-time event log (no build step).

Files:
  ui/event_log_panel.html  — layout, toolbar, empty state
  ui/event_log_panel.js    — rosbridge subscriptions, ring buffer, render
  ui/event_log_panel.css   — dark-theme, responsive grid layout

Features:
- 1000-entry ring buffer (oldest dropped when full, FIFO)
- Subscribes /rosout (rcl_interfaces/msg/Log) + /saltybot/events (std_msgs/String JSON)
- Severity filter buttons: DEBUG / INFO / WARN / ERROR / FATAL / EVENT (toggle on/off)
- Node name filter: select dropdown populated from seen nodes
- Live text search with <mark> highlight, Ctrl+F shortcut, Esc to clear
- Auto-scroll to latest entry; pauses on mouse hover (messages still buffered)
- Manual pause/resume button; detects user scroll-up and stops auto-scroll
- CSV export of current filtered view with timestamp (filename includes ISO date)
- Clear all entries button
- Color-coded by severity: left border stripe + text color per level
- Entry columns: timestamp (ms precision) | severity | node | message
- [system] entries for connect/disconnect events
- WS URL persisted in localStorage
- Responsive: node column hidden on narrow screens

ROS topics:
  SUB /rosout               rcl_interfaces/msg/Log  (all nodes)
  SUB /saltybot/events      std_msgs/String (JSON: {level,node,msg})

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-14 11:54:13 -04:00
c2d9adad25 feat: WebUI diagnostics dashboard (Issue #562)
Standalone 3-file diagnostics dashboard (ui/diagnostics_panel.{html,js,css}).
No build step — serve the ui/ directory directly. roslib.js via CDN.

Panels:
- Battery: voltage (V), SOC (%), current (A) with large readouts + gauge bars
  + 2-minute sparkline history canvas, 4S LiPo thresholds
- Temperatures: CPU/GPU (Jetson tegrastats) + Board/STM32 + Motor L/R
  color-coded temp boxes with mini progress bars (green<60 amber<75 red>75°C)
- Motor current: per-wheel current gauge bars + CMD value + balance_state label
  Thresholds: warn 8A / crit 12A
- Resources: RAM / GPU memory / Disk — gauge bars with used/total display
  Thresholds: warn 80% / crit 95%
- WiFi / Network: RSSI signal bars (5-level) + dBm readout + latency (ms)
  MQTT broker status via mqtt_connected KeyValue
- ROS2 node health: full DiagnosticArray node list with OK/WARN/ERROR/STALE
  badges, per-node message preview, MutationObserver count badge

Features:
- Auto 2 Hz refresh via rosbridge subscriptions (throttle_rate: 500ms)
- Pulsing refresh indicator dot on each update
- System status bar: HEALTHY/DEGRADED/FAULT/STALE badge + battery/thermal/net
- Alert thresholds: red/amber/green for every metric
- Responsive CSS grid: 3-col → 2-col → 1-col via media queries
- WS URL persisted in localStorage

ROS topics:
  SUB /diagnostics              diagnostic_msgs/DiagnosticArray
  SUB /saltybot/balance_state   std_msgs/String (JSON)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-14 11:41:43 -04:00
cc3a65f4a4 feat: WebUI gimbal control panel (Issue #551)
Adds a full gimbal control panel with live camera preview:

Standalone page (ui/gimbal_panel.html + .js + .css):
- Self-contained HTML page, no build step, served directly
- roslib.js via CDN, connects to rosbridge WebSocket
- 2-D canvas pan/tilt pad: click-drag + touch pointer capture
- Live camera stream (front/rear/left/right selector, base64 CompressedImage)
- FPS badge + angle overlay on video feed
- Preset positions: CENTER / LEFT / RIGHT / UP / DOWN
- Home button (0° / 0°)
- Person-tracking toggle → /gimbal/tracking_enabled
- Current angle display from /gimbal/state feedback
- WS URL persisted in localStorage

React component (GimbalPanel.jsx) + App.jsx integration:
- Same features in dashboard — TELEOP group → Gimbal tab
- Shares rosbridge connection from parent
- Mobile-responsive: stacks vertically on mobile, side-by-side on lg+

ROS topics:
  PUB /gimbal/cmd              geometry_msgs/Vector3
  SUB /gimbal/state            geometry_msgs/Vector3
  PUB /gimbal/tracking_enabled std_msgs/Bool
  SUB /camera/*/image_raw/compressed sensor_msgs/CompressedImage

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-14 10:29:29 -04:00
916ad36ad5 feat(webui): teleop web interface with live camera stream (Issue #534)
Adds TeleopWebUI component — a dedicated browser-based remote control
panel combining live video and joystick teleoperation in one view:

- Live camera stream (front/rear/left/right) via rosbridge CompressedImage
- Virtual joystick (canvas-based, touch + mouse, 10% deadzone)
- WASD / arrow-key keyboard fallback, Space for quick stop
- Speed presets: SLOW (20%), NORMAL (50%), FAST (100%)
- Latching E-stop button with pulsing visual indicator
- Real-time linear/angular velocity display
- Mobile-responsive: stacks vertically on small screens, side-by-side on lg+
- Added TELEOP tab group → Drive tab in App.jsx

Topics: /camera/<name>/image_raw/compressed (subscribe)
        /cmd_vel geometry_msgs/Twist (publish)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-07 09:51:14 -05:00
0c03060016 feat: centralized parameter server + dynamic reconfiguration (Issue #471)
ROS2 node and WebUI component for centralized dynamic parameter configuration.
- Parameter server node with validation, range checks, and persistence
- Organized into groups: hardware/perception/controls/social/safety/debug
- Service-based API for dynamic reconfiguration (/saltybot/set_param)
- Named presets: indoor/outdoor/demo/debug
- WebUI component with parameter browsing, editing, and preset loading
- Safety parameter confirmation dialogs
- Real-time parameter metadata display (type, range, description)

Files:
- jetson/ros2_ws/src/saltybot_param_server/ - ROS2 parameter server node
- ui/social-bot/src/components/ParameterServer.jsx - WebUI component
- App.jsx - Integrated into CONFIG tab group as 'Parameters'

Build status:  PASSING (127 modules)

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-03-05 12:10:04 -05:00
eff69b2037 feat(hud): add sensor feeds (GPS, LIDAR, RealSense) (Issue #413)
Add tabbed sensor feed interface to the HUD center viewport:

GPS Map Panel:
- Fetches location data from /gps HTTP endpoint on port 8888
- Renders OpenStreetMap with real-time location marker
- Displays: coordinates, altitude, accuracy

LIDAR Point Cloud Visualization:
- Subscribes to /scan topic via rosbridge WebSocket
- 2D polar plot with grid, cardinal directions, forward indicator
- Real-time point cloud rendering with range statistics
- Displays: point count, max range (0-30m)

RealSense Dual Stream:
- Subscribes to /camera/color/image_raw/compressed (RGB)
- Subscribes to /camera/depth/image_rect_raw/compressed (Depth)
- Side-by-side canvas rendering with independent scaling
- FPS counter and resolution display

Tab System:
- 4-way view switching: 3D Model ↔ GPS ↔ LIDAR ↔ RealSense
- Persistent tab state, lazy initialization on demand
- Dark theme with cyan/orange accent colors
- Status indicators for each sensor (loading/error/ready)

Architecture:
- Browser native canvas for LIDAR visualization
- WebSocket rosbridge integration for sensor subscriptions
- Fetch API for HTTP GPS data (localhost:8888)
- Leaflet.js for OSM map rendering (CDN)
- 2s polling interval for GPS updates

Rosbridge Endpoints (assumes localhost:9090):
- /scan (sensor_msgs/LaserScan) — 1Hz LIDAR
- /camera/color/image_raw/compressed — RGB stream
- /camera/depth/image_rect_raw/compressed — Depth stream

HTTP Endpoints (assumes localhost:8888):
- GET /gps → { lat, lon, alt, accuracy, timestamp }

Integration:
- Preserves existing 3D HUD viewport and controls
- Left/right sidebars remain unchanged
- Bottom PID control bar operational
- Tab switching preserves center panel size/aspect ratio

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-03-04 22:57:10 -05:00
c86abdd1b8 feat: Add VESC balance PID controller with tilt safety (Issue #407)
- ROS2 node for balance mode PID parameter management via pyvesc UART
- Tilt safety kill switch: ±45° pitch > 500ms triggers motor cutoff
- Startup ramp: gradual acceleration from 0 to full output over configurable duration
- IMU integration: subscribe to /imu/data for pitch/roll angle computation
- State publishing: /saltybot/balance_state with tilt angles, PID values, motor telemetry
- Data logging: /saltybot/balance_log publishes CSV-formatted IMU + motor data
- Configurable parameters: PID gains, tilt thresholds, ramp duration, control frequency
- Test suite: quaternion to Euler conversion, tilt safety checks, startup ramp

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-03-04 22:40:40 -05:00
9fc3e9894e feat: deaf/accessibility communication UI (Issue #371) 2026-03-04 12:19:28 -05:00
8aa4072a63 feat(webui): Salty Face animated expression UI — contextual emotions (Issue #370)
Add animated facial expression interface for MageDok 7" display:

Core Features:
✓ 8 emotional states:
  - Happy (default idle)
  - Alert (obstacles detected)
  - Confused (searching, target lost)
  - Sleeping (prolonged inactivity)
  - Excited (target reacquired)
  - Emergency (e-stop triggered)
  - Listening (microphone active)
  - Talking (TTS output)

Visual Design:
✓ Minimalist Cozmo/Vector-inspired eyes + optional mouth
✓ Canvas-based GPU-accelerated rendering
✓ 30fps target on Jetson Orin Nano
✓ Emotion-specific eye characteristics:
  - Scale changes (alert widened eyes)
  - Color coding per emotion
  - Pupil position tracking
  - Blinking rates vary by state
  - Eye wandering (confused searching)
  - Bouncing animation (excited)
  - Flash effect (emergency)

Mouth Animation:
✓ Synchronized with text-to-speech output
✓ Shape frames: closed, smile, oh, ah, ee sounds
✓ ~10fps lip sync animation

ROS2 Integration:
✓ Subscribe to /saltybot/state (emotion triggers)
✓ Subscribe to /saltybot/target_track (tracking state)
✓ Subscribe to /saltybot/obstacles (alert state)
✓ Subscribe to /social/speech/is_speaking (talking mode)
✓ Subscribe to /social/speech/is_listening (listening mode)
✓ Subscribe to /saltybot/battery (status tracking)
✓ Subscribe to /saltybot/audio_level (audio feedback)

HUD Overlay:
✓ Tap-to-toggle status display
✓ Battery percentage indicator
✓ Robot state label
✓ Distance to target (meters)
✓ Movement speed (m/s)
✓ System health percentage
✓ Color-coded health indicator (green/yellow/red)

Integration:
✓ New DISPLAY tab group (rose color)
✓ Full-screen rendering on 1024×600 MageDok display
✓ Responsive to robot state machine
✓ Supports kiosk mode deployment

Build Status:  PASSING
- 126 modules (+1 for SaltyFace)
- 281.57 KB main bundle (+11 KB)
- 0 errors

Depends on: Issue #369 (MageDok display setup)
Foundation for: Issue #371 (Accessibility mode)

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-03-03 18:14:49 -05:00
e2587b60fb feat: SaltyFace web app UI for Chromium kiosk (Issue #370)
Animated robot expression interface as lightweight web application:

**Architecture:**
- HTML5 Canvas rendering engine
- Node.js HTTP server (localhost:3000)
- ROSLIB WebSocket bridge for ROS2 topics
- Fullscreen responsive design (1024×600)

**Features:**
- 8 emotional states (happy, alert, confused, sleeping, excited, emergency, listening, talking)
- Real-time ROS2 subscriptions:
  - /saltybot/state (emotion triggers)
  - /saltybot/battery (status display)
  - /saltybot/target_track (EXCITED emotion)
  - /saltybot/obstacles (ALERT emotion)
  - /social/speech/is_speaking (TALKING emotion)
  - /social/speech/is_listening (LISTENING emotion)
- Tap-to-toggle status overlay
- 60fps Canvas animation on Wayland
- ~80MB total memory (Node.js + browser)

**Files:**
- public/index.html — Main page (1024×600 fullscreen)
- public/salty-face.js — Canvas rendering + ROS2 integration
- server.js — Node.js HTTP server with CORS support
- systemd/salty-face-server.service — Auto-start systemd service
- docs/SALTY_FACE_WEB_APP.md — Complete setup & API documentation

**Integration:**
- Runs in Chromium kiosk (Issue #374)
- Depends on rosbridge_server for WebSocket bridge
- Serves on localhost:3000 (configurable)

**Next:** Issue #371 (Accessibility enhancements)

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-03-03 16:42:41 -05:00
ddb93bec20 Issue #354: Add ROS parameter editor to Settings
Add Parameters tab for live ROS parameter editing with:
- get_parameters service integration
- set_parameter service support
- Type-specific input controls
- Node-based grouping
- Search filtering

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-03-03 13:49:51 -05:00
93028dc847 feat(webui): add dedicated CAMERAS tab group for camera viewer (Issue #349)
Move camera viewer from TELEMETRY to new CAMERAS tab group (rose color).
Reorganizes tab structure to separate media capture from system telemetry.
CameraViewer.jsx already provides comprehensive MJPEG stream support:
- Multi-camera switching (7 total: front/left/rear/right CSI, D435i RGB/depth, panoramic)
- FPS counter per camera with quality badge (FULL/GOOD/LOW/NO SIGNAL)
- Resolution and camera info display
- Detection overlays (faces, gestures, scene objects)
- Picture-in-picture support (up to 3 pinned cameras)
- Video recording (MP4/WebM) and snapshot capture
- 360° panoramic viewer with mouse drag pan
- Color-coded quality indicators based on FPS

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-03-03 13:27:09 -05:00
347449ed95 feat(webui): hand pose tracking and gesture visualization (Issue #344)
Features:
- Subscribes to /saltybot/hands (21 landmarks per hand - MediaPipe format)
- Subscribes to /saltybot/hand_gesture (String gesture label)
- Canvas-based hand skeleton rendering with bone connections
- Support for dual hand tracking (left and right)
- Handedness indicators with color coding
  * Left hand: green
  * Right hand: yellow
- Real-time gesture display with confidence indicator
- Per-landmark confidence visualization
- Bone connections between all 21 joints

Hand Skeleton Features:
- 21 MediaPipe landmarks per hand
  * Wrist (1)
  * Thumb (4)
  * Index finger (4)
  * Middle finger (4)
  * Ring finger (4)
  * Pinky finger (4)
- 20 bone connections between joints
- Confidence-based rendering (only show high-confidence points)
- Scaling and normalization for viewport
- Joint type indicators (tips with ring outline)
- Glow effects around landmarks

Gesture Recognition:
- Real-time gesture label display
- Confidence percentage (0-100%)
- Color-coded confidence:
  * Green: >80% (high confidence)
  * Yellow: 50-80% (medium confidence)
  * Blue: <50% (detecting)

Hand Status Display:
- Live detection status for both hands
- Visual indicators (✓ detected / ◯ not detected)
- Dual-hand canvas rendering
- Gesture info panel with confidence bar

Integration:
- Added to SOCIAL tab group as "Hands" tab
- Positioned after "Faces" tab
- Uses subscribe hook for real-time updates
- Dark theme with color-coded hands
- Canvas-based rendering for smooth visualization

Build: 125 modules, no errors
Main bundle: 270.08 KB

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-03-03 12:43:19 -05:00
c50899f000 feat(webui): system diagnostics panel with hardware status monitoring (Issue #340)
Features:
- Subscribes to /diagnostics (diagnostic_msgs/DiagnosticArray)
- Hardware status cards per subsystem with color-coded health
- Real-time error and warning counts
- Expandable diagnostic cards with detailed key-value pairs
- Diagnostic status timeline with timestamps
- Aggregated system health summary
- Status indicators: OK (green), WARNING (yellow), ERROR (red), STALE (gray)

- Hardware Status Display
  * Per-subsystem diagnostic cards
  * Status level with color coding
  * Expandable details with key values
  * Hardware ID tracking
  * Name and message display

- Health Summary Card
  * Total diagnostic count
  * OK/WARNING/ERROR/STALE breakdowns
  * Overall system health status
  * Visual status indicator

- Timeline and History
  * Recent status timeline (10 latest events)
  * Timestamp tracking
  * Status transitions
  * Scrollable history

- Status Legend
  * Color-coded reference guide
  * Status descriptions
  * Quick status lookup

Integration:
- Added to MONITORING tab group as first tab (highest priority)
- Uses subscribe hook for real-time updates
- Dark theme with comprehensive status visualization
- Max 100 diagnostic events in history

Build: 124 modules, no errors
Main bundle: 264.31 KB

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-03-03 11:23:51 -05:00
79505579b1 Merge pull request 'feat(webui): gamepad teleoperation panel with WASD + e-stop (Issue #319)' (#331) from sl-webui/issue-319-teleop into main 2026-03-03 11:22:28 -05:00
ba39e9ba26 fix(webui): add missing imports to App.jsx (Issue #329) — P0 BUG FIX
Critical bug fix:
- Added missing StatusHeader import (used in JSX line 215)
- Added missing LogViewer import (used in JSX line 291)
- Added missing MotorCurrentGraph import (used in JSX line 264)

These imports were referenced in JSX but not imported, causing dashboard crashes on load.

Build verification:
- 122 modules, all compiled successfully
- No errors or warnings
- Bundle: 255.38 KB main

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-03-03 06:46:19 -05:00
23e05a634f feat(webui): gamepad teleoperation panel with dual-stick control (Issue #319)
Features:
- Virtual dual-stick gamepad interface
  * Left stick: linear velocity (forward/backward)
  * Right stick: angular velocity (turn left/right)
- Canvas-based rendering with real-time visual feedback
- Velocity vector visualization on sticks
- 10% deadzone on both axes for dead-stick detection

- WASD keyboard fallback for manual driving
  * W/S: forward/backward
  * A/D: turn left/right
  * Smooth blending between gamepad and keyboard input

- Speed limiter slider (0-100%)
  * Real-time control of max velocity
  * Disabled during e-stop state

- Emergency stop button (e-stop)
  * Immediate velocity zeroing
  * Visual status indicator (red when active)
  * Prevents accidental motion during e-stop

- ROS Integration
  * Publishes geometry_msgs/Twist to /cmd_vel
  * Max linear velocity: 0.5 m/s
  * Max angular velocity: 1.0 rad/s
  * 50ms update rate

- UI Features
  * Real-time velocity display (m/s and rad/s)
  * Status bar with control mode indicator
  * Speed limit percentage display
  * Control info with keyboard fallback guide

Integration:
- Replaces previous split-view control layout
- Enhanced TELEMETRY/control tab
- Comprehensive gamepad + keyboard control system

Build: 120 modules, no errors

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-03-03 00:43:47 -05:00
3f558046c5 feat(webui): ROS node list viewer with heartbeat status
Features:
- Enumerate active ROS nodes from /rosout activity
- Real-time node status tracking (alive/dead based on heartbeat)
- Heartbeat timeout: 5 seconds without updates = dead
- Display node name, status, uptime, and last seen timestamp
- Color-coded status indicators (green=alive, gray=dead)
- Sortable table with node statistics
- Summary card showing alive/dead node counts
- Periodic status polling every 2 seconds

Integration:
- Added to MONITORING tab group as 'Nodes' tab
- Subscribes to /rosout (rcl_interfaces/Log) to detect active nodes
- Real-time updates with smooth transitions

Build: 119 modules, no errors

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-03-03 00:21:35 -05:00
20801c4a0e feat(webui): thermal status gauge with CPU/GPU temperature display (Issue #308)
Features:
- Circular gauge visualization for CPU and GPU temperatures
- Color-coded temperature zones: green <60°C, yellow 60-75°C, red >75°C
- Real-time needle pointer animation
- Fan speed percentage display for each sensor
- Peak temperature tracking and max reached indicator
- Thermal alert status (Normal/Caution/Critical)
- ROS subscription to /saltybot/thermal_status
- Integrated into TELEMETRY tab group

Build: 118 modules, no errors

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-03-02 21:37:59 -05:00
f12f0bdc2b feat(webui): motor current live graph (Issue #297)
Real-time motor current visualization with:
- Subscribes to /saltybot/motor_currents for dual-motor current data
- Rolling 60-second history window with automatic data culling
- Dual-axis line chart for left (cyan) and right (amber) motor amps
- Canvas-based rendering for performance
- Thermal warning threshold line (25A, configurable)
- Real-time statistics:
  * Current draw for left and right motors
  * Peak current tracking over 60-second window
  * Average current calculation
  * Thermal status indicator with warning badge
- Color-coded thermal alerts:
  * Red background when threshold exceeded
  * Warning indicator and message
- Grid overlay, axis labels, time labels, legend
- Takes absolute value of currents (handles reverse direction)

Integrated into TELEMETRY tab group as 'Motor Current' tab.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-03-02 21:09:17 -05:00
793ba31ada feat(webui): ROS topic bandwidth monitor (Issue #287)
Topic bandwidth tracking and visualization with:
- Tracks message rates for all subscribed ROS topics
- Estimates bandwidth based on message size and frequency
- Message size estimated from JSON serialization
- Updates every 1 second with rolling 30-second history window
- Sortable table display:
  * Topic name with truncation for long names
  * Message rate (messages per second)
  * Average message size (bytes)
  * Bandwidth estimate (B/s, KB/s, or MB/s)
  * Sparkline mini-chart showing bandwidth trend
- Total bandwidth summary at top
- Click column headers to sort (ascending/descending toggle)
- Visual indicators with color-coded columns

Integrated into MONITORING tab group as 'Bandwidth' tab.
Component provides window.__trackRosMessage() hook for optional
bandwidth tracking integration with ROS bridge.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-03-02 20:45:41 -05:00
fc26226368 feat(webui): battery voltage and percentage history chart (Issue #280)
Real-time battery history visualization with:
- Subscribes to /saltybot/battery_state for continuous battery data
- Rolling 30-minute history window with automatic data culling
- Dual-axis line chart: voltage (left, cyan) and percentage (right, amber)
- Canvas-based rendering for performance
- Charge/discharge rate calculation (last 5-minute average):
  * Voltage rate in mV/min with up/down/stable indicator
  * Percentage rate in %/min with up/down/stable indicator
- Grid overlay, axis labels, time labels, and legend
- Current stats display: voltage, percentage, rates
- Responsive canvas sizing

Integrated into TELEMETRY tab group as 'Battery History' tab.
Follows established canvas rendering and data subscription patterns.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-03-02 17:36:18 -05:00
54bc37926b Merge pull request 'feat(webui): system log tail viewer (#275)' (#277) from sl-webui/issue-275-log-viewer into main 2026-03-02 17:31:01 -05:00
5362536fb1 feat(webui): waypoint editor with click-to-navigate (Issue #261) 2026-03-02 17:29:03 -05:00
305ce6c971 feat(webui): system log tail viewer (Issue #275)
Real-time ROS log stream viewer with:
- Subscribes to /rosout (rcl_interfaces/Log)
- Severity-based color coding:
  DEBUG=grey | INFO=white | WARN=yellow | ERROR=red | FATAL=magenta
- Filter by severity level (multi-select toggle)
- Filter by node name (text input)
- Auto-scroll to latest logs
- Max 500 logs in history (configurable)
- Scrolling log output in monospace font
- Proper timestamp formatting (HH:MM:SS)

Integrated into MONITORING tab group as 'Logs' tab alongside 'Events'.
Follows established React/Tailwind patterns from other dashboard components.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-03-02 17:28:28 -05:00
201dea4c01 feat(webui): robot status dashboard header bar (Issue #269)
Persistent top bar showing real-time robot health indicators:
- Battery percentage and voltage (color-coded: green >60%, amber 30-60%, red <30%)
- WiFi signal strength (RSSI dBm with quality assessment)
- Motor status and current draw in Amperes
- Emergency state indicator (red highlight when active)
- System uptime in hours and minutes
- Current operational mode (idle/nav/social/docking)
- Connection status indicator

Component subscribes to relevant ROS topics and displays in compact
flex layout matching dashboard dark theme.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-03-02 14:19:03 -05:00
c865e84e16 feat(webui): waypoint editor with click-to-navigate (Issue #261)
Interactive waypoint editor for Nav2 goal-based navigation:
- Click on map display to place waypoints
- Drag waypoints in list to reorder navigation sequence
- Right-click waypoints to delete them
- Visual waypoint overlay on map with numbering
- Robot position indicator at center
- Waypoint list sidebar with selection and ordering
- Send Nav2 goal to individual selected waypoint
- Execute all waypoints in sequence with automatic progression
- Clear all waypoints button
- Real-time coordinate display and robot pose tracking
- Integrated into new NAVIGATION tab group
- Uses /navigate_to_pose service for goal publishing

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-03-02 13:28:01 -05:00
161cba56d4 feat(webui): map viewer with SLAM occupancy grid (Issue #250) 2026-03-02 12:49:48 -05:00
04652c73f8 feat(webui): conversation history panel (Issue #240)
Implements a chat-style conversation viewer that subscribes to
/social/conversation_text and displays user speech (STT) and robot
responses (TTS) with timestamps and speaker labels. Includes auto-scroll
to latest message, manual scroll detection, and message history limiting.

- New component: ConversationHistory.jsx (chat-style message bubbles)
  - User messages in blue, robot responses in green
  - Auto-scrolling with manual scroll detection toggle
  - Timestamp formatting (HH:MM:SS)
  - Message history limiting (max 100 messages)
  - Clear history button
- Integrated into SOCIAL tab group as "History" tab
- Subscribes to /social/conversation_text topic
  (saltybot_social_msgs/ConversationMessage)

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-03-02 12:21:38 -05:00
ff3eb340d8 feat(webui): audio level meter with speech activity (Issue #234)
Add real-time audio level visualization with VU-style meter:
- Responsive VU bar with color gradient (silent to clipping)
- Peak hold indicator with exponential decay
- Speech activity detection from /social/speech/is_speaking
- Color-coded audio levels with visual feedback
- Grid markers for level reference (25%, 50%, 75%)
- Comprehensive audio statistics (average, max, peak count)

Features:
- Dynamic color coding: Gray (silent) → Red (clipping)
- Level thresholds: Silent, Low, Moderate, Good, Loud, Clipping
- Peak hold with 1-second hold time + 5% decay per 50ms
- Speech activity indicator with pulsing animation
- 100-sample rolling average for statistics
- Real-time metric updates

Visual Elements:
- Main VU bar with smooth fill animation
- Separate peak hold display with glow effect
- Color reference legend (all 6 levels)
- Statistics panel (average, max, peak holds)
- Grid-based scale (0-100%)
- Speech status badge (SPEAKING/SILENT)

Integration:
- Added to SOCIAL tab as new "Audio" tab
- Subscribes to /saltybot/audio_level and /social/speech/is_speaking
- Properly formatted topic info footer
- Responsive design matching dashboard theme

Build: ✓ Passing (113 modules, 202.67 KB main bundle)

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-03-02 12:11:12 -05:00
5e05622628 feat(webui): 3D robot pose viewer with IMU visualization (Issue #229)
Replace basic IMU panel with interactive Three.js 3D pose viewer:
- Real-time robot box model visualization
- Rotation controlled by /saltybot/imu quaternion data
- 30-second position trail from /odom navigation
- Trail visualization with point history
- Reset button to clear trail history
- Grid ground plane and directional indicator arrow
- Interactive 3D scene with proper lighting and shadows

Features:
- Three.js scene with dynamic box model (0.3x0.3x0.5m)
- Forward direction indicator (amber cone)
- Ambient and directional lighting with shadows
- Grid helper for spatial reference
- Trail line geometry with dynamic point updates
- Automatic window resize handling
- Proper cleanup of WebGL resources and subscriptions

Integration:
- Replaces ImuPanel in TELEMETRY tab (IMU view)
- Added to flex layout for proper 3D viewport sizing
- Subscribes to /saltybot/imu and /odom topics
- Updates trail points every 100ms (max 30-second history)

Build: ✓ Passing (112 modules, 196.69 KB main bundle)
Three.js vendor: 473.57 KB

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-03-02 11:59:42 -05:00
3c0c781c3b feat(webui): network diagnostics panel (Issue #222)
Add NetworkPanel component to CONFIG tab for real-time network monitoring:
- WiFi RSSI signal strength with color-coded signal bars
- Rosbridge latency measurement with quality indicators
- WebSocket message rate and throughput tracking
- Connection status monitoring
- Network health summary with overall status assessment

Features:
- 5-level signal strength indicator (Excellent to Poor)
- 5-level latency quality assessment (Excellent to Critical)
- Real-time message rate counter
- Cumulative stats (messages received/sent, bytes transferred)
- Responsive design with Tailwind CSS styling

Integration:
- Added to CONFIG tab group alongside Settings
- Uses rosbridge connection status and WebSocket URL
- Simulates realistic metrics with configurable intervals
- Proper cleanup of monitoring intervals on unmount

Build: ✓ Passing (111 modules, 192.59 KB main bundle)

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-03-02 11:53:38 -05:00
fd7eddd44d feat: Add Issue #213 - PID auto-tuner (Astrom-Hagglund relay oscillation)
Implements PID auto-tuning ROS2 node using relay feedback (Astrom-Hagglund) method.
Service-triggered relay oscillation measures ultimate gain (Ku) and ultimate period
(Tu), then computes Ziegler-Nichols PD gains. Safety abort on >25deg tilt.

Features:
  - Service /saltybot/autotune_pid (std_srvs/Trigger) starts tuning
  - Relay oscillation method for accurate gain measurement
  - Measures Ku (ultimate gain) and Tu (ultimate period)
  - Computes Z-N PD gains: Kp=0.6*Ku, Kd=0.075*Ku*Tu
  - Real-time safety abort >25° tilt angle
  - JSON telemetry on /saltybot/autotune_info
  - Relay commands on /saltybot/autotune_cmd_vel

Tuning Process:
  1. Settle phase: zero command, allow oscillations to die
  2. Relay oscillation: apply +/-relay_magnitude commands
  3. Measure peaks: detect zero crossings, record extrema
  4. Analysis: calculate Ku from peak amplitude, Tu from period
  5. Gain computation: Ziegler-Nichols formulas
  6. Publish results: Ku, Tu, Kp, Kd

Safety Features:
  - IMU tilt monitoring (abort >25°)
  - Max tuning duration timeout
  - Configurable settle time and oscillation cycles

Published Topics:
  - /saltybot/autotune_info (std_msgs/String) - JSON with Ku, Tu, Kp, Kd
  - /saltybot/autotune_cmd_vel (geometry_msgs/Twist) - Relay control

Subscribed Topics:
  - /imu/data (sensor_msgs/Imu) - IMU tilt safety check
  - /saltybot/balance_feedback (std_msgs/Float32) - Balance feedback

Package: saltybot_pid_autotune
Entry point: pid_autotune_node
Service: /saltybot/autotune_pid

Tests: 20+ unit tests covering:
  - IMU tilt extraction
  - Relay oscillation analysis
  - Ku/Tu measurement
  - Ziegler-Nichols gain computation
  - Peak detection and averaging
  - Safety limits (tilt, timeout)
  - State machine transitions
  - JSON telemetry format

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-03-02 11:47:05 -05:00
aedb8771ad feat(webui): robot event log viewer — emergency/docking/diagnostics (Issue #192)
- Add EventLog component with real-time event streaming
- Color-coded events: red=emergency, blue=docking, cyan=diagnostics
- Filter by event type with toggle buttons
- Auto-scroll to latest event
- Timestamped event cards with details display
- Max 200 event history (FIFO)
- Add MONITORING tab group with Events tab to App.jsx
- Supports /saltybot/emergency, /saltybot/docking_status, /diagnostics topics

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-03-02 11:04:53 -05:00
57420807ca feat(webui): live camera viewer — multi-stream + detection overlays (Issue #177)
UI (src/hooks/useCamera.js, src/components/CameraViewer.jsx):
  - 7 camera sources: front/left/rear/right CSI, D435i RGB/depth, panoramic
  - Compressed image subscription via rosbridge (sensor_msgs/CompressedImage)
  - Client-side 15fps gate (drops excess frames, reduces JS pressure)
  - Per-camera FPS indicator with quality badge (FULL/GOOD/LOW/NO SIGNAL)
  - Detection overlays: face boxes + names (/social/faces/detections),
    gesture icons (/social/gestures), scene object labels + hazard colours
    (/social/scene/objects); overlay mode selector (off/faces/gestures/objects/all)
  - 360° panoramic equirect viewer with mouse/touch drag azimuth pan
  - Picture-in-picture: up to 3 pinned cameras via ⊕ button
  - One-click recording (MediaRecorder → MP4/WebM download)
  - Snapshot to PNG with detection overlay composite + timestamp watermark
  - Cameras tab added to TELEMETRY group in App.jsx

Jetson (rosbridge bringup):
  - rosbridge_params.yaml: whitelist + /camera/depth/image_rect_raw/compressed,
    /camera/panoramic/compressed, /social/faces/detections,
    /social/gestures, /social/scene/objects
  - rosbridge.launch.py: D435i colour republisher (JPEG 75%) +
    depth republisher (compressedDepth/PNG16 preserving uint16 values)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-02 10:47:01 -05:00
da3ee19688 feat(webui): settings & configuration panel (Issue #160)
Some checks failed
social-bot integration tests / Lint (flake8 + pep257) (pull_request) Failing after 2s
social-bot integration tests / Core integration tests (mock sensors, no GPU) (pull_request) Has been skipped
social-bot integration tests / Latency profiling (GPU, Orin) (pull_request) Has been cancelled
- useSettings.js: PID parameter catalogue, step-response simulation,
  ROS2 parameter apply via rcl_interfaces/srv/SetParameters, sensor
  param management, firmware info extraction from /diagnostics,
  diagnostics bundle export, JSON backup/restore, localStorage persist
- SettingsPanel.jsx: 6-view panel (PID, Sensors, Network, Firmware,
  Diagnostics, Backup); StepResponseCanvas with stable/oscillating/
  unstable colour-coding; GainSlider with range+number input; weight-
  class tabs (empty/light/heavy); parameter validation badges
- App.jsx: CONFIG tab group (purple), settings tab render, FLEET_TABS
  set to gate ConnectionBar and footer for fleet/missions/settings

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-02 10:26:42 -05:00
51dcc01bfa feat(webui): mission planner — waypoint editor, routes, geofences, schedule (Issue #145)
useMissions.js:
  - Waypoints: click-to-place, drag, dwell time, localStorage persistence
  - Routes: loop/oneshot/pingpong, per-robot assignment, waypoint sequence
  - Geofences: polygon draw (no-go / allowed zones)
  - Templates: save/load profiles + JSON export/import
  - Schedules: time+day triggers with client-side runner
  - Executions: running/paused/aborted state per route

MissionPlanner.jsx:
  - Canvas: waypoints, route lines + direction arrows, geofence fills,
    robot position overlays, scale bar, zoom/pan, execution progress
  - 7 sub-views: Map | Waypoints | Routes | Geofences | Templates | Schedule | Execute
  - Execute: start/pause/resume/abort, waypoint-by-waypoint advance,
    sends /goal_pose (single) and /outdoor/waypoints (patrol PoseArray)
  - Integrates useFleet for robot selection and /goal_pose publishing

App.jsx: adds Missions tab to FLEET group

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-02 10:04:38 -05:00
85e5777994 feat(webui): add FLEET tab group to App.jsx — Issue #139
Adds FleetPanel import and FLEET tab group (green) to the main
dashboard navigation. Fleet tab is self-contained via useFleet.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-02 09:40:27 -05:00
b09f17b787 feat(webui): fleet management dashboard — Issue #139
- useFleet.js: multi-robot ROSLIB.Ros multiplexer with localStorage
  persistence; per-robot balance_state/control_mode/odom/diagnostics
  subscriptions; sendGoal, sendPatrol, scanSubnet helpers
- FleetPanel.jsx: fleet sub-views (Robots/Map/Missions/Video/Alerts)
  plus RobotDetail overlay reusing ImuPanel/BatteryPanel/MotorPanel/
  ControlMode/SystemHealth via subscribeRobot adapter pattern
- App.jsx: adds FLEET tab group pointing to FleetPanel

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-02 09:37:10 -05:00
78374668bf feat(ui): telemetry dashboard panels (issue #126)
Adds 6 telemetry tabs to the social-bot dashboard extending PR #112.

IMU Panel (/saltybot/imu, /saltybot/balance_state):
  - Canvas artificial horizon + compass tape
  - Three.js 3D robot orientation cube with quaternion SLERP
  - Angular velocity readouts, balance state detail

Battery Panel (/diagnostics):
  - Voltage, SoC, estimated current, runtime estimate
  - 120-point voltage history sparkline (2 min)
  - Reads battery_voltage_v, battery_soc_pct KeyValues from DiagnosticArray

Motor Panel (/saltybot/balance_state, /saltybot/rover_pwm):
  - Auto-detects balance vs rover mode
  - Bidirectional duty bars for left/right motors
  - Motor temp from /diagnostics, PID detail for balance bot

Map Viewer (/map, /odom, /outdoor/route):
  - OccupancyGrid canvas renderer (unknown/free/occupied colour-coded)
  - Robot position + heading arrow, Nav2/OSM path overlay (dashed)
  - Pan (mouse/touch) + zoom, 2 m scale bar

Control Mode (/saltybot/control_mode):
  - RC / RAMP_TO_AUTO / AUTO / RAMP_TO_RC state badge
  - Blend alpha progress bar
  - Safety flags: SLAM ok, RC link ok, stick override active
  - State machine diagram

System Health (/diagnostics):
  - CPU/GPU temperature gauges with colour-coded bars
  - RAM, GPU memory, disk resource bars
  - ROS2 node status list sorted by severity (ERROR → WARN → OK)

Also:
  - Three.js vendor chunk split (471 kB separate lazy chunk)
  - Updated App.jsx with grouped SOCIAL + TELEMETRY tab nav

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-02 09:18:39 -05:00
1cd8ebeb32 feat(ui): add social-bot dashboard (issue #107)
React + Vite + TailwindCSS dashboard served on port 8080.
Connects to ROS2 via rosbridge_server WebSocket (default ws://localhost:9090).

Panels:
- StatusPanel: pipeline state (idle/listening/thinking/speaking/throttled)
  with animated pulse indicator, GPU memory bar, per-stage latency stats
- FaceGallery: enrolled persons grid with enroll/delete via
  /social/enrollment/* services; live detection indicator
- ConversationLog: real-time transcript with human/bot bubbles,
  streaming partial support, auto-scroll
- PersonalityTuner: sass/humor/verbosity sliders (0–10) writing to
  personality_node via rcl_interfaces/srv/SetParameters; live
  PersonalityState display
- NavModeSelector: shadow/lead/side/orbit/loose/tight mode buttons
  publishing to /social/nav/mode; voice command reference table

Usage:
  cd ui/social-bot && npm install && npm run dev   # dev server port 8080
  npm run build && npm run preview                  # production preview

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-02 08:36:51 -05:00
6dc7aea32f feat: modern HUD dashboard + telemetry expansion (#43)
ui/index.html — full dashboard rewrite:
- 3-column layout: LEFT telemetry gauges, CENTER 3D SaltyBot, RIGHT comms
- LEFT: artificial horizon (canvas, pitch/roll/ladder/roll-arc), yaw compass
  tape, pitch/roll/yaw readouts, bidirectional motor bar, battery bar, BME280
  environment section (auto-shows on data), MAG heading row
- CENTER: Three.js SaltyBot model (PR#41) with ground plane + animated
  wheel rolling proportional to motor_cmd
- RIGHT: USB tx/rx packet counters, mode badge (MANUAL/ASSISTED/AUTO),
  CRSF RSSI/LQ, dual RC stick overlay canvases (CH1–4), Jetson active dot
- BOTTOM: KP/KI/KD/SP/MAX sliders with APPLY + QUERY, collapsible log console
- Style: Tailwind CSS CDN, dark cyberpunk theme, neon cyan + orange accents

src/main.c — telemetry JSON additions:
- buf: 256 → 320 bytes (headroom for new fields)
- ja: Jetson active flag (0/1) via jetson_cmd_is_active()
- txc: TX telemetry frame counter (uint32, main-loop local)
- rxc: RX CDC packet counter (cdc_rx_count from usbd_cdc_if)
- ch1–ch4: CRSF channels mapped to µs (1000–2000) via crsf_to_range(),
  appended alongside rssi/lq when RC is alive

lib/USB_CDC/src/usbd_cdc_if.c:
- cdc_rx_count: volatile uint32_t, incremented in CDC_Receive on every
  packet; extern'd in main.c for telemetry

Closes #43.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-28 22:41:02 -05:00
seb
939800a9fb Merge pull request 'feat: CRSF/ELRS RC integration (Phase 2)' (#35) from sl-firmware/crsf-elrs into main 2026-02-28 21:58:05 -05:00
seb
a118582374 Merge pull request 'feat: Gyro recalibration button in web UI (#32)' (#39) from sl-firmware/gyro-recal-button into main 2026-02-28 21:58:04 -05:00
seb
e7298996d0 Merge pull request 'feat: SaltyBot 3D robot model in web UI (#37)' (#41) from sl-firmware/robot-3d-model into main 2026-02-28 21:57:55 -05:00
fbfde24aba feat: CRSF/ELRS RC integration — 16ch input with failsafe (#Phase2)
Protocol choice: implemented from spec (CRSFforArduino needs Arduino
framework; Betaflight extraction has deep scheduler dependencies).
Protocol verified against Betaflight src/main/rx/crsf.c + CRSF spec.

crsf.c:
- UART4 PA0=TX/PA1=RX (GPIO_AF8_UART4), 420000 baud 8N1, oversampling×8
  APB1=54MHz → BRR=0x101 → 418604 baud (0.33% error, within spec)
- DMA1 Stream2 Channel4, circular 64-byte buffer, IDLE interrupt
  DMA half/complete callbacks drain buffer; IDLE fires at frame boundary
- CRC8 DVB-S2 (polynomial 0xD5) validated on every frame
- Parser state machine: SYNC(0xC8)→LEN→DATA with length sanity check
- 11-bit channel unpack for all 16 channels from 22-byte payload
- RC channels frame (0x16): unpacks 16ch, updates last_rx_ms + armed
- Link stats frame (0x14): captures RSSI dBm, LQ%, SNR dB

crsf.h: added rssi_dbm, link_quality, snr fields to CRSFState

config.h: CRSF_ARM_THRESHOLD=1750, CRSF_STEER_MAX=400, CRSF_FAILSAFE_MS=300

main.c:
- crsf_init() called after motor_driver_init()
- RC failsafe: disarm if (now - last_rx_ms) > CRSF_FAILSAFE_MS, but only
  after RC was first seen (last_rx_ms != 0) — USB-only mode unaffected
- RC arm: CH5 rising edge → safety_arm_start(); falling edge → disarm
  Same ARMING_HOLD_MS interlock as USB arm command
- RC steer: CH1 → crsf_to_range() → ±CRSF_STEER_MAX → motor_driver steer
- RSSI/LQ: appended to JSON when safety_rc_alive() ("rssi","lq" fields)

ui/index.html: hidden RC RSSI row revealed on first packet with rssi/lq

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-28 21:54:58 -05:00
91fc54c3d7 feat: SaltyBot 3D robot model in web UI (#37)
Replace generic flat PCB with a standing two-wheeled balancing robot:
- Vertical navy body (1.2 tall) rising above wheel axle at y=0
- Two wheels with blue rim accents, aligned to axle
- Front display panel and status LED
- Sensor stem + head on top

Camera repositioned to frame the taller robot.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-28 21:52:53 -05:00
36abbde93a fix: correct yaw inversion in web UI (P0 #38)
Remove erroneous negate on targetYaw — yaw was spinning opposite to
physical rotation. Update resetYaw() formula to match (+ instead of -).
Pitch and roll were unaffected.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-28 21:51:29 -05:00
bd30e2b40d feat: gyro recalibration button in web UI (#32)
Add 'G' CDC command that disarms and re-runs gyro bias calibration.
safety_refresh() added to calibration loop (every 40ms) so IWDG
does not trip during the 1s blocking re-cal when watchdog is running.
GYRO CAL button in ui/index.html sends 'G' and shows status feedback.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-28 21:50:28 -05:00