Skip to main content

OpenCastor v2026.3.11.0: Plug-and-Play Hardware Detection + LeRobot Support

5 min read By Craig Merry
opencastor robotics hardware lerobot reachy release

OpenCastor v2026.3.11.0: Plug-and-Play Hardware Detection + LeRobot Support

The most annoying part of setting up a new robot isn’t the assembly. It’s the port config. You plug in your servo controller, and then you spend twenty minutes figuring out whether it’s /dev/ttyUSB0 or /dev/ttyUSB1, whether the baud rate is 57600 or 1000000, and whether you actually have the right USB driver installed. Multiply that by every sensor, camera, and accelerator on the robot.

v2026.3.11.0 fixes this. castor scan now auto-detects 12+ hardware types and tells you exactly which RCAN profile to use.

What Shipped

  • castor scan with 12+ detectors — USB VID/PID, I2C, PCIe, mDNS network discovery
  • FeetechDriver — native serial bus servo driver for SO-ARM101 and LeRobot kits
  • ReachyDriver — Pollen Robotics Reachy 2 and Reachy Mini via gRPC, host: auto
  • port: auto wiring across ODrive, Dynamixel, LiDAR, and Feetech drivers
  • RCAN profiles for SO-ARM101 (follower/leader/bimanual), Koch arm, ALOHA, Reachy 2, Reachy Mini
  • invalidate_usb_descriptors_cache() for hot-plug scenarios

Run pip install opencastor and then:

castor scan

It’ll print a table of everything it found, with suggested profiles for each device.

How castor scan Works Under the Hood

The scanner runs four discovery channels:

USB VID/PID: Calls lsusb once (memoized — no matter how many detectors run, you pay that cost exactly once) and matches descriptors against a table of known VID/PID combinations. Intel RealSense is 0x8086, Luxonis OAK-D is 0x03E7, Arduino official boards are 0x2341. For ambiguous VIDs like 0x1A86 (CH340), we inspect the product string to disambiguate.

I2C address scan: Queries all /dev/i2c-* buses and matches addresses against a built-in lookup table. 0x28/0x29 is a BNO055. 0x68/0x69 is an MPU6050. 0x76/0x77 is a BME280. You get the name, not just the hex address.

PCIe + sysfs: Checks /dev/hailo0 and runs lspci to detect Hailo-8 NPUs. Google Coral M.2 is detected via /dev/apex_*; Coral USB via VID 0x1A6E.

mDNS / network: Probes reachy.local, reachy2.local, and reachy-mini.local in concurrent daemon threads using socket.getaddrinfo() — the main thread never blocks. Timeout is 2 seconds. If none of those respond, falls back to mDNS _reachy._tcp.local. service discovery via zeroconf. If your Reachy is on the network, it shows up.

LeRobot Kit Support

Hugging Face’s LeRobot ecosystem has gotten a lot of traction this year, particularly around the SO-ARM101 5-DOF arm and the Koch arm. Until now, connecting those to OpenCastor meant manually wiring up the serial port and writing a config from scratch.

Now there are proper profiles:

castor hub install lerobot/so-arm101-follower
castor run --config so-arm101-follower.yaml

The FeetechDriver handles the Feetech STS3215 serial bus servos over USB. Set port: auto and it finds the CH340 adapter automatically:

drivers:
- id: arm
  protocol: feetech
  port: auto
  baud: 1000000
  servo_ids: [1, 2, 3, 4, 5, 6]

For Koch arm users, there’s lerobot/koch-arm (Dynamixel XL430/XL330 via U2D2) and lerobot/aloha for ALOHA bimanual setups.

pip install opencastor[lerobot]

The intended workflow: use LeRobot for dataset recording and policy training, deploy via OpenCastor. They don’t conflict — run both on the same machine.

Reachy Driver

The Pollen Robotics Reachy is a genuinely impressive piece of hardware. Full 7-DOF arms, stereo cameras, runs its own compute. It exposes a gRPC API via reachy2-sdk.

ReachyDriver wraps that API and adds host: auto discovery:

drivers:
- id: reachy
  protocol: reachy
  host: auto

On startup, the driver probes reachy.local, reachy2.local, and reachy-mini.local in parallel. Whichever answers first is used — there’s no preference ordering. If none respond, it falls back to mDNS _reachy._tcp.local. service discovery.

pip install opencastor[reachy]

The Fix That Mattered Most

The CH340 disambiguation issue was genuinely annoying in testing. CH340 is the cheapest USB-serial bridge chip on earth, so basically every Arduino clone, Feetech USB adapter, and random serial dongle uses it. They all share VID 0x1A86.

The old detect_feetech_usb() was matching any CH340 port and suggesting lerobot/so-arm101-follower. If you had an Arduino Nano clone plugged in, it would misidentify it as a LeRobot arm.

The fix: inspect the USB product string. Feetech USB adapters have a distinct product string. Arduino clones have theirs. We match on both now, not just the VID. Similarly, _auto_detect_vesc_port() was falling through to ODrive-USB as a fallback — that’s been removed entirely, so there’s no cross-contamination between VESC and ODrive detection paths.

What’s Next

The current detection is what I’d call Tier 1 — high-confidence identification of the most common hardware via exact VID/PID matches. Tier 2 detectors (coming soon) will add:

  • Heuristic detection for devices without consistent VIDs (some VESC-based boards use generic CH340 VIDs)
  • Camera resolution probing to distinguish similar USB cameras
  • I2C bus enumeration for multi-bus systems (Jetson has multiple I2C controllers)
  • CAN bus device scanning for multi-node setups

Try It

pip install opencastor[lerobot]   # for SO-ARM101 / Koch arm / ALOHA
pip install opencastor[reachy]    # for Pollen Robotics Reachy 2 / Mini
castor scan                       # detect what's connected

Full docs at opencastor.com/hardware and in the repo under docs/hardware/.