Skip to content

fix(esp_board_manager): drive DVP XCLK via LEDC before esp_video_init (AUD-7254) - #48

Open
ymzhang0319 wants to merge 1 commit into
espressif:mainfrom
ymzhang0319:fix/dvp-xclk-preinit
Open

fix(esp_board_manager): drive DVP XCLK via LEDC before esp_video_init (AUD-7254)#48
ymzhang0319 wants to merge 1 commit into
espressif:mainfrom
ymzhang0319:fix/dvp-xclk-preinit

Conversation

@ymzhang0319

Copy link
Copy Markdown

Problem

On ESP32-S3, the DVP controller does not output XCLK until CAM transfer begins. esp_video routes the GPIO to the DVP peripheral at init time, but no clock is emitted. OV-family sensors need a live XCLK to respond to SCCB, so sensor probing fails at boot:

E ov3660: get sensor ID failed
E esp_video_init: Failed to detect camera sensor with address=3c

Any board using an OV-family DVP camera on ESP32-S3 through esp_board_manager 0.5.10 hits this out of the box.

Fix

In dev_camera_sub_dvp_init, before calling esp_video_init:

  • If xclk_io >= 0 && xclk_freq > 0, start LEDC on that GPIO at the requested frequency (1-bit resolution, 50% duty).
  • Mask xclk_io to GPIO_NUM_NC and pass xclk_freq = 0 to esp_video so it does not re-route the pin.

On deinit, ledc_stop to release the channel.

The guard xclk_io >= 0 && xclk_freq > 0 makes this a no-op for boards that don't use DVP XCLK via GPIO (e.g. MIPI-CSI).

Note on long-term direction

The SPI path already uses esp_cam_sensor_xclk_* for this purpose via init_spi_clk_func. Adding an xclk_source field to the DVP YAML schema so DVP can use the same API is the symmetrical refactor. Happy to do that in a follow-up PR if preferred — this PR keeps scope minimal to unblock users.

Test

ESP32-S3 SparkBot + OV3660 DVP:

  • Without patch: get sensor ID failed at boot.
  • With patch: ov3660: Detected Camera sensor PID=0x3660, /dev/video2 opens from Lua, camera.capture() writes a ~30 KB JPEG.

Tested against ESP-IDF v5.5.4.

On ESP32-S3, the DVP controller does not output XCLK on the mapped GPIO
until CAM transfer starts, so OV-family sensors cannot respond to SCCB
at boot and detection fails with "get sensor ID failed".

Drive XCLK via LEDC before esp_video_init when xclk_io >= 0 and
xclk_freq > 0. Mask xclk_io to GPIO_NUM_NC and pass xclk_freq = 0 to
esp_video so it does not re-route the GPIO and kill the LEDC output.
Stop LEDC on deinit.

Tested on ESP32-S3 SparkBot with OV3660 DVP (SCCB 0x3C, XCLK 20 MHz).
@github-actions github-actions Bot changed the title fix(esp_board_manager): drive DVP XCLK via LEDC before esp_video_init fix(esp_board_manager): drive DVP XCLK via LEDC before esp_video_init (AUD-7254) May 13, 2026
@LiuCodee

Copy link
Copy Markdown
Contributor

Hello, thank you for reporting the issue!

I tested using the configuration file you submitted in:
espressif/esp-claw#76

My test results show that both OV3660 and OV2640 can be initialized directly through esp_video, and I was not able to reproduce the issue on my side.

My test environment is:

  • IDF v5.5.4
  • esp_board_manager v0.5.10
  • esp_video v2.1.0
  • ESP-SparkBot v1.1

Test project:

  • esp_board_manager/test_apps
  • A blank project depending on board manager and simply calling:
    esp_board_manager_init();

Is there any other environment information we should sync as well?

@ymzhang0319

ymzhang0319 commented May 18, 2026

Copy link
Copy Markdown
Author

Really appreciate you taking the time to investigate this on your side and for laying out the environment details so clearly — that's very helpful, thank you.

I can confirm your finding: esp_board_manager/test_apps + the SparkBot YAML, on the same stack (IDF v5.5.4 / esp_board_manager 0.5.10 / esp_video 2.1.0), does not reproduce the issue, and esp_board_manager_init() returns OK with the camera test path looking fine.

The reason that minimal test misses it is that esp_board_device_init_all() (src/esp_board_device.c:386-389) only logs ESP_LOGE on individual device init failures and unconditionally returns ESP_OK. So when dev_camera_sub_dvp_init fails internally, the failure is invisible unless the application actually exercises camera operations end-to-end.

In the application path (esp-claw application/edge_agent with the bundled take_picture skill) the failure does surface, with the same boot-time symptom from the original PR description:

E ov3660: Get sensor ID failed
E esp_video_init: Failed to detect camera sensor with address=3c

esp_video_init returns non-OK from inside dev_camera_sub_dvp_init, so /dev/video2 is never registered. Any subsequent open("/dev/video2") from the application then fails with ENOENT.

Suggested reproduction

  1. Build esp-claw/application/edge_agent against the SparkBot YAML with unpatched esp_board_manager 0.5.10.
  2. Unplug USB for 60+ seconds to fully power the sensor down.
  3. Re-plug, wait for boot, and immediately exercise the camera — e.g. via REPL:
    lua --run --path /fatfs/skills/take_picture/scripts/take_picture.lua --args-json {} --timeout-ms 10000
    

What the patch does

Drives XCLK via LEDC on the configured GPIO before esp_video_init, so the sensor sees stable 20 MHz from boot. The SCCB probe inside esp_video_init then has a clocked sensor to talk to, and the failure mode disappears. The xclk_io >= 0 && xclk_freq > 0 guard keeps it a strict no-op on boards that don't drive XCLK from a GPIO (CSI, etc.).

Happy to provide a minimal repro outside esp-claw if that helps — the main barrier to reproducing in test_apps today is just that the test path doesn't exercise open(/dev/video2) at the application level.

@LiuCodee

Copy link
Copy Markdown
Contributor

Hi. Actually, there is already a camera test in the bmgr test apps:
https://github.com/espressif/esp-board-manager/blob/c8aa97be6469e067643c141bc026f61e8da375c8/esp_board_manager/test_apps/main/test_dev_camera.c#L253

And from the initialization logs, we can also clearly see that the sensor initialization succeeds correctly:

I (797) DEV_CAMERA_SUB_DVP: Initializing DVP camera...
I (797) BOARD_PERIPH: Reuse periph: i2c_master, ref_count=2
I (807) ov3660: Detected Camera sensor PID=0x3660
I (807) DEV_CAMERA_SUB_DVP: DVP camera initialized successfully, dev_path: /dev/video2
I (817) DEV_CAMERA: Successfully initialized camera device: camera, sub_type: dvp, dev_path: /dev/video2

So at the moment, it seems the issue may not simply be whether /dev/video2 gets opened at the application level.

However, it does seem completely possible that unstable XCLK timing could cause SCCB configuration/probing failures during sensor initialization.

If you’re able to provide a minimal reproducible case outside of esp-claw, we’d also be very happy to do further testing and investigation on our side.

@ymzhang0319

ymzhang0319 commented May 19, 2026

Copy link
Copy Markdown
Author

Thanks for the reply! Here's a minimal reproducible case outside esp-claw, as you suggested — could you give it a try on your side?

Minimal reproduction project (a new example under esp_board_manager/examples/):
https://github.com/ymzhang0319/esp-board-manager/tree/repro/sparkbot-coldstart-min/esp_board_manager/examples/sparkbot_coldstart_repro

Reproduction

After flashing the firmware:

  1. Switch the SparkBot's battery OFF, then back ON.
  2. Read the serial output.

On a true cold start, the boot log shows:

E (xxx) ov3660: Get sensor ID failed
E (xxx) esp_video_init: Failed to detect camera sensor with address=3c

and any subsequent attempt to open the camera from application code fails with:

E (xxx) camera_service: Failed to open /dev/video2 (errno=2)

@laride

laride commented May 20, 2026

Copy link
Copy Markdown

This might be a bug in esp_video?

@LiuCodee

Copy link
Copy Markdown
Contributor

Thanks for providing the minimal reproducible case. Under a cold boot condition, it does seem that the OV3660 is not behaving correctly.

However, this issue may need to be fixed on the esp_video driver side. I’ve already reported it to the relevant colleagues, and I’ll share any further updates here once I have more information.

@LiuCodee

Copy link
Copy Markdown
Contributor

I got some feedback from the esp_video team. They think the SCCB probe failure may be related to I2C access happening before XCLK becomes stable after power-on.

They provided a patch suggestion that delays the sensor I2C access until after XCLK is stable. Could you help test it and see whether it improves the cold boot behavior?

mr455.zip

@ymzhang0319

ymzhang0319 commented May 22, 2026

Copy link
Copy Markdown
Author

Thanks for the patch! It does resolve the open("/dev/video2") → ENOENT (errno=2) failure on cold start — /dev/video2 is registered now and open/VIDIOC_STREAMON both return OK.

However, with this patch applied on my board, VIDIOC_DQBUF returns errno=1 (EPERM, "Not owner") immediately after STREAMON, with no clean frame delivered.

The minimal-repro persists the cold-start outcome to RTC_NOINIT_ATTR and prints a code/meta summary post-boot. With mr455 applied, 3/3 cold-start trials produced:

I MINREPRO: FRESH code=2(BUG_B) meta=1

where code=2 is R_BUG_B (DQBUF failed after STREAMON) and meta=1 is errno=EPERM. The standard repro bails on the first DQBUF failure; I separately ran a version with a 50 ms-spaced retry loop on DQBUF for up to 30 s — still 0 clean frames.

Happy to test any follow-up patch.

@WangYuxin-esp

Copy link
Copy Markdown

When providing the XCLK clock to the sensor via the SOC, it is recommended to run esp_video_init() as early as possible in app_main() to provide the XCLK clock to the sensor as quickly as possible.

Alternatively, adding a delay in sensor_detect can also be considered.

Providing the clock via LEDC is also worth trying. The correct usage of this method is to configure the XCLK pin to -1 and then generate the XCLK clock using the xclk generator API.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants