Skip to content

Conversation

@jasaw
Copy link
Contributor

@jasaw jasaw commented Jan 17, 2025

Some OV5640 modules have the auto focus circuit. There are 3 common variants on the market, but they all should have the same registers. The only difference is the AF-VDD and AF-GND pins on the flat connector. More info here: #2162 (comment)
This is the one that I have: https://www.aliexpress.com/item/1005004072836395.html

The auto focus circuit is normally not wired up and requires soldering AF-VDD to the ESP32 module's 2.8V LDO, and AF-GND to ground. This is another variant of ESP32 + OV5640 with auto focus AF-VDD and AF-GND already hooked up properly, so no need for any soldering. See #2162 (comment)

This PR adds support for manual focus and auto focus. The idea is to allow users to set focus level during "create reference image" and we use manual focus with the preconfigured focus level on each image capture. Alternatively, users can just use auto focus and not worry about manually adjusting it.

This is my manual focus test. Focus near vs far.
alg_roi_manual_focus_near
alg_roi_manual_focus_far

@jasaw
Copy link
Contributor Author

jasaw commented Jan 17, 2025

@SybexX Can I please get your thoughts on whether this is going to play nicely with your camera deep sleep changes?

@SybexX
Copy link
Collaborator

SybexX commented Jan 17, 2025

can this also be implemented as a function? e.g. like this:

show code
// HACK: testing only
CCstatus.CameraFocusEnabled = true;
CCstatus.CameraManualFocus = true;
// CCstatus.CameraManualFocusLevel = 0x01e4;
CCstatus.CameraManualFocusLevel = 0x0014;

esp_err_t CCamera::SetCamAutoFocus(void)
{
    if (CCstatus.CameraFocusEnabled && CCstatus.CamSensor_id == OV5640_PID && CCstatus.CameraAFInitSuccessful) {
        ESP_LOGI(TAG, "OV5640 and AF inited");
        sensor_t *s = esp_camera_sensor_get();
		
        if (s != NULL) {
            if (CCstatus.CameraManualFocus) {
                int rc = ov5640_manual_focus_set(s, CCstatus.CameraManualFocusLevel);
				
                if (rc == 0) {
                    ESP_LOGI(TAG, "Set manual focus level success");
                } 
                else {
                    ESP_LOGI(TAG, "Set manual focus level failed: %d", rc);
                    return ESP_FAIL;
                }
            } 
            else {
                int rc = ov5640_autofocus_set_mode(s, AF_TRIG_SINGLE_AUTO_FOCUS);
				
                if (rc == 0) {
                    ESP_LOGI(TAG, "Set single autofocus mode success");
                } 
                else {
                    ESP_LOGI(TAG, "Set single autofocus mode failed: %d", rc);
                    return ESP_FAIL;
                }
				
                ESP_LOGI(TAG, "adjusting focus");
				
                for (int i = 0; i < 10; i++) {
                    camera_fb_t *fb = esp_camera_fb_get();
                    uint8_t S_Zone[5];
                    uint8_t focus_status = ov5640_autofocus_get_status(s, S_Zone, 5);
					
                    for (int i = 0; i < 5; i++) {
                        ESP_LOGI(TAG, "Zone[%d]: 0x%02x", i, S_Zone[i]);
                    }
					
                    esp_camera_fb_return(fb);
					
                    if (focus_status == FW_STATUS_S_FOCUSING) {
                        ESP_LOGI(TAG, "Focusing: 0x%02x", focus_status);
                    } 
                    else if (focus_status == FW_STATUS_S_FOCUSED) {
                        ESP_LOGI(TAG, "Focused: 0x%02x", focus_status);
                        break;
                    } 
                    else {
                        ESP_LOGI(TAG, "Focus status 0x%02x", focus_status);
                    }
                }
            }
        }
    }
    return ESP_OK;
}

esp_err_t CCamera::ReleaseCamAutoFocus(void)
{
    if (CCstatus.CameraFocusEnabled && CCstatus.CamSensor_id == OV5640_PID && CCstatus.CameraAFInitSuccessful) {
        ESP_LOGI(TAG, "OV5640 release AF");
        sensor_t *s = esp_camera_sensor_get();
		
        if (s != NULL) {
            if (CCstatus.CameraManualFocus) {
                int rc = ov5640_manual_focus_release(s);
				
                if (rc == 0) {
                    ESP_LOGI(TAG, "Release manual focus success");
                } 
                else {
                    ESP_LOGI(TAG, "Release manual focus failed: %d", rc);
                    return ESP_FAIL;
                }
            } 
            else {
                int rc = ov5640_autofocus_release(s);
				
                if (rc == 0) {
                    ESP_LOGI(TAG, "Release autofocus success");
                } 
                else {
                    ESP_LOGI(TAG, "Release autofocus failed: %d", rc);
                    return ESP_FAIL;
                }
            }
        }
    }
    return ESP_OK;
}

Do you always have to set the focus before taking the picture, or can you only do this once when starting the ESP32 and it is retained?

@SybexX
Copy link
Collaborator

SybexX commented Jan 17, 2025

@jasaw
Copy link
Contributor Author

jasaw commented Jan 20, 2025

@SybexX

Do you always have to set the focus before taking the picture, or can you only do this once when starting the ESP32 and it is retained?

For our use case, I do not recommend retaining the focus constantly. Technically, we could set the focus once and retain the focus for subsequent image captures, but the focus VCM circuit will constantly draw 100mA, which will heat up the camera module. The way the focus is adjusted is by driving the VCM coil to generate magnetic field to repel against a permanent magnet, therefore moving the focus lens. If we turn off the power to the VCM coil, the lens snaps back to the resting position.

Long story short, yes, we should set the focus on each capture and release focus after the capture. This applies to both manual and auto focus.

Theoretically, you would have to determine the focus value when creating a reference image and then save it to ensure that the alignment and evaluation works without any problems.
Reference image with autofocus >>> Save focus value >>> Image capture with manual focus (with the values ​​determined from autofocus)

Yes, pretty much with the option of manually adjusting the focus level determined by autofocus. The autofocus sometimes gets it wrong, so may require a bit of manual adjustment.

@SybexX
Copy link
Collaborator

SybexX commented Jan 20, 2025

hast du die angepasste firmware schon getestet?

@jasaw
Copy link
Contributor Author

jasaw commented Jan 20, 2025

@SybexX Thanks for integrating the focus stuff into your deep sleep code.

I'll rebase my branch to your add-CameraDeepSleep-for-OV5640 branch and keep working from there.

hast du die angepasste firmware schon getestet?

Sorry, I haven't tested your test branch where you included the focus code into your deep sleep support. I'll test it as soon as possible and report back.

@SybexX
Copy link
Collaborator

SybexX commented Jan 20, 2025

My test fork is only intended as an example to show what I meant by “Can this also be implemented as a function?”
I have now deleted my deep sleep PR, if you want you can add it in your PR.

This is the main code for deep sleep + auto focus:

show code
// only available on OV3660 and OV5640
// https://github.com/espressif/esp32-camera/issues/672
int CCamera::SetCamDeepSleep(bool enable)
{
    int ret = 0;

    if (CCstatus.CameraDeepSleepEnable != enable)
    {
        CCstatus.CameraDeepSleepEnable = enable;

        // release focus from deep sleep
        if ((CCstatus.CameraDeepSleepEnable) && (CCstatus.CamSensor_id != OV2640_PID))
        {
            ret |= ReleaseCamAutoFocus();
        }

        if (CCstatus.CamSensor_id == OV2640_PID)
        {
            // OV2640 (Normal mode >>> Standby mode = OK), (Standby mode >>> Normal mode = n.OK)
            // ret |= s->set_reg(s, 0x109, 0x10, enable ? 0x10 : 0);
            // LogFile.WriteToFile(ESP_LOG_INFO, TAG, "DeepSleep is not supported by OV2640");
        }
        else
        {
            sensor_t *s = esp_camera_sensor_get();

            if (s != NULL)
            {
                ret |= s->set_reg(s, 0x3008, 0x42, enable ? 0x42 : 0x02);
            }
            else
            {
                ret = -1;
            }
        }

        vTaskDelay(100 / portTICK_PERIOD_MS);

        // after the camera has been brought out of deep sleep, set the focus
        if ((!CCstatus.CameraDeepSleepEnable) && (CCstatus.CamSensor_id != OV2640_PID))
        {
            ret |= SetCamAutoFocus();
        }
    }

    return ret;
}

int CCamera::SetCamAutoFocus(void)
{
    int ret = 0;

    if (CCstatus.CamSensor_id == OV5640_PID)
    {
        if (CCstatus.CameraFocusEnabled && CCstatus.CameraAFInitSuccessful)
        {
            ESP_LOGI(TAG, "OV5640 and AF inited");
			
            if (CCstatus.CameraDeepSleepEnable == true)
            {
                ret |= SetCamDeepSleep(false);
                vTaskDelay(100 / portTICK_PERIOD_MS);
            }
			
            sensor_t *s = esp_camera_sensor_get();

            if (s != NULL)
            {
                if (CCstatus.CameraManualFocus)
                {
                    ret |= ov5640_manual_focus_set(s, CCstatus.CameraManualFocusLevel);

                    if (ret == 0)
                    {
                        ESP_LOGI(TAG, "Set manual focus level success");
                    }
                    else
                    {
                        ESP_LOGI(TAG, "Set manual focus level failed: %d", ret);
                    }
                }
                else
                {
                    ret |= ov5640_autofocus_set_mode(s, AF_TRIG_SINGLE_AUTO_FOCUS);

                    if (ret == 0)
                    {
                        ESP_LOGI(TAG, "Set single autofocus mode success");
                    }
                    else
                    {
                        ESP_LOGI(TAG, "Set single autofocus mode failed: %d", ret);
                        return ret;
                    }

                    ESP_LOGI(TAG, "adjusting focus");

                    for (int i = 0;  i < 10;  i++)
                    {
                        camera_fb_t *fb = esp_camera_fb_get();
                        uint8_t S_Zone[5];
                        uint8_t focus_status = ov5640_autofocus_get_status(s, S_Zone, 5);

                        for (int j = 0;  j < 5;  j++)
                        {
                            ESP_LOGI(TAG, "Zone[%d]: 0x%02x", j, S_Zone[i]);
                        }

                        esp_camera_fb_return(fb);

                        if (focus_status == FW_STATUS_S_FOCUSING)
                        {
                            ESP_LOGI(TAG, "Focusing: 0x%02x", focus_status);
                        }
                        else if (focus_status == FW_STATUS_S_FOCUSED)
                        {
                            ESP_LOGI(TAG, "Focused: 0x%02x", focus_status);
                            break;
                        }
                        else
                        {
                            ESP_LOGI(TAG, "Focus status 0x%02x", focus_status);
                        }
                    }
                }
            }
            else
            {
                ret = -1;
            }
        }
    }

    return ret;
}

int CCamera::ReleaseCamAutoFocus(void)
{
    int ret = 0;

    if (CCstatus.CamSensor_id == OV5640_PID)
    {
        if (CCstatus.CameraFocusEnabled && CCstatus.CameraAFInitSuccessful)
        {
            ESP_LOGI(TAG, "OV5640 release AF");
			
            if (CCstatus.CameraDeepSleepEnable == true)
            {
                ret |= SetCamDeepSleep(false);
                vTaskDelay(100 / portTICK_PERIOD_MS);
            }
			
            sensor_t *s = esp_camera_sensor_get();

            if (s != NULL)
            {
                if (CCstatus.CameraManualFocus)
                {
                    ret |= ov5640_manual_focus_release(s);

                    if (ret == 0)
                    {
                        ESP_LOGI(TAG, "Release manual focus success");
                    }
                    else
                    {
                        ESP_LOGI(TAG, "Release manual focus failed: %d", ret);
                    }
                }
                else
                {
                    ret |= ov5640_autofocus_release(s);

                    if (ret == 0)
                    {
                        ESP_LOGI(TAG, "Release autofocus success");
                    }
                    else
                    {
                        ESP_LOGI(TAG, "Release autofocus failed: %d", ret);
                    }
                }
            }
            else
            {
                ret = -1;
            }
        }
    }

    return ret;
}

@jasaw
Copy link
Contributor Author

jasaw commented Jan 23, 2025

@SybexX I've added focus control to the "create new reference" page and configuration page. I've tested it and it's working as expected.
There are a few issues that I'm not sure what the best solution is yet:

  1. When creating a new reference image, the parameters configured there are temporary until a new reference image is saved. The way I've implemented it now, the focus control won't revert back to the original values after "update reference image" but before new reference image is saved.
  2. For best user experience, ideally the "create a new reference image" page should show the focus level determined by auto focus. I'm not sure what's the best way to relay the focus level back to the webpage.

Any reason why you closed your deep sleep PR? Did you discover problems with it? I'm thinking of adding deep sleep to this PR based on your deep sleep PR.

@SybexX
Copy link
Collaborator

SybexX commented Jan 23, 2025

@jasaw I made some adjustments that should solve the first problem:
AI-on-the-edge-device-feature-focus_support.zip

@jasaw jasaw marked this pull request as ready for review January 24, 2025 04:14
@jasaw
Copy link
Contributor Author

jasaw commented Jan 24, 2025

@SybexX Thanks for your code adjustment. Both issues that I mentioned are solved now.

@jasaw jasaw mentioned this pull request Jan 24, 2025
@jasaw
Copy link
Contributor Author

jasaw commented Jan 24, 2025

@SybexX I've added camera deep sleep based on your deep sleep function. It appears to be working properly with autofocus. Can you please test this branch with your non-autofocus OV5640?

@SybexX
Copy link
Collaborator

SybexX commented Jan 24, 2025

@jasaw AI-on-the-edge-device/sd-card/html/edit_reference.html >>> Line 959 >>> WriteParameter(param, category, "TakeImage", "CamFocusManualLevel", false, true); causes an error.
You have to change it to WriteParameter(param, category, "TakeImage", "CamFocusManualLevel", false);

@SybexX
Copy link
Collaborator

SybexX commented Jan 24, 2025

@jasaw What do you think about adding a debug output during deep sleep so that you can follow the functionality better?

        if (CCstatus.CamSensor_id == OV2640_PID)
        {
            // OV2640 (Normal mode >>> Standby mode = OK), (Standby mode >>> Normal mode = n.OK)
            // ret |= s->set_reg(s, 0x109, 0x10, enable ? 0x10 : 0);
            // LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "DeepSleep is not supported by OV2640");
            ESP_LOGD(TAG, "DeepSleep is not supported by OV2640");
        }
        else
        {
            sensor_t *s = esp_camera_sensor_get();
            s->set_reg(s, 0x3008, 0x42, enable ? 0x42 : 0x02);
            // LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "DeepSleep: %d", enable);
            ESP_LOGD(TAG, "DeepSleep: %d", enable);
        }

@SybexX
Copy link
Collaborator

SybexX commented Jan 24, 2025

@jasaw I have prepared something, maybe you would like to include it in your PR.
SybexX@b2f8924
SybexX@cb39551
SybexX@675a8cb

then you can e.g. add something like this:

if ((camId != "OV5640") && (camFocus == "unavailable")){
    EnDisableItem(false, param, category, "TakeImage", "CamFocusAuto", false);
    EnDisableItem(false, param, category, "TakeImage", "CamFocusManualLevel", false);
    // or hide it completely with setVisible(className, visible)
    ........................................
}

@jasaw
Copy link
Contributor Author

jasaw commented Jan 25, 2025

@SybexX Thanks. I've added hiding of focus control when camera does not support focus control.

@SybexX
Copy link
Collaborator

SybexX commented Jan 25, 2025

@jasaw Something does not fit with the initialization of the focus, although my camera does not have one, it is successfully initialized and also displayed on the reference page ^^ When I try it with an OV2640, it successfully hides on the reference page.

================ Start app_main =================
I (1698) MAIN: =================================================
I (1728) MAIN: ==================== Start ======================
I (1768) MAIN: =================================================
I (1918) MAIN: PSRAM size: 8388608 byte (8MB / 64MBit)
I (1988) MAIN: Total heap: 4380691 byte
I (2128) gpio: GPIO[25]| InputEn: 1| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:2 
I (2128) cam_hal: cam init ok
I (2128) sccb: pin_sda 26 pin_scl 27
I (2128) sccb: sccb_i2c_port=1
I (2128) gpio: GPIO[32]| InputEn: 0| OutputEn: 1| OpenDrain: 0| Pullup: 0| Pulldown: 0| Intr:0
I (2168) camera: Detected camera at address=0x3c
I (2168) camera: Detected OV5640 camera
I (2168) camera: Camera PID=0x5640 VER=0x00 MIDL=0x00 MIDH=0x00
I (2868) cam_hal: buffer_size: 32768, half_buffer_size: 4096, node_buffer_size: 2048, node_cnt: 16, total_cnt: 15
I (2868) cam_hal: Allocating 61440 Byte frame buffer in PSRAM
I (2868) cam_hal: cam config ok
I (2888) ov5640: Set PLL: bypass: 0, multiplier: 180, sys_div: 4, pre_div: 2, root_2x: 0, pclk_root_div: 2, pclk_manual: 1, pclk_div: 4
I (2888) ov5640: Calculated XVCLK: 20000000 Hz, REFIN: 10000000 Hz, VCO: 1800000000 Hz, PLL_CLK: 180000000 Hz, SYSCLK: 45000000 Hz, PCLK: 11250000 Hz
I (3028) CAM: OV5640 camera module detected
I (3028) CAM: Initializing autofocus ...
I (8768) CAM: Autofocus init success
I (8768) CAM: Release autofocus success
I (10768) MAIN: Camera info: PID: 0x5640, VER: 0x00, MIDL: 0x00, MIDH: 0x00

@jasaw
Copy link
Contributor Author

jasaw commented Jan 25, 2025

@SybexX Unfortunately, there is no way to identify whether the focus VCM coil is physically attached or not. The only difference between autofocus and non-autofocus OV5640 is the presence of external VCM coil.

@jasaw
Copy link
Contributor Author

jasaw commented Feb 7, 2025

@SybexX

if you use zoom, the camera sporadically causes errors when the quality is set below 10.

From my experience with ov5640, the stable value for jpeg quality depends on the complexity of the captured image.
For example, I can get away with quality value 8 if I do grey scale and denoise value 8 because the image is smoother, so resulting jpeg is small. If I reduce denoise value to 3, or enable colour, it starts failing depending on lighting conditions (regardless of zoom).

Again, from my limited experience, jpeg quality 16 appears to work in all configurations that I've tested (colour, denoise, etc). If I do grey scale, it's safe to run with jpeg quality 12.

@jasaw
Copy link
Contributor Author

jasaw commented Feb 10, 2025

@SybexX This is just my observation, further investigation is needed.

It looks like hitting the Overview button on the html page repeatedly during "Taking image" step has a higher chance of causing the captured image to be corrupted. I ran this test with wifi running on core 1, everything else on core 0. Wifi is on the default min low power mode with -67dB signal strength. I can't reproduce this on my other ESP32cam with -38dB wifi signal strength.

@jasaw
Copy link
Contributor Author

jasaw commented Feb 12, 2025

@SybexX Another theory is after waking up from deepsleep, the camera needs a few frames to adjust the auto exposure and auto white balance.
See espressif/esp32-camera#314

I'm testing with this code to see whether the resulting capture is more stable.

        for (int i = 0; i < 3; i++)
        {
            fb = esp_camera_fb_get();
            esp_camera_fb_return(fb);
        }
        fb = esp_camera_fb_get();

@SybexX
Copy link
Collaborator

SybexX commented Feb 12, 2025

I don't know if that brings something, because this problem should actually eliminate with "WaitBeforePicture".

change this:

    if (delay > 0)
    {
        LightOnOff(true); // Flash-LED on
        const TickType_t xDelay = delay / portTICK_PERIOD_MS;
        vTaskDelay(xDelay);
    }

    bool _focusEnabled = false;
    bool _manualFocus = false;
    bool _reloadZoomConfig = false;
    PrecaptureCamSetup(&_focusEnabled, &_manualFocus, &_reloadZoomConfig);

to:

    bool _focusEnabled = false;
    bool _manualFocus = false;
    bool _reloadZoomConfig = false;
    PrecaptureCamSetup(&_focusEnabled, &_manualFocus, &_reloadZoomConfig);

    if (delay > 0)
    {
        LightOnOff(true); // Flash-LED on
        const TickType_t xDelay = delay / portTICK_PERIOD_MS;
        vTaskDelay(xDelay);
    }

In the functions:

esp_err_t CCamera::CaptureToBasisImage(CImageBasis *_Image, int delay) { }
esp_err_t CCamera::CaptureToFile(std::string nm, int delay) { }
esp_err_t CCamera::CaptureToHTTP(httpd_req_t *req, int delay) { }

So that "WaitBeforePicture" has the desired effect again.

@jasaw
Copy link
Contributor Author

jasaw commented Feb 13, 2025

@SybexX I have moved the wifi back to core 0 because the camera task is running on core 1 and we want to minimize interruption to the camera SCCB running on I2C1 interface.

You are right that moving WaitBeforePicture to after PrecaptureCamSetup should do the trick, but the LightOnOff has to be done before PrecaptureCamSetup because it needs the light for autofocus to work.
I think WaitBeforePicture defaults to 5 seconds but now I'm thinking whether that is necessary. I feel that calling esp_camera_fb_get() in a loop of 5 or 10 is more appropriate. The ov5640 auto-exposure control has auto banding where it needs a few frames to determine the best exposure and gain. I'm currently testing looping esp_camera_fb_get() 5 times before saving the image instead of 5s delay. From my initial testing, I haven't encountered a single badly exposed with bad white balance image yet. Only time will tell whether the bad exposure bad white balance problem is completely gone, so I'll continue to monitor it.

Regarding the corrupted image that I get occasionally, it happens regardless of WaitBeforePicture 5s delay and/or looping esp_camera_fb_get() 5 times, but I only ever see this on one of my ov5640 cameras that's running at higher ambient temperature (under direct sunlight). Now I'm suspecting that it is temperature related.
The ambient temperature here is around 40°C today and I happen to notice more corrupted image than usual. I'm currently testing my theory by running the CPU at 80MHz to keep it as cool as possible and I've reduced the xclk_freq_hz from 20MHz down to 15MHz.

I've also increased the minimum quality value for ov5640 to 16 just to make sure that I avoid the corrupted jpeg issue when quality value is set too low.

All these 3 changes on still on my local branch. I'll continue to monitor and report back as usual.

ps: I found a bug in the CPU frequency config handling code.

@jasaw
Copy link
Contributor Author

jasaw commented Feb 15, 2025

@SybexX A quick update:

On my local branch, I have re-incorporated the 5 seconds WaitBeforePicture before looping esp_camera_fb_get() 5 times. I'm not sure whether I need both, but that can be optimized later.

I have also reduced the xclk_freq_hz down to 10MHz and this made a HUGE difference to the OV5640 image quality. Compared to 20MHz clock, the 10MHz gives me less noise, sharper image, well balanced exposure. I have auto-denoise and auto-exposure enabled. I have observed a change in image quality when I play around with the clock frequency and have seen reports of this on forums too. The downside of reducing the clock frequency is the reduced frame rate, but we do not need high frame rate anyway.

@Slider0007
Copy link
Collaborator

I have also reduced the xclk_freq_hz down to 10MHz and this made a HUGE difference to the OV5640 image quality. Compared to 20MHz clock, the 10MHz gives me less noise, sharper image, well balanced exposure. I have auto-denoise and auto-exposure enabled. I have observed a change in image quality when I play around with the clock frequency and have seen reports of this on forums too. The downside of reducing the clock frequency is the reduced frame rate, but we do not need high frame rate anyway.

@jasaw: Interesting :-)
I also have my OV5640 camera running with 10Mhz only for board responiveness and lower camera temperatures reason for a longer time without issues. In my environment I couldn't see that much difference between 20Mhz an d 10Mhz in terms of image quality, though.
I also have OV2640 which has horribly image quality and transmission issues using 8Mhz, but running without issues and decent image quality at 10Mhz. It seems it depends somehow on camera module quality, board/camera module combination and maybe environment.
Haven't tested with auto focus variant nor sleep mode...

@jasaw
Copy link
Contributor Author

jasaw commented Feb 24, 2025

Now I highly suspect some OV5640 camera modules are more susceptible to electrical noise than others and therefore some are not stable at 20MHz xclk. Best workaround that I've found is to run it at 10 MHz. I've been testing both my OV5640 at 10MHz for 1 week now and I haven't seen a single corrupted image. @SybexX @Slider0007 Have you guys seen any corrupted image running at 10 MHz?

The better image quality when running at 10 MHz is a mystery but I'll happily accept better quality image with lower camera temperature. That's win-win for me :-)

The other problem that I was observing on one of my ov5640 was caused by high temperature. On extremely hot days, under direct sunlight, my ov5640 gets overheated and I ended up the web interface saying "Flow not yet created", so that's my hardware fault for running it too hot. I need better heat management on my hardware.

I believe the occasional corrupted image mystery is solved now. I'll continue to monitor it and do a full test with my ov5640+AF camera module. I'm going to re-test without the 5s WaitBeforePicture and instead loop the image capture 10 times before capture. My initial test shows that there's no difference in image quality when running at 10 MHz. @SybexX Do you know what is the purpose of WaitBeforePicture?

@caco3 caco3 marked this pull request as draft February 28, 2025 08:15
@Slider0007
Copy link
Collaborator

Slider0007 commented Feb 28, 2025

@jasaw

Now I highly suspect some OV5640 camera modules are more susceptible to electrical noise than others and therefore some are not stable at 20MHz xclk. Best workaround that I've found is to run it at 10 MHz. I've been testing both my OV5640 at 10MHz for 1 week now and I haven't seen a single corrupted image. @SybexX @Slider0007 Have you guys seen any corrupted image running at 10 MHz?

I haven't any issues so far while operating OV5640 with 10Mhz. All my devices (not all with OV5640, though) are operated either with 8Mhz or 10Mhz. Going lower than 8Mhz, the image quality is decreasing somehow (artifacts, disturbing lines, etc.). Not every camera module behaves the same in this respect either, regardless of whether it's an OV2640 or OV5640.

The better image quality when running at 10 MHz is a mystery but I'll happily accept better quality image with lower camera temperature. That's win-win for me :-)

Fully agree.

@jasaw
Copy link
Contributor Author

jasaw commented Mar 5, 2025

@SybexX I've been testing without the 5s WaitBeforePicture delay and these are my findings:

  • WaitBeforePicture is not needed as far as I can tell. I am looping image capture 10 times before the actual capture and the image quality has been consistently good.
  • The 5s WaitBeforePicture is causing my OV5640 to heat up unnecessarily, causing my camera module to overheat on hot days (over 36°C under direct sunlight). After taking out the 5s WaitBeforePicture, my OV5640 hasn't overheated yet even on hot days.

I'm pretty happy with the stability of the set up now. @SybexX @Slider0007 Question is, how do we want to proceed wrt the issues below:

  1. 10 MHz xclk_freq_hz: Do we want to make this the default and let users set a different value if it doesn't work for them?
  2. WaitBeforePicture: Do we really need this? What problem does this actually solve? Sorry I don't know the history of it.

@SybexX
Copy link
Collaborator

SybexX commented Mar 5, 2025

WaitBeforePicture: Do we really need this? What problem does this actually solve? Sorry I don't know the history of it.

It is necessary because some digital meters need some time to wake up from standby mode (have to be illuminated with a light source for a certain time).

10 MHz xclk_freq_hz: Do we want to make this the default and let users set a different value if it doesn't work for them?

I asked @jomjol and @caco3 because of that.
Since we want to publish a releas soon(next or after next week), no further changes will be carried out on the main, so this is done if after the next release.

@Teleftaios
Copy link

Hi. I tried the stable 16.0.0 but autofocus (with manual soldered 3,3V) does not work. Even is there an chance to make the livestream in full resolution instead of 640?

@jasaw
Copy link
Contributor Author

jasaw commented Apr 17, 2025

Hi. I tried the stable 16.0.0 but autofocus (with manual soldered 3,3V) does not work. Even is there an chance to make the livestream in full resolution instead of 640?

@Teleftaios autofocus support has not been released yet, so 16.0.0 does not support autofocus. If you want to try autofocus, you'll have to compile the firmware from this branch with the xclk_freq_hz changed to 10MHz.
I'm still working on merging this PR, but I've been busy recently.

@SybexX I've been testing this PR with 10MHz xclk_freq_hz and zero seconds WaitBeforePicture and looping image capture 10 times before the actual capture. It has been running stable on both my ov5640 with uptime of 28 days. The outstanding questions are:

  1. Regarding the WaitBeforePicture, I think we should turn on the LED, wait for WaitBeforePicture 5 seconds, then only take the camera out of deep sleep. That should solve the camera module heating up too much.
  2. Should we change the default value of WaitBeforePicture?
  3. We should keep the looping image capture 10 times before the actual capture. I find that this produces stable image consistently.
  4. Regarding the 10MHz xclk_freq_hz, should we just change the default xclk_freq_hz to 10MHz?
  5. Should we allow users to set a new xclk_freq_hz value?

@thomaslunze
Copy link

would love to see the ov5640 supported

@allexoK
Copy link

allexoK commented Sep 15, 2025

Very cool, thanks for implementing it! I will add auto-focus power to Ai-On-The-Edge-Cam boards next revision (#2963).

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.

6 participants