- docs/: rewrite AGENTS.md, wiring-diagram.md (SAUL-TEE arch); update SALTYLAB.md, FACE_LCD_ANIMATION.md, board-viz.html, SALTYLAB-DETAILED refs - cad/: dimensions.scad FC params → ESP32-S3 BALANCE params - chassis/: ASSEMBLY.md, BOM.md, ip54_BOM.md, *.scad — FC_MOUNT_SPACING/ FC_PITCH → TBD ESP32-S3; Drone FC → MCU mount throughout - CLAUDE.md, TEAM.md: project desc → SAUL-TEE; hardware table → ESP32-S3/VESC - USB_CDC_BUG.md: marked ARCHIVED (legacy STM32 era) - AUTONOMOUS_ARMING.md: USB CDC → inter-board UART (ESP32-S3 BALANCE) - projects/saltybot/SLAM-SETUP-PLAN.md: FC/STM32F722 → BALANCE/CAN - jetson/docs/pinout.md, power-budget.md, README.md: STM32 bridge → CAN bridge - jetson/config/RECOVERY_BEHAVIORS.md: FC+Hoverboard → BALANCE+VESC - jetson/ros2_ws: stm32_protocol.py → esp32_protocol.py, stm32_cmd_node.py → esp32_cmd_node.py, mamba_protocol.py → balance_protocol.py; can_bridge_node imports updated - scripts/flash_firmware.py: DFU/STM32 → pio run -t upload - src/ include/: ARCHIVED headers added (legacy code preserved) - test/: ARCHIVED notices; STM32F722 comments marked LEGACY - ui/diagnostics_panel.html: Board/STM32 → ESP32-S3 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
7.7 KiB
7.7 KiB
AGENTS.md — SAUL-TEE Agent Onboarding
You're working on SAUL-TEE, a 4-wheel wagon robot. Read this entire file before touching anything.
Full system reference: docs/SAUL-TEE-SYSTEM-REFERENCE.md
Project Overview
A 4-wheel wagon robot (870×510×550 mm, 23 kg) with three compute layers:
- ESP32-S3 BALANCE (Waveshare Touch LCD 1.28) — QMI8658 IMU, PID drive / stability loop, CAN bus master for VESCs. Safety-critical layer. Firmware in
esp32/balance/. - ESP32-S3 IO (bare board) — RC input (TBS Crossfire + ELRS failover), BTS7960 motor drivers, I2C sensors (NFC/baro/ToF), WS2812 LEDs, accessories. Firmware in
esp32/io/. - Jetson Orin Nano Super — AI brain: ROS2, SLAM, Nav2, perception. Sends high-level velocity commands over CAN (0x300–0x303). Receives telemetry on CAN (0x400–0x401).
Orin (CAN 0x300-0x303) ←→ TBS Crossfire / ELRS (CRSF @ 420000)
│ │
▼ CAN 500kbps │ inter-board UART 460800
ESP32-S3 BALANCE ───────────────── ESP32-S3 IO
QMI8658 IMU BTS7960 × 4 motor drivers
PID loop NFC / baro / ToF (I2C)
SN65HVD230 CAN WS2812 LEDs
│ Horn / headlight / fan / buzzer
▼ CAN 500kbps
VESC left (ID 68) VESC right (ID 56)
│ │
Hub motors FL/RL Hub motors FR/RR
⚠️ SAFETY — READ THIS OR PEOPLE GET HURT
This is not a toy. 4× hub motors + 36V × high-current VESCs can crush fingers, break toes, and throw the 23 kg frame. Every firmware change must preserve these invariants:
- Motors NEVER spin on power-on. Requires deliberate arming: deliberate ARM command.
- RC kill switch — dedicated ELRS/Crossfire channel, checked every loop iteration. Always overrides.
- CAN watchdog — if no Orin heartbeat for 500 ms, drop to RC-only mode.
- ESTOP CAN frame — 0x303 with magic byte 0xE5 cuts all motors instantly.
- Inter-board heartbeat — if IO board misses BALANCE heartbeat for 200 ms, IO disables all BTS7960 enables.
- Speed hard cap — firmware limit, start at 10%. Increase only after proven stable.
- Never test without RC transmitter in hand.
If you break any of these, you are removed from the project.
Repository Layout
esp32/
├── balance/ — ESP32-S3 BALANCE firmware (PlatformIO)
│ ├── src/
│ │ ├── main.cpp
│ │ ├── config.h ← GPIO assignments — update here first
│ │ ├── imu_qmi8658.cpp/.h
│ │ ├── can_vesc.cpp/.h
│ │ └── protocol.cpp/.h
│ └── platformio.ini
├── io/ — ESP32-S3 IO firmware (PlatformIO)
│ ├── src/
│ │ ├── main.cpp
│ │ ├── config.h ← GPIO assignments — update here first
│ │ ├── rc_crsf.cpp/.h
│ │ ├── motor_bts7960.cpp/.h
│ │ └── protocol.cpp/.h
│ └── platformio.ini
└── shared/
└── protocol.h ← inter-board frame types — authoritative
src/ — LEGACY STM32 code (ARCHIVED — do not touch)
include/ — LEGACY STM32 headers (ARCHIVED — do not touch)
chassis/ — OpenSCAD mechanical parts
├── ASSEMBLY.md — assembly instructions
├── BOM.md — bill of materials
└── *.scad — parametric parts
docs/
├── SAUL-TEE-SYSTEM-REFERENCE.md ← MASTER REFERENCE — read this
├── AGENTS.md — this file
├── wiring-diagram.md — wiring reference (see SAUL-TEE-SYSTEM-REFERENCE.md)
└── SALTYLAB.md — legacy design doc (historical)
Hardware Quick Reference
ESP32-S3 BALANCE (Waveshare Touch LCD 1.28)
| Spec | Value |
|---|---|
| MCU | ESP32-S3, dual-core 240 MHz, 8MB flash, 8MB PSRAM |
| USB | CH343G USB-UART bridge (UART0 / GPIO43 TX, GPIO44 RX) |
| Display | 1.28" round GC9A01 240×240 (SPI, onboard) |
| IMU | QMI8658 6-axis (I2C-0 SDA=GPIO6, SCL=GPIO7, INT=GPIO3) |
| CAN | SN65HVD230 external transceiver (GPIO TBD — see esp32/balance/src/config.h) |
| Inter-board UART | UART1 (GPIO TBD) ↔ ESP32-IO @ 460800 baud |
ESP32-S3 IO (bare board)
| Peripheral | Interface | GPIO |
|---|---|---|
| TBS Crossfire RX | UART0 CRSF @ 420000 | GPIO43 TX / GPIO44 RX |
| ELRS failover RX | UART2 CRSF @ 420000 | TBD |
| BTS7960 FL/FR/RL/RR | PWM + GPIO | TBD — see config.h |
| I2C bus (NFC/baro/ToF) | I2C | TBD |
| WS2812B LEDs | RMT GPIO | TBD |
| Horn / headlight / fan / buzzer | GPIO/PWM | TBD |
| Inter-board UART | UART1 @ 460800 | TBD |
All TBD GPIO assignments are confirmed in
esp32/io/src/config.h.
CAN Bus
| Node | CAN ID | Notes |
|---|---|---|
| VESC left motor | 68 (0x44) | FSESC 6.7 Pro Mini Dual |
| VESC right motor | 56 (0x38) | FSESC 6.7 Pro Mini Dual |
| Orin → robot cmds | 0x300–0x303 | drive / arm / PID / ESTOP |
| BALANCE → Orin telemetry | 0x400–0x401 | attitude + battery + faults |
Physical Dimensions
| Parameter | Value |
|---|---|
| Robot (SAUL-TEE) | 870 × 510 × 550 mm, 23 kg |
| Hub motor axle base OD | Ø16.11 mm (caliper-verified) |
| Hub motor axle D-cut OD | Ø15.95 mm, 13.00 mm flat chord |
| Bearing seat collar | Ø37.8 mm |
| Tire | 10 × 2.125" pneumatic (Ø254 mm) |
| ESP32-S3 BALANCE PCB | ~40×40 mm (TBD — caliper before machining) |
| Orin carrier hole pattern | 58 × 49 mm M3 |
Inter-Board Protocol
UART @ 460800 baud, 8N1. Frame: [0xAA][LEN][TYPE][PAYLOAD…][CRC8]
CRC polynomial: CRC-8/MAXIM (poly 0x31, init 0x00, RefIn/RefOut true).
Authoritative message type definitions: esp32/shared/protocol.h
Build & Flash
# ESP32-S3 BALANCE
cd esp32/balance
pio run -t upload # Upload via USB (CH343 serial)
# ESP32-S3 IO
cd esp32/io
pio run -t upload # Upload via USB (JTAG/CDC)
Critical Lessons Learned
-(int)0 == 0— checkingif (-result)doesn't detect a zero error result. Use explicit error codes.- NEVER auto-run untested firmware on boot — we bricked hardware doing this. Test manually first.
- One variable at a time — never change PID gains and speed limit in the same test session.
- QMI8658 data ready — poll INT pin (GPIO3) or use interrupt; don't poll status register in a tight loop.
- CAN bus termination — 120 Ω at each physical end of the bus. Missing termination = unreliable comms.
LED States (WS2812B on ESP32-IO)
| State | Pattern | Color |
|---|---|---|
| Disarmed | Slow breathe | White |
| Arming | Fast blink | Yellow |
| Armed | Solid | Green |
| Turning | Sweep direction | Orange |
| Braking | Flash rear | Red |
| Fault / ESTOP | Triple flash | Red |
| RC lost | Alternating flash | Red/Blue |
Printing (Bambu Lab)
- X1C (192.168.87.190) — structural PETG/ASA parts
- A1 (192.168.86.161) — TPU bumpers, prototypes
- STL export from OpenSCAD, slice in Bambu Studio
Rules for Agents
- Read
docs/SAUL-TEE-SYSTEM-REFERENCE.mdfully before any design or firmware decision - Never remove safety checks — add more if needed
- All mechanical measurements go in
cad/dimensions.scad— single source of truth - Test firmware on bench first — VESCs/BTS7960 disconnected, verify outputs on serial
- GPIO assignments live in
config.h— change there, not scattered in source - Document hardware quirks here — if you find a gotcha, add a "Critical Lesson Learned"
- Ask before wiring changes — wrong connections can fry ESP32-S3 boards