Arducam 2MP OV2311 overexposed outdoors on Linux, fine indoors and on Windows

I’m having a strange issue with the Arducam OV2311 USB camera (M40210M09S 2.1mm lens). Under bright outdoor lighting, the camera works perfectly on Windows — crisp image, proper exposure. But when I connect it to my Jetson Orin Nano (Linux, kernel 5.15.185) in the exact same outdoor conditions, all I get is a completely white, saturated frame. Indoors on Linux, it works fine. Any ideas why this would be platform-specific?

Thanks for the detailed report! That’s definitely odd. Before I dig in — could you tell me what you’ve already tried on the Linux side? Have you done any V4L2 parameter tuning, tried different pixel formats, or adjusted exposure/gain settings?

Yeah, I’ve been at this for about a month actually. Here’s everything I’ve tried so far:

  • Tested both MJPG and YUYV pixel formats — same result.
  • Went through the full range of V4L2 controls: brightness, gamma, gain, backlight_compensation — nothing helped outdoors.
  • Tried auto_exposure modes 1 and 3.
  • Tested on both Jetson Orin Nano and a Raspberry Pi (both Linux) — exact same overexposure issue outdoors on both.
  • Applied ND filtering AND set exposure to the absolute minimum V4L2 allows (value 1 = 0.1ms) — still completely saturated.
  • All 4 identical camera units I have behave the same way.

It’s definitely a Linux-specific issue since Windows handles it fine. Feels like some kind of driver limitation, but I’m stuck.

That’s incredibly thorough — thank you. After looking into this, I believe I understand the root cause. The OV2311 sensor hardware is fully capable of handling bright outdoor scenes, but the generic Linux UVC driver doesn’t expose the camera’s full extended range of hardware-level controls.

Specifically: the minimum exposure value V4L2 reports (1 = 0.1ms) is the shortest exposure the generic driver allows you to set, but it is NOT the shortest exposure the OV2311 hardware can physically achieve. Windows uses its own driver stack which can access that deeper, shorter exposure time — which is why it works there.

I’d like you to try something to verify this. Can you install and run qv4l2? Here are the steps:

  1. Install it:
    sudo apt-get update
    sudo apt-get install qv4l2

  2. Launch qv4l2 from terminal.

  3. In the left panel, select the OV2311 camera (likely /dev/video0).

  4. In the right panel, go to the “Controls” tab.

  5. Locate “Exposure, Auto” and change it from “Auto” to “Manual Mode”.

  6. Find the “Exposure (Absolute)” slider.

  7. While pointing the camera outdoors at a bright scene, drag that slider all the way to its absolute minimum.

Then let me know:

  • Does the image become correctly exposed, or does it stay completely white?
  • Also, if possible, grab a screenshot of the entire qv4l2 window showing the controls and the image preview.

Alright, I installed qv4l2 and ran the test. Here’s what I found:

When I switch “Exposure, Auto” to “Manual Mode” and drag the “Exposure (Absolute)” slider all the way down… the image actually becomes correctly exposed! It’s not completely white anymore — I can see the scene clearly.

So it looks like you were right — the slider goes way lower than what I was able to set through V4L2 directly. The minimum the slider allows seems much shorter than the “1” value I was setting before. I’ve attached a screenshot showing the qv4l2 window with the controls and the corrected image.

So what’s the fix here? Do I need to write custom code to access those lower exposure values programmatically?

Great, that confirms the diagnosis. The qv4l2 tool accesses the camera’s controls through the V4L2 extended controls API, which gives you fuller access to the hardware registers compared to the basic V4L2 controls you were using before.

Here’s the solution: you need to use the V4L2 extended controls API (VIDIOC_S_EXT_CTRLS) instead of the basic controls (VIDIOC_S_CTRL) in your code. The extended API can reach the hardware’s full exposure range.

In C, you’d use something like:

struct v4l2_ext_controls ext_ctrls = {0};
struct v4l2_ext_control ext_ctrl = {0};

ext_ctrl.id = V4L2_CID_EXPOSURE_ABSOLUTE;
ext_ctrl.value = 1;  // or whatever minimum gives you proper exposure

ext_ctrls.ctrl_class = V4L2_CTRL_CLASS_USER;
ext_ctrls.count = 1;
ext_ctrls.controls = &ext_ctrl;

ioctl(fd, VIDIOC_S_EXT_CTRLS, &ext_ctrls);

If you’re using Python with OpenCV or v4l2-python, you can use the v4l2-ctl tool to set it from the command line for quick testing:

v4l2-ctl -d /dev/video0 -c exposure_auto=1    # Manual mode
v4l2-ctl -d /dev/video0 -c exposure_absolute=1  # Minimum exposure

The key takeaway is: the OV2311 hardware supports much shorter exposure times than what the generic V4L2 basic controls report as the minimum. By using the extended controls API (which qv4l2 uses), you can access the camera’s true hardware capabilities. Give that a try and let me know if it works in your application!