sl-android c52dc786a2 chore: remove all Mamba F722S / STM32 / BlackPill refs from docs/AGENTS.md
Replace old hardware (Mamba F722S, STM32F722, Jetson Nano, hoverboard ESC)
with new architecture: Orin Nano Super, ESP32-S3 BALANCE, ESP32-S3 IO,
VESC IDs 68/56. Update architecture diagram, hardware tables, UART
assignments, firmware build instructions, protocol docs, and 3D parts list.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-04 08:47:42 -04:00

8.7 KiB
Raw Blame History

AGENTS.md — SaltyLab Agent Onboarding

You're working on SaltyLab, a self-balancing two-wheeled indoor robot. Read this entire file before touching anything.

Project Overview

A hoverboard-based balancing robot with two compute layers:

  1. ESP32-S3 BALANCE — runs the PID balance loop. Safety-critical, operates independently of the Orin.
  2. ESP32-S3 IO — handles I/O: motor commands to VESCs, sensor polling, CAN/UART comms.
  3. Orin Nano Super — AI brain. ROS2, SLAM, person tracking. Sends velocity commands to ESP32-S3 BALANCE via UART. Not safety-critical.
Orin Nano Super (speed+steer via UART)  ←→  ELRS RC (kill switch)
         │
         ▼
   ESP32-S3 BALANCE (IMU, PID balance loop)
         │
         ▼ CAN / UART
   ESP32-S3 IO → VESC 68 (left) + VESC 56 (right)

⚠️ SAFETY — READ THIS OR PEOPLE GET HURT

This is not a toy. 8" hub motors + 36V battery can crush fingers, break toes, and launch the frame. Every firmware change must preserve these invariants:

  1. Motors NEVER spin on power-on. Requires deliberate arming: hold button 3s while upright.
  2. Tilt cutoff at ±25° — motors to zero, require manual re-arm. No retry, no recovery.
  3. Hardware watchdog (50ms) — if firmware hangs, motors cut.
  4. RC kill switch — dedicated ELRS channel, checked every loop iteration. Always overrides.
  5. Orin UART timeout (200ms) — if Orin disconnects, motors cut.
  6. Speed hard cap — firmware limit, start at 10%. Increase only after proven stable.
  7. Never test untethered until PID is stable for 5+ minutes on a tether.

If you break any of these, you are removed from the project.

Repository Layout

esp32/                 # ESP32-S3 firmware (ESP-IDF)
├── balance/           # ESP32-S3 BALANCE — PID loop, IMU, safety
└── io/                # ESP32-S3 IO — VESC CAN, sensors, comms

cad/                   # OpenSCAD parametric parts (16 files)
├── dimensions.scad    # ALL measurements live here — single source of truth
├── assembly.scad      # Full robot assembly visualization
├── motor_mount_plate.scad
├── battery_shelf.scad
├── esp32_balance_mount.scad  # Vibration-isolated ESP32-S3 BALANCE mount
├── jetson_shelf.scad
├── esc_mount.scad
├── sensor_tower_top.scad
├── lidar_standoff.scad
├── realsense_bracket.scad
├── bumper.scad        # TPU bumpers (front + rear)
├── handle.scad
├── kill_switch_mount.scad
├── tether_anchor.scad
├── led_diffuser_ring.scad
└── esp32c3_mount.scad

ui/                    # Web UI (Three.js + WebSerial)
└── index.html         # 3D board visualization, real-time IMU data

SALTYLAB.md            # Master design doc — architecture, wiring, build phases
SALTYLAB-DETAILED.md   # Power budget, weight budget, detailed schematics
PLATFORM.md            # Hardware platform reference

Hardware Quick Reference

ESP32-S3 BALANCE

Spec Value
MCU ESP32-S3 (dual-core Xtensa LX7, 240MHz, 512KB SRAM, 8MB flash)
Primary IMU MPU6000 (SPI)
Role PID balance loop, tilt cutoff, arming
Comms to Orin UART (velocity commands in, telemetry out)
Flash idf.py -p /dev/ttyUSB0 flash

ESP32-S3 IO

Spec Value
MCU ESP32-S3
Role VESC CAN driver, sensor polling, peripheral I/O
VESC IDs 68 (left), 56 (right)
Motor bus CAN 1Mbit/s
Flash idf.py -p /dev/ttyUSB1 flash

UART Assignments (ESP32-S3 BALANCE)

UART Connected To Baud
UART0 Orin Nano Super 115200
UART1 ESP32-S3 IO 115200
UART2 ELRS Receiver 420000 (CRSF)

Motor/ESC

  • 2× 8" pneumatic hub motors (36V, hoverboard type)
  • 2× VESC motor controllers (CAN IDs 68, 56)
  • VESC CAN protocol: standard SET_DUTY / SET_CURRENT / SET_RPM
  • Speed range: -1.0 to +1.0 (duty cycle)

Physical Dimensions (from cad/dimensions.scad)

Part Key Measurement
ESP32-S3 BALANCE board ~55×28mm (DevKit form factor)
ESP32-S3 IO board ~55×28mm (DevKit form factor)
Hub motor body Ø200mm (~8")
Motor axle Ø12mm, 45mm long
Orin Nano Super 100×79mm, M2.5 holes at 86×58mm
RealSense D435i 90×25×25mm, 1/4-20 tripod mount
RPLIDAR A1 Ø70×41mm, 4× M2.5 on Ø67mm circle
Kill switch hole Ø22mm panel mount
Battery pack ~180×80×40mm
VESC (each) ~70×50×15mm
2020 extrusion 20mm square, M5 center bore
Frame width ~350mm (axle to axle)
Frame height ~500-550mm total
Target weight <8kg (current estimate: 7.4kg)

3D Printed Parts (16 files in cad/)

Part Material Infill
motor_mount_plate (350×150×6mm) PETG 80%
battery_shelf PETG 60%
esc_mount PETG 40%
jetson_shelf PETG 40%
sensor_tower_top ASA 80%
lidar_standoff (Ø80×80mm) ASA 40%
realsense_bracket PETG 60%
esp32_balance_mount (vibration isolated) TPU+PETG
bumper front + rear (350×50×30mm) TPU 30%
handle PETG 80%
kill_switch_mount PETG 80%
tether_anchor PETG 100%
led_diffuser_ring (Ø120×15mm) Clear PETG 30%
esp32c3_mount PETG 40%

Firmware Architecture

Critical Lessons Learned (DON'T REPEAT THESE)

  1. NEVER auto-run untested code on_boot — we bricked the NSPanel 3x doing this. Test manually first.
  2. -(int)0 == 0 — checking if (-result) to detect errors doesn't work when result is 0. Always use explicit error codes.
  3. USB CDC needs RX primed in init — without it, the OUT endpoint never starts listening.
  4. Watchdog must be fed every loop iteration — if balance loop stalls, motors must cut within 50ms.
  5. Never change PID and speed limit in the same test — one variable at a time.

Build & Flash (ESP32-S3)

# Balance board
cd esp32/balance/
idf.py build && idf.py -p /dev/ttyUSB0 flash monitor

# IO board
cd esp32/io/
idf.py build && idf.py -p /dev/ttyUSB1 flash monitor

Dev machine: mbpm4 (seb@192.168.87.40)

Current Status & Known Issues

Working

  • IMU streaming (50Hz JSON: {"ax":...,"ay":...,"az":...,"gx":...,"gy":...,"gz":...})
  • VESC CAN communication (IDs 68, 56)
  • LED status patterns
  • Web UI with WebSerial + Three.js 3D visualization

In Progress

  • PID balance loop tuning
  • ELRS CRSF receiver integration
  • Orin UART integration

TODO (Priority Order)

  1. Tune PID balance loop on ESP32-S3 BALANCE
  2. Implement complementary filter (pitch angle)
  3. Wire ELRS receiver, implement CRSF parser
  4. Bench test (VESCs disconnected, verify PID output)
  5. First tethered balance test at 10% speed
  6. Orin UART integration
  7. LED subsystem (ESP32-S3 IO)

Communication Protocols

Orin → ESP32-S3 BALANCE (UART0, 50Hz)

struct { uint8_t header=0xAA; int16_t speed; int16_t steer; uint8_t mode; uint8_t checksum; };
// mode: 0=idle, 1=balance, 2=follow, 3=RC

ESP32-S3 IO → VESC (CAN, loop rate)

  • Standard VESC CAN protocol (SET_DUTY / SET_CURRENT / SET_RPM)
  • Node IDs: VESC 68 (left), VESC 56 (right)

ESP32-S3 BALANCE → Orin Telemetry (UART0 TX, 50Hz)

T:12.3,P:45,L:100,R:-80,S:3\n
// T=tilt°, P=PID output, L/R=motor commands, S=state (0-3)

ESP32-S3 → USB (50Hz JSON, debug/tuning)

{"ax":123,"ay":-456,"az":16384,"gx":10,"gy":-5,"gz":3,"t":250,"p":0}
// Raw IMU values (int16), t=temp×10, p=PID output

LED Subsystem (ESP32-S3 IO)

ESP32-S3 IO eavesdrops on BALANCE→Orin telemetry (listen-only). No extra UART needed.

State Pattern Color
Disarmed Slow breathe White
Arming Fast blink Yellow
Armed idle Solid Green
Turning Sweep direction Orange
Braking Flash rear Red
Fault Triple flash Red
RC lost Alternating flash Red/Blue

Printing (Bambu Lab)

  • X1C (192.168.87.190) — for structural PETG/ASA parts
  • A1 (192.168.86.161) — for TPU bumpers and prototypes
  • LAN access codes and MQTT details in main workspace MEMORY.md
  • STL export from OpenSCAD, slice in Bambu Studio

Rules for Agents

  1. Read SALTYLAB.md fully before making any design decisions
  2. Never remove safety checks from firmware — add more if needed
  3. All measurements go in cad/dimensions.scad — single source of truth
  4. Test firmware on bench before any motor test — VESCs disconnected, verify outputs on serial
  5. One variable at a time — don't change PID and speed limit in the same test
  6. Document what you change — update this file if you add pins, change protocols, or discover hardware quirks
  7. Ask before wiring changes — wrong connections can fry an ESP32 or VESC