sl-controls created pull request seb/saltylab-firmware#33

sl-controls
Sat, 28 Feb 2026 21:06:52 -0500

33#feat: RC/Autonomous mode switch (Phase 2)#

Summary

Mode enum: RC_MANUAL, RC_ASSISTED, AUTONOMOUS selected by RC CH6 (3-pos switch)

Smooth 500ms blend ramp between modes — single blend float (0=RC, 1=auto) drives all interpolation

CH5 always has kill authority regardless of mode

Telemetry: "md" field added to JSON stream

Safe on USB-only build: CRSF stub keeps rc_alive=false → RC inputs zero, CH5 kill never fires

Mode Behaviour

Mode | CH6 | Steer source | Speed source
RC_MANUAL (0) | ≤600 | RC CH4 | PID + RC CH3 bias
RC_ASSISTED (1) | 601–1199 | 50/50 blend RC+auto | PID + blended bias
AUTONOMOUS (2) | ≥1200 | Jetson via mode_manager_set_auto_cmd() | PID + auto bias

Key Design Decisions

Balance PID never bypassed — speed channel always includes PID output; RC/auto only adds a bias (±300 counts max) to avoid destabilising

Steer-only ramped at MOTOR_STEER_RAMP_RATE inside motor_driver; mode blend is a separate higher-level ramp

Deadband ±30 CRSF counts (~2%) on CH3/CH4 to mask stick center noise

Jetson integration: mode_manager_set_auto_cmd() called every loop when jetson_cmd_is_active(); steer is blended per mode

Files

include/mode_manager.h — API + robot_mode_t enum

src/mode_manager.c — full implementation

include/config.h — CRSF CH indices, deadband, speed-bias max, blend timing

src/main.c — init, mode update, CH5 kill, ESC blended steer/speed, "md" telemetry

Test plan

Build passes (no warnings)

USB-only: "md":0 in telemetry, no steer applied

CH6 low → "md":0; CH6 mid → "md":1; CH6 high → "md":2

Mode transition: blend advances smoothly, no step change at ESC

CH5 disarm from any mode → immediate disarm

AUTONOMOUS mode with mode_manager_set_auto_cmd(steer=500): steer ≈500

RC_ASSISTED: steer is mean of RC and auto values

RC CH3 pushed forward in MANUAL: bal.motor_cmd + bias sent to ESC

🤖 Generated with Claude Code