System designTroubleshooting

Multi camera mux still capture: switch the channel before you stream

Empty stills from a muxed multi-camera board mean the channel was never switched before the camera stack opened the sensor. Switch first, then capture.

LumaSpec Vision10 min read Share on LinkedIn

On this page 9 sections
  1. 01What a switched quad kit does between your code and the sensor
  2. 02The two log lines that identify the failure
  3. 03The working sequence: close, switch, reopen, capture
  4. 04Worked example: what a four-channel still sweep costs
  5. 05Test the causes in this order
  6. 06When to drop the mux and run independent cameras
  7. 07Prevention on the bench
  8. 08Checks that prove the sweep is healthy
  9. 09Questions

Quad and dual camera bundles that share one CSI-2 connector through a multiplexer look like the cheapest way to cover four views with one host. Then the saved stills come back empty while the vendor's preview works fine. The sensor is almost never at fault. The mux channel was never switched before the camera stack enumerated, or it was switched while a stream was still open. The trail below runs from two log lines to a per-channel capture sequence that works every time, puts a second-by-second cost on a four-channel still sweep, and shows when independent board-level cameras beat the mux outright.

What a switched quad kit does between your code and the sensor

A switched bundle wires four sensors to one CSI-2 receiver through an analog switch, and only the selected sensor is electrically connected at any instant. Selection takes two writes, not one. The first sets three header select lines: pins 7, 11 and 12, which are GPIO 4, GPIO 17 and GPIO 18. The second writes the channel register over I2C to the adapter's expander chip — that write is what actually moves the switch.

The camera stack probes the I2C bus when it builds each camera object. Probe while the mux points somewhere else and you have opened a device with no sensor behind it: capture calls return nothing and the save writes an empty file. The stack also serves one application per camera at a time, so an instance left open quietly blocks the next one.

The two log lines that identify the failure

Both lines from the failing session point at switching state, not at the sensor.

WARN V4L2 v4l2_videodevice.cpp:1803 /dev/video0[14:cap]: Zero sequence expected for first frame (got 2) — the first delivered frame carried sequence number 2 instead of 0, so frames were already flowing before your capture began. A previous instance left the pipeline warm, or the mux moved while the receiver was listening.

ERROR IPARPI cam_helper.cpp:181 Embedded data buffer parsing failed — the driver reads embedded metadata lines ahead of every frame and they no longer match the sensor the pipeline opened. In this failure it appeared immediately after i2cset -y 10 0x24 0x24 0x00 followed by libcamera-still -t 0: a register write that moved the switch under a live stream.

Treat the warning as the early stage of the same fault, and the error as what happens when you switch anyway.

The working sequence: close, switch, reopen, capture

  1. Close everything first. Stop and release every camera instance, including the vendor preview, and confirm nothing holds the capture node before you touch the mux.
  2. Set the select pins. Drive pins 7, 11 and 12 — GPIO 4, GPIO 17 and GPIO 18 — to the level combination for the channel.
  3. Write the switch register. On the adapter behind this failure, channel 0 answers to i2cset -y 10 0x24 0x24 0x00; take the other channels' values from the adapter's own example script, because the expander can gate more than the switch.
  4. Let the stack re-probe. Create a fresh camera object per channel. One object carried across a switch is the most common cause of the empty file.
  5. Capture one still and close. Configure a still configuration, start, save exactly one frame, then stop and release.
  6. Log it. Channel number, register value and file size, so a wrong channel shows up in the log instead of the review queue.
# One still per mux channel. set_mux_pins() and write_mux_register() wrap
# the GPIO writes (GPIO 4, 17, 18) and the I2C register write of step 3.
def capture_channel(index: int) -> str:
    close_any_camera()                  # step 1: nothing streaming
    set_mux_pins(index)                 # step 2: three select lines
    write_mux_register(index)           # step 3: expander register write
    with Picamera2() as cam:            # step 4: fresh probe per channel
        cam.configure(cam.create_still_configuration())
        cam.start()
        path = f"channel_{index}.png"
        cam.capture_file(path)          # step 5: exactly one still
    return path

for channel in range(4):
    capture_channel(channel)

Picamera2, create_still_configuration and capture_file are the library's own names; the two helpers are yours to fill from the adapter's script. The proof: run it, then open channel_0.png through channel_3.png — each must be non-empty and show the scene at its own position. If channel_1.png repeats position 0, the select levels or the register value for channel 1 are wrong, not the capture code.

Worked example: what a four-channel still sweep costs

Every channel switch forces a full close-and-reopen, and the reopen dominates. The failing session's own log measures it. The camera manager started at 41.67444 s; the first capture node opened at 42.34101 s. One probe-and-open therefore took 42.34101 − 41.67444 = 0.667 s, and that cost repeats on every channel.

t_channel = t_open + t_exposure + t_readout + t_save t_sweep = N × t_channel

For the four 12 MP sensors of this bundle at a 1/100 s exposure:

  • t_open = 0.67 s — measured above, paid again on every channel.
  • t_exposure = 0.01 s.
  • t_readout ≈ 0.1 s — assume the full-resolution still rate of this 12 MP class at 10 frames/s, so the frame period is 1 / 10 frames/s = 0.1 s. One 10-bit frame is 12,000,000 px × 10 bit = 120 Mbit ≈ 15 MB, which also sizes your storage.
  • t_save ≈ 0.05 s to encode and write the file on the same board.

So t_channel ≈ 0.67 + 0.01 + 0.1 + 0.05 = 0.83 s, and the four-channel sweep costs 4 × 0.83 ≈ 3.3 s. Exposure and save move the total by tens of milliseconds; the reopen moves it by half a second per channel.

Decision: a machine cycle that needs all four stills inside 1 s cannot run muxed stills — no sequence tweak recovers the reopen. If a 3-4 s sweep fits the cycle, the sequence above is all you need.

Test the causes in this order

Likelihood Cause Test Fix
1 An instance still open while you switch fuser /dev/video0 shows a holder; the embedded-data error names a live stream Close and re-probe after every switch
2 Select pins set in the wrong numbering scheme Read the three lines back after setting them One scheme end to end: pins 7, 11, 12 = GPIO 4, 17, 18
3 Register written to the wrong I2C bus or address i2cdetect each bus with no camera open, then read the register back Detect, write, verify
4 Wrong video node opened v4l2-ctl --list-devices; open the capture node the stack reports, never a media node Correct the node path
5 Overlay binds the wrong sensor after a switch Probe lines in dmesg name the driver loaded per channel Fix the overlay so each channel probes its own driver

Two look-alikes are not mux faults. An empty file with no log errors is usually an empty capture buffer — the cv2 imwrite empty-image fix shows how to prove a frame exists before saving. And when the stack lists the camera but raw capture dies before the first frame with no mux involved, buffer geometry is the cause: raw capture failing when the frame height is undersized covers that signature.

A sensor that probes but never creates a video node points at the devicetree chain, and a sensor that is detected but still fails streaming sends you to the power, clock and device-tree checks. In a container, the same dead-open symptom usually means only /dev/video0 was passed in — passing the V4L2 media nodes into the container fixes it.

When to drop the mux and run independent cameras

A mux saves connectors, not cycle time. Give each sensor its own link and the host enumerates every camera separately, your code opens all of them in parallel, and a shared hardware trigger fires them together — the 3.3 s sweep collapses into a single exposure. Four GigE cameras each keep a full 1 Gb/s of their own; four USB3 cameras share one controller's 5 Gb/s, which still beats four sequential probes. The board-level camera selection guide walks the per-camera criteria once the mux is gone.

The board cameras that drop straight into that pattern:

Board-level camerasHikrobot MV-CB016-10GM-M-W 1.6 MP GigE board-level camera, IMX296, Mono, M12 lens mount, WTB GigE portMV-CB016-10GM-M-W
Sensor format
1/2.9"
Lens mount
M12
$559.00View part
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
$690.00View part
Board-level camerasGigE board camera / module, 12 MP, Color/Mono, 1.6 μm×1.6 μm pixels (LS-BCM-0056)LS-BCM-0056
Sensor format
1/2"
Lens mount
C-mount
$209.00View part

The MV-CB016-10GM-M-W pairs a 1.6 MP IMX296 global-shutter sensor in 1/2.9" format with an M12 mount on GigE — a natural triggered station camera. The LS-BCM-0003 reaches 120 fps at 3.1 MP over USB3 (IMX252, 1/1.8" format, global shutter) where sweep rate matters. The LS-BCM-0056 carries a 12 MP sensor with 1.6 μm × 1.6 μm pixels over GigE, for the resolution the quad bundle was chosen for.

Where the camera disappears into your own carrier board, a bare-board module is the pattern that matches:

Board-level camerasHikrobot MV-CB060-10UM-B 6 MP Board Level Camera, USB3, IMX178, Mono, Bare Board, V4MV-CB060-10UM-B
Sensor format
1/1.8"
Interface
USB3
Resolution
6-10 MP
Shutter
Rolling
$239.00View partBuild a system with it

As a bare 6 MP USB3 board with an IMX178 sensor in 1/1.8" format and no lens mount, it lets your carrier and optics define the geometry instead of a bundled module.

Prevention on the bench

  • Bring the adapter up with the vendor's example script, one channel, before it enters your application; the register constants then come from a known-good source.
  • Print the channel, register value and result file size for every capture, so a wrong channel fails in the log rather than at review.
  • Keep one numbering scheme for the select pins across every script; mixed header and GPIO numbering causes most silent channel errors.
  • Hold the adapter and each sensor rigidly — a camera stand with a slide keeps channel-to-channel geometry repeatable when four views feed one decision.

Checks that prove the sweep is healthy

  • Every file is non-empty and shows its own channel's scene; eyeball all four once at bring-up.
  • A fresh open starts at sequence 0, so the Zero sequence warning disappears without being suppressed.
  • Nothing holds /dev/video0 after the run — the node-holder check from the table returns nothing.
  • The measured sweep sits within about 10% of the 3.3 s budget; a sudden jump means a sensor dropped off the bus.

More capture-stack isolation, from sensor probe to first frame, lives on the systems topic page. When the cycle-time math says the mux cannot make it, size independent cameras in the camera finder or start at the camera step of the configurator — or send your four-view layout through the contact page and we will help match sensors, mounts and triggering to the shots you need.

Questions

Can I capture from all four sensors of a muxed quad kit at the same moment?

No. The adapter connects one sensor to the CSI-2 link at a time, so a still sweep is sequential by construction: switch, reopen, capture, close, next channel. For genuinely simultaneous frames, give each sensor its own link — separate GigE or USB3 board cameras — and fire them from one shared hardware trigger. That also removes the per-channel reopen cost, which dominates the sweep time.

Why does Embedded data buffer parsing failed appear right after my mux register write?

Because the write moved the switch while a camera was still open. The driver reads embedded metadata lines ahead of every frame; once the mux selects a different sensor, those lines no longer match the pipeline's sensor and parsing fails. Stop and release every camera instance first, write the register, and only then start a new capture.

Is the Zero sequence expected for first frame warning harmful?

No. It is a warning, not a failure: the first delivered frame carried sequence number 2 instead of 0, meaning frames were already produced before your capture began. Stills still save normally. It is a useful fingerprint though — it usually means an instance was left streaming across a channel switch, so close cleanly and let the stack re-probe.

How do I find the I2C bus and address of my adapter's channel switch?

List the I2C buses on the host, run a detect on each with no camera driver open, and look for the expander address; on the adapter behind this failure it answered at device 0x24 on bus 10. Take the per-channel register values from the adapter's own example script — one of those writes can select more than the switch.

What cycle time should I expect for four muxed stills?

About 0.83 s per channel: 0.67 s of that is the stack re-probing and opening the sensor, measured from this failure's log, with exposure, readout and save adding roughly 0.16 s. Four channels cost about 3.3 s per sweep. If the machine needs all four images in under a second, no code change recovers that — move to independent cameras.