diff --git a/esphome/components/esp32_camera/__init__.py b/esphome/components/esp32_camera/__init__.py index d42d4f5de3..912e705766 100644 --- a/esphome/components/esp32_camera/__init__.py +++ b/esphome/components/esp32_camera/__init__.py @@ -23,6 +23,7 @@ AUTO_LOAD = ["psram"] esp32_camera_ns = cg.esphome_ns.namespace("esp32_camera") ESP32Camera = esp32_camera_ns.class_("ESP32Camera", cg.PollingComponent, cg.EntityBase) + ESP32CameraFrameSize = esp32_camera_ns.enum("ESP32CameraFrameSize") FRAME_SIZES = { "160X120": ESP32CameraFrameSize.ESP32_CAMERA_SIZE_160X120, @@ -46,30 +47,76 @@ FRAME_SIZES = { "1600X1200": ESP32CameraFrameSize.ESP32_CAMERA_SIZE_1600X1200, "UXGA": ESP32CameraFrameSize.ESP32_CAMERA_SIZE_1600X1200, } +ESP32GainControlMode = esp32_camera_ns.enum("ESP32GainControlMode") +ENUM_GAIN_CONTROL_MODE = { + "MANUAL": ESP32GainControlMode.ESP32_GC_MODE_MANU, + "AUTO": ESP32GainControlMode.ESP32_GC_MODE_AUTO, +} +ESP32AgcGainCeiling = esp32_camera_ns.enum("ESP32AgcGainCeiling") +ENUM_GAIN_CEILING = { + "2X": ESP32AgcGainCeiling.ESP32_GAINCEILING_2X, + "4X": ESP32AgcGainCeiling.ESP32_GAINCEILING_4X, + "8X": ESP32AgcGainCeiling.ESP32_GAINCEILING_8X, + "16X": ESP32AgcGainCeiling.ESP32_GAINCEILING_16X, + "32X": ESP32AgcGainCeiling.ESP32_GAINCEILING_32X, + "64X": ESP32AgcGainCeiling.ESP32_GAINCEILING_64X, + "128X": ESP32AgcGainCeiling.ESP32_GAINCEILING_128X, +} +ESP32WhiteBalanceMode = esp32_camera_ns.enum("ESP32WhiteBalanceMode") +ENUM_WB_MODE = { + "AUTO": ESP32WhiteBalanceMode.ESP32_WB_MODE_AUTO, + "SUNNY": ESP32WhiteBalanceMode.ESP32_WB_MODE_SUNNY, + "CLOUDY": ESP32WhiteBalanceMode.ESP32_WB_MODE_CLOUDY, + "OFFICE": ESP32WhiteBalanceMode.ESP32_WB_MODE_OFFICE, + "HOME": ESP32WhiteBalanceMode.ESP32_WB_MODE_HOME, +} +ESP32SpecialEffect = esp32_camera_ns.enum("ESP32SpecialEffect") +ENUM_SPECIAL_EFFECT = { + "NONE": ESP32SpecialEffect.ESP32_SPECIAL_EFFECT_NONE, + "NEGATIVE": ESP32SpecialEffect.ESP32_SPECIAL_EFFECT_NEGATIVE, + "GRAYSCALE": ESP32SpecialEffect.ESP32_SPECIAL_EFFECT_GRAYSCALE, + "RED_TINT": ESP32SpecialEffect.ESP32_SPECIAL_EFFECT_RED_TINT, + "GREEN_TINT": ESP32SpecialEffect.ESP32_SPECIAL_EFFECT_GREEN_TINT, + "BLUE_TINT": ESP32SpecialEffect.ESP32_SPECIAL_EFFECT_BLUE_TINT, + "SEPIA": ESP32SpecialEffect.ESP32_SPECIAL_EFFECT_SEPIA, +} +# pin assignment CONF_VSYNC_PIN = "vsync_pin" CONF_HREF_PIN = "href_pin" CONF_PIXEL_CLOCK_PIN = "pixel_clock_pin" CONF_EXTERNAL_CLOCK = "external_clock" CONF_I2C_PINS = "i2c_pins" CONF_POWER_DOWN_PIN = "power_down_pin" - -CONF_MAX_FRAMERATE = "max_framerate" -CONF_IDLE_FRAMERATE = "idle_framerate" +# image CONF_JPEG_QUALITY = "jpeg_quality" CONF_VERTICAL_FLIP = "vertical_flip" CONF_HORIZONTAL_MIRROR = "horizontal_mirror" +CONF_SATURATION = "saturation" +CONF_SPECIAL_EFFECT = "special_effect" +# exposure +CONF_AEC_MODE = "aec_mode" CONF_AEC2 = "aec2" CONF_AE_LEVEL = "ae_level" CONF_AEC_VALUE = "aec_value" -CONF_SATURATION = "saturation" +# gains +CONF_AGC_MODE = "agc_mode" +CONF_AGC_VALUE = "agc_value" +CONF_AGC_GAIN_CEILING = "agc_gain_ceiling" +# white balance +CONF_WB_MODE = "wb_mode" +# test pattern CONF_TEST_PATTERN = "test_pattern" +# framerates +CONF_MAX_FRAMERATE = "max_framerate" +CONF_IDLE_FRAMERATE = "idle_framerate" camera_range_param = cv.int_range(min=-2, max=2) CONFIG_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend( { cv.GenerateID(): cv.declare_id(ESP32Camera), + # pin assignment cv.Required(CONF_DATA_PINS): cv.All( [pins.internal_gpio_input_pin_number], cv.Length(min=8, max=8) ), @@ -92,12 +139,7 @@ CONFIG_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend( ), cv.Optional(CONF_RESET_PIN): pins.internal_gpio_output_pin_number, cv.Optional(CONF_POWER_DOWN_PIN): pins.internal_gpio_output_pin_number, - cv.Optional(CONF_MAX_FRAMERATE, default="10 fps"): cv.All( - cv.framerate, cv.Range(min=0, min_included=False, max=60) - ), - cv.Optional(CONF_IDLE_FRAMERATE, default="0.1 fps"): cv.All( - cv.framerate, cv.Range(min=0, max=1) - ), + # image cv.Optional(CONF_RESOLUTION, default="640X480"): cv.enum( FRAME_SIZES, upper=True ), @@ -107,29 +149,66 @@ CONFIG_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend( cv.Optional(CONF_SATURATION, default=0): camera_range_param, cv.Optional(CONF_VERTICAL_FLIP, default=True): cv.boolean, cv.Optional(CONF_HORIZONTAL_MIRROR, default=True): cv.boolean, + cv.Optional(CONF_SPECIAL_EFFECT, default="NONE"): cv.enum( + ENUM_SPECIAL_EFFECT, upper=True + ), + # exposure + cv.Optional(CONF_AGC_MODE, default="AUTO"): cv.enum( + ENUM_GAIN_CONTROL_MODE, upper=True + ), cv.Optional(CONF_AEC2, default=False): cv.boolean, cv.Optional(CONF_AE_LEVEL, default=0): camera_range_param, cv.Optional(CONF_AEC_VALUE, default=300): cv.int_range(min=0, max=1200), + # gains + cv.Optional(CONF_AEC_MODE, default="AUTO"): cv.enum( + ENUM_GAIN_CONTROL_MODE, upper=True + ), + cv.Optional(CONF_AGC_VALUE, default=0): cv.int_range(min=0, max=30), + cv.Optional(CONF_AGC_GAIN_CEILING, default="2X"): cv.enum( + ENUM_GAIN_CEILING, upper=True + ), + # white balance + cv.Optional(CONF_WB_MODE, default="AUTO"): cv.enum(ENUM_WB_MODE, upper=True), + # test pattern cv.Optional(CONF_TEST_PATTERN, default=False): cv.boolean, + # framerates + cv.Optional(CONF_MAX_FRAMERATE, default="10 fps"): cv.All( + cv.framerate, cv.Range(min=0, min_included=False, max=60) + ), + cv.Optional(CONF_IDLE_FRAMERATE, default="0.1 fps"): cv.All( + cv.framerate, cv.Range(min=0, max=1) + ), } ).extend(cv.COMPONENT_SCHEMA) SETTERS = { + # pin assignment CONF_DATA_PINS: "set_data_pins", CONF_VSYNC_PIN: "set_vsync_pin", CONF_HREF_PIN: "set_href_pin", CONF_PIXEL_CLOCK_PIN: "set_pixel_clock_pin", CONF_RESET_PIN: "set_reset_pin", CONF_POWER_DOWN_PIN: "set_power_down_pin", + # image CONF_JPEG_QUALITY: "set_jpeg_quality", CONF_VERTICAL_FLIP: "set_vertical_flip", CONF_HORIZONTAL_MIRROR: "set_horizontal_mirror", - CONF_AEC2: "set_aec2", - CONF_AE_LEVEL: "set_ae_level", - CONF_AEC_VALUE: "set_aec_value", CONF_CONTRAST: "set_contrast", CONF_BRIGHTNESS: "set_brightness", CONF_SATURATION: "set_saturation", + CONF_SPECIAL_EFFECT: "set_special_effect", + # exposure + CONF_AEC_MODE: "set_aec_mode", + CONF_AEC2: "set_aec2", + CONF_AE_LEVEL: "set_ae_level", + CONF_AEC_VALUE: "set_aec_value", + # gains + CONF_AGC_MODE: "set_agc_mode", + CONF_AGC_VALUE: "set_agc_value", + CONF_AGC_GAIN_CEILING: "set_agc_gain_ceiling", + # white balance + CONF_WB_MODE: "set_wb_mode", + # test pattern CONF_TEST_PATTERN: "set_test_pattern", } diff --git a/esphome/components/esp32_camera/esp32_camera.cpp b/esphome/components/esp32_camera/esp32_camera.cpp index 7d11f98d09..851926b083 100644 --- a/esphome/components/esp32_camera/esp32_camera.cpp +++ b/esphome/components/esp32_camera/esp32_camera.cpp @@ -11,10 +11,14 @@ namespace esp32_camera { static const char *const TAG = "esp32_camera"; +/* ---------------- public API (derivated) ---------------- */ void ESP32Camera::setup() { global_esp32_camera = this; + /* initialize time to now */ this->last_update_ = millis(); + + /* initialize camera */ esp_err_t err = esp_camera_init(&this->config_); if (err != ESP_OK) { ESP_LOGE(TAG, "esp_camera_init failed: %s", esp_err_to_name(err)); @@ -23,16 +27,10 @@ void ESP32Camera::setup() { return; } - sensor_t *s = esp_camera_sensor_get(); - s->set_vflip(s, this->vertical_flip_); - s->set_hmirror(s, this->horizontal_mirror_); - s->set_aec2(s, this->aec2_); // 0 = disable , 1 = enable - s->set_ae_level(s, this->ae_level_); // -2 to 2 - s->set_aec_value(s, this->aec_value_); // 0 to 1200 - s->set_contrast(s, this->contrast_); - s->set_brightness(s, this->brightness_); - s->set_saturation(s, this->saturation_); - s->set_colorbar(s, this->test_pattern_); + /* initialize camera parameters */ + this->update_camera_parameters(); + + /* initialize RTOS */ this->framebuffer_get_queue_ = xQueueCreate(1, sizeof(camera_fb_t *)); this->framebuffer_return_queue_ = xQueueCreate(1, sizeof(camera_fb_t *)); xTaskCreatePinnedToCore(&ESP32Camera::framebuffer_task, @@ -44,6 +42,7 @@ void ESP32Camera::setup() { 1 // core ); } + void ESP32Camera::dump_config() { auto conf = this->config_; ESP_LOGCONFIG(TAG, "ESP32 Camera:"); @@ -106,17 +105,17 @@ void ESP32Camera::dump_config() { ESP_LOGCONFIG(TAG, " Saturation: %d", st.saturation); ESP_LOGCONFIG(TAG, " Vertical Flip: %s", ONOFF(st.vflip)); ESP_LOGCONFIG(TAG, " Horizontal Mirror: %s", ONOFF(st.hmirror)); - // ESP_LOGCONFIG(TAG, " Special Effect: %u", st.special_effect); - // ESP_LOGCONFIG(TAG, " White Balance Mode: %u", st.wb_mode); + ESP_LOGCONFIG(TAG, " Special Effect: %u", st.special_effect); + ESP_LOGCONFIG(TAG, " White Balance Mode: %u", st.wb_mode); // ESP_LOGCONFIG(TAG, " Auto White Balance: %u", st.awb); // ESP_LOGCONFIG(TAG, " Auto White Balance Gain: %u", st.awb_gain); - // ESP_LOGCONFIG(TAG, " Auto Exposure Control: %u", st.aec); + ESP_LOGCONFIG(TAG, " Auto Exposure Control: %u", st.aec); ESP_LOGCONFIG(TAG, " Auto Exposure Control 2: %u", st.aec2); ESP_LOGCONFIG(TAG, " Auto Exposure Level: %d", st.ae_level); ESP_LOGCONFIG(TAG, " Auto Exposure Value: %u", st.aec_value); - // ESP_LOGCONFIG(TAG, " AGC: %u", st.agc); - // ESP_LOGCONFIG(TAG, " AGC Gain: %u", st.agc_gain); - // ESP_LOGCONFIG(TAG, " Gain Ceiling: %u", st.gainceiling); + ESP_LOGCONFIG(TAG, " AGC: %u", st.agc); + ESP_LOGCONFIG(TAG, " AGC Gain: %u", st.agc_gain); + ESP_LOGCONFIG(TAG, " Gain Ceiling: %u", st.gainceiling); // ESP_LOGCONFIG(TAG, " BPC: %u", st.bpc); // ESP_LOGCONFIG(TAG, " WPC: %u", st.wpc); // ESP_LOGCONFIG(TAG, " RAW_GMA: %u", st.raw_gma); @@ -124,6 +123,7 @@ void ESP32Camera::dump_config() { // ESP_LOGCONFIG(TAG, " DCW: %u", st.dcw); ESP_LOGCONFIG(TAG, " Test Pattern: %s", YESNO(st.colorbar)); } + void ESP32Camera::loop() { // check if we can return the image if (this->can_return_image_()) { @@ -170,15 +170,10 @@ void ESP32Camera::loop() { this->last_update_ = now; this->single_requesters_ = 0; } -void ESP32Camera::framebuffer_task(void *pv) { - while (true) { - camera_fb_t *framebuffer = esp_camera_fb_get(); - xQueueSend(global_esp32_camera->framebuffer_get_queue_, &framebuffer, portMAX_DELAY); - // return is no-op for config with 1 fb - xQueueReceive(global_esp32_camera->framebuffer_return_queue_, &framebuffer, portMAX_DELAY); - esp_camera_fb_return(framebuffer); - } -} + +float ESP32Camera::get_setup_priority() const { return setup_priority::DATA; } + +/* ---------------- constructors ---------------- */ ESP32Camera::ESP32Camera(const std::string &name) : EntityBase(name) { this->config_.pin_pwdn = -1; this->config_.pin_reset = -1; @@ -193,6 +188,9 @@ ESP32Camera::ESP32Camera(const std::string &name) : EntityBase(name) { global_esp32_camera = this; } ESP32Camera::ESP32Camera() : ESP32Camera("") {} + +/* ---------------- setters ---------------- */ +/* set pin assignment */ void ESP32Camera::set_data_pins(std::array pins) { this->config_.pin_d0 = pins[0]; this->config_.pin_d1 = pins[1]; @@ -214,6 +212,10 @@ void ESP32Camera::set_i2c_pins(uint8_t sda, uint8_t scl) { this->config_.pin_sscb_sda = sda; this->config_.pin_sscb_scl = scl; } +void ESP32Camera::set_reset_pin(uint8_t pin) { this->config_.pin_reset = pin; } +void ESP32Camera::set_power_down_pin(uint8_t pin) { this->config_.pin_pwdn = pin; } + +/* set image parameters */ void ESP32Camera::set_frame_size(ESP32CameraFrameSize size) { switch (size) { case ESP32_CAMERA_SIZE_160X120: @@ -249,36 +251,81 @@ void ESP32Camera::set_frame_size(ESP32CameraFrameSize size) { } } void ESP32Camera::set_jpeg_quality(uint8_t quality) { this->config_.jpeg_quality = quality; } -void ESP32Camera::set_reset_pin(uint8_t pin) { this->config_.pin_reset = pin; } -void ESP32Camera::set_power_down_pin(uint8_t pin) { this->config_.pin_pwdn = pin; } -void ESP32Camera::add_image_callback(std::function)> &&f) { - this->new_image_callback_.add(std::move(f)); -} void ESP32Camera::set_vertical_flip(bool vertical_flip) { this->vertical_flip_ = vertical_flip; } void ESP32Camera::set_horizontal_mirror(bool horizontal_mirror) { this->horizontal_mirror_ = horizontal_mirror; } -void ESP32Camera::set_aec2(bool aec2) { this->aec2_ = aec2; } -void ESP32Camera::set_ae_level(int ae_level) { this->ae_level_ = ae_level; } -void ESP32Camera::set_aec_value(uint32_t aec_value) { this->aec_value_ = aec_value; } void ESP32Camera::set_contrast(int contrast) { this->contrast_ = contrast; } void ESP32Camera::set_brightness(int brightness) { this->brightness_ = brightness; } void ESP32Camera::set_saturation(int saturation) { this->saturation_ = saturation; } -float ESP32Camera::get_setup_priority() const { return setup_priority::DATA; } -uint32_t ESP32Camera::hash_base() { return 3010542557UL; } -void ESP32Camera::request_image(CameraRequester requester) { this->single_requesters_ |= 1 << requester; } -void ESP32Camera::start_stream(CameraRequester requester) { this->stream_requesters_ |= 1 << requester; } -void ESP32Camera::stop_stream(CameraRequester requester) { this->stream_requesters_ &= ~(1 << requester); } -bool ESP32Camera::has_requested_image_() const { return this->single_requesters_ || this->stream_requesters_; } -bool ESP32Camera::can_return_image_() const { return this->current_image_.use_count() == 1; } +void ESP32Camera::set_special_effect(ESP32SpecialEffect effect) { this->special_effect_ = effect; } +/* set exposure parameters */ +void ESP32Camera::set_aec_mode(ESP32GainControlMode mode) { this->aec_mode_ = mode; } +void ESP32Camera::set_aec2(bool aec2) { this->aec2_ = aec2; } +void ESP32Camera::set_ae_level(int ae_level) { this->ae_level_ = ae_level; } +void ESP32Camera::set_aec_value(uint32_t aec_value) { this->aec_value_ = aec_value; } +/* set gains parameters */ +void ESP32Camera::set_agc_mode(ESP32GainControlMode mode) { this->agc_mode_ = mode; } +void ESP32Camera::set_agc_value(uint8_t agc_value) { this->agc_value_ = agc_value; } +void ESP32Camera::set_agc_gain_ceiling(ESP32AgcGainCeiling gain_ceiling) { this->agc_gain_ceiling_ = gain_ceiling; } +/* set white balance */ +void ESP32Camera::set_wb_mode(ESP32WhiteBalanceMode mode) { this->wb_mode_ = mode; } +/* set test mode */ +void ESP32Camera::set_test_pattern(bool test_pattern) { this->test_pattern_ = test_pattern; } +/* set fps */ void ESP32Camera::set_max_update_interval(uint32_t max_update_interval) { this->max_update_interval_ = max_update_interval; } void ESP32Camera::set_idle_update_interval(uint32_t idle_update_interval) { this->idle_update_interval_ = idle_update_interval; } -void ESP32Camera::set_test_pattern(bool test_pattern) { this->test_pattern_ = test_pattern; } + +/* ---------------- public API (specific) ---------------- */ +void ESP32Camera::add_image_callback(std::function)> &&f) { + this->new_image_callback_.add(std::move(f)); +} +void ESP32Camera::start_stream(CameraRequester requester) { this->stream_requesters_ |= (1U << requester); } +void ESP32Camera::stop_stream(CameraRequester requester) { this->stream_requesters_ &= ~(1U << requester); } +void ESP32Camera::request_image(CameraRequester requester) { this->single_requesters_ |= (1U << requester); } +void ESP32Camera::update_camera_parameters() { + sensor_t *s = esp_camera_sensor_get(); + /* update image */ + s->set_vflip(s, this->vertical_flip_); + s->set_hmirror(s, this->horizontal_mirror_); + s->set_contrast(s, this->contrast_); + s->set_brightness(s, this->brightness_); + s->set_saturation(s, this->saturation_); + s->set_special_effect(s, (int) this->special_effect_); // 0 to 6 + /* update exposure */ + s->set_exposure_ctrl(s, (bool) this->aec_mode_); + s->set_aec2(s, this->aec2_); // 0 = disable , 1 = enable + s->set_ae_level(s, this->ae_level_); // -2 to 2 + s->set_aec_value(s, this->aec_value_); // 0 to 1200 + /* update gains */ + s->set_gain_ctrl(s, (bool) this->agc_mode_); + s->set_agc_gain(s, (int) this->agc_value_); // 0 to 30 + s->set_gainceiling(s, (gainceiling_t) this->agc_gain_ceiling_); + /* update white balance mode */ + s->set_wb_mode(s, (int) this->wb_mode_); // 0 to 4 + /* update test patern */ + s->set_colorbar(s, this->test_pattern_); +} + +/* ---------------- Internal methods ---------------- */ +uint32_t ESP32Camera::hash_base() { return 3010542557UL; } +bool ESP32Camera::has_requested_image_() const { return this->single_requesters_ || this->stream_requesters_; } +bool ESP32Camera::can_return_image_() const { return this->current_image_.use_count() == 1; } +void ESP32Camera::framebuffer_task(void *pv) { + while (true) { + camera_fb_t *framebuffer = esp_camera_fb_get(); + xQueueSend(global_esp32_camera->framebuffer_get_queue_, &framebuffer, portMAX_DELAY); + // return is no-op for config with 1 fb + xQueueReceive(global_esp32_camera->framebuffer_return_queue_, &framebuffer, portMAX_DELAY); + esp_camera_fb_return(framebuffer); + } +} ESP32Camera *global_esp32_camera; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) +/* ---------------- CameraImageReader class ---------------- */ void CameraImageReader::set_image(std::shared_ptr image) { this->image_ = std::move(image); this->offset_ = 0; @@ -293,13 +340,15 @@ void CameraImageReader::return_image() { this->image_.reset(); } void CameraImageReader::consume_data(size_t consumed) { this->offset_ += consumed; } uint8_t *CameraImageReader::peek_data_buffer() { return this->image_->get_data_buffer() + this->offset_; } +/* ---------------- CameraImage class ---------------- */ +CameraImage::CameraImage(camera_fb_t *buffer, uint8_t requesters) : buffer_(buffer), requesters_(requesters) {} + camera_fb_t *CameraImage::get_raw_buffer() { return this->buffer_; } uint8_t *CameraImage::get_data_buffer() { return this->buffer_->buf; } size_t CameraImage::get_data_length() { return this->buffer_->len; } bool CameraImage::was_requested_by(CameraRequester requester) const { return (this->requesters_ & (1 << requester)) != 0; } -CameraImage::CameraImage(camera_fb_t *buffer, uint8_t requesters) : buffer_(buffer), requesters_(requesters) {} } // namespace esp32_camera } // namespace esphome diff --git a/esphome/components/esp32_camera/esp32_camera.h b/esphome/components/esp32_camera/esp32_camera.h index b2670078f3..743b5bde5f 100644 --- a/esphome/components/esp32_camera/esp32_camera.h +++ b/esphome/components/esp32_camera/esp32_camera.h @@ -14,34 +14,9 @@ namespace esp32_camera { class ESP32Camera; +/* ---------------- enum classes ---------------- */ enum CameraRequester { IDLE, API_REQUESTER, WEB_REQUESTER }; -class CameraImage { - public: - CameraImage(camera_fb_t *buffer, uint8_t requester); - camera_fb_t *get_raw_buffer(); - uint8_t *get_data_buffer(); - size_t get_data_length(); - bool was_requested_by(CameraRequester requester) const; - - protected: - camera_fb_t *buffer_; - uint8_t requesters_; -}; - -class CameraImageReader { - public: - void set_image(std::shared_ptr image); - size_t available() const; - uint8_t *peek_data_buffer(); - void consume_data(size_t consumed); - void return_image(); - - protected: - std::shared_ptr image_; - size_t offset_{0}; -}; - enum ESP32CameraFrameSize { ESP32_CAMERA_SIZE_160X120, // QQVGA ESP32_CAMERA_SIZE_176X144, // QCIF @@ -55,57 +30,155 @@ enum ESP32CameraFrameSize { ESP32_CAMERA_SIZE_1600X1200, // UXGA }; +enum ESP32AgcGainCeiling { + ESP32_GAINCEILING_2X = GAINCEILING_2X, + ESP32_GAINCEILING_4X = GAINCEILING_4X, + ESP32_GAINCEILING_8X = GAINCEILING_8X, + ESP32_GAINCEILING_16X = GAINCEILING_16X, + ESP32_GAINCEILING_32X = GAINCEILING_32X, + ESP32_GAINCEILING_64X = GAINCEILING_64X, + ESP32_GAINCEILING_128X = GAINCEILING_128X, +}; + +enum ESP32GainControlMode { + ESP32_GC_MODE_MANU = false, + ESP32_GC_MODE_AUTO = true, +}; + +enum ESP32WhiteBalanceMode { + ESP32_WB_MODE_AUTO = 0U, + ESP32_WB_MODE_SUNNY = 1U, + ESP32_WB_MODE_CLOUDY = 2U, + ESP32_WB_MODE_OFFICE = 3U, + ESP32_WB_MODE_HOME = 4U, +}; + +enum ESP32SpecialEffect { + ESP32_SPECIAL_EFFECT_NONE = 0U, + ESP32_SPECIAL_EFFECT_NEGATIVE = 1U, + ESP32_SPECIAL_EFFECT_GRAYSCALE = 2U, + ESP32_SPECIAL_EFFECT_RED_TINT = 3U, + ESP32_SPECIAL_EFFECT_GREEN_TINT = 4U, + ESP32_SPECIAL_EFFECT_BLUE_TINT = 5U, + ESP32_SPECIAL_EFFECT_SEPIA = 6U, +}; + +/* ---------------- CameraImage class ---------------- */ +class CameraImage { + public: + CameraImage(camera_fb_t *buffer, uint8_t requester); + camera_fb_t *get_raw_buffer(); + uint8_t *get_data_buffer(); + size_t get_data_length(); + bool was_requested_by(CameraRequester requester) const; + + protected: + camera_fb_t *buffer_; + uint8_t requesters_; +}; + +/* ---------------- CameraImageReader class ---------------- */ +class CameraImageReader { + public: + void set_image(std::shared_ptr image); + size_t available() const; + uint8_t *peek_data_buffer(); + void consume_data(size_t consumed); + void return_image(); + + protected: + std::shared_ptr image_; + size_t offset_{0}; +}; + +/* ---------------- ESP32Camera class ---------------- */ class ESP32Camera : public Component, public EntityBase { public: ESP32Camera(const std::string &name); ESP32Camera(); + + /* setters */ + /* -- pin assignment */ void set_data_pins(std::array pins); void set_vsync_pin(uint8_t pin); void set_href_pin(uint8_t pin); void set_pixel_clock_pin(uint8_t pin); void set_external_clock(uint8_t pin, uint32_t frequency); void set_i2c_pins(uint8_t sda, uint8_t scl); - void set_frame_size(ESP32CameraFrameSize size); - void set_jpeg_quality(uint8_t quality); void set_reset_pin(uint8_t pin); void set_power_down_pin(uint8_t pin); + /* -- image */ + void set_frame_size(ESP32CameraFrameSize size); + void set_jpeg_quality(uint8_t quality); void set_vertical_flip(bool vertical_flip); void set_horizontal_mirror(bool horizontal_mirror); - void set_aec2(bool aec2); - void set_ae_level(int ae_level); - void set_aec_value(uint32_t aec_value); void set_contrast(int contrast); void set_brightness(int brightness); void set_saturation(int saturation); + void set_special_effect(ESP32SpecialEffect effect); + /* -- exposure */ + void set_aec_mode(ESP32GainControlMode mode); + void set_aec2(bool aec2); + void set_ae_level(int ae_level); + void set_aec_value(uint32_t aec_value); + /* -- gains */ + void set_agc_mode(ESP32GainControlMode mode); + void set_agc_value(uint8_t agc_value); + void set_agc_gain_ceiling(ESP32AgcGainCeiling gain_ceiling); + /* -- white balance */ + void set_wb_mode(ESP32WhiteBalanceMode mode); + /* -- test */ + void set_test_pattern(bool test_pattern); + /* -- framerates */ void set_max_update_interval(uint32_t max_update_interval); void set_idle_update_interval(uint32_t idle_update_interval); - void set_test_pattern(bool test_pattern); + + /* public API (derivated) */ void setup() override; void loop() override; void dump_config() override; - void add_image_callback(std::function)> &&f); float get_setup_priority() const override; + /* public API (specific) */ + void add_image_callback(std::function)> &&f); void start_stream(CameraRequester requester); void stop_stream(CameraRequester requester); void request_image(CameraRequester requester); + void update_camera_parameters(); protected: + /* internal methods */ uint32_t hash_base() override; bool has_requested_image_() const; bool can_return_image_() const; static void framebuffer_task(void *pv); + /* attributes */ + /* camera configuration */ camera_config_t config_{}; + /* -- image */ bool vertical_flip_{true}; bool horizontal_mirror_{true}; - bool aec2_{false}; - int ae_level_{0}; - uint32_t aec_value_{300}; int contrast_{0}; int brightness_{0}; int saturation_{0}; + ESP32SpecialEffect special_effect_{ESP32_SPECIAL_EFFECT_NONE}; + /* -- exposure */ + ESP32GainControlMode aec_mode_{ESP32_GC_MODE_AUTO}; + bool aec2_{false}; + int ae_level_{0}; + uint32_t aec_value_{300}; + /* -- gains */ + ESP32GainControlMode agc_mode_{ESP32_GC_MODE_AUTO}; + uint8_t agc_value_{0}; + ESP32AgcGainCeiling agc_gain_ceiling_{ESP32_GAINCEILING_2X}; + /* -- white balance */ + ESP32WhiteBalanceMode wb_mode_{ESP32_WB_MODE_AUTO}; + /* -- Test */ bool test_pattern_{false}; + /* -- framerates */ + uint32_t max_update_interval_{1000}; + uint32_t idle_update_interval_{15000}; esp_err_t init_error_{ESP_OK}; std::shared_ptr current_image_; @@ -114,8 +187,7 @@ class ESP32Camera : public Component, public EntityBase { QueueHandle_t framebuffer_get_queue_; QueueHandle_t framebuffer_return_queue_; CallbackManager)> new_image_callback_; - uint32_t max_update_interval_{1000}; - uint32_t idle_update_interval_{15000}; + uint32_t last_idle_request_{0}; uint32_t last_update_{0}; };