feat: IMU mount cal, CAN telemetry, LED CAN override (Issues #680, #672, #685) #686

Merged
sl-jetson merged 2 commits from sl-jetson/issue-681-vesc-telemetry-publish into main 2026-03-18 07:49:12 -04:00
Collaborator

Summary

Issue #680 — IMU mount angle calibration

  • imu_cal_flash.h/.c: pitch/roll offset storage in flash sector 7 (0x0807FF00, 64 bytes; preserves PID records across sector erase)
  • mpu6000_set_mount_offset(): subtracts saved offsets from pitch/roll output on every read
  • mpu6000_has_mount_offset(): reports cal_status=2 to Orin
  • O CDC command (USB dev path): capture current pitch/roll → save to flash → ACK JSON
  • Load offsets on boot; log to printf

CAN telemetry correction (no USB in production)

  • FC_IMU (0x402): pitch/roll/yaw/cal_status/balance_state at 50 Hz
  • FC_BARO (0x403): pressure_pa/temp_x10/alt_cm at 1 Hz (Issue #672)
  • orin_can_broadcast_imu() + orin_can_broadcast_baro() — internally rate-limited

Issue #685 — LED CAN override

  • ORIN_CAN_ID_LED_CMD (0x304): pattern/brightness/duration_ms from Orin
  • main.c applies pattern to LED state machine; safety states always win

Orin side — saltybot_can_node.py

  • Production SocketCAN bridge (replaces USB saltybot_cmd_node in production)
  • Reads 0x400–0x403 → publishes /saltybot/imu, /saltybot/balance_state, /saltybot/barometer
  • Subscribes /cmd_vel → 0x301 DRIVE; /saltybot/leds → 0x304 LED_CMD
  • Sends 0x300 HEARTBEAT at 5 Hz; 0x303 ESTOP on shutdown

Test plan

  • USB: send O command → verify JSON ACK + flash persist across reboot
  • CAN: confirm 0x402 frames at 50 Hz on CANable candump
  • CAN: confirm 0x403 frames at 1 Hz (requires BME280 present)
  • Orin: ros2 run saltybot_bridge saltybot_can_node → verify IMU + balance_state topics
  • LED override: publish {"pattern":3,"brightness":200,"duration_ms":3000} to /saltybot/leds

Closes #680, #672, #685

🤖 Generated with Claude Code

## Summary ### Issue #680 — IMU mount angle calibration - `imu_cal_flash.h/.c`: pitch/roll offset storage in flash sector 7 (0x0807FF00, 64 bytes; preserves PID records across sector erase) - `mpu6000_set_mount_offset()`: subtracts saved offsets from pitch/roll output on every read - `mpu6000_has_mount_offset()`: reports `cal_status=2` to Orin - **`O` CDC command** (USB dev path): capture current pitch/roll → save to flash → ACK JSON - Load offsets on boot; log to printf ### CAN telemetry correction (no USB in production) - `FC_IMU` (0x402): pitch/roll/yaw/cal_status/balance_state at **50 Hz** - `FC_BARO` (0x403): pressure_pa/temp_x10/alt_cm at **1 Hz** (Issue #672) - `orin_can_broadcast_imu()` + `orin_can_broadcast_baro()` — internally rate-limited ### Issue #685 — LED CAN override - `ORIN_CAN_ID_LED_CMD` (0x304): pattern/brightness/duration_ms from Orin - main.c applies pattern to LED state machine; safety states always win ### Orin side — `saltybot_can_node.py` - Production SocketCAN bridge (replaces USB `saltybot_cmd_node` in production) - Reads 0x400–0x403 → publishes `/saltybot/imu`, `/saltybot/balance_state`, `/saltybot/barometer` - Subscribes `/cmd_vel` → 0x301 DRIVE; `/saltybot/leds` → 0x304 LED_CMD - Sends 0x300 HEARTBEAT at 5 Hz; 0x303 ESTOP on shutdown ## Test plan - [ ] USB: send `O` command → verify JSON ACK + flash persist across reboot - [ ] CAN: confirm 0x402 frames at 50 Hz on CANable candump - [ ] CAN: confirm 0x403 frames at 1 Hz (requires BME280 present) - [ ] Orin: `ros2 run saltybot_bridge saltybot_can_node` → verify IMU + balance_state topics - [ ] LED override: publish `{"pattern":3,"brightness":200,"duration_ms":3000}` to `/saltybot/leds` Closes #680, #672, #685 🤖 Generated with Claude Code
sl-jetson added 3 commits 2026-03-17 22:50:23 -04:00
UnboundLocalError when _ser is None — lines was only assigned inside
the else branch. Move initialisation to function scope so the for-loop
outside the lock always has a valid list.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
vesc_driver_node.py:
- Add VescState dataclass with to_dict() serialization
- Add CAN_PACKET_STATUS/STATUS_4/STATUS_5 (9/16/27) RX constants
- Add FAULT_NAMES lookup (11 VESC FW 6.6 fault codes)
- Add background CAN RX thread (_rx_loop / _dispatch_frame) that
  parses STATUS broadcast frames using struct.unpack
- Add publishers for /saltybot/vesc/left and /saltybot/vesc/right
  (std_msgs/String JSON) at configurable telemetry_rate_hz (default 10 Hz)
- Combine watchdog + publish into single timer callback
- Proper thread cleanup in destroy_node()

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Issue #680 — IMU mount angle calibration:
- imu_cal_flash.h/.c: store pitch/roll offsets in flash sector 7
  (0x0807FF00, 64 bytes; preserves PID records across sector erase)
- mpu6000_set_mount_offset(): subtracts offsets from pitch/roll output
- mpu6000_has_mount_offset(): reports cal_status=2 to Orin
- 'O' CDC command: capture current pitch/roll → save to flash → ACK JSON
- Load offsets on boot; report in printf log

CAN telemetry correction (Tee: production has no USB to Orin):
- FC_IMU (0x402): pitch/roll/yaw/cal_status/balance_state at 50 Hz
- orin_can_broadcast_imu() rate-limited to ORIN_IMU_TLM_HZ (50 Hz)
- FC_BARO (0x403): pressure_pa/temp_x10/alt_cm at 1 Hz (Issue #672)
- orin_can_broadcast_baro() rate-limited to ORIN_BARO_TLM_HZ (1 Hz)

Issue #685 — LED CAN override:
- ORIN_CAN_ID_LED_CMD (0x304): pattern/brightness/duration_ms from Orin
- orin_can_led_override volatile state + orin_can_led_updated flag
- main.c: apply pattern to LED state machine on each LED_CMD received

Orin side:
- saltybot_can_node.py: production SocketCAN bridge — reads 0x400-0x403,
  publishes /saltybot/imu, /saltybot/balance_state, /saltybot/barometer;
  subscribes /cmd_vel → 0x301 DRIVE; /saltybot/leds → 0x304 LED_CMD;
  sends 0x300 HEARTBEAT at 5 Hz; sends 0x303 ESTOP on shutdown
- setup.py: register saltybot_can_node entry point + uart_bridge launch

Fix: re-apply --defsym __stack_end=_estack-0x1000 linker fix to branch

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
sl-jetson merged commit c11cbaf3e6 into main 2026-03-18 07:49:12 -04:00
Sign in to join this conversation.
No Reviewers
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: seb/saltylab-firmware#686
No description provided.