claudeDroneteam-docs
documentation · reference
Docs reference

Structured knowledge from collected_doc_media/claudedrone_docs/. Browse the tree on the left; the source of truth is markdown in the repo.

VL53L0X×6 array design on RPi 5 (research)

XSHUT + Adafruit CircuitPython continuous mode (no TCA9548A mux) for a 6-sensor array. Realistic ~25-30 Hz total, <10% CPU on RPi 5, latency <50 ms.

stableresearchbestupdated 2026-05-11T00:00:00.000ZClaudeDroneResearchVL53L0XArrayST_AN4846RPi5

Context

For 360° indoor coverage, the drone needs an array of 6 VL53L0X around the frame. All sensors have the default I²C address 0x29 → conflict. Two solutions: TCA9548A multiplexer or XSHUT-based re-addressing (ST AN4846).

Final recommendation: XSHUT + Adafruit CircuitPython continuous mode

Why:

  1. ST AN4846 — official approach; the only one without reliability caveats. TCA9548A in continuous mode occasionally blocks the I²C bus (Adafruit forum issue #27 + thread t=126621).
  2. Adafruit CircuitPython VL53L0X — actively maintained Python lib, RPi-ready, multi-sensor example for 2 sensors out of the box (extends to 6 by simply adding XSHUT pins).
  3. Realistic ~25-30 Hz total array on RPi 5 in Python (timing budget 20 ms, continuous mode, polling data_ready). 3× headroom over 10 Hz mission FSM.
  4. CPU < 10% on one core of RPi 5, event → publish latency < 25 ms.

Pattern: XSHUT + software re-addressing

  1. At start, all sensors in reset (XSHUT LOW via GPIO).
  2. Bring up XSHUT HIGH on one sensor at a time; write a unique address (0x30, 0x31, …) over I²C.
  3. Address change is not permanent — the procedure repeats on each power cycle.
  4. After init — all sensors on unique addresses, call start_continuous() on each.
  5. Polling cycle via data_ready property — sequential read; sensors physically work in parallel.

GPIO budget: 6 GPIOs for the XSHUT lines (RPi 5 has 27 GPIOs — not a blocker).

Realistic Hz numbers (estimates)

Scenario Per-sensor Total array Comments
A — Naive (sleep 1s) 1 Hz ~1 Hz unusable (Pololu forum)
B — Continuous + polling data_ready (TB=20 ms) 3-5 Hz ~25-30 Hz chosen — enough for 10 Hz FSM
C — GPIO interrupt + select() 30-40 Hz ~200-240 Hz +12 GPIO, RT issues
D — C++ rclcpp + ioctl + interrupt ~50 Hz ~300 Hz bandwidth limit

Comparison of approaches

XSHUT (ST AN4846 recommended) ✅

  • ✅ Native ST approach; references everywhere
  • ✅ Continuous mode without bus blocking
  • ✅ Sensor-failure isolation (cut a faulty one via XSHUT)
  • ❌ N GPIOs for N sensors
  • ❌ Software re-addressing on every power cycle

TCA9548A multiplexer ❌

  • ✅ Minimum GPIO (only 2 for I²C + power to TCA itself)
  • ✅ Built-in in Pimoroni VL53L0X-python
  • ❌ Channel-switching overhead (~25-300 µs per switch)
  • ❌ Reliability issues — Adafruit issue #27 (unresolved), forum 126621 — blocks the I²C bus in continuous mode
  • ❌ Reset / fail recovery harder

Driver landscape (gap analysis)

Driver Lang Multi-sensor RPi 5 ready Maintained Verdict
Adafruit_CircuitPython_VL53L0X Python ✅ XSHUT ✅ via blinka ✅ active 2024+ baseline pick
Gadgetoid/VL53L0X-python Python ctypes ✅ XSHUT + TCA9548A ⚠ stale ❌ since 2018 fallback / reference
sebastiengemme/vl53l0x-ros C++ ❌ single unspec unspec old, single-sensor
slaghuis/ROS2-VL53L1X C++/Py ❌ single ✅ ⚠ for VL53L1X, not L0X
Custom rclpy wrapper on Adafruit Python ✅ ✅ self our path

Gap analysis: a production-grade ROS 2 driver for a VL53L0X array doesn’t exist in 2024-2025. All candidates are single-sensor / ROS1 / stale. → write our own researchbest_drone/sensors/vl53l0x_array_node.py over Adafruit.

Minimal code (skeleton)

import board, busio
from digitalio import DigitalInOut, Direction
import adafruit_vl53l0x

# 6 XSHUT pins on RPi 5 GPIO 5, 6, 12, 13, 16, 17
xshut_pins = [board.D5, board.D6, board.D12, board.D13, board.D16, board.D17]
xshuts = [DigitalInOut(p) for p in xshut_pins]
for x in xshuts:
    x.direction = Direction.OUTPUT
    x.value = False  # all in reset

i2c = busio.I2C(board.SCL, board.SDA)

sensors = []
for idx, x in enumerate(xshuts):
    x.value = True             # bring up sensor N
    time.sleep(0.05)
    s = adafruit_vl53l0x.VL53L0X(i2c)
    s.set_address(0x30 + idx)  # unique address
    s.measurement_timing_budget = 20000   # 20 ms = 50 Hz max
    s.start_continuous()
    sensors.append(s)

# In the ROS 2 node:
while rclpy.ok():
    for idx, s in enumerate(sensors):
        if s.data_ready:
            rng = s.range          # mm
            self.publish_range(idx, rng / 1000.0)
    time.sleep(0.005)

Wiring

  • 6× VL53L0X: VCC + GND + SDA + SCL + XSHUT = 30 wires
  • A custom PCB or ribbon cable is recommended — not spaghetti
  • Common GND mandatory
  • I²C pull-ups on 3.3 V (often built into the breakout, verify)

RPi 5 I²C @ 400 kHz config

/boot/config.txt:

dtparam=i2c_baudrate=400000
core_freq=250

I²C physical-layer baseline: a 10-sensor read at 400 kHz = ~2.5 ms.

Skip

  • TCA9548A multiplexer — ~1.5× more channel-switch overhead + reliability issues. Minimum-GPIO advantage isn’t justified on RPi 5 (27 GPIOs).
  • Naive single-shot mode with sleep between reads — ~1 Hz/sensor, bottleneck without cause.
  • Pimoroni Gadgetoid/VL53L0X-python in production — stale since 2018, RPi 5 compatibility not guaranteed.

Risks

  1. VL53L0X 2 m max range may be too short for open spaces — fallback to VL53L1X (4 m) or a hybrid of forward-facing TF-Luna + VL53L0X×4-5 side/up/down.
  2. Adafruit Blinka on RPi 5 BCM2712 — not first-class supported. Risk mitigation: smbus2 fallback (lower-level).
  3. GPIO interrupt latency variance on non-RT Linux — Scenario C needs chrt RR scheduling.

Effort estimate

  • 4 hours: write vl53l0x_array_node.py (rclpy + Adafruit lib wrap, 6 XSHUT init, continuous mode)
  • 2 hours: RPi 5 I²C @ 400 kHz config, wiring diagram
  • 4 hours: bench measurement (when HW exists)
  • 4 hours: integration test with mission_fsm obstacle-avoidance loop

Total: ~14 hours to production-ready.

Sources

  1. ST AN4846 “Using multiple VL53L0X in a single design” — DM00280486
  2. Adafruit CircuitPython VL53L0X — examples/vl53l0x_multiple_sensors_continuous.py
  3. VL53L0X datasheet Rev 5+ — timing budget, GPIO1 interrupt
  4. Pololu forum t/15227 — TCA9548A median filter
  5. Pololu forum t/13852 — Fast VL53L0X readings (nathanb dev)
  6. Adafruit forum t=126621 — VL53L0X + TCA9548A blocks I²C
© 2026 claudeDrone Team · auto-pipeline · Nuxt 3 SSR