Open Jpsy opened 1 year ago
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.
This issue is still valid.
This issue is still valid! I just got bitten by it.
EDIT: it remains to be seen if this is an issue with OV5640 or only with OV2640. I ordered a 5640 device to test.
+1 here, this should be reopened. CC @OttoWinter for signal boost.
Sorry, I had this referred by a question about changing the resolution and I didn't look close enough. That would be a feature request. But this does seem to be a valid issue since this should probably work. However, the analysis doesn't seem to be entirely correct because all the initial settings are set using this same method and those work. So I don't see why this wouldn't work as well.
With further thought, I remember that people have been adjusting these settings from the beginning, so I don't know why it wouldn't be working now. And @fornellas , Otto is no longer involved with this project.
Hi Samuel, I don't really know what to respond now. Changing these settings does not work for me and for others and I could track the reason down to an obvious code problem. I also provided YAML to reproduce the problem. If you "remember that people have been adjusting these settings" and that proves me wrong, then I have no other means left to convince you. Please tell me, what else would be needed.
BTW there are in fact many camera settings that CAN be changed and that are not broken. Maybe it is those that you remember. I am only talking about effects that are additionally controlled by the bit mask in SDE indirect register.
Yes, the SDE indirect register bit mask. If any of the settings controlled by that bit mask is changed, changes to the second or third setting from the same group have no effect. Other settings like camera flipping work perfectly and aren't overridden by changes in contrast or brightness or saturation.
It's therefore impossible to implement something in ESPHome like the ESP webcam demo (with settings on the left sidebar) that's common to see shipped in ESP devices and in YouTube videos. (This happens to be precisely what I have been working on lately.)
If you set those values in the config, do they work?
Hard to tell because the changes are subtle. But if I run the exact same code (using C++ lambda) that the settings run, no, they don't work.
In my tests:
PS: for reference, this is the config I'm using which enable various settings to be adjusted at either ESPHome web UI or at Home Assistant:
esphome:
name: "esp32-cam"
friendly_name: "ESP32 Cam"
name_add_mac_suffix: true
on_boot:
- priority: 600.0
then:
- lambda: |-
// Default: Connecting
id(wifi_led_pwm).set_level(0.5);
id(wifi_led_pwm).set_period(1000);
id(wifi_led_pwm).turn_on();
- script.execute: fast_reboot_factory_reset
on_loop:
then:
- lambda: |-
// Captive portal
if(id(wifi_component).has_ap() && !id(wifi_component).has_sta()) {
id(wifi_led_pwm).set_level(0.5);
id(wifi_led_pwm).set_period(200);
} else {
id(wifi_led_pwm).set_period(1000);
// Connected
if(id(wifi_component).is_connected()) {
id(wifi_led_pwm).set_level(1);
if (id(esp32cam_update)) {
id(esp32cam).update_camera_parameters();
id(esp32cam_update) = false;
}
// Connecting
} else
id(wifi_led_pwm).set_level(0.5);
}
esp32:
board: esp32cam
external_components:
- source:
type: git
url: https://github.com/fornellas/esphome
ref: fix_esp32_camera_update
components: [ esp32_camera ]
refresh: 0s
globals:
- id: fast_reboot
type: int
restore_value: yes
initial_value: '0'
- id: factory_reset_reboot_counter
type: int
initial_value: '5'
- id: esp32cam_update
type: bool
initial_value: 'false'
logger:
baud_rate: 0
level: INFO
deassert_rts_dtr: true
script:
- id: fast_reboot_factory_reset
then:
- if:
condition:
lambda: return ( id(fast_reboot) >= id(factory_reset_reboot_counter) );
then:
- lambda: |-
ESP_LOGI("Fast Boot Factory Reset", "Performing factotry reset");
id(fast_reboot) = 0;
fast_reboot->loop();
global_preferences->sync();
- button.press: factory_reset_button
- lambda: |-
if(id(fast_reboot) > 0)
ESP_LOGI("Fast Boot Factory Reset", "Quick reboot %d/%d, do it %d more times to factory reset", id(fast_reboot), id(factory_reset_reboot_counter), id(factory_reset_reboot_counter) - id(fast_reboot));
id(fast_reboot) += 1;
fast_reboot->loop();
global_preferences->sync();
- delay: 10s
- lambda: |-
id(fast_reboot) = 0;
fast_reboot->loop();
global_preferences->sync();
wifi:
id: wifi_component
ap:
ap_timeout: 0s
reboot_timeout: 0s
power_save_mode: none
captive_portal:
web_server:
api:
reboot_timeout: 0s
ota:
safe_mode: false
esp32_camera:
external_clock:
pin:
number: GPIO0
ignore_strapping_warning: true
frequency: 20MHz
i2c_pins:
sda: GPIO26
scl: GPIO27
data_pins: [GPIO5, GPIO18, GPIO19, GPIO21, GPIO36, GPIO39, GPIO34, GPIO35]
vsync_pin: GPIO25
href_pin: GPIO23
pixel_clock_pin: GPIO22
power_down_pin: GPIO32
name: "Camera"
id: esp32cam
max_framerate: "60 fps"
# resolution: 160x120 # (QQVGA)
# resolution: 176x144 # (QCIF)
# resolution: 240x176 # (HQVGA)
# resolution: 320x240 # (QVGA)
# resolution: 400x296 # (CIF) Sub-sampled
# resolution: 640x480 # (VGA)
resolution: 800x600 # (SVGA) Sub-sampled
# resolution: 1024x768 # (XGA)
# resolution: 1280x1024 # (SXGA)
# resolution: 1600x1200 # (UXGA) Native
on_stream_start:
then:
- if:
condition:
lambda: return id(flash_automatic).state;
then:
- switch.turn_on: flash
on_stream_stop:
then:
- if:
condition:
lambda: return id(flash_automatic).state;
then:
- switch.turn_off: flash
esp32_camera_web_server:
- port: 8080
mode: stream
- port: 8081
mode: snapshot
binary_sensor:
- platform: status
name: "Status"
sensor:
- platform: uptime
name: 'Uptime'
update_interval: 15s
- platform: wifi_signal
name: "WiFi Signal"
update_interval: 15s
output:
- platform: slow_pwm
id: wifi_led_pwm
period: 1s
pin: GPIO33
inverted: true
button:
- platform: factory_reset
id: factory_reset_button
name: "Factory reset"
- platform: restart
name: "Restart"
- platform: safe_mode
name: "Safe Mode"
text_sensor:
- platform: wifi_info
ip_address:
name: "Wifi Info: IP Address"
ssid:
name: "Wifi Info: SSID"
bssid:
name: "Wifi Info: BSSID"
mac_address:
name: "Wifi Info: MAC Address"
dns_address:
name: "Wifi Info: DNS Address"
switch:
- platform: template
name: "Vertical flip"
icon: mdi:swap-vertical
entity_category: config
restore_mode: RESTORE_DEFAULT_ON
optimistic: true
turn_on_action:
- lambda: |-
id(esp32cam).set_vertical_flip(true);
id(esp32cam_update) = true;
turn_off_action:
- lambda: |-
id(esp32cam).set_vertical_flip(false);
id(esp32cam_update) = true;
- platform: template
name: "Horizontal mirror"
icon: mdi:reflect-horizontal
entity_category: config
restore_mode: RESTORE_DEFAULT_ON
optimistic: true
turn_on_action:
- lambda: |-
id(esp32cam).set_horizontal_mirror(true);
id(esp32cam_update) = true;
turn_off_action:
- lambda: |-
id(esp32cam).set_horizontal_mirror(false);
id(esp32cam_update) = true;
- platform: template
name: "Auto exposure compensation 2"
icon: mdi:auto-fix
entity_category: config
restore_mode: RESTORE_DEFAULT_OFF
optimistic: true
turn_on_action:
- lambda: |-
id(esp32cam).set_aec2(true);
id(esp32cam_update) = true;
turn_off_action:
- lambda: |-
id(esp32cam).set_aec2(false);
id(esp32cam_update) = true;
- platform: template
name: "Flash automatic on stream"
id: flash_automatic
icon: mdi:flash
entity_category: config
restore_mode: RESTORE_DEFAULT_ON
optimistic: true
- platform: gpio
name: "Flash"
icon: mdi:flash
id: flash
pin: GPIO4
select:
# FIXME when reset after boot, yields panic some time after
# - platform: template
# name: "Resolution"
# icon: mdi:video-standard-definition
# entity_category: config
# optimistic: true
# options:
# - "160x120 (QQVGA)"
# - "176x144 (QCIF)"
# - "240x176 (HQVGA)"
# - "320x240 (QVGA)"
# - "400x296 (CIF)"
# - "640x480 (VGA)"
# - "800x600 (SVGA)"
# - "1024x768 (XGA)"
# - "1280x1024 (SXGA)"
# - "1600x1200 (UXGA)"
# initial_option: "640x480 (VGA)"
# restore_value: true
# on_value:
# then:
# - lambda: |-
# if ("160x120 (QQVGA)" == x )
# id(esp32cam).set_jpeg_quality(esphome::esp32_camera::ESP32_CAMERA_SIZE_160X120);
# else if ("176x144 (QCIF)" == x)
# id(esp32cam).set_jpeg_quality(esphome::esp32_camera::ESP32_CAMERA_SIZE_176X144);
# else if ("240x176 (HQVGA)" == x)
# id(esp32cam).set_jpeg_quality(esphome::esp32_camera::ESP32_CAMERA_SIZE_240X176);
# else if ("320x240 (QVGA)" == x)
# id(esp32cam).set_jpeg_quality(esphome::esp32_camera::ESP32_CAMERA_SIZE_320X240);
# else if ("400x296 (CIF)" == x)
# id(esp32cam).set_jpeg_quality(esphome::esp32_camera::ESP32_CAMERA_SIZE_400X296);
# else if ("640x480 (VGA)" == x)
# id(esp32cam).set_jpeg_quality(esphome::esp32_camera::ESP32_CAMERA_SIZE_640X480);
# else if ("800x600 (SVGA)" == x)
# id(esp32cam).set_jpeg_quality(esphome::esp32_camera::ESP32_CAMERA_SIZE_800X600);
# else if ("1024x768 (XGA)" == x)
# id(esp32cam).set_jpeg_quality(esphome::esp32_camera::ESP32_CAMERA_SIZE_1024X768);
# else if ("1280x1024 (SXGA)" == x)
# id(esp32cam).set_jpeg_quality(esphome::esp32_camera::ESP32_CAMERA_SIZE_1280X1024);
# else if ("1600x1200 (UXGA)" == x)
# id(esp32cam).set_jpeg_quality(esphome::esp32_camera::ESP32_CAMERA_SIZE_1600X1200);
# id(esp32cam_update) = true;
- platform: template
name: "Special effect"
icon: mdi:filter
entity_category: config
optimistic: true
options:
- "none"
- "negative"
- "grayscale"
- "red_tint"
- "green_tint"
- "blue_tint"
- "sepia"
initial_option: "none"
restore_value: true
on_value:
then:
- lambda: |-
if ("none" == x)
id(esp32cam).set_special_effect(esphome::esp32_camera::ESP32_SPECIAL_EFFECT_NONE);
else if ("negative" == x)
id(esp32cam).set_special_effect(esphome::esp32_camera::ESP32_SPECIAL_EFFECT_NEGATIVE);
else if ("grayscale" == x)
id(esp32cam).set_special_effect(esphome::esp32_camera::ESP32_SPECIAL_EFFECT_GRAYSCALE);
else if ("red_tint" == x)
id(esp32cam).set_special_effect(esphome::esp32_camera::ESP32_SPECIAL_EFFECT_RED_TINT);
else if ("green_tint" == x)
id(esp32cam).set_special_effect(esphome::esp32_camera::ESP32_SPECIAL_EFFECT_GREEN_TINT);
else if ("blue_tint" == x)
id(esp32cam).set_special_effect(esphome::esp32_camera::ESP32_SPECIAL_EFFECT_BLUE_TINT);
else if ("sepia" == x)
id(esp32cam).set_special_effect(esphome::esp32_camera::ESP32_SPECIAL_EFFECT_SEPIA);
id(esp32cam_update) = true;
- platform: template
name: "Auto exposure compensation"
icon: mdi:auto-fix
entity_category: config
optimistic: true
options:
- "manual"
- "auto"
initial_option: "auto"
restore_value: true
on_value:
then:
- lambda: |-
if ("manual" == x)
id(esp32cam).set_aec_mode(esphome::esp32_camera::ESP32_GC_MODE_MANU);
else if ("auto" == x)
id(esp32cam).set_aec_mode(esphome::esp32_camera::ESP32_GC_MODE_AUTO);
id(esp32cam_update) = true;
- platform: template
name: "Auto gain control"
icon: mdi:auto-fix
entity_category: config
optimistic: true
options:
- "manual"
- "auto"
initial_option: "auto"
restore_value: true
on_value:
then:
- lambda: |-
if ("manual" == x)
id(esp32cam).set_agc_mode(esphome::esp32_camera::ESP32_GC_MODE_MANU);
else if ("auto" == x)
id(esp32cam).set_agc_mode(esphome::esp32_camera::ESP32_GC_MODE_AUTO);
id(esp32cam_update) = true;
- platform: template
name: "Auto gain control ceiling"
icon: mdi:auto-fix
entity_category: config
optimistic: true
options:
- "2x"
- "4x"
- "8x"
- "16x"
- "32x"
- "64x"
- "128x"
initial_option: "2x"
restore_value: true
on_value:
then:
- lambda: |-
if ("2x" == x)
id(esp32cam).set_agc_gain_ceiling(esphome::esp32_camera::ESP32_GAINCEILING_2X);
else if ("4x" == x)
id(esp32cam).set_agc_gain_ceiling(esphome::esp32_camera::ESP32_GAINCEILING_4X);
else if ("8x" == x)
id(esp32cam).set_agc_gain_ceiling(esphome::esp32_camera::ESP32_GAINCEILING_8X);
else if ("16x" == x)
id(esp32cam).set_agc_gain_ceiling(esphome::esp32_camera::ESP32_GAINCEILING_16X);
else if ("32x" == x)
id(esp32cam).set_agc_gain_ceiling(esphome::esp32_camera::ESP32_GAINCEILING_32X);
else if ("64x" == x)
id(esp32cam).set_agc_gain_ceiling(esphome::esp32_camera::ESP32_GAINCEILING_64X);
else if ("128x" == x)
id(esp32cam).set_agc_gain_ceiling(esphome::esp32_camera::ESP32_GAINCEILING_128X);
id(esp32cam_update) = true;
- platform: template
name: "White balance mode"
icon: mdi:white-balance-auto
entity_category: config
optimistic: true
options:
- "auto"
- "sunny"
- "cloudy"
- "office"
- "home"
initial_option: "auto"
restore_value: true
on_value:
then:
- lambda: |-
if ("auto" == x)
id(esp32cam).set_wb_mode(esphome::esp32_camera::ESP32_WB_MODE_AUTO);
else if ("sunny" == x)
id(esp32cam).set_wb_mode(esphome::esp32_camera::ESP32_WB_MODE_SUNNY);
else if ("cloudy" == x)
id(esp32cam).set_wb_mode(esphome::esp32_camera::ESP32_WB_MODE_CLOUDY);
else if ("office" == x)
id(esp32cam).set_wb_mode(esphome::esp32_camera::ESP32_WB_MODE_OFFICE);
else if ("home" == x)
id(esp32cam).set_wb_mode(esphome::esp32_camera::ESP32_WB_MODE_HOME);
id(esp32cam_update) = true;
number:
- platform: template
name: "JPEG Quality"
icon: mdi:file-jpg-box
entity_category: config
mode: slider
initial_value: 10
min_value: 10
max_value: 63
step: 1
set_action:
- lambda: |-
id(esp32cam).set_jpeg_quality(x);
id(esp32cam_update) = true;
- platform: template
name: "Contrast"
icon: mdi:contrast-box
entity_category: config
mode: slider
initial_value: 0
min_value: -2
max_value: 2
step: 1
set_action:
- lambda: |-
id(esp32cam).set_contrast(x);
id(esp32cam_update) = true;
- platform: template
name: "Brightness"
icon: mdi:brightness-6
entity_category: config
mode: slider
initial_value: 0
min_value: -2
max_value: 2
step: 1
set_action:
- lambda: |-
id(esp32cam).set_brightness(x);
id(esp32cam_update) = true;
- platform: template
name: "Saturation"
icon: mdi:palette-outline
entity_category: config
mode: slider
initial_value: 0
min_value: -2
max_value: 2
step: 1
set_action:
- lambda: |-
id(esp32cam).set_saturation(x);
id(esp32cam_update) = true;
- platform: template
name: "Auto exposure level"
icon: mdi:auto-fix
entity_category: config
mode: slider
initial_value: 0
min_value: -2
max_value: 2
step: 1
set_action:
- lambda: |-
id(esp32cam).set_ae_level(x);
id(esp32cam_update) = true;
- platform: template
name: "Auto exposure compensation value"
icon: mdi:auto-fix
entity_category: config
mode: slider
initial_value: 300
min_value: 0
max_value: 1200
step: 1
set_action:
- lambda: |-
id(esp32cam).set_aec_value(x);
id(esp32cam_update) = true;
- platform: template
name: "Auto gain control value"
icon: mdi:auto-fix
entity_category: config
mode: slider
initial_value: 0
min_value: 0
max_value: 30
step: 1
set_action:
- lambda: |-
id(esp32cam).set_aec_value(x);
id(esp32cam_update) = true;
I will soon try your code.
Many thanks for this. I have spent the last 2 days trying to incorporate the camera into ESPHOME with the same parameters available as with the arduino installation and was about to give up until I found your code. So far, with a couple of minor tweaks, its works just fine..
In my tests:
* Changing most settings seem to work (eg: effect, auto-gain). * Some settings (eg: brightness) seem to not do anything (though I'm not sure if this is the limited effect of the setting itself...). * Quality and framesize setting were missing, [fornellas/esphome@0022486](https://github.com/fornellas/esphome/commit/0022486422cd02491c7204fc82f467c19dd0c4a4) should fix that. * Quality changes does work. * Resolution change does not work, and leads to a crash after any change.
PS: for reference, this is the config I'm using which enable various settings to be adjusted at either ESPHome web UI or at Home Assistant:
esphome: name: "esp32-cam" friendly_name: "ESP32 Cam" name_add_mac_suffix: true on_boot: - priority: 600.0 then: - lambda: |- // Default: Connecting id(wifi_led_pwm).set_level(0.5); id(wifi_led_pwm).set_period(1000); id(wifi_led_pwm).turn_on(); - script.execute: fast_reboot_factory_reset on_loop: then: - lambda: |- // Captive portal if(id(wifi_component).has_ap() && !id(wifi_component).has_sta()) { id(wifi_led_pwm).set_level(0.5); id(wifi_led_pwm).set_period(200); } else { id(wifi_led_pwm).set_period(1000); // Connected if(id(wifi_component).is_connected()) { id(wifi_led_pwm).set_level(1); if (id(esp32cam_update)) { id(esp32cam).update_camera_parameters(); id(esp32cam_update) = false; } // Connecting } else id(wifi_led_pwm).set_level(0.5); } esp32: board: esp32cam external_components: - source: type: git url: https://github.com/fornellas/esphome ref: fix_esp32_camera_update components: [ esp32_camera ] refresh: 0s globals: - id: fast_reboot type: int restore_value: yes initial_value: '0' - id: factory_reset_reboot_counter type: int initial_value: '5' - id: esp32cam_update type: bool initial_value: 'false' logger: baud_rate: 0 level: INFO deassert_rts_dtr: true script: - id: fast_reboot_factory_reset then: - if: condition: lambda: return ( id(fast_reboot) >= id(factory_reset_reboot_counter) ); then: - lambda: |- ESP_LOGI("Fast Boot Factory Reset", "Performing factotry reset"); id(fast_reboot) = 0; fast_reboot->loop(); global_preferences->sync(); - button.press: factory_reset_button - lambda: |- if(id(fast_reboot) > 0) ESP_LOGI("Fast Boot Factory Reset", "Quick reboot %d/%d, do it %d more times to factory reset", id(fast_reboot), id(factory_reset_reboot_counter), id(factory_reset_reboot_counter) - id(fast_reboot)); id(fast_reboot) += 1; fast_reboot->loop(); global_preferences->sync(); - delay: 10s - lambda: |- id(fast_reboot) = 0; fast_reboot->loop(); global_preferences->sync(); wifi: id: wifi_component ap: ap_timeout: 0s reboot_timeout: 0s power_save_mode: none captive_portal: web_server: api: reboot_timeout: 0s ota: safe_mode: false esp32_camera: external_clock: pin: number: GPIO0 ignore_strapping_warning: true frequency: 20MHz i2c_pins: sda: GPIO26 scl: GPIO27 data_pins: [GPIO5, GPIO18, GPIO19, GPIO21, GPIO36, GPIO39, GPIO34, GPIO35] vsync_pin: GPIO25 href_pin: GPIO23 pixel_clock_pin: GPIO22 power_down_pin: GPIO32 name: "Camera" id: esp32cam max_framerate: "60 fps" # resolution: 160x120 # (QQVGA) # resolution: 176x144 # (QCIF) # resolution: 240x176 # (HQVGA) # resolution: 320x240 # (QVGA) # resolution: 400x296 # (CIF) Sub-sampled # resolution: 640x480 # (VGA) resolution: 800x600 # (SVGA) Sub-sampled # resolution: 1024x768 # (XGA) # resolution: 1280x1024 # (SXGA) # resolution: 1600x1200 # (UXGA) Native on_stream_start: then: - if: condition: lambda: return id(flash_automatic).state; then: - switch.turn_on: flash on_stream_stop: then: - if: condition: lambda: return id(flash_automatic).state; then: - switch.turn_off: flash esp32_camera_web_server: - port: 8080 mode: stream - port: 8081 mode: snapshot binary_sensor: - platform: status name: "Status" sensor: - platform: uptime name: 'Uptime' update_interval: 15s - platform: wifi_signal name: "WiFi Signal" update_interval: 15s output: - platform: slow_pwm id: wifi_led_pwm period: 1s pin: GPIO33 inverted: true button: - platform: factory_reset id: factory_reset_button name: "Factory reset" - platform: restart name: "Restart" - platform: safe_mode name: "Safe Mode" text_sensor: - platform: wifi_info ip_address: name: "Wifi Info: IP Address" ssid: name: "Wifi Info: SSID" bssid: name: "Wifi Info: BSSID" mac_address: name: "Wifi Info: MAC Address" dns_address: name: "Wifi Info: DNS Address" switch: - platform: template name: "Vertical flip" icon: mdi:swap-vertical entity_category: config restore_mode: RESTORE_DEFAULT_ON optimistic: true turn_on_action: - lambda: |- id(esp32cam).set_vertical_flip(true); id(esp32cam_update) = true; turn_off_action: - lambda: |- id(esp32cam).set_vertical_flip(false); id(esp32cam_update) = true; - platform: template name: "Horizontal mirror" icon: mdi:reflect-horizontal entity_category: config restore_mode: RESTORE_DEFAULT_ON optimistic: true turn_on_action: - lambda: |- id(esp32cam).set_horizontal_mirror(true); id(esp32cam_update) = true; turn_off_action: - lambda: |- id(esp32cam).set_horizontal_mirror(false); id(esp32cam_update) = true; - platform: template name: "Auto exposure compensation 2" icon: mdi:auto-fix entity_category: config restore_mode: RESTORE_DEFAULT_OFF optimistic: true turn_on_action: - lambda: |- id(esp32cam).set_aec2(true); id(esp32cam_update) = true; turn_off_action: - lambda: |- id(esp32cam).set_aec2(false); id(esp32cam_update) = true; - platform: template name: "Flash automatic on stream" id: flash_automatic icon: mdi:flash entity_category: config restore_mode: RESTORE_DEFAULT_ON optimistic: true - platform: gpio name: "Flash" icon: mdi:flash id: flash pin: GPIO4 select: # FIXME when reset after boot, yields panic some time after # - platform: template # name: "Resolution" # icon: mdi:video-standard-definition # entity_category: config # optimistic: true # options: # - "160x120 (QQVGA)" # - "176x144 (QCIF)" # - "240x176 (HQVGA)" # - "320x240 (QVGA)" # - "400x296 (CIF)" # - "640x480 (VGA)" # - "800x600 (SVGA)" # - "1024x768 (XGA)" # - "1280x1024 (SXGA)" # - "1600x1200 (UXGA)" # initial_option: "640x480 (VGA)" # restore_value: true # on_value: # then: # - lambda: |- # if ("160x120 (QQVGA)" == x ) # id(esp32cam).set_jpeg_quality(esphome::esp32_camera::ESP32_CAMERA_SIZE_160X120); # else if ("176x144 (QCIF)" == x) # id(esp32cam).set_jpeg_quality(esphome::esp32_camera::ESP32_CAMERA_SIZE_176X144); # else if ("240x176 (HQVGA)" == x) # id(esp32cam).set_jpeg_quality(esphome::esp32_camera::ESP32_CAMERA_SIZE_240X176); # else if ("320x240 (QVGA)" == x) # id(esp32cam).set_jpeg_quality(esphome::esp32_camera::ESP32_CAMERA_SIZE_320X240); # else if ("400x296 (CIF)" == x) # id(esp32cam).set_jpeg_quality(esphome::esp32_camera::ESP32_CAMERA_SIZE_400X296); # else if ("640x480 (VGA)" == x) # id(esp32cam).set_jpeg_quality(esphome::esp32_camera::ESP32_CAMERA_SIZE_640X480); # else if ("800x600 (SVGA)" == x) # id(esp32cam).set_jpeg_quality(esphome::esp32_camera::ESP32_CAMERA_SIZE_800X600); # else if ("1024x768 (XGA)" == x) # id(esp32cam).set_jpeg_quality(esphome::esp32_camera::ESP32_CAMERA_SIZE_1024X768); # else if ("1280x1024 (SXGA)" == x) # id(esp32cam).set_jpeg_quality(esphome::esp32_camera::ESP32_CAMERA_SIZE_1280X1024); # else if ("1600x1200 (UXGA)" == x) # id(esp32cam).set_jpeg_quality(esphome::esp32_camera::ESP32_CAMERA_SIZE_1600X1200); # id(esp32cam_update) = true; - platform: template name: "Special effect" icon: mdi:filter entity_category: config optimistic: true options: - "none" - "negative" - "grayscale" - "red_tint" - "green_tint" - "blue_tint" - "sepia" initial_option: "none" restore_value: true on_value: then: - lambda: |- if ("none" == x) id(esp32cam).set_special_effect(esphome::esp32_camera::ESP32_SPECIAL_EFFECT_NONE); else if ("negative" == x) id(esp32cam).set_special_effect(esphome::esp32_camera::ESP32_SPECIAL_EFFECT_NEGATIVE); else if ("grayscale" == x) id(esp32cam).set_special_effect(esphome::esp32_camera::ESP32_SPECIAL_EFFECT_GRAYSCALE); else if ("red_tint" == x) id(esp32cam).set_special_effect(esphome::esp32_camera::ESP32_SPECIAL_EFFECT_RED_TINT); else if ("green_tint" == x) id(esp32cam).set_special_effect(esphome::esp32_camera::ESP32_SPECIAL_EFFECT_GREEN_TINT); else if ("blue_tint" == x) id(esp32cam).set_special_effect(esphome::esp32_camera::ESP32_SPECIAL_EFFECT_BLUE_TINT); else if ("sepia" == x) id(esp32cam).set_special_effect(esphome::esp32_camera::ESP32_SPECIAL_EFFECT_SEPIA); id(esp32cam_update) = true; - platform: template name: "Auto exposure compensation" icon: mdi:auto-fix entity_category: config optimistic: true options: - "manual" - "auto" initial_option: "auto" restore_value: true on_value: then: - lambda: |- if ("manual" == x) id(esp32cam).set_aec_mode(esphome::esp32_camera::ESP32_GC_MODE_MANU); else if ("auto" == x) id(esp32cam).set_aec_mode(esphome::esp32_camera::ESP32_GC_MODE_AUTO); id(esp32cam_update) = true; - platform: template name: "Auto gain control" icon: mdi:auto-fix entity_category: config optimistic: true options: - "manual" - "auto" initial_option: "auto" restore_value: true on_value: then: - lambda: |- if ("manual" == x) id(esp32cam).set_agc_mode(esphome::esp32_camera::ESP32_GC_MODE_MANU); else if ("auto" == x) id(esp32cam).set_agc_mode(esphome::esp32_camera::ESP32_GC_MODE_AUTO); id(esp32cam_update) = true; - platform: template name: "Auto gain control ceiling" icon: mdi:auto-fix entity_category: config optimistic: true options: - "2x" - "4x" - "8x" - "16x" - "32x" - "64x" - "128x" initial_option: "2x" restore_value: true on_value: then: - lambda: |- if ("2x" == x) id(esp32cam).set_agc_gain_ceiling(esphome::esp32_camera::ESP32_GAINCEILING_2X); else if ("4x" == x) id(esp32cam).set_agc_gain_ceiling(esphome::esp32_camera::ESP32_GAINCEILING_4X); else if ("8x" == x) id(esp32cam).set_agc_gain_ceiling(esphome::esp32_camera::ESP32_GAINCEILING_8X); else if ("16x" == x) id(esp32cam).set_agc_gain_ceiling(esphome::esp32_camera::ESP32_GAINCEILING_16X); else if ("32x" == x) id(esp32cam).set_agc_gain_ceiling(esphome::esp32_camera::ESP32_GAINCEILING_32X); else if ("64x" == x) id(esp32cam).set_agc_gain_ceiling(esphome::esp32_camera::ESP32_GAINCEILING_64X); else if ("128x" == x) id(esp32cam).set_agc_gain_ceiling(esphome::esp32_camera::ESP32_GAINCEILING_128X); id(esp32cam_update) = true; - platform: template name: "White balance mode" icon: mdi:white-balance-auto entity_category: config optimistic: true options: - "auto" - "sunny" - "cloudy" - "office" - "home" initial_option: "auto" restore_value: true on_value: then: - lambda: |- if ("auto" == x) id(esp32cam).set_wb_mode(esphome::esp32_camera::ESP32_WB_MODE_AUTO); else if ("sunny" == x) id(esp32cam).set_wb_mode(esphome::esp32_camera::ESP32_WB_MODE_SUNNY); else if ("cloudy" == x) id(esp32cam).set_wb_mode(esphome::esp32_camera::ESP32_WB_MODE_CLOUDY); else if ("office" == x) id(esp32cam).set_wb_mode(esphome::esp32_camera::ESP32_WB_MODE_OFFICE); else if ("home" == x) id(esp32cam).set_wb_mode(esphome::esp32_camera::ESP32_WB_MODE_HOME); id(esp32cam_update) = true; number: - platform: template name: "JPEG Quality" icon: mdi:file-jpg-box entity_category: config mode: slider initial_value: 10 min_value: 10 max_value: 63 step: 1 set_action: - lambda: |- id(esp32cam).set_jpeg_quality(x); id(esp32cam_update) = true; - platform: template name: "Contrast" icon: mdi:contrast-box entity_category: config mode: slider initial_value: 0 min_value: -2 max_value: 2 step: 1 set_action: - lambda: |- id(esp32cam).set_contrast(x); id(esp32cam_update) = true; - platform: template name: "Brightness" icon: mdi:brightness-6 entity_category: config mode: slider initial_value: 0 min_value: -2 max_value: 2 step: 1 set_action: - lambda: |- id(esp32cam).set_brightness(x); id(esp32cam_update) = true; - platform: template name: "Saturation" icon: mdi:palette-outline entity_category: config mode: slider initial_value: 0 min_value: -2 max_value: 2 step: 1 set_action: - lambda: |- id(esp32cam).set_saturation(x); id(esp32cam_update) = true; - platform: template name: "Auto exposure level" icon: mdi:auto-fix entity_category: config mode: slider initial_value: 0 min_value: -2 max_value: 2 step: 1 set_action: - lambda: |- id(esp32cam).set_ae_level(x); id(esp32cam_update) = true; - platform: template name: "Auto exposure compensation value" icon: mdi:auto-fix entity_category: config mode: slider initial_value: 300 min_value: 0 max_value: 1200 step: 1 set_action: - lambda: |- id(esp32cam).set_aec_value(x); id(esp32cam_update) = true; - platform: template name: "Auto gain control value" icon: mdi:auto-fix entity_category: config mode: slider initial_value: 0 min_value: 0 max_value: 30 step: 1 set_action: - lambda: |- id(esp32cam).set_aec_value(x); id(esp32cam_update) = true;
thank you so much for this! Works fine!
The problem
Several method calls of the esp32_camera component do not result in any visible change, i.e. (but not complete):
The reason is probably an oversimplified approach of the ESP32-CAM driver that is used by the component. The central point here is the "SDE indirect register" of the OV2640, which contains a bit mask of enable flags for changes to contrast, brightness, saturation, color tint, negative and others. With each call to the driver's set_contrast / setbrightness / set... the enable bit for the requested effect is set and all others are zeroed. Thus only the last changed effect remains active and all others are switched off again.
Now, the esp32_camera component's set_xyz() methods do not call the corresponding driver methods at all. Instead they store the requested effect strength parameter in an internal status var. All these status vars are then written to the OV2640 by a single call to update_camera_parameters(). This fires a series of method calls of the driver (including set_contrast, set_brightness, set_saturation and others). As described, only the last of these wins, which is probably the color tint change, as this seems to work. With this flow the brightness/saturation/contrast will never work.
I see two possible solutions here:
Simple but limited solution Move the set_xyz() calls from update_camera_parameters() directly into the corresponding set_xyz() calls of the ESPHome component. This would at least allow to set all of the effects separately, but not in combination.
Take control of the enable flags in SDE indirect register This would allow for all combinations of effects, but I see several comments on the web, that there are interferences between different effects that might need to be taken care of. I.e. brightness and contrast seem to interfere. But then again, this could also be handled by the user by sending adjusting method calls herself.
Solution 1. seems easy to implement and I think I can file a PR for that. Solution 2. is beyond my reach. I tried but got stuck because I needed the write_SCCB() method of the driver. This method is currently not available in the ESPHome esp32_camera component and I have no idea how I could get hold of that. If someone could provide me a way to get hold of this method, I could also try to work on 2.
Which version of ESPHome has the issue?
2023.5.5
What type of installation are you using?
Home Assistant Add-on
Which version of Home Assistant has the issue?
2023.6.1
What platform are you using?
ESP32
Board
ESP32-CAM (AI Thinker Clone)
Component causing the issue
esp32_camera
Example YAML snippet
Anything in the logs that might be useful for us?
No response
Additional information
OV2640 Datasheet v2.2
OV2640 Software Application Notes
Information about enable flags in SDE indirect register Example of handling interference between brightness and contrast settings