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