ESP3D  3.0
Firmware for ESP boards connected to 3D Printer
camera.cpp
Go to the documentation of this file.
1 /*
2  camera.cpp - camera functions class
3 
4  Copyright (c) 2014 Luc Lebosse. All rights reserved.
5 
6  This library is free software; you can redistribute it and/or
7  modify it under the terms of the GNU Lesser General Public
8  License as published by the Free Software Foundation; either
9  version 2.1 of the License, or (at your option) any later version.
10 
11  This library is distributed in the hope that it will be useful,
12  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  Lesser General Public License for more details.
15 
16  You should have received a copy of the GNU Lesser General Public
17  License along with this library; if not, write to the Free Software
18  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20 
21 #include "../../include/esp3d_config.h"
22 #ifdef CAMERA_DEVICE
23 #include "camera.h"
24 #include "../../core/settings_esp3d.h"
25 #include "../network/netservices.h"
26 #include "../../core/esp3doutput.h"
27 #include "../network/netconfig.h"
28 #include "esp_http_server.h"
29 #include <esp_camera.h>
30 #include "fd_forward.h"
31 //#include <soc/soc.h> //not sure this one is needed
32 #include <soc/rtc_cntl_reg.h>
33 #include <driver/i2c.h>
34 
35 #define DEFAULT_FRAME_SIZE FRAMESIZE_SVGA
36 #define HTTP_TASK_PRIORITY 5
37 #define PART_BUFFER_SIZE 64
38 #define JPEG_COMPRESSION 80
39 #define MIN_WIDTH_COMPRESSION 400
40 #define PART_BOUNDARY "123456789000000000000987654321"
41 static const char* _STREAM_CONTENT_TYPE = "multipart/x-mixed-replace;boundary=" PART_BOUNDARY;
42 static const char* _STREAM_BOUNDARY = "\r\n--" PART_BOUNDARY "\r\n";
43 static const char* _STREAM_PART = "Content-Type: image/jpeg\r\nContent-Length: %u\r\n\r\n";
44 
45 httpd_handle_t stream_httpd = NULL;
47 
48 //to break the loop
49 static void disconnected_uri(httpd_handle_t hd, int sockfd)
50 {
51  log_esp3d("Camera stream disconnected");
52  esp3d_camera.connect(false);
53 }
54 
55 static esp_err_t stream_handler(httpd_req_t *req)
56 {
57  log_esp3d("Camera stream reached");
58  if (!esp3d_camera.started()) {
59  const char* resp = "Camera not started";
60  log_esp3d("Camera not started");
61  httpd_resp_send(req, resp, strlen(resp));
62  return ESP_FAIL;
63  }
64  esp3d_camera.connect(true);
65 #ifdef ESP_ACCESS_CONTROL_ALLOW_ORIGIN
66  httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*");
67 #endif //ESP_ACCESS_CONTROL_ALLOw_ORIGIN
68  camera_fb_t * fb = NULL;
69  esp_err_t res = ESP_OK;
70  size_t _jpg_buf_len = 0;
71  uint8_t * _jpg_buf = NULL;
72  char * part_buf[PART_BUFFER_SIZE];
73  dl_matrix3du_t *image_matrix = NULL;
74  res = httpd_resp_set_type(req, _STREAM_CONTENT_TYPE);
75  if(res != ESP_OK) {
76  esp3d_camera.connect(false);
77  const char* resp = "Stream type failed";
78  log_esp3d("Stream type failed");
79  httpd_resp_send(req, resp, strlen(resp));
80  return res;
81  }
82  uint8_t retry = 0;
83  while(true) {
84  if (!esp3d_camera.isconnected()) {
85  const char* resp = "Camera is not connected";
86  log_esp3d("Camera is not connected");
87  httpd_resp_send(req, resp, strlen(resp));
88  return ESP_FAIL;
89  }
90  log_esp3d("Camera capture ongoing");
91  fb = esp_camera_fb_get();
92  if (!fb) {
93  log_esp3d("Camera capture failed");
94  if ( retry < 3) {
95  log_esp3d("Retry %d",retry );
96  retry ++;
97  continue;
98  } else {
99  res = ESP_FAIL;
100  }
101  } else {
102  if(fb->width > MIN_WIDTH_COMPRESSION) {
103  if(fb->format != PIXFORMAT_JPEG) {
104  bool jpeg_converted = frame2jpg(fb, JPEG_COMPRESSION, &_jpg_buf, &_jpg_buf_len);
105  esp_camera_fb_return(fb);
106  fb = NULL;
107  if(!jpeg_converted) {
108  log_esp3d("JPEG compression failed");
109  res = ESP_FAIL;
110  }
111  } else {
112  _jpg_buf_len = fb->len;
113  _jpg_buf = fb->buf;
114  }
115  } else {
116  image_matrix = dl_matrix3du_alloc(1, fb->width, fb->height, 3);
117 
118  if (!image_matrix) {
119  log_esp3d("dl_matrix3du_alloc failed");
120  res = ESP_FAIL;
121  } else {
122  if(!fmt2rgb888(fb->buf, fb->len, fb->format, image_matrix->item)) {
123  log_esp3d("fmt2rgb888 failed");
124  res = ESP_FAIL;
125  } else {
126  if (fb->format != PIXFORMAT_JPEG) {
127  if(!fmt2jpg(image_matrix->item, fb->width*fb->height*3, fb->width, fb->height, PIXFORMAT_RGB888, 90, &_jpg_buf, &_jpg_buf_len)) {
128  log_esp3d("fmt2jpg failed");
129  res = ESP_FAIL;
130  }
131  esp_camera_fb_return(fb);
132  fb = NULL;
133  } else {
134  _jpg_buf = fb->buf;
135  _jpg_buf_len = fb->len;
136  }
137  }
138  dl_matrix3du_free(image_matrix);
139  }
140  }
141  }
142 
143  if(res == ESP_OK) {
144  size_t hlen = snprintf((char *)part_buf, PART_BUFFER_SIZE, _STREAM_PART, _jpg_buf_len);
145  res = httpd_resp_send_chunk(req, (const char *)part_buf, hlen);
146  }
147  if(res == ESP_OK) {
148  res = httpd_resp_send_chunk(req, (const char *)_jpg_buf, _jpg_buf_len);
149  }
150  if(res == ESP_OK) {
151  res = httpd_resp_send_chunk(req, _STREAM_BOUNDARY, strlen(_STREAM_BOUNDARY));
152  }
153  if(fb) {
154  esp_camera_fb_return(fb);
155  fb = NULL;
156  _jpg_buf = NULL;
157  } else if(_jpg_buf) {
158  free(_jpg_buf);
159  _jpg_buf = NULL;
160  }
161  if(res != ESP_OK) {
162  break;
163  }
164  }
165  esp3d_camera.connect(false);
166  return res;
167 }
168 
170 {
171  _started = false;
172  _initialised = false;
173  _connected = false;
174 }
175 
177 {
178  end();
179 }
180 
181 
182 int Camera::command(const char * param, const char * value)
183 {
184  int res = 0;
185  int val = atoi(value);
186  sensor_t * s = esp_camera_sensor_get();
187  if (s == nullptr) {
188  res = -1;
189  }
190 #if CAM_LED_PIN != -1
191  if (!strcmp(param, "light")) {
192  digitalWrite(CAM_LED_PIN, val==1?HIGH:LOW);
193  } else
194 #endif //CAM_LED_PIN
195  if(!strcmp(param, "framesize")) {
196  if(s->pixformat == PIXFORMAT_JPEG) {
197  res = s->set_framesize(s, (framesize_t)val);
198  }
199  } else if(!strcmp(param, "quality")) {
200  res = s->set_quality(s, val);
201  } else if(!strcmp(param, "contrast")) {
202  res = s->set_contrast(s, val);
203  } else if(!strcmp(param, "brightness")) {
204  res = s->set_brightness(s, val);
205  } else if(!strcmp(param, "saturation")) {
206  res = s->set_saturation(s, val);
207  } else if(!strcmp(param, "gainceiling")) {
208  res = s->set_gainceiling(s, (gainceiling_t)val);
209  } else if(!strcmp(param, "colorbar")) {
210  res = s->set_colorbar(s, val);
211  } else if(!strcmp(param, "awb")) {
212  res = s->set_whitebal(s, val);
213  } else if(!strcmp(param, "agc")) {
214  res = s->set_gain_ctrl(s, val);
215  } else if(!strcmp(param, "aec")) {
216  res = s->set_exposure_ctrl(s, val);
217  } else if(!strcmp(param, "hmirror")) {
218  res = s->set_hmirror(s, val);
219  } else if(!strcmp(param, "vflip")) {
220  res = s->set_vflip(s, val);
221  } else if(!strcmp(param, "awb_gain")) {
222  res = s->set_awb_gain(s, val);
223  } else if(!strcmp(param, "agc_gain")) {
224  res = s->set_agc_gain(s, val);
225  } else if(!strcmp(param, "aec_value")) {
226  res = s->set_aec_value(s, val);
227  } else if(!strcmp(param, "aec2")) {
228  res = s->set_aec2(s, val);
229  } else if(!strcmp(param, "dcw")) {
230  res = s->set_dcw(s, val);
231  } else if(!strcmp(param, "bpc")) {
232  res = s->set_bpc(s, val);
233  } else if(!strcmp(param, "wpc")) {
234  res = s->set_wpc(s, val);
235  } else if(!strcmp(param, "raw_gma")) {
236  res = s->set_raw_gma(s, val);
237  } else if(!strcmp(param, "lenc")) {
238  res = s->set_lenc(s, val);
239  } else if(!strcmp(param, "special_effect")) {
240  res = s->set_special_effect(s, val);
241  } else if(!strcmp(param, "wb_mode")) {
242  res = s->set_wb_mode(s, val);
243  } else if(!strcmp(param, "ae_level")) {
244  res = s->set_ae_level(s, val);
245  } else {
246  res = -1;
247  }
248  return res;
249 }
250 
251 bool Camera::initHardware(bool forceinit)
252 {
253  if (forceinit) {
254  _initialised = false;
255  }
256  if (_initialised) {
257  return _initialised;
258  }
259  log_esp3d("Disable brown out");
260  WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0); //disable brownout detector
261  camera_config_t config;
262  config.ledc_channel = LEDC_CHANNEL_0;
263  config.ledc_timer = LEDC_TIMER_0;
264  config.pin_d0 = Y2_GPIO_NUM;
265  config.pin_d1 = Y3_GPIO_NUM;
266  config.pin_d2 = Y4_GPIO_NUM;
267  config.pin_d3 = Y5_GPIO_NUM;
268  config.pin_d4 = Y6_GPIO_NUM;
269  config.pin_d5 = Y7_GPIO_NUM;
270  config.pin_d6 = Y8_GPIO_NUM;
271  config.pin_d7 = Y9_GPIO_NUM;
272  config.pin_xclk = XCLK_GPIO_NUM;
273  config.pin_pclk = PCLK_GPIO_NUM;
274  config.pin_vsync = VSYNC_GPIO_NUM;
275  config.pin_href = HREF_GPIO_NUM;
276  config.pin_sscb_sda = SIOD_GPIO_NUM;
277  config.pin_sscb_scl = SIOC_GPIO_NUM;
278  config.pin_pwdn = PWDN_GPIO_NUM;
279  config.pin_reset = RESET_GPIO_NUM;
280  config.xclk_freq_hz = 10000000;
281  config.pixel_format = PIXFORMAT_JPEG;
282  config.jpeg_quality = 5;
283  config.fb_count = 1;
284  config.frame_size = DEFAULT_FRAME_SIZE;
285  if(!psramFound()) {
286  _initialised = false;
287  log_esp3d("psram is not enabled");
288  return false;
289  }
290  if (!stopHardware()) {
291  log_esp3d("Stop camera failed");
292  }
293  log_esp3d("Init camera");
294 #if CAM_PULLUP1 != -1
295  pinMode(CAM_PULLUP1, INPUT_PULLUP);
296 #endif //CAM_PULLUP1
297 #if CAM_PULLUP2 != -1
298  pinMode(CAM_PULLUP2, INPUT_PULLUP);
299 #endif //CAM_PULLUP2
300 #if CAM_LED_PIN != -1
301  pinMode(CAM_LED_PIN, OUTPUT);
302  digitalWrite(CAM_LED_PIN, LOW);
303 #endif //CAM_LED_PIN
304  //initialize the camera
305 
306 //https://github.com/espressif/esp32-camera/issues/66#issuecomment-526283681
307 #if CAMERA_DEVICE == CAMERA_MODEL_AI_THINKER
308  log_esp3d("Specific config for CAMERA_MODEL_AI_THINKER");
309  gpio_config_t gpio_pwr_config;
310  gpio_pwr_config.pin_bit_mask = (1ULL << 32);
311  gpio_pwr_config.mode = GPIO_MODE_OUTPUT;
312  gpio_pwr_config.pull_down_en = GPIO_PULLDOWN_DISABLE;
313  gpio_pwr_config.pull_up_en = GPIO_PULLUP_DISABLE;
314  gpio_pwr_config.intr_type = GPIO_INTR_DISABLE;
315  gpio_config(&gpio_pwr_config);
316  gpio_set_level(GPIO_NUM_32,0);
317 #endif //CAMERA_DEVICE == CAMERA_MODEL_AI_THINKER
318  log_esp3d("Init camera config");
319  esp_err_t err = esp_camera_init(&config);
320  if (err != ESP_OK) {
321  log_esp3d("Camera init failed with error 0x%x", err);
322  _initialised = false;
323  } else {
324  _initialised = true;
325  }
326  return _initialised;
327 }
328 
330 {
331  end();
332  _initialised = false;
333  log_esp3d("deinit camera");
334  esp_err_t err = esp_camera_deinit();
335  if(err == ESP_OK) {
336  //seems sometimes i2c install failed when doing camera init so let's remove if already installed
337  if(i2c_driver_delete(I2C_NUM_1)!= ESP_OK) {
338  log_esp3d("I2C 1 delete failed");
339  }
340  return true;
341  } else {
342  log_esp3d("Camera deinit failed with error 0x%x", err);
343  return false;
344  }
345 }
346 
347 //need to be call by device and by network
348 bool Camera::begin(bool forceinit)
349 {
350  end();
351  log_esp3d("Begin camera");
352  if (!initHardware(forceinit) ) {
353  log_esp3d("Init hardware failed");
354  return false;
355  }
356  delay(1000);
357  log_esp3d("Init camera sensor settings");
358  sensor_t * s = esp_camera_sensor_get();
359  if (s != nullptr) {
360  //initial sensors are flipped vertically and colors are a bit saturated
361  if (s->id.PID == OV3660_PID) {
362  s->set_brightness(s, 1);//up the blightness just a bit
363  s->set_saturation(s, -2);//lower the saturation
364  }
365 
366  s->set_framesize(s, DEFAULT_FRAME_SIZE);
367 
368 #if defined(CAMERA_DEVICE_FLIP_HORIZONTALY)
369  s->set_hmirror(s, 1);
370 #endif //CAMERA_DEVICE_FLIP_HORIZONTALY
371 #if defined(CAMERA_DEVICE_FLIP_VERTICALY)
372  s->set_vflip(s, 1);
373 #endif //CAMERA_DEVICE_FLIP_VERTICALY
374  } else {
375  log_esp3d("Cannot access camera sensor");
376  }
379  httpd_config_t httpdconfig = HTTPD_DEFAULT_CONFIG();
380  httpdconfig.close_fn =&disconnected_uri;
381  httpd_uri_t stream_uri = {
382  .uri = "/stream",
383  .method = HTTP_GET,
384  .handler = stream_handler,
385  .user_ctx = NULL
386  };
388  httpdconfig.server_port = _port;
389  httpdconfig.ctrl_port = httpdconfig.server_port +1;
390  httpdconfig.task_priority = HTTP_TASK_PRIORITY;
391  log_esp3d("Starting camera server");
392  if (httpd_start(&stream_httpd, &httpdconfig) == ESP_OK) {
393  String stmp = "Camera server started port " + String(httpdconfig.server_port);
394  output.printMSG(stmp.c_str());
395  log_esp3d("Registering /stream");
396  if (httpd_register_uri_handler(stream_httpd, &stream_uri) != ESP_OK) {
397  log_esp3d("Registering /stream failed");
398  }
399  } else {
400  log_esp3d("Starting camera server failed");
401  output.printERROR("Starting camera server failed");
402  return false;
403  }
404  _started = true;
405  }
406  for (int j = 0; j < 5; j++) {
407  camera_fb_t * fb = esp_camera_fb_get(); // start the camera ... warm it up
408  if (fb == nullptr) {
409  log_esp3d("Failed to get fb");
410  }
411  esp_camera_fb_return(fb);
412  delay(20);
413  }
414  return _started;
415 }
416 
417 void Camera::end()
418 {
419  if (_started) {
420  _started = false;
421  _connected = false;
422  log_esp3d("unregister /stream");
423  if (ESP_OK != httpd_unregister_uri(stream_httpd, "/stream")) {
424  log_esp3d("Error unregistering /stream");
425  }
426  log_esp3d("Stop httpd");
427  if (ESP_OK != httpd_stop(stream_httpd)) {
428  log_esp3d("Error stopping stream server");
429  }
430  }
431 }
432 
433 void Camera::handle()
434 {
435  //so far nothing to do
436 }
437 
438 uint8_t Camera::GetModel()
439 {
440  return CAMERA_DEVICE;
441 }
442 
443 const char *Camera::GetModelString()
444 {
445 #if defined(CUSTOM_CAMERA_NAME)
446  return CUSTOM_CAMERA_NAME;
447 #else
448  switch(CAMERA_DEVICE) {
450  return "WROVER Kit";
451  break;
453  return "ESP Eye";
454  break;
456  return "M5Stack with PSRam";
457  break;
459  return "M5Stack wide";
460  break;
462  return "ESP32 Cam";
463  break;
464  default:
465  return "Unknow Camera";
466  }
467 #endif //CUSTOM_CAMERA_NAME
468 }
469 #endif //CAMERA_DEVICE
Y6_GPIO_NUM
#define Y6_GPIO_NUM
Definition: pins.h:157
VSYNC_GPIO_NUM
#define VSYNC_GPIO_NUM
Definition: pins.h:162
CUSTOM_CAMERA_NAME
#define CUSTOM_CAMERA_NAME
Definition: configuration.h:176
Camera::~Camera
~Camera()
CAMERA_MODEL_WROVER_KIT
#define CAMERA_MODEL_WROVER_KIT
Definition: defines.h:102
Camera::started
bool started()
Definition: camera.h:38
Camera::begin
bool begin(bool forceinit=false)
Camera::stopHardware
bool stopHardware()
CAMERA_MODEL_M5STACK_WIDE
#define CAMERA_MODEL_M5STACK_WIDE
Definition: defines.h:100
Camera::isconnected
bool isconnected()
Definition: camera.h:51
XCLK_GPIO_NUM
#define XCLK_GPIO_NUM
Definition: pins.h:151
Camera::GetModel
uint8_t GetModel()
Y8_GPIO_NUM
#define Y8_GPIO_NUM
Definition: pins.h:155
Camera::command
int command(const char *param, const char *value)
PCLK_GPIO_NUM
#define PCLK_GPIO_NUM
Definition: pins.h:164
HREF_GPIO_NUM
#define HREF_GPIO_NUM
Definition: pins.h:163
Y2_GPIO_NUM
#define Y2_GPIO_NUM
Definition: pins.h:161
Y7_GPIO_NUM
#define Y7_GPIO_NUM
Definition: pins.h:156
CAMERA_MODEL_AI_THINKER
#define CAMERA_MODEL_AI_THINKER
Definition: defines.h:101
RESET_GPIO_NUM
#define RESET_GPIO_NUM
Definition: pins.h:150
CAM_PULLUP1
#define CAM_PULLUP1
Definition: pins.h:146
ESP_CAMERA_PORT
#define ESP_CAMERA_PORT
Definition: settings_esp3d.h:90
Camera::Camera
Camera()
CAMERA_MODEL_M5STACK_PSRAM
#define CAMERA_MODEL_M5STACK_PSRAM
Definition: defines.h:99
Camera::GetModelString
const char * GetModelString()
Camera::end
void end()
SIOD_GPIO_NUM
#define SIOD_GPIO_NUM
Definition: pins.h:152
NetConfig::getMode
static uint8_t getMode()
Definition: netconfig.cpp:207
Camera::connect
void connect(bool status)
Definition: camera.h:47
SIOC_GPIO_NUM
#define SIOC_GPIO_NUM
Definition: pins.h:153
CAMERA_MODEL_ESP_EYE
#define CAMERA_MODEL_ESP_EYE
Definition: defines.h:98
Y5_GPIO_NUM
#define Y5_GPIO_NUM
Definition: pins.h:158
CAM_PULLUP2
#define CAM_PULLUP2
Definition: pins.h:147
camera.h
log_esp3d
#define log_esp3d(format,...)
Definition: debug_esp3d.h:29
ESP_BT
#define ESP_BT
Definition: netconfig.h:43
Settings_ESP3D::read_uint32
static uint32_t read_uint32(int pos, bool *haserror=NULL)
Definition: settings_esp3d.cpp:919
Camera
Definition: camera.h:26
NetConfig::started
static bool started()
Definition: netconfig.h:74
ESP_ALL_CLIENTS
#define ESP_ALL_CLIENTS
Definition: esp3doutput.h:30
Y3_GPIO_NUM
#define Y3_GPIO_NUM
Definition: pins.h:160
Y9_GPIO_NUM
#define Y9_GPIO_NUM
Definition: pins.h:154
esp3d_camera
Camera esp3d_camera
Camera::initHardware
bool initHardware(bool forceinit=false)
Camera::handle
void handle()
PWDN_GPIO_NUM
#define PWDN_GPIO_NUM
Definition: pins.h:149
ESP3DOutput
Definition: esp3doutput.h:48
CAM_LED_PIN
#define CAM_LED_PIN
Definition: pins.h:145
Y4_GPIO_NUM
#define Y4_GPIO_NUM
Definition: pins.h:159