We had a sample Spatial Vision main board on the bench: an OPNOUS OPN6002 ISP behind a SigmaStar SoC, enumerating over USB as a standard UVC camera. Every tool saw it. Not one delivered a single frame. Here is the full debugging path — it's a trap a lot of people hit with white-label ToF modules, and the fix is not where you'd first look.
The symptom that looks like a hardware fault
Plug the board in and everything is right: it enumerates as UVC Camera
(VID 0x1D6B, PID 0x0102), Device Manager shows it, and format negotiation succeeds for every
mode — YUYV, NV12, MJPEG, and the tell-tale 320 × 720 raw mode that a ToF depth+IR frame
packs into. Then you start a capture and get nothing. ffmpeg hangs; the Windows Camera app throws
0xA00F4240; OpenCV times out. Every format, zero bytes.
The instinct is to blame the cable, the port, or the board. All three are fine. The problem is a layer up.
What the vendor SDK revealed
The manufacturer pointed us at their reference SDK, and its source made the mechanism obvious. The
ToF sensor — the VCSEL laser and the ISP pipeline — is off by default. It has to be
switched on with a 60-byte vendor command sent over the UVC Extension Unit (a
control channel that rides alongside the video interface), on unit ID 2, selector 0x05.
The command is 0x8001 START.
And there's a sequencing trap inside the trap: that command is only accepted after the video stream has been PROBE+COMMITted, but before streaming actually starts. Send it before COMMIT and the firmware silently ignores it. No stock camera app does any of this — which is exactly why they all get zero frames.
The correct start sequence
Distilled from the vendor SDK, the sequence that actually produces frames is:
- Open the device (WinUSB via Zadig on Windows, libusb elsewhere).
- PROBE + COMMIT the NV12 320 × 720 stream format.
- Send XU
0x8001=0(stop), then0x8001=1(start), then0x8005to set the frame rate. - Start the actual video transfer.
- Decode the RAW12-packed frame into depth and IR.
We cross-compiled a single static Windows tool that replays exactly this, so you don't need Visual Studio and the vendor toolchain to get first light off one of these boards. On our sample it printed firmware and module identity over the XU, negotiated the raw mode, sent the start command — and the board still returned empty isochronous packets. Which told us something specific.
Reading the frame: RAW12, three lines to one
The data format is worth knowing because it's shared across a lot of these OPNOUS-based modules. The camera outputs packed 12-bit data in a 320 × 720 container — 345,600 bytes per frame, three source bytes encoding two 12-bit pixels. Every three raw lines decode to one output row: a depth-high line, a depth-low + status + background line, and an IR line, giving a 320 × 240 depth map (in millimetres) and a 320 × 240 IR image. Depth is in quarter-millimetre units, so a right shift by two gives millimetres. We validated our decoder against a real captured frame from the vendor and it reproduced their image pixel-for-pixel.
When "no frames" really is the sensor
Instrumenting the USB layer showed the transport was healthy — isochronous transfers submitting, URBs completing at ~250/second — but every payload was empty. That rules out the host, the driver, the timing, and the decoder, and points straight at the sensor side. On our specific sample the module's calibration EEPROM read back as uninitialised garbage (focal lengths of 10⁻²⁶), and it turned out the sensor ribbon had been connected the wrong way and cooked the module. A dead sensor and a never-configured pipeline produce identical symptoms to a missing start command — the difference is only visible once you can see the USB layer and read the XU status registers.
The takeaway
If a ToF board enumerates as UVC and negotiates formats but never sends a frame, don't replace the cable — the sensor pipeline almost certainly needs a vendor Extension Unit command after COMMIT. Reproducing the vendor's start sequence is the unlock; a USB-layer diagnostic is what tells you whether the remaining silence is software or a physical fault. Our capture tool and the RAW12 decoder that came out of this are open in the developer docs, and the same decode path ships in the SpatialAI SDK.
Building on OPNOUS-based depth modules? The Spatial Vision board and the SpatialAI SDK share this exact pipeline — read the quickstart.
