Archive STM32 firmware to legacy/stm32/: - src/, include/, lib/USB_CDC/, platformio.ini, test stubs, flash_firmware.py - test/test_battery_adc.c, test_hw_button.c, test_pid_schedule.c, test_vesc_can.c, test_can_watchdog.c - USB_CDC_BUG.md Rename: stm32_protocol → esp32_protocol, mamba_protocol → balance_protocol, stm32_cmd_node → esp32_cmd_node, stm32_cmd_params → esp32_cmd_params, stm32_cmd.launch.py → esp32_cmd.launch.py, test_stm32_protocol → test_esp32_protocol, test_stm32_cmd_node → test_esp32_cmd_node Content cleanup across all files: - Mamba F722S → ESP32-S3 BALANCE - BlackPill → ESP32-S3 IO - STM32F722/F7xx → ESP32-S3 - stm32Mode/Version/Port → esp32Mode/Version/Port - STM32 State/Mode labels → ESP32 State/Mode - Jetson Nano → Jetson Orin Nano Super - /dev/stm32 → /dev/esp32 - stm32_bridge → esp32_bridge - STM32 HAL → ESP-IDF docs/SALTYLAB.md: - Update "Drone FC Details" to describe ESP32-S3 BALANCE board (Waveshare ESP32-S3 Touch LCD 1.28) - Replace verbose "Self-Balancing Control" STM32 section with brief note pointing to SAUL-TEE-SYSTEM-REFERENCE.md TEAM.md: Update Embedded Firmware Engineer role to ESP32-S3 / ESP-IDF No new functionality — cleanup only. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
12 KiB
Jetson Orin Nano Super — GPIO / I2C / UART / CSI Pinout Reference
<<<<<<< HEAD
Self-Balancing Robot: ESP32 Bridge + RealSense D435i + RPLIDAR A1M8 + 4× IMX219
=======
Self-Balancing Robot: ESP32-S3 Bridge + RealSense D435i + RPLIDAR A1M8 + 4× IMX219
291dd68(feat: remove all STM32/Mamba/BlackPill references — ESP32-S3 only)
Last updated: 2026-02-28 JetPack version: 6.x (L4T R36.x / Ubuntu 22.04)
40-Pin Header Overview
The Jetson Orin Nano Super 40-pin header is physically compatible with Raspberry Pi HATs. Pin numbering follows physical board pin (1–40).
3.3V [ 1] [ 2] 5V
SDA1 [ 3] [ 4] 5V ← I2C SDA (i2c-7 on Orin Nano)
SCL1 [ 5] [ 6] GND ← I2C SCL (i2c-7 on Orin Nano)
GPIO [ 7] [ 8] TXD0 ← UART TX (ttyTHS0)
GND [ 9] [10] RXD0 ← UART RX (ttyTHS0)
GPIO [11] [12] GPIO
GPIO [13] [14] GND
GPIO [15] [16] GPIO
3.3V [17] [18] GPIO
MOSI [19] [20] GND ← SPI1 MOSI
MISO [21] [22] GPIO ← SPI1 MISO
SCLK [23] [24] CE0 ← SPI1 CLK / CS0
GND [25] [26] CE1 ← SPI1 CS1
ID_SD[27] [28] ID_SC ← I2C ID EEPROM (reserved)
GPIO [29] [30] GND
GPIO [31] [32] GPIO
GPIO [33] [34] GND
GPIO [35] [36] GPIO
GPIO [37] [38] GPIO
GND [39] [40] GPIO
Note on Orin Nano I2C bus numbering: The 40-pin header I2C pins (3/5) map to
/dev/i2c-7 on Orin Nano (not i2c-1 as on the older Nano). Verify with:
ls /dev/i2c-*
i2cdetect -l
<<<<<<< HEAD
1. ESP32 Bridge (USB CDC — Primary)
The ESP32 BALANCE acts as a real-time motor + IMU controller. Communication is via USB CDC serial.
1. ESP32-S3 Bridge (USB Serial (CH343) — Primary)
The ESP32-S3 acts as a real-time motor + IMU controller. Communication is via USB Serial (CH343) serial.
291dd68(feat: remove all STM32/Mamba/BlackPill references — ESP32-S3 only)
USB Serial (CH343) Connection
| Connection | Detail |
|---|---|
| <<<<<<< HEAD | |
| Interface | USB on ESP32 BALANCE board → USB-A on Jetson |
| Device node | /dev/ttyACM0 → symlink /dev/esp32-bridge (via udev) |
| Baud rate | 921600 (configured in ESP32 BALANCE firmware) |
=======
| Interface | USB Micro-B on ESP32-S3 dev board → USB-A on Jetson |
| Device node | /dev/ttyACM0 → symlink /dev/esp32-bridge (via udev) |
| Baud rate | 921600 (configured in ESP32-S3 firmware) |
291dd68(feat: remove all STM32/Mamba/BlackPill references — ESP32-S3 only) | Protocol | JSON telemetry RX + ASCII command TX (see bridge docs) | | Power | Powered via robot 5V bus (data-only via USB) |
Hardware UART (Fallback — 40-pin header)
<<<<<<< HEAD | Jetson Pin | Signal | ESP32 Pin | Notes |
| Jetson Pin | Signal | ESP32-S3 Pin | Notes |
291dd68(feat: remove all STM32/Mamba/BlackPill references — ESP32-S3 only)Pin 8 (TXD0) TX → PA10 (UART1 RX) Cross-connect TX→RX Pin 10 (RXD0) RX ← PA9 (UART1 TX) Cross-connect RX→TX Pin 6 (GND) GND GND Common ground required
Jetson device node: /dev/ttyTHS0
Baud rate: 921600, 8N1
<<<<<<< HEAD
Voltage level: 3.3V — both Jetson Orin and ESP32 are 3.3V GPIO
Voltage level: 3.3V — both Jetson Orin and ESP32-S3 are 3.3V GPIO
291dd68(feat: remove all STM32/Mamba/BlackPill references — ESP32-S3 only)
# Verify UART
ls /dev/ttyTHS0
sudo usermod -aG dialout $USER
# Quick test
picocom -b 921600 /dev/ttyTHS0
| ROS2 Topic | Direction | Content |
|---|---|---|
/saltybot/imu |
ESP32 BALANCE→Jetson | IMU data (accel, gyro) at 50Hz |
/saltybot/balance_state |
ESP32 BALANCE→Jetson | Motor cmd, pitch, state |
/cmd_vel |
Jetson→ESP32 BALANCE | Velocity commands → C<spd>,<str>\n |
/saltybot/estop |
Jetson→ESP32 BALANCE | Emergency stop |
<<<<<<< HEAD ROS2 topics (ESP32 bridge node):
ROS2 topics (ESP32-S3 bridge node):
| ROS2 Topic | Direction | Content |
|---|---|---|
/saltybot/imu |
ESP32-S3→Jetson | IMU data (accel, gyro) at 50Hz |
/saltybot/balance_state |
ESP32-S3→Jetson | Motor cmd, pitch, state |
/cmd_vel |
Jetson→ESP32-S3 | Velocity commands → C<spd>,<str>\n |
/saltybot/estop |
Jetson→ESP32-S3 | Emergency stop |
291dd68(feat: remove all STM32/Mamba/BlackPill references — ESP32-S3 only)
2. RealSense D435i (USB 3.1)
Connection
| Parameter | Value |
|---|---|
| Interface | USB 3.1 Gen 1 (USB-A on Jetson — use blue port) |
| Device node | /dev/bus/usb/... (udev-managed) |
| USB PID:VID | 0x8086:0x0b3a (D435i) |
| Power draw | ~1.5W active, 3.5W peak during init |
| Cable | USB 3.1 — use short cable ≤1m for stability |
Note: Orin Nano Developer Kit has 2× USB-A + 1× USB-C. Use USB-A (blue = USB 3.1) for D435i.
lsusb | grep Intel
# Expected: Bus 002 Device 003: ID 8086:0b3a Intel Corp. Intel RealSense D435i
ROS2 topics:
| Topic | Type | Rate |
|---|---|---|
/camera/color/image_raw |
sensor_msgs/Image |
30Hz |
/camera/depth/image_rect_raw |
sensor_msgs/Image |
30Hz |
/camera/aligned_depth_to_color/image_raw |
sensor_msgs/Image |
30Hz |
/camera/imu |
sensor_msgs/Imu |
400Hz |
3. RPLIDAR A1M8 (UART via USB adapter)
Connection
| Parameter | Value |
|---|---|
| Interface | USB Micro-B (via included CP2102 USB-UART adapter) |
| Device node | /dev/ttyUSB0 → symlink /dev/rplidar (via udev) |
| Baud rate | 115200 |
| Power draw | ~2.6W motor on, 0.4W idle |
| Motor control | DTR line (handled by rplidar_ros driver) |
ls /dev/rplidar # should exist after udev rule applied
ros2 launch rplidar_ros rplidar_a1_launch.py serial_port:=/dev/rplidar
ROS2 topics:
| Topic | Type | Rate |
|---|---|---|
/scan |
sensor_msgs/LaserScan |
10Hz |
4. 4× IMX219 CSI Cameras (MIPI CSI-2)
The IMX219 (Sony 8MP) cameras connect via MIPI CSI-2 FFC cables to the Jetson Orin Nano.
CSI Connector Layout (Orin Nano Developer Kit)
The Orin Nano Developer Kit has two MIPI CSI-2 connectors:
- CSI-A (J5): 15-pin FFC — connects to ArduCam adapter A
- CSI-B (J8): 15-pin FFC — connects to ArduCam adapter B
Each ArduCam multi-camera adapter multiplexes 2× IMX219 cameras onto one CSI lane.
ArduCam Multi-Camera Adapter Wiring
| Adapter | Cameras | CSI Connector | V4L2 Devices |
|---|---|---|---|
| Adapter A (CSI-A) | front + left | J5 | /dev/video0, /dev/video2 |
| Adapter B (CSI-B) | rear + right | J8 | /dev/video4, /dev/video6 |
IMX219 15-pin FFC Pinout (each camera module)
| Pin | Signal | Notes |
|---|---|---|
| 1 | GND | |
| 2 | CSI D0- | MIPI data lane 0 negative |
| 3 | CSI D0+ | MIPI data lane 0 positive |
| 4 | GND | |
| 5 | CSI D1- | MIPI data lane 1 negative |
| 6 | CSI D1+ | MIPI data lane 1 positive |
| 7 | GND | |
| 8 | CSI CLK- | MIPI clock negative |
| 9 | CSI CLK+ | MIPI clock positive |
| 10 | GND | |
| 11 | CAM_GPIO | Camera enable (active high) |
| 12 | CAM_CLK | I2C / control clock |
| 13 | CAM_SDA | I2C data |
| 14 | GND | |
| 15 | 3.3V | Power |
V4L2 Device Nodes
# List all video devices
v4l2-ctl --list-devices
# Expected output:
# vi-output, imx219 2-0010 (platform:tegra-capture-vi:0):
# /dev/video0
# vi-output, imx219 2-0010 (platform:tegra-capture-vi:1):
# /dev/video2
# vi-output, imx219 4-0010 (platform:tegra-capture-vi:2):
# /dev/video4
# vi-output, imx219 4-0010 (platform:tegra-capture-vi:3):
# /dev/video6
# Capture test (GStreamer)
gst-launch-1.0 nvarguscamerasrc sensor-id=0 ! \
'video/x-raw(memory:NVMM),width=640,height=480,framerate=30/1' ! \
nvvidconv ! xvimagesink
# Capture via V4L2
v4l2-ctl --device=/dev/video0 --stream-mmap --stream-count=1 \
--set-fmt-video=width=640,height=480,pixelformat=RG10
ROS2 Topics (saltybot_cameras package)
| Topic | Camera | Type | Rate |
|---|---|---|---|
/camera/front/image_raw |
front (video0) | sensor_msgs/Image |
30Hz |
/camera/left/image_raw |
left (video2) | sensor_msgs/Image |
30Hz |
/camera/rear/image_raw |
rear (video4) | sensor_msgs/Image |
30Hz |
/camera/right/image_raw |
right (video6) | sensor_msgs/Image |
30Hz |
TF Frames
| Camera | Frame ID | Offset from sensor_head_link |
|---|---|---|
| front | camera_front_link |
x=+0.05m, yaw=0° |
| left | camera_left_link |
y=+0.05m, yaw=+90° |
| rear | camera_rear_link |
x=-0.05m, yaw=180° |
| right | camera_right_link |
y=-0.05m, yaw=-90° |
5. I2C Bus (i2c-7) — Pin 3 / Pin 5
Available for future peripherals (IMU breakout, OLED display, etc.).
| Parameter | Value |
|---|---|
| Jetson I2C bus | i2c-7 (pins 3 = SDA, 5 = SCL) on Orin Nano |
| Voltage | 3.3V pull-up |
| Max clock | 400kHz (Fast Mode) |
# Scan i2c-7 bus
i2cdetect -y -r 7
Note: i2c-0 (pins 27/28) is reserved for EEPROM ID — do not use.
6. M.2 NVMe Storage
The Orin Nano Developer Kit includes an M.2 Key M slot.
| Parameter | Value |
|---|---|
| Interface | PCIe Gen 3 ×4 |
| Form factor | M.2 2230 / 2242 / 2280 |
| Recommended | 256GB+ NVMe SSD (e.g., WD SN530, Samsung PM991) |
| Mount point | /mnt/nvme |
# Verify NVMe detected
lsblk | grep nvme
nvme list
# Partition + format (one-time setup — see setup-jetson.sh)
sudo parted /dev/nvme0n1 mklabel gpt
sudo parted /dev/nvme0n1 mkpart primary ext4 0% 100%
sudo mkfs.ext4 /dev/nvme0n1p1
sudo mkdir -p /mnt/nvme
7. USB Ports Summary
| Port | Type | Used For |
|---|---|---|
| USB-A (top, blue) | USB 3.1 Gen 1 | RealSense D435i |
| USB-A (bottom) | USB 2.0 | RPLIDAR (via USB-UART adapter) |
| <<<<<<< HEAD | ||
| USB-C | USB 3.1 Gen 1 (+ DP) | ESP32 CDC or host flash |
======= | USB-C | USB 3.1 Gen 1 (+ DP) | ESP32-S3 CDC or host flash |
291dd68(feat: remove all STM32/Mamba/BlackPill references — ESP32-S3 only) | Micro-USB | Debug/flash | JetPack flash only |
8. GPIO Summary Table
| Physical Pin | Function | Voltage | Used For |
|---|---|---|---|
| 3 | SDA1 | 3.3V | I2C data (i2c-7) |
| 5 | SCL1 | 3.3V | I2C clock (i2c-7) |
| <<<<<<< HEAD | |||
| 8 | TXD0 | 3.3V | UART TX → ESP32 BALANCE (fallback) |
| 10 | RXD0 | 3.3V | UART RX ← ESP32 BALANCE (fallback) |
| USB-A ×2 | — | 5V | D435i, RPLIDAR |
| USB-C | — | 5V | ESP32 CDC |
======= | 8 | TXD0 | 3.3V | UART TX → ESP32-S3 (fallback) | | 10 | RXD0 | 3.3V | UART RX ← ESP32-S3 (fallback) | | USB-A ×2 | — | 5V | D435i, RPLIDAR | | USB-C | — | 5V | ESP32-S3 CDC |
291dd68(feat: remove all STM32/Mamba/BlackPill references — ESP32-S3 only) | CSI-A (J5) | MIPI CSI-2 | — | Cameras front + left | | CSI-B (J8) | MIPI CSI-2 | — | Cameras rear + right | | M.2 Key M | PCIe Gen3 ×4 | — | NVMe SSD |
9. udev Rules
Apply stable device names:
# /etc/udev/rules.d/99-saltybot.rules
# RPLIDAR A1M8 (SiliconLabs CP2102)
KERNEL=="ttyUSB*", ATTRS{idVendor}=="10c4", ATTRS{idProduct}=="ea60", \
SYMLINK+="rplidar", MODE="0666"
<<<<<<< HEAD
# ESP32 USB CDC (STMicroelectronics)
=======
# ESP32-S3 USB Serial (CH343) (STMicroelectronics)
>>>>>>> 291dd68 (feat: remove all STM32/Mamba/BlackPill references — ESP32-S3 only)
KERNEL=="ttyACM*", ATTRS{idVendor}=="0483", ATTRS{idProduct}=="5740", \
SYMLINK+="esp32-bridge", MODE="0666"
# Intel RealSense D435i
SUBSYSTEM=="usb", ATTRS{idVendor}=="8086", ATTRS{idProduct}=="0b3a", \
MODE="0666"
# IMX219 CSI cameras (V4L2)
KERNEL=="video[0246]", SUBSYSTEM=="video4linux", MODE="0666"
sudo cp docs/99-saltybot.rules /etc/udev/rules.d/
sudo udevadm control --reload-rules && sudo udevadm trigger