ESPHome  2024.10.2
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"
6 
7 #include <freertos/task.h>
8 
9 namespace esphome {
10 namespace esp32_camera {
11 
12 static const char *const TAG = "esp32_camera";
13 
14 /* ---------------- public API (derivated) ---------------- */
16  global_esp32_camera = this;
17 
18  /* initialize time to now */
19  this->last_update_ = millis();
20 
21  /* initialize camera */
22  esp_err_t err = esp_camera_init(&this->config_);
23  if (err != ESP_OK) {
24  ESP_LOGE(TAG, "esp_camera_init failed: %s", esp_err_to_name(err));
25  this->init_error_ = err;
26  this->mark_failed();
27  return;
28  }
29 
30  /* initialize camera parameters */
32 
33  /* initialize RTOS */
34  this->framebuffer_get_queue_ = xQueueCreate(1, sizeof(camera_fb_t *));
35  this->framebuffer_return_queue_ = xQueueCreate(1, sizeof(camera_fb_t *));
36  xTaskCreatePinnedToCore(&ESP32Camera::framebuffer_task,
37  "framebuffer_task", // name
38  1024, // stack size
39  nullptr, // task pv params
40  1, // priority
41  nullptr, // handle
42  1 // core
43  );
44 }
45 
47  auto conf = this->config_;
48  ESP_LOGCONFIG(TAG, "ESP32 Camera:");
49  ESP_LOGCONFIG(TAG, " Name: %s", this->name_.c_str());
50  ESP_LOGCONFIG(TAG, " Internal: %s", YESNO(this->internal_));
51  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,
52  conf.pin_d2, conf.pin_d3, conf.pin_d4, conf.pin_d5, conf.pin_d6, conf.pin_d7);
53  ESP_LOGCONFIG(TAG, " VSYNC Pin: %d", conf.pin_vsync);
54  ESP_LOGCONFIG(TAG, " HREF Pin: %d", conf.pin_href);
55  ESP_LOGCONFIG(TAG, " Pixel Clock Pin: %d", conf.pin_pclk);
56  ESP_LOGCONFIG(TAG, " External Clock: Pin:%d Frequency:%u", conf.pin_xclk, conf.xclk_freq_hz);
57 #ifdef USE_ESP_IDF // Temporary until the espressif/esp32-camera library is updated
58  ESP_LOGCONFIG(TAG, " I2C Pins: SDA:%d SCL:%d", conf.pin_sscb_sda, conf.pin_sscb_scl);
59 #else
60  ESP_LOGCONFIG(TAG, " I2C Pins: SDA:%d SCL:%d", conf.pin_sccb_sda, conf.pin_sccb_scl);
61 #endif
62  ESP_LOGCONFIG(TAG, " Reset Pin: %d", 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, " JPEG Quality: %u", st.quality);
130  ESP_LOGCONFIG(TAG, " Framebuffer Count: %u", conf.fb_count);
131  ESP_LOGCONFIG(TAG, " Contrast: %d", st.contrast);
132  ESP_LOGCONFIG(TAG, " Brightness: %d", st.brightness);
133  ESP_LOGCONFIG(TAG, " Saturation: %d", st.saturation);
134  ESP_LOGCONFIG(TAG, " Vertical Flip: %s", ONOFF(st.vflip));
135  ESP_LOGCONFIG(TAG, " Horizontal Mirror: %s", ONOFF(st.hmirror));
136  ESP_LOGCONFIG(TAG, " Special Effect: %u", st.special_effect);
137  ESP_LOGCONFIG(TAG, " White Balance Mode: %u", st.wb_mode);
138  // ESP_LOGCONFIG(TAG, " Auto White Balance: %u", st.awb);
139  // ESP_LOGCONFIG(TAG, " Auto White Balance Gain: %u", st.awb_gain);
140  ESP_LOGCONFIG(TAG, " Auto Exposure Control: %u", st.aec);
141  ESP_LOGCONFIG(TAG, " Auto Exposure Control 2: %u", st.aec2);
142  ESP_LOGCONFIG(TAG, " Auto Exposure Level: %d", st.ae_level);
143  ESP_LOGCONFIG(TAG, " Auto Exposure Value: %u", st.aec_value);
144  ESP_LOGCONFIG(TAG, " AGC: %u", st.agc);
145  ESP_LOGCONFIG(TAG, " AGC Gain: %u", st.agc_gain);
146  ESP_LOGCONFIG(TAG, " Gain Ceiling: %u", st.gainceiling);
147  // ESP_LOGCONFIG(TAG, " BPC: %u", st.bpc);
148  // ESP_LOGCONFIG(TAG, " WPC: %u", st.wpc);
149  // ESP_LOGCONFIG(TAG, " RAW_GMA: %u", st.raw_gma);
150  // ESP_LOGCONFIG(TAG, " Lens Correction: %u", st.lenc);
151  // ESP_LOGCONFIG(TAG, " DCW: %u", st.dcw);
152  ESP_LOGCONFIG(TAG, " Test Pattern: %s", YESNO(st.colorbar));
153 }
154 
156  // check if we can return the image
157  if (this->can_return_image_()) {
158  // return image
159  auto *fb = this->current_image_->get_raw_buffer();
160  xQueueSend(this->framebuffer_return_queue_, &fb, portMAX_DELAY);
161  this->current_image_.reset();
162  }
163 
164  // request idle image every idle_update_interval
165  const uint32_t now = millis();
166  if (this->idle_update_interval_ != 0 && now - this->last_idle_request_ > this->idle_update_interval_) {
167  this->last_idle_request_ = now;
168  this->request_image(IDLE);
169  }
170 
171  // Check if we should fetch a new image
172  if (!this->has_requested_image_())
173  return;
174  if (this->current_image_.use_count() > 1) {
175  // image is still in use
176  return;
177  }
178  if (now - this->last_update_ <= this->max_update_interval_)
179  return;
180 
181  // request new image
182  camera_fb_t *fb;
183  if (xQueueReceive(this->framebuffer_get_queue_, &fb, 0L) != pdTRUE) {
184  // no frame ready
185  ESP_LOGVV(TAG, "No frame ready");
186  return;
187  }
188 
189  if (fb == nullptr) {
190  ESP_LOGW(TAG, "Got invalid frame from camera!");
191  xQueueSend(this->framebuffer_return_queue_, &fb, portMAX_DELAY);
192  return;
193  }
194  this->current_image_ = std::make_shared<CameraImage>(fb, this->single_requesters_ | this->stream_requesters_);
195 
196  ESP_LOGD(TAG, "Got Image: len=%u", fb->len);
197  this->new_image_callback_.call(this->current_image_);
198  this->last_update_ = now;
199  this->single_requesters_ = 0;
200 }
201 
203 
204 /* ---------------- constructors ---------------- */
206  this->config_.pin_pwdn = -1;
207  this->config_.pin_reset = -1;
208  this->config_.pin_xclk = -1;
209  this->config_.ledc_timer = LEDC_TIMER_0;
210  this->config_.ledc_channel = LEDC_CHANNEL_0;
211  this->config_.pixel_format = PIXFORMAT_JPEG;
212  this->config_.frame_size = FRAMESIZE_VGA; // 640x480
213  this->config_.jpeg_quality = 10;
214  this->config_.fb_count = 1;
215  this->config_.grab_mode = CAMERA_GRAB_WHEN_EMPTY;
216  this->config_.fb_location = CAMERA_FB_IN_PSRAM;
217 
218  global_esp32_camera = this;
219 }
220 
221 /* ---------------- setters ---------------- */
222 /* set pin assignment */
223 void ESP32Camera::set_data_pins(std::array<uint8_t, 8> pins) {
224  this->config_.pin_d0 = pins[0];
225  this->config_.pin_d1 = pins[1];
226  this->config_.pin_d2 = pins[2];
227  this->config_.pin_d3 = pins[3];
228  this->config_.pin_d4 = pins[4];
229  this->config_.pin_d5 = pins[5];
230  this->config_.pin_d6 = pins[6];
231  this->config_.pin_d7 = pins[7];
232 }
233 void ESP32Camera::set_vsync_pin(uint8_t pin) { this->config_.pin_vsync = pin; }
234 void ESP32Camera::set_href_pin(uint8_t pin) { this->config_.pin_href = pin; }
235 void ESP32Camera::set_pixel_clock_pin(uint8_t pin) { this->config_.pin_pclk = pin; }
236 void ESP32Camera::set_external_clock(uint8_t pin, uint32_t frequency) {
237  this->config_.pin_xclk = pin;
238  this->config_.xclk_freq_hz = frequency;
239 }
240 void ESP32Camera::set_i2c_pins(uint8_t sda, uint8_t scl) {
241 #ifdef USE_ESP_IDF // Temporary until the espressif/esp32-camera library is updated
242  this->config_.pin_sscb_sda = sda;
243  this->config_.pin_sscb_scl = scl;
244 #else
245  this->config_.pin_sccb_sda = sda;
246  this->config_.pin_sccb_scl = scl;
247 #endif
248 }
249 void ESP32Camera::set_reset_pin(uint8_t pin) { this->config_.pin_reset = pin; }
250 void 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 }
311 void ESP32Camera::set_jpeg_quality(uint8_t quality) { this->config_.jpeg_quality = quality; }
312 void ESP32Camera::set_vertical_flip(bool vertical_flip) { this->vertical_flip_ = vertical_flip; }
313 void ESP32Camera::set_horizontal_mirror(bool horizontal_mirror) { this->horizontal_mirror_ = horizontal_mirror; }
314 void ESP32Camera::set_contrast(int contrast) { this->contrast_ = contrast; }
315 void ESP32Camera::set_brightness(int brightness) { this->brightness_ = brightness; }
316 void ESP32Camera::set_saturation(int saturation) { this->saturation_ = saturation; }
318 /* set exposure parameters */
320 void ESP32Camera::set_aec2(bool aec2) { this->aec2_ = aec2; }
321 void ESP32Camera::set_ae_level(int ae_level) { this->ae_level_ = ae_level; }
322 void ESP32Camera::set_aec_value(uint32_t aec_value) { this->aec_value_ = aec_value; }
323 /* set gains parameters */
325 void ESP32Camera::set_agc_value(uint8_t agc_value) { this->agc_value_ = agc_value; }
326 void ESP32Camera::set_agc_gain_ceiling(ESP32AgcGainCeiling gain_ceiling) { this->agc_gain_ceiling_ = gain_ceiling; }
327 /* set white balance */
329 /* set test mode */
330 void ESP32Camera::set_test_pattern(bool test_pattern) { this->test_pattern_ = test_pattern; }
331 /* set fps */
332 void ESP32Camera::set_max_update_interval(uint32_t max_update_interval) {
333  this->max_update_interval_ = max_update_interval;
334 }
335 void ESP32Camera::set_idle_update_interval(uint32_t idle_update_interval) {
336  this->idle_update_interval_ = idle_update_interval;
337 }
338 /* set frame buffer parameters */
339 void ESP32Camera::set_frame_buffer_mode(camera_grab_mode_t mode) { this->config_.grab_mode = mode; }
340 void ESP32Camera::set_frame_buffer_count(uint8_t fb_count) {
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) ---------------- */
346 void ESP32Camera::add_image_callback(std::function<void(std::shared_ptr<CameraImage>)> &&callback) {
347  this->new_image_callback_.add(std::move(callback));
348 }
349 void ESP32Camera::add_stream_start_callback(std::function<void()> &&callback) {
350  this->stream_start_callback_.add(std::move(callback));
351 }
352 void 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 }
363 void 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 ---------------- */
390 bool 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 
401 ESP32Camera *global_esp32_camera; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
402 
403 /* ---------------- CameraImageReader class ---------------- */
404 void 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 }
414 void CameraImageReader::return_image() { this->image_.reset(); }
415 void CameraImageReader::consume_data(size_t consumed) { this->offset_ += consumed; }
416 uint8_t *CameraImageReader::peek_data_buffer() { return this->image_->get_data_buffer() + this->offset_; }
417 
418 /* ---------------- CameraImage class ---------------- */
419 CameraImage::CameraImage(camera_fb_t *buffer, uint8_t requesters) : buffer_(buffer), requesters_(requesters) {}
420 
421 camera_fb_t *CameraImage::get_raw_buffer() { return this->buffer_; }
422 uint8_t *CameraImage::get_data_buffer() { return this->buffer_->buf; }
423 size_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
std::shared_ptr< CameraImage > current_image_
Definition: esp32_camera.h:202
void set_test_pattern(bool test_pattern)
ESP32WhiteBalanceMode wb_mode_
Definition: esp32_camera.h:194
const float DATA
For components that import data from directly connected sensors like DHT.
Definition: component.cpp:19
void request_image(CameraRequester requester)
void add_stream_start_callback(std::function< void()> &&callback)
void set_vertical_flip(bool vertical_flip)
void set_agc_mode(ESP32GainControlMode mode)
void start_stream(CameraRequester requester)
void set_max_update_interval(uint32_t max_update_interval)
void set_wb_mode(ESP32WhiteBalanceMode mode)
bool is_failed() const
Definition: component.cpp:143
void set_jpeg_quality(uint8_t quality)
void add_image_callback(std::function< void(std::shared_ptr< CameraImage >)> &&callback)
float get_setup_priority() const override
void stop_stream(CameraRequester requester)
uint32_t IRAM_ATTR HOT millis()
Definition: core.cpp:25
ESP32GainControlMode aec_mode_
Definition: esp32_camera.h:185
CallbackManager< void()> stream_stop_callback_
Definition: esp32_camera.h:209
void set_aec_mode(ESP32GainControlMode mode)
ESP32Camera * global_esp32_camera
BedjetMode mode
BedJet operating mode.
Definition: bedjet_codec.h:183
void add_stream_stop_callback(std::function< void()> &&callback)
ESP32GainControlMode agc_mode_
Definition: esp32_camera.h:190
void set_external_clock(uint8_t pin, uint32_t frequency)
void set_frame_buffer_count(uint8_t fb_count)
CallbackManager< void()> stream_start_callback_
Definition: esp32_camera.h:208
uint16_le_t frequency
Definition: bl0942.h:72
void set_data_pins(std::array< uint8_t, 8 > pins)
void set_agc_gain_ceiling(ESP32AgcGainCeiling gain_ceiling)
CallbackManager< void(std::shared_ptr< CameraImage >)> new_image_callback_
Definition: esp32_camera.h:207
static void framebuffer_task(void *pv)
ESP32AgcGainCeiling agc_gain_ceiling_
Definition: esp32_camera.h:192
void set_horizontal_mirror(bool horizontal_mirror)
bool was_requested_by(CameraRequester requester) const
constexpr const char * c_str() const
Definition: string_ref.h:68
void set_image(std::shared_ptr< CameraImage > image)
ESP32SpecialEffect special_effect_
Definition: esp32_camera.h:183
void set_aec_value(uint32_t aec_value)
void set_frame_size(ESP32CameraFrameSize size)
virtual void mark_failed()
Mark this component as failed.
Definition: component.cpp:118
void set_agc_value(uint8_t agc_value)
CameraImage(camera_fb_t *buffer, uint8_t requester)
void set_frame_buffer_mode(camera_grab_mode_t mode)
void set_special_effect(ESP32SpecialEffect effect)
Implementation of SPI Controller mode.
Definition: a01nyub.cpp:7
void set_idle_update_interval(uint32_t idle_update_interval)
void set_i2c_pins(uint8_t sda, uint8_t scl)
void set_brightness(int brightness)
void set_saturation(int saturation)