Skip to content

Conversation

@PhosphorosVR
Copy link
Contributor

  • Add general version configuration to FaceFocusVR board files
  • Added XCLK_FREQ_OVERRIDE and logging
  • Moved XCLK_FREQ out of configs, default now 20MHz
  • Adjusted camera XCLK frequency for OV2640/OV3660
  • Reduced OV2640 XCLK to 20MHz for FaceFocusVR boards (~50fps)
  • Tweaked JPEG quality and LED timer/channel settings (didnt fix NO SOI Error)

Copilot AI review requested due to automatic review settings November 11, 2025 21:50
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR refactors camera XCLK frequency configuration to support per-sensor overrides and resolves LEDC timer/channel conflicts. The main changes include replacing the global CONFIG_CAMERA_USB_XCLK_FREQ with sensor-specific override options (CONFIG_CAMERA_XCLK_FREQ_OVERRIDE_OV2640 and CONFIG_CAMERA_XCLK_FREQ_OVERRIDE_OV3660), implementing override logic in CameraManager, and moving LED PWM from TIMER_0/CHANNEL_0 to TIMER_1/CHANNEL_1 to avoid conflicts with camera XCLK.

  • Replaced global USB XCLK config with sensor-specific overrides for OV2640/OV3660
  • Implemented runtime XCLK frequency override logic based on detected camera sensor
  • Moved LED PWM to TIMER_1/CHANNEL_1 to avoid hardware conflict with camera XCLK (TIMER_0)

Reviewed Changes

Copilot reviewed 10 out of 10 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
sdkconfig Removed USB_XCLK_FREQ config, added override configs for OV2640/OV3660
main/Kconfig.projbuild Removed USB XCLK configuration, added sensor-specific override options with help text
boards/sdkconfig.base_defaults Removed default USB_XCLK_FREQ setting
boards/seed_studio/xiao_esp32s3 Updated to use new override configs (OV2640: 23MHz)
boards/project_babble/project_babble Updated to use new override configs (OV2640: 23MHz)
boards/facefocusvr/face Updated to use new override configs (OV2640: 20MHz, OV3660: 24MHz), added version 1.1.0
boards/facefocusvr/eye_R Updated to use new override configs (OV2640: 20MHz, OV3660: 24MHz), added version 1.1.0
boards/facefocusvr/eye_L Updated to use new override configs (OV2640: 20MHz, OV3660: 24MHz), added version 1.1.0
components/CameraManager/CameraManager/CameraManager.cpp Implemented sensor-specific XCLK override logic, added logging, adjusted JPEG quality, introduced USB default XCLK of 20MHz
components/LEDManager/LEDManager/LEDManager.cpp Changed LED PWM from TIMER_0/CHANNEL_0 to TIMER_1/CHANNEL_1 to avoid conflict with camera

range 0 40000000
help
If non-zero, overrides the selected XCLK frequency specifically when an OV2640 sensor
is detected. Use 0 to disable and fall back to CAMERA_USB_XCLK_FREQ / CAMERA_WIFI_XCLK_FREQ.
Copy link

Copilot AI Nov 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The help text references CAMERA_USB_XCLK_FREQ, which was removed in this PR. Since USB mode now uses a hardcoded default of 20MHz (see CAMERA_USB_XCLK_FREQ_DEFAULT in CameraManager.cpp), the help text should be updated to reflect that it falls back to the USB default (20MHz) or CAMERA_WIFI_XCLK_FREQ depending on the mode.

Copilot uses AI. Check for mistakes.
range 0 40000000
help
If non-zero, overrides the selected XCLK frequency specifically when an OV3660 sensor
is detected. Use 0 to disable and fall back to CAMERA_USB_XCLK_FREQ / CAMERA_WIFI_XCLK_FREQ.
Copy link

Copilot AI Nov 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The help text references CAMERA_USB_XCLK_FREQ, which was removed in this PR. Since USB mode now uses a hardcoded default of 20MHz (see CAMERA_USB_XCLK_FREQ_DEFAULT in CameraManager.cpp), the help text should be updated to reflect that it falls back to the USB default (20MHz) or CAMERA_WIFI_XCLK_FREQ depending on the mode.

Copilot uses AI. Check for mistakes.
const auto pid = temp_sensor->id.PID;
int desired_freq = 0;
#ifdef CONFIG_CAMERA_XCLK_FREQ_OVERRIDE_OV2640
if (pid == OV2640_PID && CONFIG_CAMERA_XCLK_FREQ_OVERRIDE_OV2640 > 0)
Copy link

Copilot AI Nov 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The override logic does not prevent applying overrides to OV5640 sensors. If an OV5640 is detected, it gets its frequency set on line 212, but then the override logic (lines 224-262) will still execute. If OV5640's PID happens to match OV2640_PID or OV3660_PID, an unintended override could be applied. While unlikely due to different PIDs, the logic should explicitly exclude OV5640 from the override check (e.g., add && pid != OV5640_PID) or restructure to use else-if to prevent processing OV5640 twice.

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

True lol

}
#endif
#ifdef CONFIG_CAMERA_XCLK_FREQ_OVERRIDE_OV3660
if (pid == OV3660_PID && CONFIG_CAMERA_XCLK_FREQ_OVERRIDE_OV3660 > 0)
Copy link

Copilot AI Nov 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The override logic does not prevent applying overrides to OV5640 sensors. If an OV5640 is detected, it gets its frequency set on line 212, but then the override logic (lines 224-262) will still execute. If OV5640's PID happens to match OV2640_PID or OV3660_PID, an unintended override could be applied. While unlikely due to different PIDs, the logic should explicitly exclude OV5640 from the override check (e.g., add && pid != OV5640_PID) or restructure to use else-if to prevent processing OV5640 twice.

Copilot uses AI. Check for mistakes.
Comment on lines +175 to +177
uint32_t duty = (state == LED_ON) ? ((50 * 255) / 100) : 0;
ESP_ERROR_CHECK_WITHOUT_ABORT(ledc_set_duty(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_1, duty));
ESP_ERROR_CHECK_WITHOUT_ABORT(ledc_update_duty(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_1));
Copy link

Copilot AI Nov 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The indentation is inconsistent with the surrounding code. This line should be indented to align with the other statements inside the if block (lines 176-177).

Suggested change
uint32_t duty = (state == LED_ON) ? ((50 * 255) / 100) : 0;
ESP_ERROR_CHECK_WITHOUT_ABORT(ledc_set_duty(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_1, duty));
ESP_ERROR_CHECK_WITHOUT_ABORT(ledc_update_duty(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_1));
uint32_t duty = (state == LED_ON) ? ((50 * 255) / 100) : 0;
ESP_ERROR_CHECK_WITHOUT_ABORT(ledc_set_duty(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_1, duty));
ESP_ERROR_CHECK_WITHOUT_ABORT(ledc_update_duty(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_1));

Copilot uses AI. Check for mistakes.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch by copilot, though, I'm not enforcing any formatting just yet. I probably should

ESP_LOGI(LED_MANAGER_TAG, "Updating external LED duty to %u%% (raw %lu)", dutyPercent, dutyCycle);

// Apply to LEDC hardware live
// We configured channel 0 in setup with LEDC_LOW_SPEED_MODE
Copy link

Copilot AI Nov 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment states 'channel 0' but the code now uses CHANNEL_1 (line 190). The comment should be updated to 'channel 1' to match the current implementation.

Suggested change
// We configured channel 0 in setup with LEDC_LOW_SPEED_MODE
// We configured channel 1 in setup with LEDC_LOW_SPEED_MODE

Copilot uses AI. Check for mistakes.
@PhosphorosVR PhosphorosVR marked this pull request as draft December 4, 2025 02:35
.frame_size = FRAMESIZE_240X240, // QQVGA-UXGA, For ESP32, do not use sizes above QVGA when not JPEG. The performance of the ESP32-S series has improved a lot, but JPEG mode always gives better frame rates.

.jpeg_quality = 8, // 0-63, for OV series camera sensors, lower number means higher quality // Below 6 stability problems
.jpeg_quality = 10, // 0-63, for OV series camera sensors, lower number means higher quality // Below 6 stability problems
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we wanna lower the quality once more? Were there any problems with it at 8 for facefocus boards or?

// 20000000 max freq on ESP32-CAM
// 24000000 optimal freq on ESP32-S3 // 23MHz same fps
int xclk_freq_hz = CONFIG_CAMERA_WIFI_XCLK_FREQ;
ESP_LOGI(CAMERA_MANAGER_TAG, "Base WIFI XCLK freq (from config): %d Hz", xclk_freq_hz);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wifi? o.O

Comment on lines +53 to +55
// Use code default for UVC if no per-sensor override applies
constexpr int CAMERA_USB_XCLK_FREQ_DEFAULT = 20000000; // 20 MHz default
xclk_freq_hz = CAMERA_USB_XCLK_FREQ_DEFAULT;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is there a reason to define CAMERA_USB_XCLK_FREQ_DEFAULT instead of just directly assigning 20000000 to xclk_freq_hz ?

Comment on lines +224 to +227
// Minimal per-sensor override logic (only if enabled via Kconfig and different)
if (temp_sensor)
{
const auto pid = temp_sensor->id.PID;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good catch with the check, but since we're checking for the camera_id a couple of lines above, we will never hit it. We will crash right away if things failed to grab the sensor.

Also, I'm genuinely not sure why we're applying the overrides here like that. Can't we check if they're defined and > 0 right in the beginning and if so - override the base XCLK variable and then let the system handle everything on its own?

We could make this even cleaner by defining a method for it - like int determineXCLKFrequency(int cameraPID) {}

What do you think?

.speed_mode = LEDC_LOW_SPEED_MODE,
.duty_resolution = resolution,
.timer_num = LEDC_TIMER_0,
.timer_num = LEDC_TIMER_1, // moved to TIMER_1 to avoid conflict with camera XCLK (TIMER_0)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good catch, thank you!

Comment on lines +175 to +177
uint32_t duty = (state == LED_ON) ? ((50 * 255) / 100) : 0;
ESP_ERROR_CHECK_WITHOUT_ABORT(ledc_set_duty(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_1, duty));
ESP_ERROR_CHECK_WITHOUT_ABORT(ledc_update_duty(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_1));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch by copilot, though, I'm not enforcing any formatting just yet. I probably should

@lorow
Copy link
Member

lorow commented Dec 13, 2025

Sorry it took me so long to reviewing your PR, a couple of things happened, but we're back on track. I've left some questions, I'm not sure if I'm sold on the current implementation of overrides BUT I do think it's a good idea to have two separate and clearly defined freq for the different sensors. Also, once more, good catch with the timers, thank you!

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.

2 participants