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