System designTroubleshooting

Stereo camera hat not on the I2C bus: enable i2c_vc

The depth demo cannot open /dev/i2c-0 because a stereo camera hat puts its sensors on the VC bus. Enable it with dtparam=i2c_vc=on, reboot, scan bus 0.

LumaSpec Vision9 min read Share on LinkedIn

On this page 9 sections
  1. 01Read the error: a missing device file, not a dead camera
  2. 02Your isolation steps already proved where the fault is not
  3. 03The two I2C controllers, and which one the hat uses
  4. 04The fix: one line in the boot config
  5. 05Worked check: reading a scan as 128 answers
  6. 06When the bus is up but the demo still fails
  7. 07Bring-up order that avoids the trap
  8. 08When to skip MIPI entirely
  9. 09Questions

Your 5 MP stereo hat passes the still-image test, then the depth demo dies on its first I2C call. You have reseated both camera ribbons, mounted the hat directly on the board, installed the library the readme names, and proved the header bus healthy with another device — and sudo i2cdetect -y 1 still scans empty. The wiring is fine and the hat is fine. Its two sensors hang on a second I2C controller, the VC bus i2c0, which the Raspberry Pi 4 ships with switched off; the bus you enabled is i2c1, and the hat never touches it. This guide shows you how to read the error, turn the VC bus on with one line in /boot/config.txt, and confirm the two sensor addresses appear.

Read the error: a missing device file, not a dead camera

The demo's first output carries the whole diagnosis:

Could not open file /dev/i2c-0' or /dev/i2c/0': No such file or directory
I2C check functionality fail.
init_camera: Unexpected result

A missing /dev entry means the kernel never created the device: the controller behind that bus number is switched off in configuration. The other two lines are consequences — the I2C self-check fails, so camera initialization reports an unexpected result. A wiring fault behaves differently at every layer: the device file exists, the scan runs, and individual addresses either stay unanswered or error during the probe. Sort every I2C failure by its layer first:

  • Device file missing → the controller is disabled. Fix it in configuration.
  • Device file present, scan empty → wrong bus, or a wiring fault on that bus.
  • Device file present, probe errors → the physical link: ribbon seating, length, power.

The first row is where a stereo hat bring-up almost always lands, and the rest of this guide stays there.

Your isolation steps already proved where the fault is not

Three of the checks you ran are worth keeping in the record. Reseating the ribbons and remounting the hat changed nothing, which argues against a connector fault. The empty scan of i2c-1 is a complete page, every address unanswered, and it proves the tools work end to end: drivers loaded, i2cdetect functional, the bus electrically idle. The other I2C device you attached to the header answered and ran on that same bus, which clears the host side of i2c1 entirely.

Two steps were unrelated by construction. The wiringPi package is a GPIO library, not a bus switch, and the libatomic.so.1 preload on the demo command satisfies a runtime dependency. Neither can create a device file, so neither could have changed the outcome. Nothing was wasted: the same three checks, pointed at the right bus number, are exactly how you will confirm the fix below.

The two I2C controllers, and which one the hat uses

Bus Device file Switched on by What hangs on it
Header bus, i2c1 /dev/i2c-1 The I2C interface option in raspi-config HAT identification, RTCs, sensor modules
VC bus, i2c0 /dev/i2c-0 dtparam=i2c_vc=on in the boot config The stereo hat's two camera sensors

You enabled the first row and scanned it: right tool, wrong bus. The stereo hat routes both sensors' control lines to the VC bus, the controller the firmware uses for camera and display control, and the parameter that exposes it ships off on every image. Nothing on the hat ever touches i2c1, which is why a healthy bus with a working test device still scanned empty.

The passing still capture belongs to a different code path. raspistill drives the sensor through the firmware's own control channel and never opens a /dev/i2c node, so stills work while every userspace I2C call fails. The depth demo controls the sensors from user space and needs the bus exposed as a device file — that is the entire gap between your two results.

The fix: one line in the boot config

  1. Open the boot configuration: sudo nano /boot/config.txt. On the newest OS images the same file sits in a firmware subfolder of the boot partition; edit whichever copy the system actually reads.
  2. Add the parameter on its own line, next to the other dtparam lines: dtparam=i2c_vc=on.
  3. Save and reboot: sudo reboot.
  4. List the buses again with ls /dev/i2c-*. The VC bus now appears next to i2c-1 — on the images this demo targets it carries the number the script asks for and shows up as i2c-0. Newer kernels number the firmware bus differently; the entry that appeared is your bus, whatever number it carries.
  5. Scan it by that number: sudo i2cdetect -y 0. The two sensor addresses replace dashes in the table, and the demo clears its I2C check.

The header bus is untouched: the parameter adds a controller rather than moving yours, so an RTC or sensor module already running on i2c-1 keeps working.

Worked check: reading a scan as 128 answers

A full i2cdetect page prints addresses 0x00–0x7f as 8 rows × 16 columns = 128 slots. Before the fix there was no VC bus to scan at all; the empty page you captured on i2c-1 was 128 unanswered slots — a complete, electrically healthy bus carrying zero devices, a result about the bus and not about the hat. After the fix, the same command on the VC bus answers at the sensor slots: one per camera, because each camera on the stereo pair keeps its own control address (a hat that shares one address across the pair shows one, and its readme says so). Then rerun the demo from the stereo_depth_demo folder: the I2C check completes, and init_camera: Unexpected result disappears with its cause.

When the bus is up but the demo still fails

Past the bus check, the remaining failures split into three layers, and each has its own write-up:

Bring-up order that avoids the trap

Run a hat bring-up in this order and a failure surfaces in one step instead of five:

  • Collect every boot-config line the hat's readme names before the first boot. The i2c_vc line is in there because the sensors need it, not because it is historical.
  • Enable both controllers deliberately: the header interface through raspi-config, the VC bus through its config line. They are independent switches, and enabling one tells you nothing about the other.
  • After each config change, run ls /dev/i2c-* and write down the bus numbers. The number is the contract between your scan command and the demo.
  • Scan each bus before starting the demo. An empty scan on the hat's bus is a wiring result; an empty scan on any other bus carries no information.
  • Treat the still capture as a sensor test only. It exercises the firmware path and proves nothing about userspace buses.

When to skip MIPI entirely

If the hat is a prototype and the deliverable is a machine, moving the cameras to USB3 removes the layer this article repairs altogether. A USB3 camera enumerates from its own USB descriptors when plugged in: no overlay, no device-tree bus numbering, no boot-config line, and a driver stack the camera vendor maintains. You trade the short ribbon for a standard C-mount or CS-mount lens mount and a cable that reaches across a machine instead of across a board.

Board-level cameras3.1 MP USB3 board-level camera, IMX252, 1/1.8" global shutter, 120 fps (LS-BCM-0003)LS-BCM-0003
Sensor format
1/1.8"
Lens mount
C-mount
Interface
USB3
Resolution
5 MP
$690.00View partBuild a system with it

Compare candidates in the board-level cameras category, filter by resolution, shutter and mount in the camera finder, or assemble the whole chain in the camera configurator. For the full selection method, the board level camera selection for embedded vision machines guide covers host and lens matching, and the board camera evaluation checklist for inspection systems covers the bench tests to run before you commit. More bus-level bring-up material sits on the systems topic page, and if you would rather send a parts list than configure one yourself, contact the team.

Questions

Why does raspistill work when the depth demo cannot open the I2C bus?

The firmware camera stack drives the sensor over its own control channel inside the GPU and never opens a /dev/i2c device, so stills can capture while every userspace I2C call fails. The depth demo drives the sensors from user space and needs the VC bus exposed as a device file. The two paths are independent: the still test proves the sensor and the CSI link, and it cannot prove the bus.

Can the VC bus and the header I2C bus run at the same time?

Yes. They are two independent hardware controllers. Adding dtparam=i2c_vc=on creates the camera-side bus and leaves the header bus untouched, so an RTC, sensor module or display already hanging off i2c-1 keeps working. Enable both, then scan each by its own bus number with i2cdetect -y to see which devices answered where.

The line is added and the board rebooted, but the demo still cannot find i2c-0 — what now?

Work through three checks. Confirm the edit landed in the config file on the boot partition of the card the board actually boots from — multi-card and multi-boot set-ups routinely get edited on the wrong one. List /dev/i2c-* after the reboot: newer kernels number the VC bus differently, and both the scan and the demo must use the number that appeared. Finally check the line reads exactly dtparam=i2c_vc=on, with no spaces around the equals signs.

Is i2c_vc an obsolete setting left over from older board revisions?

No. The VC I2C controller exists on current boards too, and the stereo demo looks for it by bus number on every generation. The line is easy to misread as legacy because the bus it creates carries number 0, which looks like the old-Pi bus, while the header bus you enabled carries number 1. The two numbers are two controllers, not two generations.

Does a longer ribbon or an off-board mount change any of this?

Not the missing device file: no amount of cabling creates a /dev entry, which is why reseating and remounting changed nothing. Once the bus exists, though, physical link quality matters again — probe errors during i2cdetect or dropouts while streaming usually point at ribbon seating and length rather than at configuration. Keep extensions short, reseat both camera ribbons, and rescan before blaming a sensor.