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

244 lines
8.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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)
```bash
# 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)
```c
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)
```json
{"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 BALANCEOrin 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