Assemble a small differential-drive robot, wire compute GPIOs to an L298N dual H-bridge, publish /cmd_vel and /odom, then hand the same stack to an AI agent (OpenClaw, Claude, Codex, Hermes, Gemini).
This page is hardware only. After the base moves and RealSense publishes, continue with Robot setup (ROS 2 + OpenClaw plugin) and the CLI README (npx agenticros init).
cmd_vel → motor controller → GPIO / Firmata → L298N → left + right TT motors
└── /odom (dead-reckon from cmd_vel, or Firmata wheel encoders)
RealSense D4xx → /camera/... (color + depth)
The reference robot is a DFRobot 2WD chassis with two encoded TT motors, an L298N, a RealSense depth camera, and a Jetson Orin Nano. Swap the compute for a Raspberry Pi 5, RADXA X4, LattePanda Delta 3, or any x86 box plus an Arduino running Firmata — the L298N wiring pattern stays the same; only the pin numbers and agenticros start motors -b backend change.
Prices are approximate USD (what this kit cost to buy). Shop around; they move.
| Qty | Item | Approx. | Link |
|---|---|---|---|
| 1 | NVIDIA Jetson Orin Nano Developer Kit | $384.90 | Amazon |
| 1 | RealSense Depth Camera D436 | $354 | RealSense store |
| 1 | DFRobot 2WD MiniQ chassis (includes wheels without encoders) | $34.90 | DFRobot |
| 2 | TT geared motor with encoder, 6V 160RPM 120:1 L-shape (FIT0458) - only needed if you want encoders | $14.80 | DFRobot |
| 1 | L298N H-bridge (from a 4-pack) | $2.50 | Amazon |
| 1 | Patriot 512GB NVMe (Jetson has no onboard SSD) | $88 | Amazon |
Accessories
| Qty | Item | Approx. | Link |
|---|---|---|---|
| 1 | 3S LiPo (motors / L298N only — not the Jetson) | $27 | Amazon |
| 1 | Logitech wireless keyboard + trackpad | $27 | Amazon |
| 1 | DisplayPort cable | $9 | Amazon |
| 1 | Velcro | $10 | — |
| 1 | Dupont / GPIO jumper wires | $5 | — |
| 1 | Adjustable AC/DC adapter (set to 19V for the Orin Nano Dev Kit) | $15.49 | Amazon |
Reference total ≈ $973 with a D436. A RealSense D421 (~$85) drops the kit to ≈ $704. D421 is depth-oriented; color snapshots and “what do you see?” work better on D436 / D435 / D435i. Start RealSense with agenticros start realsense --model=D421 if that is the camera you have (also auto-selected when the cloud portal camera field is D421).
Wheel-encoder odometry is implemented on the Firmata motor backend only. Jetson GPIO and Raspberry Pi GPIO publish /odom by dead-reckoning cmd_vel (good enough to start). To use the TT motor encoder wires, add an Arduino and flash StandardFirmata:
| Qty | Item | Notes |
|---|---|---|
| 1 | Arduino Uno / Mega / Nano (USB) | Default Firmata device /dev/ttyACM0. LattePanda Delta 3 already has an onboard Leonardo — no extra board. |
Jetson native GPIO is opt-in (-b jetson). Every other board in this table defaults to Firmata unless you pass -b rpi.
| Compute | Motor backend | Notes |
|---|---|---|
| Jetson Orin Nano | -b jetson (must pass this) or -b firmata |
Native: install JETGPIO. Firmata: USB Arduino. |
| Raspberry Pi 5 | -b rpi (auto if portal compute is Raspberry Pi) |
BCM pin numbers via @iiot2k/gpiox. |
| RADXA X4 | -b firmata (default) |
USB Arduino running StandardFirmata. |
| LattePanda Delta 3 | -b firmata (default) |
Flash StandardFirmata to the onboard ATmega32u4 (Arduino Leonardo). |
| Any x86 Intel NUC / mini PC | -b firmata (default) |
USB Arduino running StandardFirmata. |
Install Firmata once (Arduino IDE → File → Examples → Firmata → StandardFirmata → Upload). Official sketch and protocol: firmata/arduino. Then sudo usermod -aG dialout $USER and reboot so /dev/ttyACM0 is usable without root.
- Compute (Jetson / Pi / NUC) gets its own supply. Orin Nano Dev Kit: barrel jack, typically 19V. Do not feed the Jetson from the L298N 5V pin or from the 3S pack directly.
- Motors get the 3S LiPo on the L298N +12V / VCC screw terminal (motor supply), with battery GND tied to L298N GND and compute GND.
- The TT motors are rated 6V (3–7.5V). A 3S pack is ~11.1–12.6V; the L298N drops ~1.5–2V, so the motors still see more than 6V. Keep PWM conservative on first tests, or use a 2S pack if the gearboxes run hot.
- Encoder logic is 4.5–7.5V (FIT0458 wiki). Power encoder VCC from Arduino 5V or the Jetson/Pi header 5V pin — not 3.3V.
- Remove the L298N 5V jumper when motor voltage is ~12V, and feed the L298N +5V logic pin from the compute 5V rail (or leave the jumper only if your motor supply is well under 12V).
- Put the robot on blocks until
/cmd_veldirection is correct.
One module drives both wheels:
| L298N | Role |
|---|---|
| IN1 / IN2 | Left motor direction (and, on Pi/Jetson, PWM) |
| ENA | Left motor enable / PWM (jumpered HIGH on Pi/Jetson; Arduino PWM pin on Firmata) |
| OUT1 / OUT2 | Left motor + / − |
| IN3 / IN4 | Right motor direction |
| ENB | Right motor enable / PWM |
| OUT3 / OUT4 | Right motor + / − |
| +12V | Motor battery (3S +) |
| GND | Common ground |
| +5V | Logic (see jumper note above) |
agenticros start motors pin lists are left-to-right. If a side runs backward, swap that motor’s OUT leads or swap that side’s two IN numbers in -p.
BOARD numbering (physical 40-pin header). Defaults match motors-jetson.js: 16, 18, 22, 26.
Drive style: two-pin PWM per motor (PWM on one IN, the other IN held low). ENA and ENB stay jumpered / tied HIGH — they are not in the pin list.
-p order |
Meaning | Orin Nano BOARD pin | L298N |
|---|---|---|---|
| 1 | Left forward PWM | 16 | IN1 |
| 2 | Left reverse PWM | 18 | IN2 |
| 3 | Right forward PWM | 22 | IN3 |
| 4 | Right reverse PWM | 26 | IN4 |
| — | Left enable | jumper / 5V | ENA |
| — | Right enable | jumper / 5V | ENB |
| — | Ground | header GND (e.g. pin 14, 20, 30, 34) | GND |
Jetson 40-pin L298N motors
BOARD 16 ───────────────────────── IN1
BOARD 18 ───────────────────────── IN2 OUT1/OUT2 ────── left TT +/−
BOARD 22 ───────────────────────── IN3
BOARD 26 ───────────────────────── IN4 OUT3/OUT4 ────── right TT +/−
5V ── (logic, if 5V jumper off) +5V
GND ───────────────────────── GND ── LiPo −
+12V ── LiPo +
ENA/ENB jumpers ON (enable always high)
Requires system JETGPIO (libjetgpio.so) and often root / /dev/mem. This backend is never auto-selected.
agenticros start motors -b jetson -p 16,18,22,26 --odomBCM numbers (not physical pin numbers). Defaults: 27, 22, 17, 18. Same two-pin PWM + ENA/ENB jumpered as Jetson.
-p order |
Meaning | BCM | Header pin | L298N |
|---|---|---|---|---|
| 1 | Left forward PWM | 27 | 13 | IN1 |
| 2 | Left reverse PWM | 22 | 15 | IN2 |
| 3 | Right forward PWM | 17 | 11 | IN3 |
| 4 | Right reverse PWM | 18 | 12 | IN4 |
| — | Enables | jumper | — | ENA, ENB |
| — | Ground | GND | 6, 9, 14, … | GND |
agenticros start motors -b rpi -p 27,22,17,18 --odomSix pins: dir, cdir, pwm per motor. PWM goes to ENA/ENB (remove those jumpers). Defaults: 3, 4, 5, 7, 8, 9. Pins 5 and 9 must be PWM-capable.
-p order |
Meaning | Arduino | L298N |
|---|---|---|---|
| 1 | Left dir | 3 | IN1 |
| 2 | Left cdir | 4 | IN2 |
| 3 | Left PWM | 5 | ENA (jumper off) |
| 4 | Right dir | 7 | IN3 |
| 5 | Right cdir | 8 | IN4 |
| 6 | Right PWM | 9 | ENB (jumper off) |
sudo usermod -aG dialout $USER # once, then reboot
agenticros start motors -b firmata -d /dev/ttyACM0 -p 3,4,5,7,8,9 --odomFlash StandardFirmata before this will enumerate: Arduino Firmata README (IDE: File → Examples → Firmata → StandardFirmata → Upload). On LattePanda Delta 3, select board Arduino Leonardo and the onboard USB serial port.
Two modes share the same motor process. Geometry: DFRobot MiniQ + TT wheels are close to the code defaults 64 mm diameter and 135 mm track. Set those on the robot profile at agenticros register, or pass --odom to use the defaults.
| Mode | Backends | How it works |
|---|---|---|
Dead-reckon (cmd_vel) |
rpi, jetson, firmata | Integrates the last Twist. No extra wires. Always start here. |
| Encoders | firmata only | -e leftA,leftB,rightA,rightB. Polls quadrature-lite on the Arduino. |
Dead-reckon (any backend):
agenticros start motors -b jetson -p 16,18,22,26 --odom
# or Pi / Firmata equivalents aboveEach motor has a 2-pin motor cable (to L298N OUT) and a 4-pin encoder cable:
| Encoder | Connect to |
|---|---|
| VCC | Arduino 5V or compute header 5V (4.5–7.5V) |
| GND | Common GND |
| A (phase A) | Firmata leftA / rightA |
| B (phase B) | Firmata leftB / rightB |
Default -e is 13, 2, 12, 11 (leftA, leftB, rightA, rightB) — chosen so it does not collide with motor pins 3,4,5,7,8,9.
agenticros start motors -b firmata -d /dev/ttyACM0 \
-p 3,4,5,7,8,9 \
-e 13,2,12,11 \
--tpr 960 --odomDFRobot documents 8 pulses per motor revolution × 120:1 ≈ 960 counts per wheel turn. Pass --tpr 960 (or set ticksPerRevolution on the cloud profile). If /odom distance is 2× or ½× a tape-measure run, double or halve --tpr. The CLI fallback 351 is a different robot — do not use it on these TT motors.
On Jetson/Pi without Arduino, leave the encoder signal wires unconnected (tape them). /odom still publishes from cmd_vel when --odom is set.
On the robot, with ROS 2 Humble or Jazzy already installed (JetPack 6 → Ubuntu 22.04 → Humble is the usual Jetson path):
npx agenticros init
# optional cloud: agenticros login && agenticros register
# compute: "Nvidia Orin" | camera: D436 (or D421) | wheels: 64 mm / 135 mm
agenticros start motors -b jetson -p 16,18,22,26 --odom
agenticros start realsensePi: -b rpi -p 27,22,17,18. Firmata boards: omit -b jetson / -b rpi and pass -d /dev/ttyACM0.
One-shot demo stack (RealSense + motors + MCP): agenticros up real (add --no-motors / --no-camera as needed). Full flags: CLI README.
Check motion (robot on blocks). Publish a slow Twist on the topic your controller subscribed to (/cmd_vel or /<namespace>/cmd_vel):
# via AgenticROS MCP / OpenClaw later; for a smoke test from another ROS node:
# geometry_msgs/Twist linear.x = 0.15, angular.z = 0Wheels should both spin “forward.” Swap OUT leads or the two IN pins for a side that is mirrored. Then:
ros2 topic echo /odom --once(or the namespaced/<ns>/odom) should show a pose updating while you command motion.ros2 topic hz /camera/camera/color/image_raw(or the compressed topic) for RealSense. Recovery-mode cameras: Cameras.- If Twist publishes but nothing moves: Robot not receiving cmd_vel.
Hardware is done when /cmd_vel turns wheels, /odom exists, and a camera topic is live. Then:
- Robot setup — ROS 2 workspace, rosbridge or local DDS, and installing the OpenClaw plugin (
./scripts/setup_gateway_plugin.sh). - Root README —
npx agenticros, First-time setup,agenticros up real. - CLI reference and packages/agenticros-cli/README.md — motors, RealSense, cloud login.
- OpenClaw on the robot (Mode A) or laptop (Mode B) — plugin tools, teleop, chat. LAN dashboard notes: OpenClaw plugin routes.
- Local VLM / Ollama — no cloud API key.
- Other agents, same robot: MCP setup, Codex, Hermes, NemoClaw on Jetson.
Try: “drive forward slowly”, “what do you see?”, /estop.
| Symptom | Check |
|---|---|
start motors does nothing on Jetson |
You must pass -b jetson. Default for non-Pi compute is Firmata (expects /dev/ttyACM0). JETGPIO installed? May need sudo. |
| One wheel dead | ENA/ENB jumper (Pi/Jetson) or PWM pin actually on ENA/ENB (Firmata). Common GND. |
| Both wheels spin the wrong way | Invert by swapping both motors’ OUT leads, or swap each pair in -p. |
| Encoders ignored | Only -b firmata -e …. Encoder VCC must be 5V, not 3.3V. |
| Jetson brown-out when motors start | Motors on LiPo, compute on 19V adapter; common GND only. |
| RealSense no publishers | rs-enumerate-devices; recovery mode steps in cameras.md. |