System designExplainer

Arduino and multiple cameras: where the data bottleneck is

Where the data bottleneck sits when you wire multiple cameras to an Arduino, and the trigger-plus-host architecture that streams each camera to a real host.

LumaSpec Vision10 min read Share on LinkedIn

On this page 9 sections
  1. 01Can an Arduino carry multiple cameras' image data?
  2. 02What happens if I shrink the frames to fit?
  3. 03How should the system be built so every camera streams?
  4. 04How do you keep the cameras frame-synchronized?
  5. 05Which cameras suit a trigger-plus-host layout?
  6. 06What wiring and cables do the trigger and data lines need?
  7. 07When is keeping everything on the Arduino the right call?
  8. 08What should you check before ordering parts?
  9. 09Questions

Fanning one trigger pulse out to several cameras is easy; the hard question is who carries the frames. A research rig or a lab prototype often starts with an Arduino because it is already on the bench, and the first assumption — that SPI or I2C will move the images the way it moves sensor readings — collapses the moment you put real numbers on it. This guide sizes that bottleneck: what one camera's frames cost in bytes per second, what an 8-bit microcontroller's buses can actually clock, and the architecture that resolves the conflict. You will be able to decide, from your camera's resolution and frame rate, whether the microcontroller stays as the trigger and sequencer while each camera's data path runs straight to a host — and which cameras, interfaces and cables that layout calls for.

Can an Arduino carry multiple cameras' image data?

No — not at inspection frame rates: one 640 × 480 8-bit monochrome frame is 307,200 bytes, so a single camera at 30 fps already demands 9.2 MB/s, about nine times the 1 MB/s an Arduino Uno's hardware SPI clocks at its 8 MHz ceiling.

The memory makes it worse than the bus numbers alone suggest. An Uno carries 2 kB of SRAM, so one VGA frame is roughly 150 times the chip's entire RAM; nothing is buffered, everything streams through byte by byte, and the streaming ceiling is the bus itself. On the 16 MHz ATmega328P, hardware SPI peaks at fosc/2 = 8 MHz — 8 Mbit/s, or 1 MB/s before overhead. I2C tops out at 400 kHz, about 50 kB/s. A UART pushed to 2 Mbit/s with 8N1 framing carries roughly 200 kB/s. Each of the three falls short of one camera's demand by an order of magnitude or more, and every added camera multiplies the bill.

bytes/s = width × height × bytes per pixel × fps 640 px × 480 px × 1 B/px × 30 fps = 9,216,000 B/s ≈ 9.2 MB/s
Data path on an 8-bit Arduino board Ceiling One VGA mono camera at 30 fps needs
Hardware SPI at fosc/2 = 8 MHz 1 MB/s 9.2 MB/s
I2C at 400 kHz ~50 kB/s 9.2 MB/s
UART at 2 Mbit/s, 8N1 ~200 kB/s 9.2 MB/s

What happens if I shrink the frames to fit?

You can, but only barely: 160 × 120 at 30 fps is 19,200 bytes per frame, or 0.58 MB/s, which fits under the 1 MB/s SPI ceiling with roughly 0.4 MB/s of headroom — and it leaves no room for a second camera.

The arithmetic closes fast. At QVGA, 320 × 240, one camera at 30 fps still needs 76,800 × 30 = 2,304,000 B/s ≈ 2.3 MB/s, more than double the SPI ceiling. Add a second camera at 160 × 120 and the pair needs 1.2 MB/s — over the ceiling again. The RAM constraint never moves: a 19.2 kB frame still passes through a chip with 2 kB of SRAM, so there is no room to buffer a frame for processing, and any per-pixel work you fold into the transfer competes with the clock cycles the bytes themselves need. Thumbnail-sized, single-camera capture is the only case that genuinely fits an 8-bit part.

How should the system be built so every camera streams?

Use the Arduino as trigger and sequencer only, and give each camera its own data path to a host: the Arduino fans out one sync pulse to every camera, while each camera's USB3, GigE, MIPI CSI-2 or parallel bus lands directly on a host that can absorb megabytes per second.

The split works because the two traffic types differ by three orders of magnitude. Configuration is control traffic: an I2C write that sets exposure or selects the trigger mode moves tens of bytes, and any microcontroller handles it. Frames are data traffic, and they belong on a path sized for them. A host PC's GigE port absorbs about 110 MB/s of camera payload; a USB3 machine-vision camera streams roughly 350-400 MB/s; a 2-lane MIPI CSI-2 receiver on an embedded host carries on the order of 200 MB/s. Those are estimates from line rates, and every one clears the microcontroller's buses by a factor of a hundred or more.

The Arduino keeps a real job in this layout. It fires the exposure sequence, times strobes, advances a stage between shots and tells the host, over a serial line, which shot each arriving frame belongs to. When frames never arrive, the fault is almost always on the data path rather than the control path; the guide to separating I2C from MIPI failures gives the isolation order. If the host is an embedded board, the parallel DVP versus CSI comparison for microcontrollers explains why the parallel bus survives there, and the MIPI CSI-2 to USB bridge bandwidth write-up runs the same bytes-per-second arithmetic for bridge chips.

How do you keep the cameras frame-synchronized?

Connect one Arduino output pin to every camera's frame-sync input: driven together, the sensors start each frame on the same edge, and the residual skew between cameras sits in the nanoseconds-to-microseconds range instead of the milliseconds a software start command produces.

Sensor modules commonly label the input FSIN and an output VSYNC; where a camera exposes a plain external trigger input, the same edge-driven idea applies. Tie all grounds together, fan the one output out to every camera's input, and every sensor starts its line timing from the same rising edge. The trigger fan-out wiring guide covers one pulse driving several cameras, and the stereo trigger synchronization article walks the paired-capture case end to end.

Sync quality then depends on the cameras' own clocks, not on the Arduino's timing precision. Two crystals differ by tens of parts per million, so frame starts drift tens of microseconds per second; because the sync input re-arms from the shared edge every frame, that shared pulse keeps pulling them back into line. Industrial cameras treat this input as a first-class feature, and the selection points that matter are laid out in this guide to IP cameras with digital trigger inputs for PLC systems.

Which cameras suit a trigger-plus-host layout?

Board-level cameras are the natural fit, because they expose their sync and trigger pins and come with a host-facing interface already chosen: pick USB3 for short runs and 3-4× the bandwidth of GigE, and GigE when the cameras sit metres from the host.

Order the interface by distance and bandwidth, then the shutter by motion. Rolling-shutter sensors read out line by line, so a strobe or a multi-angle view distorts them; global shutter freezes the whole frame at once, which is what synchronized rigs and combined measurements want. For a short-run USB3 path with strobe-friendly timing, a global-shutter board camera is the pattern:

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

When the camera sits metres from the host, the GigE path runs on standard Ethernet:

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
Interface
GigE
Resolution
Up to 3 MP
$559.00View partBuild a system with it

Pixels multiply the bytes-per-second bill, and the rule does not change with resolution — a 12 MP board still hands every frame to its own GigE link:

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
Interface
GigE
Resolution
12 MP
$209.00View partBuild a system with it

The board-level cameras category is the pool to choose from, the camera finder filters it by interface, shutter and sensor format, and the camera step of the configurator pairs the camera with cable and host-side parts in one pass.

What wiring and cables do the trigger and data lines need?

Keep trigger lines short and data cables dedicated: a TTL trigger is content on a lead under about 30 cm, one series resistor guards the driving pin, and each camera's image stream gets its own USB3 or GigE cable sized for its megabytes per second.

On the trigger line, put a series resistor at the driving pin — 220 Ω is a common default — and keep the ground between Arduino and camera solid. Runs beyond a few tens of centimetres, or any electrically noisy cell, argue for an opto-isolated or differential trigger input on the camera side; the opto-isolated trigger wiring article works the resistor value out.

Image data never shares a wire with control. Each camera gets its own cable, chosen for flex and length: drag-chain-rated USB3 cable for moving rigs, screw-locked GigE cable for static multi-metre runs. USB3 passive cables reach about 3-5 m; GigE runs to 100 m on standard Ethernet. The camera cables category and its accessories finder filter by interface and length. Two drag-chain-rated data cables cover the common cases:

When is keeping everything on the Arduino the right call?

Keep everything on the microcontroller only when the frames are genuinely small: one low-resolution module, thumbnail rates around a few hundred kB/s, or a UART JPEG camera streaming about 11.5 kB/s at 115200 baud — a logger that stores one small image per second fits comfortably.

A JPEG-over-UART module compresses on its own chip and hands the microcontroller a finished file. What you give up is bit-exact pixels, deterministic timing and low latency: JPEG moves edges by a fraction of a pixel, and compression time varies with scene content, so frames drift apart. For a data logger, a preview thumbnail or an occasionally sampled view, that trade is fine; for any measurement at speed it is not. The SPI frame-transfer sizing article runs the same arithmetic for raw sensors on SPI, and the shared I2C bus limits article covers the configuration side when several modules hang off one microcontroller.

What should you check before ordering parts?

Work down the bytes-per-second bill before anything else, because every other choice follows from it: one camera at 640 × 480 and 30 fps already forces a host data path, and the trigger-plus-host layout that follows costs no extra engineering beyond the wiring described above.

  • Total the traffic first: width × height × bytes per pixel × fps, one line per camera, summed against the host ports — about 110 MB/s per GigE port, 350-400 MB/s per USB3 camera path.
  • Confirm every camera module breaks out a trigger or frame-sync pin alongside its host interface; a data path with no sync input turns the rig back into loosely timed software captures.
  • Map the I2C addresses before two identical modules land on one bus.
  • Pick global shutter wherever cameras are strobed or combined into one measurement.
  • Match cables to motion and length: drag-chain for moving axes, screw locks for static runs.
  • Leave the Arduino its sequencer duties on paper: strobes, stage moves, and a serial line tagging each shot for the host.

The decision, then: the Arduino stays — as trigger and sequencer — and the frames leave on each camera's own bus. If your bandwidth bill lands anywhere near a megabyte per second, which inspection-sized frames do by default, the parts list is one trigger-capable board camera per view, one data cable per camera, and one host port per bandwidth slice. Sketch the bill, then confirm cable and accessory choices at the extras step of the configurator; if the numbers look marginal, send them to us — expert support comes with every order. More system-level reading lives on the systems topic page.

Questions

Why does hardware frame sync matter if every camera streams to the host separately?

Because independent capture loops drift. Software start commands leave cameras milliseconds apart, and that offset moves from frame to frame; a shared trigger edge lines exposures up within microseconds and holds them there. For stereo depth, photogrammetry or any measurement that combines views of one event, microsecond alignment is what makes the frames usable together.

Can several camera modules share the Arduino's I2C bus for register setup?

Yes, provided each module presents a different 7-bit address; two fixed at the same address need an address strap, an I2C multiplexer or a second bus. Keep a 400 kHz shared bus to about 1 m, and treat it as control traffic only — the image frames never touch it.

How do I make the cameras expose at slightly different times instead of together?

Either fire separate trigger pins with a timer delay between edges — a 16 MHz Arduino times delays to about a microsecond — or keep one shared line and write each camera's internal trigger-delay register over I2C. The second way adds no wiring and offsets each camera from the same edge by a value you set per camera.

Can a JPEG-over-UART camera module live entirely on the Arduino?

Yes, as a logging or preview camera. An 8N1 UART at 115200 baud moves about 11.5 kB/s, enough for compressed thumbnails. Accept compression artifacts, content-dependent latency and no deterministic frame timing in return — which is why measurement frames stay on the host-facing raw paths and the UART module stays a side camera.