Add stm32_protocol.py — pure-Python binary frame codec: - Frame: STX(0x02)+TYPE+LEN+PAYLOAD+CRC16-CCITT(BE)+ETX(0x03) - CRC covers TYPE+LEN+PAYLOAD; polynomial 0x1021, init 0xFFFF - Encoders: HEARTBEAT, SPEED_STEER(-1000..+1000 int16), ARM, SET_MODE, PID_UPDATE - Telemetry decoders: ImuFrame, BatteryFrame, MotorRpmFrame, ArmStateFrame, ErrorFrame - FrameParser: streaming byte-by-byte state machine, resync on corrupt data Add stm32_cmd_node.py — ROS2 bidirectional bridge node: - /cmd_vel → SPEED_STEER at up to 50 Hz - HEARTBEAT timer (default 200ms); STM32 watchdog fires at 500ms - Jetson-side watchdog: no /cmd_vel for 500ms → send SPEED_STEER(0,0) - /saltybot/arm service (SetBool) → ARM frame - /saltybot/pid_update topic → PID_UPDATE frame - Telemetry RX: IMU→/saltybot/imu, BATTERY→/saltybot/telemetry/battery, MOTOR_RPM→/saltybot/telemetry/motor_rpm, ARM_STATE→/saltybot/arm_state, ERROR→/saltybot/error - Auto-reconnect on USB disconnect (serial.SerialException caught) - Zero-speed + disarm sent on node shutdown - /diagnostics with serial health, frame counts, last cmd age Add test_stm32_protocol.py (60+ tests): - CRC16-CCITT correctness, known test vectors - All encoder output structures and payload values - FrameParser: all telemetry types, bad CRC, bad ETX, resync, streaming, oversized payload, frame counters, reset Add test_stm32_cmd_node.py (30+ tests): - MockSerial: TX/RX byte-level testing without hardware - Speed/steer clamping, scaling, frame structure - Watchdog fires/doesn't fire relative to timeout - CRC error counted, resync after garbage Add stm32_cmd_params.yaml, stm32_cmd.launch.py. Update package.xml (add std_srvs, geometry_msgs deps). Update setup.py (add stm32_cmd_node entry point + new config/launch). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Jetson Nano — AI/SLAM Platform Setup
Self-balancing robot: Jetson Nano dev environment for ROS2 Humble + SLAM stack.
Stack
| Component | Version / Part |
|---|---|
| Platform | Jetson Nano 4GB |
| JetPack | 4.6 (L4T R32.6.1, CUDA 10.2) |
| ROS2 | Humble Hawksbill |
| DDS | CycloneDDS |
| SLAM | slam_toolbox |
| Nav | Nav2 |
| Depth camera | Intel RealSense D435i |
| LiDAR | RPLIDAR A1M8 |
| MCU bridge | STM32F722 (USB CDC @ 921600) |
Quick Start
# 1. Host setup (once, on fresh JetPack 4.6)
sudo bash scripts/setup-jetson.sh
# 2. Build Docker image
bash scripts/build-and-run.sh build
# 3. Start full stack
bash scripts/build-and-run.sh up
# 4. Open ROS2 shell
bash scripts/build-and-run.sh shell
Docs
docs/pinout.md— GPIO/I2C/UART pinout for all peripheralsdocs/power-budget.md— 10W power envelope analysis
Files
jetson/
├── Dockerfile # L4T base + ROS2 Humble + SLAM packages
├── docker-compose.yml # Multi-service stack (ROS2, RPLIDAR, D435i, STM32)
├── README.md # This file
├── docs/
│ ├── pinout.md # GPIO/I2C/UART pinout reference
│ └── power-budget.md # Power budget analysis (10W envelope)
└── scripts/
├── entrypoint.sh # Docker container entrypoint
├── setup-jetson.sh # Host setup (udev, Docker, nvpmodel)
└── build-and-run.sh # Build/run helper
Power Budget (Summary)
| Scenario | Total |
|---|---|
| Idle | 2.9W |
| Nominal (SLAM active) | ~10.2W |
| Peak | 15.4W |
Target: 10W (MAXN nvpmodel). Use RPLIDAR standby + 640p D435i for compliance.
See docs/power-budget.md for full analysis.