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