ESPHome 2025.9.0-dev
Loading...
Searching...
No Matches
esp32_camera.cpp
Go to the documentation of this file.
1#ifdef USE_ESP32
2
3#include "esp32_camera.h"
5#include "esphome/core/hal.h"
6#include "esphome/core/log.h"
7
8#include <freertos/task.h>
9
10namespace esphome {
11namespace esp32_camera {
12
13static const char *const TAG = "esp32_camera";
14
15/* ---------------- public API (derivated) ---------------- */
17#ifdef USE_I2C
18 if (this->i2c_bus_ != nullptr) {
19 this->config_.sccb_i2c_port = this->i2c_bus_->get_port();
20 }
21#endif
22
23 /* initialize time to now */
24 this->last_update_ = millis();
25
26 /* initialize camera */
27 esp_err_t err = esp_camera_init(&this->config_);
28 if (err != ESP_OK) {
29 ESP_LOGE(TAG, "esp_camera_init failed: %s", esp_err_to_name(err));
30 this->init_error_ = err;
31 this->mark_failed();
32 return;
33 }
34
35 /* initialize camera parameters */
37
38 /* initialize RTOS */
39 this->framebuffer_get_queue_ = xQueueCreate(1, sizeof(camera_fb_t *));
40 this->framebuffer_return_queue_ = xQueueCreate(1, sizeof(camera_fb_t *));
41 xTaskCreatePinnedToCore(&ESP32Camera::framebuffer_task,
42 "framebuffer_task", // name
43 1024, // stack size
44 this, // task pv params
45 1, // priority
46 nullptr, // handle
47 1 // core
48 );
49}
50
52 auto conf = this->config_;
53 ESP_LOGCONFIG(TAG,
54 "ESP32 Camera:\n"
55 " Name: %s\n"
56 " Internal: %s\n"
57 " Data Pins: D0:%d D1:%d D2:%d D3:%d D4:%d D5:%d D6:%d D7:%d\n"
58 " VSYNC Pin: %d\n"
59 " HREF Pin: %d\n"
60 " Pixel Clock Pin: %d\n"
61 " External Clock: Pin:%d Frequency:%u\n"
62 " I2C Pins: SDA:%d SCL:%d\n"
63 " Reset Pin: %d",
64 this->name_.c_str(), YESNO(this->is_internal()), conf.pin_d0, conf.pin_d1, conf.pin_d2, conf.pin_d3,
65 conf.pin_d4, conf.pin_d5, conf.pin_d6, conf.pin_d7, conf.pin_vsync, conf.pin_href, conf.pin_pclk,
66 conf.pin_xclk, conf.xclk_freq_hz, conf.pin_sccb_sda, conf.pin_sccb_scl, conf.pin_reset);
67 switch (this->config_.frame_size) {
68 case FRAMESIZE_QQVGA:
69 ESP_LOGCONFIG(TAG, " Resolution: 160x120 (QQVGA)");
70 break;
71 case FRAMESIZE_QCIF:
72 ESP_LOGCONFIG(TAG, " Resolution: 176x155 (QCIF)");
73 break;
74 case FRAMESIZE_HQVGA:
75 ESP_LOGCONFIG(TAG, " Resolution: 240x176 (HQVGA)");
76 break;
77 case FRAMESIZE_QVGA:
78 ESP_LOGCONFIG(TAG, " Resolution: 320x240 (QVGA)");
79 break;
80 case FRAMESIZE_CIF:
81 ESP_LOGCONFIG(TAG, " Resolution: 400x296 (CIF)");
82 break;
83 case FRAMESIZE_VGA:
84 ESP_LOGCONFIG(TAG, " Resolution: 640x480 (VGA)");
85 break;
86 case FRAMESIZE_SVGA:
87 ESP_LOGCONFIG(TAG, " Resolution: 800x600 (SVGA)");
88 break;
89 case FRAMESIZE_XGA:
90 ESP_LOGCONFIG(TAG, " Resolution: 1024x768 (XGA)");
91 break;
92 case FRAMESIZE_SXGA:
93 ESP_LOGCONFIG(TAG, " Resolution: 1280x1024 (SXGA)");
94 break;
95 case FRAMESIZE_UXGA:
96 ESP_LOGCONFIG(TAG, " Resolution: 1600x1200 (UXGA)");
97 break;
98 case FRAMESIZE_FHD:
99 ESP_LOGCONFIG(TAG, " Resolution: 1920x1080 (FHD)");
100 break;
101 case FRAMESIZE_P_HD:
102 ESP_LOGCONFIG(TAG, " Resolution: 720x1280 (P_HD)");
103 break;
104 case FRAMESIZE_P_3MP:
105 ESP_LOGCONFIG(TAG, " Resolution: 864x1536 (P_3MP)");
106 break;
107 case FRAMESIZE_QXGA:
108 ESP_LOGCONFIG(TAG, " Resolution: 2048x1536 (QXGA)");
109 break;
110 case FRAMESIZE_QHD:
111 ESP_LOGCONFIG(TAG, " Resolution: 2560x1440 (QHD)");
112 break;
113 case FRAMESIZE_WQXGA:
114 ESP_LOGCONFIG(TAG, " Resolution: 2560x1600 (WQXGA)");
115 break;
116 case FRAMESIZE_P_FHD:
117 ESP_LOGCONFIG(TAG, " Resolution: 1080x1920 (P_FHD)");
118 break;
119 case FRAMESIZE_QSXGA:
120 ESP_LOGCONFIG(TAG, " Resolution: 2560x1920 (QSXGA)");
121 break;
122 default:
123 break;
124 }
125
126 if (this->is_failed()) {
127 ESP_LOGE(TAG, " Setup Failed: %s", esp_err_to_name(this->init_error_));
128 return;
129 }
130
131 sensor_t *s = esp_camera_sensor_get();
132 auto st = s->status;
133 ESP_LOGCONFIG(TAG,
134 " JPEG Quality: %u\n"
135 " Framebuffer Count: %u\n"
136 " Framebuffer Location: %s\n"
137 " Contrast: %d\n"
138 " Brightness: %d\n"
139 " Saturation: %d\n"
140 " Vertical Flip: %s\n"
141 " Horizontal Mirror: %s\n"
142 " Special Effect: %u\n"
143 " White Balance Mode: %u",
144 st.quality, conf.fb_count, this->config_.fb_location == CAMERA_FB_IN_PSRAM ? "PSRAM" : "DRAM",
145 st.contrast, st.brightness, st.saturation, ONOFF(st.vflip), ONOFF(st.hmirror), st.special_effect,
146 st.wb_mode);
147 // ESP_LOGCONFIG(TAG, " Auto White Balance: %u", st.awb);
148 // ESP_LOGCONFIG(TAG, " Auto White Balance Gain: %u", st.awb_gain);
149 ESP_LOGCONFIG(TAG,
150 " Auto Exposure Control: %u\n"
151 " Auto Exposure Control 2: %u\n"
152 " Auto Exposure Level: %d\n"
153 " Auto Exposure Value: %u\n"
154 " AGC: %u\n"
155 " AGC Gain: %u\n"
156 " Gain Ceiling: %u",
157 st.aec, st.aec2, st.ae_level, st.aec_value, st.agc, st.agc_gain, st.gainceiling);
158 // ESP_LOGCONFIG(TAG, " BPC: %u", st.bpc);
159 // ESP_LOGCONFIG(TAG, " WPC: %u", st.wpc);
160 // ESP_LOGCONFIG(TAG, " RAW_GMA: %u", st.raw_gma);
161 // ESP_LOGCONFIG(TAG, " Lens Correction: %u", st.lenc);
162 // ESP_LOGCONFIG(TAG, " DCW: %u", st.dcw);
163 ESP_LOGCONFIG(TAG, " Test Pattern: %s", YESNO(st.colorbar));
164}
165
167 // check if we can return the image
168 if (this->can_return_image_()) {
169 // return image
170 auto *fb = this->current_image_->get_raw_buffer();
171 xQueueSend(this->framebuffer_return_queue_, &fb, portMAX_DELAY);
172 this->current_image_.reset();
173 }
174
175 // request idle image every idle_update_interval
176 const uint32_t now = App.get_loop_component_start_time();
177 if (this->idle_update_interval_ != 0 && now - this->last_idle_request_ > this->idle_update_interval_) {
178 this->last_idle_request_ = now;
180 }
181
182 // Check if we should fetch a new image
183 if (!this->has_requested_image_())
184 return;
185 if (this->current_image_.use_count() > 1) {
186 // image is still in use
187 return;
188 }
190 return;
191
192 // request new image
193 camera_fb_t *fb;
194 if (xQueueReceive(this->framebuffer_get_queue_, &fb, 0L) != pdTRUE) {
195 // no frame ready
196 ESP_LOGVV(TAG, "No frame ready");
197 return;
198 }
199
200 if (fb == nullptr) {
201 ESP_LOGW(TAG, "Got invalid frame from camera!");
202 xQueueSend(this->framebuffer_return_queue_, &fb, portMAX_DELAY);
203 return;
204 }
205 this->current_image_ = std::make_shared<ESP32CameraImage>(fb, this->single_requesters_ | this->stream_requesters_);
206
207 ESP_LOGD(TAG, "Got Image: len=%u", fb->len);
208 this->new_image_callback_.call(this->current_image_);
209 this->last_update_ = now;
210 this->single_requesters_ = 0;
211}
212
214
215/* ---------------- constructors ---------------- */
217 this->config_.pin_pwdn = -1;
218 this->config_.pin_reset = -1;
219 this->config_.pin_xclk = -1;
220 this->config_.ledc_timer = LEDC_TIMER_0;
221 this->config_.ledc_channel = LEDC_CHANNEL_0;
222 this->config_.pixel_format = PIXFORMAT_JPEG;
223 this->config_.frame_size = FRAMESIZE_VGA; // 640x480
224 this->config_.jpeg_quality = 10;
225 this->config_.fb_count = 1;
226 this->config_.grab_mode = CAMERA_GRAB_WHEN_EMPTY;
227 this->config_.fb_location = CAMERA_FB_IN_PSRAM;
228}
229
230/* ---------------- setters ---------------- */
231/* set pin assignment */
232void ESP32Camera::set_data_pins(std::array<uint8_t, 8> pins) {
233 this->config_.pin_d0 = pins[0];
234 this->config_.pin_d1 = pins[1];
235 this->config_.pin_d2 = pins[2];
236 this->config_.pin_d3 = pins[3];
237 this->config_.pin_d4 = pins[4];
238 this->config_.pin_d5 = pins[5];
239 this->config_.pin_d6 = pins[6];
240 this->config_.pin_d7 = pins[7];
241}
242void ESP32Camera::set_vsync_pin(uint8_t pin) { this->config_.pin_vsync = pin; }
243void ESP32Camera::set_href_pin(uint8_t pin) { this->config_.pin_href = pin; }
244void ESP32Camera::set_pixel_clock_pin(uint8_t pin) { this->config_.pin_pclk = pin; }
245void ESP32Camera::set_external_clock(uint8_t pin, uint32_t frequency) {
246 this->config_.pin_xclk = pin;
247 this->config_.xclk_freq_hz = frequency;
248}
249void ESP32Camera::set_i2c_pins(uint8_t sda, uint8_t scl) {
250 this->config_.pin_sccb_sda = sda;
251 this->config_.pin_sccb_scl = scl;
252}
253#ifdef USE_I2C
255 this->i2c_bus_ = i2c_bus;
256 this->config_.pin_sccb_sda = -1;
257 this->config_.pin_sccb_scl = -1;
258}
259#endif // USE_I2C
260void ESP32Camera::set_reset_pin(uint8_t pin) { this->config_.pin_reset = pin; }
261void ESP32Camera::set_power_down_pin(uint8_t pin) { this->config_.pin_pwdn = pin; }
262
263/* set image parameters */
265 switch (size) {
267 this->config_.frame_size = FRAMESIZE_QQVGA;
268 break;
270 this->config_.frame_size = FRAMESIZE_QCIF;
271 break;
273 this->config_.frame_size = FRAMESIZE_HQVGA;
274 break;
276 this->config_.frame_size = FRAMESIZE_QVGA;
277 break;
279 this->config_.frame_size = FRAMESIZE_CIF;
280 break;
282 this->config_.frame_size = FRAMESIZE_VGA;
283 break;
285 this->config_.frame_size = FRAMESIZE_SVGA;
286 break;
288 this->config_.frame_size = FRAMESIZE_XGA;
289 break;
291 this->config_.frame_size = FRAMESIZE_SXGA;
292 break;
294 this->config_.frame_size = FRAMESIZE_UXGA;
295 break;
297 this->config_.frame_size = FRAMESIZE_FHD;
298 break;
300 this->config_.frame_size = FRAMESIZE_P_HD;
301 break;
303 this->config_.frame_size = FRAMESIZE_P_3MP;
304 break;
306 this->config_.frame_size = FRAMESIZE_QXGA;
307 break;
309 this->config_.frame_size = FRAMESIZE_QHD;
310 break;
312 this->config_.frame_size = FRAMESIZE_WQXGA;
313 break;
315 this->config_.frame_size = FRAMESIZE_P_FHD;
316 break;
318 this->config_.frame_size = FRAMESIZE_QSXGA;
319 break;
320 }
321}
322void ESP32Camera::set_jpeg_quality(uint8_t quality) { this->config_.jpeg_quality = quality; }
323void ESP32Camera::set_vertical_flip(bool vertical_flip) { this->vertical_flip_ = vertical_flip; }
324void ESP32Camera::set_horizontal_mirror(bool horizontal_mirror) { this->horizontal_mirror_ = horizontal_mirror; }
325void ESP32Camera::set_contrast(int contrast) { this->contrast_ = contrast; }
326void ESP32Camera::set_brightness(int brightness) { this->brightness_ = brightness; }
327void ESP32Camera::set_saturation(int saturation) { this->saturation_ = saturation; }
329/* set exposure parameters */
331void ESP32Camera::set_aec2(bool aec2) { this->aec2_ = aec2; }
332void ESP32Camera::set_ae_level(int ae_level) { this->ae_level_ = ae_level; }
333void ESP32Camera::set_aec_value(uint32_t aec_value) { this->aec_value_ = aec_value; }
334/* set gains parameters */
336void ESP32Camera::set_agc_value(uint8_t agc_value) { this->agc_value_ = agc_value; }
338/* set white balance */
340/* set test mode */
341void ESP32Camera::set_test_pattern(bool test_pattern) { this->test_pattern_ = test_pattern; }
342/* set fps */
343void ESP32Camera::set_max_update_interval(uint32_t max_update_interval) {
344 this->max_update_interval_ = max_update_interval;
345}
346void ESP32Camera::set_idle_update_interval(uint32_t idle_update_interval) {
347 this->idle_update_interval_ = idle_update_interval;
348}
349/* set frame buffer parameters */
350void ESP32Camera::set_frame_buffer_mode(camera_grab_mode_t mode) { this->config_.grab_mode = mode; }
352 this->config_.fb_count = fb_count;
353 this->set_frame_buffer_mode(fb_count > 1 ? CAMERA_GRAB_LATEST : CAMERA_GRAB_WHEN_EMPTY);
354}
355void ESP32Camera::set_frame_buffer_location(camera_fb_location_t fb_location) {
356 this->config_.fb_location = fb_location;
357}
358
359/* ---------------- public API (specific) ---------------- */
360void ESP32Camera::add_image_callback(std::function<void(std::shared_ptr<camera::CameraImage>)> &&callback) {
361 this->new_image_callback_.add(std::move(callback));
362}
363void ESP32Camera::add_stream_start_callback(std::function<void()> &&callback) {
364 this->stream_start_callback_.add(std::move(callback));
365}
366void ESP32Camera::add_stream_stop_callback(std::function<void()> &&callback) {
367 this->stream_stop_callback_.add(std::move(callback));
368}
370 this->stream_start_callback_.call();
371 this->stream_requesters_ |= (1U << requester);
372}
374 this->stream_stop_callback_.call();
375 this->stream_requesters_ &= ~(1U << requester);
376}
377void ESP32Camera::request_image(camera::CameraRequester requester) { this->single_requesters_ |= (1U << requester); }
380 sensor_t *s = esp_camera_sensor_get();
381 /* update image */
382 s->set_vflip(s, this->vertical_flip_);
383 s->set_hmirror(s, this->horizontal_mirror_);
384 s->set_contrast(s, this->contrast_);
385 s->set_brightness(s, this->brightness_);
386 s->set_saturation(s, this->saturation_);
387 s->set_special_effect(s, (int) this->special_effect_); // 0 to 6
388 /* update exposure */
389 s->set_exposure_ctrl(s, (bool) this->aec_mode_);
390 s->set_aec2(s, this->aec2_); // 0 = disable , 1 = enable
391 s->set_ae_level(s, this->ae_level_); // -2 to 2
392 s->set_aec_value(s, this->aec_value_); // 0 to 1200
393 /* update gains */
394 s->set_gain_ctrl(s, (bool) this->agc_mode_);
395 s->set_agc_gain(s, (int) this->agc_value_); // 0 to 30
396 s->set_gainceiling(s, (gainceiling_t) this->agc_gain_ceiling_);
397 /* update white balance mode */
398 s->set_wb_mode(s, (int) this->wb_mode_); // 0 to 4
399 /* update test pattern */
400 s->set_colorbar(s, this->test_pattern_);
401}
402
403/* ---------------- Internal methods ---------------- */
405bool ESP32Camera::can_return_image_() const { return this->current_image_.use_count() == 1; }
407 ESP32Camera *that = (ESP32Camera *) pv;
408 while (true) {
409 camera_fb_t *framebuffer = esp_camera_fb_get();
410 xQueueSend(that->framebuffer_get_queue_, &framebuffer, portMAX_DELAY);
411 // return is no-op for config with 1 fb
412 xQueueReceive(that->framebuffer_return_queue_, &framebuffer, portMAX_DELAY);
413 esp_camera_fb_return(framebuffer);
414 }
415}
416
417/* ---------------- ESP32CameraImageReader class ----------- */
418void ESP32CameraImageReader::set_image(std::shared_ptr<camera::CameraImage> image) {
419 this->image_ = std::static_pointer_cast<ESP32CameraImage>(image);
420 this->offset_ = 0;
421}
423 if (!this->image_)
424 return 0;
425
426 return this->image_->get_data_length() - this->offset_;
427}
429void ESP32CameraImageReader::consume_data(size_t consumed) { this->offset_ += consumed; }
430uint8_t *ESP32CameraImageReader::peek_data_buffer() { return this->image_->get_data_buffer() + this->offset_; }
431
432/* ---------------- ESP32CameraImage class ----------- */
433ESP32CameraImage::ESP32CameraImage(camera_fb_t *buffer, uint8_t requesters)
434 : buffer_(buffer), requesters_(requesters) {}
435
436camera_fb_t *ESP32CameraImage::get_raw_buffer() { return this->buffer_; }
437uint8_t *ESP32CameraImage::get_data_buffer() { return this->buffer_->buf; }
438size_t ESP32CameraImage::get_data_length() { return this->buffer_->len; }
440 return (this->requesters_ & (1 << requester)) != 0;
441}
442
443} // namespace esp32_camera
444} // namespace esphome
445
446#endif
BedjetMode mode
BedJet operating mode.
uint16_le_t frequency
Definition bl0942.h:6
uint32_t IRAM_ATTR HOT get_loop_component_start_time() const
Get the cached time in milliseconds from when the current component started its loop execution.
virtual void mark_failed()
Mark this component as failed.
bool is_failed() const
bool is_internal() const
Definition entity_base.h:39
constexpr const char * c_str() const
Definition string_ref.h:69
Abstract image reader base class.
Definition camera.h:36
void set_i2c_pins(uint8_t sda, uint8_t scl)
void set_agc_value(uint8_t agc_value)
void set_test_pattern(bool test_pattern)
void set_jpeg_quality(uint8_t quality)
void set_wb_mode(ESP32WhiteBalanceMode mode)
ESP32AgcGainCeiling agc_gain_ceiling_
void set_vertical_flip(bool vertical_flip)
void set_aec_value(uint32_t aec_value)
CallbackManager< void(std::shared_ptr< camera::CameraImage >)> new_image_callback_
void set_special_effect(ESP32SpecialEffect effect)
void set_aec_mode(ESP32GainControlMode mode)
CallbackManager< void()> stream_stop_callback_
float get_setup_priority() const override
void set_data_pins(std::array< uint8_t, 8 > pins)
void stop_stream(camera::CameraRequester requester) override
void add_stream_start_callback(std::function< void()> &&callback)
void set_i2c_id(i2c::InternalI2CBus *i2c_bus)
void set_frame_size(ESP32CameraFrameSize size)
void set_external_clock(uint8_t pin, uint32_t frequency)
void set_max_update_interval(uint32_t max_update_interval)
std::shared_ptr< ESP32CameraImage > current_image_
void add_image_callback(std::function< void(std::shared_ptr< camera::CameraImage >)> &&callback) override
void add_stream_stop_callback(std::function< void()> &&callback)
void set_horizontal_mirror(bool horizontal_mirror)
camera::CameraImageReader * create_image_reader() override
void set_frame_buffer_count(uint8_t fb_count)
void set_agc_gain_ceiling(ESP32AgcGainCeiling gain_ceiling)
void set_agc_mode(ESP32GainControlMode mode)
void set_frame_buffer_location(camera_fb_location_t fb_location)
void set_frame_buffer_mode(camera_grab_mode_t mode)
void set_idle_update_interval(uint32_t idle_update_interval)
CallbackManager< void()> stream_start_callback_
void start_stream(camera::CameraRequester requester) override
void request_image(camera::CameraRequester requester) override
static void framebuffer_task(void *pv)
bool was_requested_by(camera::CameraRequester requester) const override
ESP32CameraImage(camera_fb_t *buffer, uint8_t requester)
void set_image(std::shared_ptr< camera::CameraImage > image) override
std::shared_ptr< ESP32CameraImage > image_
void consume_data(size_t consumed) override
virtual int get_port() const =0
Returns the I2C port number.
CameraRequester
Different sources for filtering.
Definition camera.h:16
const float DATA
For components that import data from directly connected sensors like DHT.
Definition component.cpp:50
Providing packet encoding functions for exchanging data with a remote host.
Definition a01nyub.cpp:7
uint32_t IRAM_ATTR HOT millis()
Definition core.cpp:28
Application App
Global storage of Application pointer - only one Application can exist.