ClassControllCamera.cpp 31 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028
  1. #include "ClassControllCamera.h"
  2. #include "ClassLogFile.h"
  3. #include <stdio.h>
  4. #include "driver/gpio.h"
  5. #include "esp_timer.h"
  6. #include "esp_log.h"
  7. #include "Helper.h"
  8. #include "statusled.h"
  9. #include "CImageBasis.h"
  10. #include "server_ota.h"
  11. #include "server_GPIO.h"
  12. #include "../../include/defines.h"
  13. #include <esp_event.h>
  14. #include <esp_log.h>
  15. #include <esp_system.h>
  16. #include <nvs_flash.h>
  17. #include <sys/param.h>
  18. #include <string.h>
  19. #include <sys/stat.h>
  20. #include "freertos/FreeRTOS.h"
  21. #include "freertos/task.h"
  22. #include "esp_camera.h"
  23. #include "driver/ledc.h"
  24. #include "MainFlowControl.h"
  25. #if (ESP_IDF_VERSION_MAJOR >= 5)
  26. #include "soc/periph_defs.h"
  27. #include "esp_private/periph_ctrl.h"
  28. #include "soc/gpio_sig_map.h"
  29. #include "soc/gpio_periph.h"
  30. #include "soc/io_mux_reg.h"
  31. #include "esp_rom_gpio.h"
  32. #define gpio_pad_select_gpio esp_rom_gpio_pad_select_gpio
  33. #define gpio_matrix_in(a,b,c) esp_rom_gpio_connect_in_signal(a,b,c)
  34. #define gpio_matrix_out(a,b,c,d) esp_rom_gpio_connect_out_signal(a,b,c,d)
  35. #define ets_delay_us(a) esp_rom_delay_us(a)
  36. #endif
  37. static const char *TAG = "CAM";
  38. /* Camera live stream */
  39. #define PART_BOUNDARY "123456789000000000000987654321"
  40. static const char* _STREAM_CONTENT_TYPE = "multipart/x-mixed-replace;boundary=" PART_BOUNDARY;
  41. static const char* _STREAM_BOUNDARY = "\r\n--" PART_BOUNDARY "\r\n";
  42. static const char* _STREAM_PART = "Content-Type: image/jpeg\r\nContent-Length: %u\r\n\r\n";
  43. // OV Camera SDE Indirect Register Access
  44. #define OV_IRA_BPADDR 0x7C
  45. #define OV_IRA_BPDATA 0x7D
  46. static camera_config_t camera_config = {
  47. .pin_pwdn = CAM_PIN_PWDN,
  48. .pin_reset = CAM_PIN_RESET,
  49. .pin_xclk = CAM_PIN_XCLK,
  50. .pin_sscb_sda = CAM_PIN_SIOD,
  51. .pin_sscb_scl = CAM_PIN_SIOC,
  52. .pin_d7 = CAM_PIN_D7,
  53. .pin_d6 = CAM_PIN_D6,
  54. .pin_d5 = CAM_PIN_D5,
  55. .pin_d4 = CAM_PIN_D4,
  56. .pin_d3 = CAM_PIN_D3,
  57. .pin_d2 = CAM_PIN_D2,
  58. .pin_d1 = CAM_PIN_D1,
  59. .pin_d0 = CAM_PIN_D0,
  60. .pin_vsync = CAM_PIN_VSYNC,
  61. .pin_href = CAM_PIN_HREF,
  62. .pin_pclk = CAM_PIN_PCLK,
  63. //XCLK 20MHz or 10MHz for OV2640 double FPS (Experimental)
  64. .xclk_freq_hz = 20000000, // Orginal value
  65. // .xclk_freq_hz = 5000000, // Test to get rid of the image errors !!!! Hangs in version 9.2 !!!!
  66. .ledc_timer = LEDC_TIMER_0,
  67. .ledc_channel = LEDC_CHANNEL_0,
  68. .pixel_format = PIXFORMAT_JPEG, //YUV422,GRAYSCALE,RGB565,JPEG
  69. .frame_size = FRAMESIZE_VGA, //QQVGA-UXGA Do not use sizes above QVGA when not JPEG
  70. // .frame_size = FRAMESIZE_UXGA, //QQVGA-UXGA Do not use sizes above QVGA when not JPEG
  71. .jpeg_quality = 4, //0-63 lower number means higher quality
  72. .fb_count = 1, //if more than one, i2s runs in continuous mode. Use only with JPEG
  73. .fb_location = CAMERA_FB_IN_PSRAM, /*!< The location where the frame buffer will be allocated */
  74. .grab_mode = CAMERA_GRAB_LATEST, // only from new esp32cam version
  75. };
  76. CCamera Camera;
  77. uint8_t *demoImage = NULL; // Buffer holding the demo image in bytes
  78. #define DEMO_IMAGE_SIZE 30000 // Max size of demo image in bytes
  79. typedef struct {
  80. httpd_req_t *req;
  81. size_t len;
  82. } jpg_chunking_t;
  83. bool CCamera::testCamera(void) {
  84. bool success;
  85. camera_fb_t *fb = esp_camera_fb_get();
  86. if (fb) {
  87. success = true;
  88. }
  89. else {
  90. success = false;
  91. }
  92. esp_camera_fb_return(fb);
  93. return success;
  94. }
  95. void CCamera::ledc_init(void)
  96. {
  97. #ifdef USE_PWM_LEDFLASH
  98. // Prepare and then apply the LEDC PWM timer configuration
  99. ledc_timer_config_t ledc_timer = { };
  100. ledc_timer.speed_mode = LEDC_MODE;
  101. ledc_timer.timer_num = LEDC_TIMER;
  102. ledc_timer.duty_resolution = LEDC_DUTY_RES;
  103. ledc_timer.freq_hz = LEDC_FREQUENCY; // Set output frequency at 5 kHz
  104. ledc_timer.clk_cfg = LEDC_AUTO_CLK;
  105. ESP_ERROR_CHECK(ledc_timer_config(&ledc_timer));
  106. // Prepare and then apply the LEDC PWM channel configuration
  107. ledc_channel_config_t ledc_channel = { };
  108. ledc_channel.speed_mode = LEDC_MODE;
  109. ledc_channel.channel = LEDC_CHANNEL;
  110. ledc_channel.timer_sel = LEDC_TIMER;
  111. ledc_channel.intr_type = LEDC_INTR_DISABLE;
  112. ledc_channel.gpio_num = LEDC_OUTPUT_IO;
  113. ledc_channel.duty = 0; // Set duty to 0%
  114. ledc_channel.hpoint = 0;
  115. ESP_ERROR_CHECK(ledc_channel_config(&ledc_channel));
  116. #endif
  117. }
  118. static size_t jpg_encode_stream(void * arg, size_t index, const void* data, size_t len)
  119. {
  120. jpg_chunking_t *j = (jpg_chunking_t *)arg;
  121. if(!index) {
  122. j->len = 0;
  123. }
  124. if(httpd_resp_send_chunk(j->req, (const char *)data, len) != ESP_OK) {
  125. return 0;
  126. }
  127. j->len += len;
  128. return len;
  129. }
  130. bool CCamera::SetBrightnessContrastSaturation(int _brightness, int _contrast, int _saturation, int _autoExposureLevel, bool _grayscale)
  131. {
  132. _brightness = min(2, max(-2, _brightness));
  133. _contrast = min(2, max(-2, _contrast));
  134. _saturation = min(2, max(-2, _saturation));
  135. _autoExposureLevel = min(2, max(-2, _autoExposureLevel));
  136. sensor_t * s = esp_camera_sensor_get();
  137. if (s) {
  138. // auto exposure controls
  139. s->set_ae_level(s, _autoExposureLevel); // -2 to 2
  140. s->set_gainceiling(s, GAINCEILING_2X); // GAINCEILING_2X 4X 8X 16X 32X 64X 128X
  141. // post processing
  142. s->set_saturation(s, _saturation);
  143. s->set_contrast(s, _contrast);
  144. s->set_brightness(s, _brightness);
  145. /* Workaround - bug in cam library - enable bits are set without using bitwise OR logic -> only latest enable setting is used */
  146. /* Library version: https://github.com/espressif/esp32-camera/commit/5c8349f4cf169c8a61283e0da9b8cff10994d3f3 */
  147. /* Reference: https://esp32.com/viewtopic.php?f=19&t=14376#p93178 */
  148. /* The memory structure is as follows for
  149. byte_0 = enable_bits
  150. byte_0->bit0 = enable saturation and hue --> OK
  151. byte_0->bit1 = enable saturation --> OK
  152. byte_0->bit2 = enable brightness and contrast --> OK
  153. byte_0->bit3 = enable green -> blue spitial effect (Antique and blunish and greenish and readdish and b&w) enable
  154. byte_0->bit4 = anable gray -> read spitial effect (Antique and blunish and greenish and readdish and b&w) enable
  155. byte_0->bit5 = remove (UV) in YUV color system
  156. byte_0->bit6 = enable negative
  157. byte_0->bit7 = remove (Y) in YUV color system
  158. byte_1 = saturation1 0-255 --> ?
  159. byte_2 = hue 0-255 --> OK
  160. byte_3 = saturation2 0-255 --> OK
  161. byte_4 = reenter saturation2 in documents --> ?
  162. byte_5 = spital effect green -> blue 0-255 --> ?
  163. byte_6 = spital effect gray -> read 0-255 --> ?
  164. byte_7 = contrast lower byte 0-255 --> OK
  165. byte_8 = contrast higher byte 0-255 --> OK
  166. byte_9 = brightness 0-255 --> OK
  167. byte_10= if byte_10==4 contrast effective --> ?
  168. */
  169. //s->set_reg(s, 0x7C, 0xFF, 2); // Optional feature - hue setting: Select byte 2 in register 0x7C to set hue value
  170. //s->set_reg(s, 0x7D, 0xFF, 0); // Optional feature - hue setting: Hue value 0 - 255
  171. if (_grayscale) {
  172. // Indirect register access
  173. s->set_reg(s, 0xFF, 0x01, 0); // Select DSP bank
  174. s->set_reg(s, OV_IRA_BPADDR, 0xFF, 0x00); // Address 0x00
  175. s->set_reg(s, OV_IRA_BPDATA, 0xFF, 0x1F); // Set bit 0, 1, 2 to enable saturation, contrast, brightness and hue control
  176. s->set_reg(s, OV_IRA_BPADDR, 0xFF, 0x05); // Address 0x05
  177. s->set_reg(s, OV_IRA_BPDATA, 0xFF, 0x80);
  178. s->set_reg(s, OV_IRA_BPDATA, 0xFF, 0x80);
  179. } else {
  180. // Indirect register access
  181. s->set_reg(s, 0xFF, 0x01, 0); // Select DSP bank
  182. s->set_reg(s, OV_IRA_BPADDR, 0xFF, 0x00); // Address 0x00
  183. s->set_reg(s, OV_IRA_BPDATA, 0xFF, 7); // Set bit 0, 1, 2 to enable saturation, contrast, brightness and hue control
  184. s->set_reg(s, OV_IRA_BPADDR, 0xFF, 0x05); // Address 0x05
  185. s->set_reg(s, OV_IRA_BPDATA, 0xFF, 0x80);
  186. s->set_reg(s, OV_IRA_BPDATA, 0xFF, 0x80);
  187. }
  188. }
  189. else {
  190. LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "SetBrightnessContrastSaturation: Failed to get control structure");
  191. }
  192. if (((_brightness != brightness) || (_contrast != contrast) || (_saturation != saturation)) && isFixedExposure)
  193. EnableAutoExposure(waitbeforepicture_org);
  194. brightness = _brightness;
  195. contrast = _contrast;
  196. saturation = _saturation;
  197. autoExposureLevel = _autoExposureLevel;
  198. imageGrayscale = _grayscale;
  199. ESP_LOGD(TAG, "brightness %d, contrast: %d, saturation %d, autoExposureLevel %d, grayscale %d", brightness, contrast, saturation, autoExposureLevel, (int)imageGrayscale);
  200. return true;
  201. }
  202. /*
  203. * resolution = 0 \\ 1600 x 1200
  204. * resolution = 1 \\ 800 x 600
  205. * resolution = 2 \\ 400 x 296
  206. */
  207. void CCamera::SetCamWindow(sensor_t *s, int resolution, int xOffset, int yOffset, int xLength, int yLength)
  208. {
  209. s->set_res_raw(s, resolution, 0, 0, 0, xOffset, yOffset, xLength, yLength, xLength, yLength, false, false);
  210. }
  211. void CCamera::SetImageWidthHeightFromResolution(framesize_t resol)
  212. {
  213. if (resol == FRAMESIZE_QVGA)
  214. {
  215. image_height = 240;
  216. image_width = 320;
  217. }
  218. else if (resol == FRAMESIZE_VGA)
  219. {
  220. image_height = 480;
  221. image_width = 640;
  222. }
  223. else if (resol == FRAMESIZE_SVGA)
  224. {
  225. image_height = 600;
  226. image_width = 800;
  227. }
  228. else if (resol == FRAMESIZE_XGA)
  229. {
  230. image_height = 768;
  231. image_width = 1024;
  232. }
  233. else if (resol == FRAMESIZE_HD)
  234. {
  235. image_height = 720;
  236. image_width = 1280;
  237. }
  238. else if (resol == FRAMESIZE_SXGA)
  239. {
  240. image_height = 1024;
  241. image_width = 1280;
  242. }
  243. else if (resol == FRAMESIZE_UXGA)
  244. {
  245. image_height = 1200;
  246. image_width = 1600;
  247. }
  248. }
  249. void CCamera::SetZoom(bool zoomEnabled, int zoomMode, int zoomOffsetX, int zoomOffsetY)
  250. {
  251. imageZoomEnabled = zoomEnabled;
  252. imageZoomMode = zoomMode;
  253. imageZoomOffsetX = zoomOffsetX;
  254. imageZoomOffsetY = zoomOffsetY;
  255. sensor_t *s = esp_camera_sensor_get();
  256. if (s) {
  257. if (imageZoomEnabled) {
  258. int z = imageZoomMode;
  259. int x = imageZoomOffsetX;
  260. int y = imageZoomOffsetY;
  261. if (z > 1)
  262. z = 1;
  263. if (image_width >= 800 || image_height >= 600) {
  264. z = 0;
  265. }
  266. int maxX = 1600 - image_width;
  267. int maxY = 1200 - image_height;
  268. if (z == 1) {
  269. maxX = 800 - image_width;
  270. maxY = 600 - image_height;
  271. }
  272. if (x > maxX)
  273. x = maxX;
  274. if (y > maxY)
  275. y = maxY;
  276. SetCamWindow(s, z, x, y, image_width, image_height);
  277. } else {
  278. s->set_framesize(s, ActualResolution);
  279. }
  280. }
  281. }
  282. void CCamera::SetQualitySize(int qual, framesize_t resol, bool zoomEnabled, int zoomMode, int zoomOffsetX, int zoomOffsetY)
  283. {
  284. qual = min(63, max(8, qual)); // Limit quality from 8..63 (values lower than 8 tent to be unstable)
  285. ActualResolution = resol;
  286. ActualQuality = qual;
  287. imageZoomEnabled = zoomEnabled;
  288. imageZoomMode = zoomMode;
  289. imageZoomOffsetX = zoomOffsetX;
  290. imageZoomOffsetY = zoomOffsetY;
  291. SetImageWidthHeightFromResolution(resol);
  292. sensor_t * s = esp_camera_sensor_get();
  293. if (s) {
  294. s->set_quality(s, qual);
  295. SetZoom(zoomEnabled, zoomMode, zoomOffsetX, zoomOffsetY);
  296. }
  297. else {
  298. LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "SetQualitySize: Failed to get control structure");
  299. }
  300. }
  301. void CCamera::EnableAutoExposure(int flash_duration)
  302. {
  303. ESP_LOGD(TAG, "EnableAutoExposure");
  304. LEDOnOff(true);
  305. if (flash_duration > 0) {
  306. LightOnOff(true);
  307. const TickType_t xDelay = flash_duration / portTICK_PERIOD_MS;
  308. vTaskDelay( xDelay );
  309. }
  310. camera_fb_t * fb = esp_camera_fb_get();
  311. esp_camera_fb_return(fb);
  312. fb = esp_camera_fb_get();
  313. if (!fb) {
  314. LEDOnOff(false);
  315. LightOnOff(false);
  316. LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "EnableAutoExposure: Capture Failed. "
  317. "Check camera module and/or proper electrical connection");
  318. //doReboot();
  319. }
  320. esp_camera_fb_return(fb);
  321. sensor_t * s = esp_camera_sensor_get();
  322. if (s) {
  323. s->set_gain_ctrl(s, 0);
  324. s->set_exposure_ctrl(s, 0);
  325. }
  326. else {
  327. LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "EnableAutoExposure: Failed to get control structure to set gain+exposure");
  328. }
  329. LEDOnOff(false);
  330. LightOnOff(false);
  331. isFixedExposure = true;
  332. waitbeforepicture_org = flash_duration;
  333. }
  334. esp_err_t CCamera::CaptureToBasisImage(CImageBasis *_Image, int delay)
  335. {
  336. #ifdef DEBUG_DETAIL_ON
  337. LogFile.WriteHeapInfo("CaptureToBasisImage - Start");
  338. #endif
  339. _Image->EmptyImage(); //Delete previous stored raw image -> black image
  340. LEDOnOff(true);
  341. if (delay > 0) {
  342. LightOnOff(true);
  343. const TickType_t xDelay = delay / portTICK_PERIOD_MS;
  344. vTaskDelay( xDelay );
  345. }
  346. #ifdef DEBUG_DETAIL_ON
  347. LogFile.WriteHeapInfo("CaptureToBasisImage - After LightOn");
  348. #endif
  349. camera_fb_t * fb = esp_camera_fb_get();
  350. esp_camera_fb_return(fb);
  351. fb = esp_camera_fb_get();
  352. if (!fb) {
  353. LEDOnOff(false);
  354. LightOnOff(false);
  355. LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "is not working anymore (CaptureToBasisImage) - most probably caused "
  356. "by a hardware problem (instablility, ...). System will reboot.");
  357. doReboot();
  358. return ESP_FAIL;
  359. }
  360. if (demoMode) { // Use images stored on SD-Card instead of camera image
  361. /* Replace Framebuffer with image from SD-Card */
  362. loadNextDemoImage(fb);
  363. }
  364. CImageBasis* _zwImage = new CImageBasis("zwImage");
  365. if (_zwImage) {
  366. _zwImage->LoadFromMemory(fb->buf, fb->len);
  367. }
  368. else {
  369. LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "CaptureToBasisImage: Can't allocate _zwImage");
  370. }
  371. esp_camera_fb_return(fb);
  372. #ifdef DEBUG_DETAIL_ON
  373. LogFile.WriteHeapInfo("CaptureToBasisImage - After fb_get");
  374. #endif
  375. LEDOnOff(false);
  376. if (delay > 0)
  377. LightOnOff(false);
  378. // TickType_t xDelay = 1000 / portTICK_PERIOD_MS;
  379. // vTaskDelay( xDelay ); // wait for power to recover
  380. #ifdef DEBUG_DETAIL_ON
  381. LogFile.WriteHeapInfo("CaptureToBasisImage - After LoadFromMemory");
  382. #endif
  383. if (_zwImage == NULL) {
  384. return ESP_OK;
  385. }
  386. if (_zwImage->getWidth() > image_width || _zwImage->getHeight() > image_height) {
  387. int cropLeft = (_zwImage->getWidth() - image_width) / 2;
  388. if (cropLeft < 0)
  389. cropLeft = 0;
  390. int cropRight = _zwImage->getWidth() - cropLeft - image_width;
  391. if (cropRight < 0)
  392. cropRight = 0;
  393. int cropTop = (_zwImage->getHeight() - image_height) / 2;
  394. if (cropTop < 0)
  395. cropTop = 0;
  396. int cropBottom = _zwImage->getHeight() - cropTop - image_height;
  397. if (cropBottom < 0)
  398. cropBottom = 0;
  399. _zwImage->crop_image(cropLeft, cropRight, cropTop, cropBottom);
  400. }
  401. stbi_uc* p_target;
  402. stbi_uc* p_source;
  403. int channels = 3;
  404. int width = image_width;
  405. int height = image_height;
  406. #ifdef DEBUG_DETAIL_ON
  407. std::string _zw = "Targetimage: " + std::to_string((int) _Image->rgb_image) + " Size: " + std::to_string(_Image->width) + ", " + std::to_string(_Image->height);
  408. _zw = _zw + " _zwImage: " + std::to_string((int) _zwImage->rgb_image) + " Size: " + std::to_string(_zwImage->width) + ", " + std::to_string(_zwImage->height);
  409. LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, _zw);
  410. #endif
  411. for (int x = 0; x < width; ++x)
  412. for (int y = 0; y < height; ++y)
  413. {
  414. p_target = _Image->rgb_image + (channels * (y * width + x));
  415. p_source = _zwImage->rgb_image + (channels * (y * width + x));
  416. for (int c = 0; c < channels; c++) {
  417. p_target[c] = p_source[c];
  418. }
  419. }
  420. delete _zwImage;
  421. #ifdef DEBUG_DETAIL_ON
  422. LogFile.WriteHeapInfo("CaptureToBasisImage - Done");
  423. #endif
  424. return ESP_OK;
  425. }
  426. esp_err_t CCamera::CaptureToFile(std::string nm, int delay)
  427. {
  428. string ftype;
  429. LEDOnOff(true); // Switched off to save power !
  430. if (delay > 0) {
  431. LightOnOff(true);
  432. const TickType_t xDelay = delay / portTICK_PERIOD_MS;
  433. vTaskDelay( xDelay );
  434. }
  435. camera_fb_t * fb = esp_camera_fb_get();
  436. esp_camera_fb_return(fb);
  437. fb = esp_camera_fb_get();
  438. if (!fb) {
  439. LEDOnOff(false);
  440. LightOnOff(false);
  441. LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "CaptureToFile: Capture Failed. "
  442. "Check camera module and/or proper electrical connection");
  443. //doReboot();
  444. return ESP_FAIL;
  445. }
  446. LEDOnOff(false);
  447. #ifdef DEBUG_DETAIL_ON
  448. ESP_LOGD(TAG, "w %d, h %d, size %d", fb->width, fb->height, fb->len);
  449. #endif
  450. nm = FormatFileName(nm);
  451. #ifdef DEBUG_DETAIL_ON
  452. ESP_LOGD(TAG, "Save Camera to: %s", nm.c_str());
  453. #endif
  454. ftype = toUpper(getFileType(nm));
  455. #ifdef DEBUG_DETAIL_ON
  456. ESP_LOGD(TAG, "Filetype: %s", ftype.c_str());
  457. #endif
  458. uint8_t * buf = NULL;
  459. size_t buf_len = 0;
  460. bool converted = false;
  461. if (ftype.compare("BMP") == 0)
  462. {
  463. frame2bmp(fb, &buf, &buf_len);
  464. converted = true;
  465. }
  466. if (ftype.compare("JPG") == 0)
  467. {
  468. if(fb->format != PIXFORMAT_JPEG){
  469. bool jpeg_converted = frame2jpg(fb, ActualQuality, &buf, &buf_len);
  470. converted = true;
  471. if(!jpeg_converted){
  472. ESP_LOGE(TAG, "JPEG compression failed");
  473. }
  474. } else {
  475. buf_len = fb->len;
  476. buf = fb->buf;
  477. }
  478. }
  479. FILE * fp = fopen(nm.c_str(), "wb");
  480. if (fp == NULL) { // If an error occurs during the file creation
  481. LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "CaptureToFile: Failed to open file " + nm);
  482. }
  483. else {
  484. fwrite(buf, sizeof(uint8_t), buf_len, fp);
  485. fclose(fp);
  486. }
  487. if (converted)
  488. free(buf);
  489. esp_camera_fb_return(fb);
  490. if (delay > 0)
  491. LightOnOff(false);
  492. return ESP_OK;
  493. }
  494. esp_err_t CCamera::CaptureToHTTP(httpd_req_t *req, int delay)
  495. {
  496. esp_err_t res = ESP_OK;
  497. size_t fb_len = 0;
  498. int64_t fr_start = esp_timer_get_time();
  499. LEDOnOff(true);
  500. if (delay > 0) {
  501. LightOnOff(true);
  502. const TickType_t xDelay = delay / portTICK_PERIOD_MS;
  503. vTaskDelay( xDelay );
  504. }
  505. camera_fb_t *fb = esp_camera_fb_get();
  506. esp_camera_fb_return(fb);
  507. fb = esp_camera_fb_get();
  508. if (!fb) {
  509. LEDOnOff(false);
  510. LightOnOff(false);
  511. LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "CaptureToFile: Capture Failed. "
  512. "Check camera module and/or proper electrical connection");
  513. httpd_resp_send_500(req);
  514. // doReboot();
  515. return ESP_FAIL;
  516. }
  517. LEDOnOff(false);
  518. res = httpd_resp_set_type(req, "image/jpeg");
  519. if(res == ESP_OK){
  520. res = httpd_resp_set_hdr(req, "Content-Disposition", "inline; filename=raw.jpg");
  521. }
  522. if(res == ESP_OK){
  523. if (demoMode) { // Use images stored on SD-Card instead of camera image
  524. LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Using Demo image!");
  525. /* Replace Framebuffer with image from SD-Card */
  526. loadNextDemoImage(fb);
  527. res = httpd_resp_send(req, (const char *)fb->buf, fb->len);
  528. }
  529. else {
  530. if(fb->format == PIXFORMAT_JPEG){
  531. fb_len = fb->len;
  532. res = httpd_resp_send(req, (const char *)fb->buf, fb->len);
  533. } else {
  534. jpg_chunking_t jchunk = {req, 0};
  535. res = frame2jpg_cb(fb, 80, jpg_encode_stream, &jchunk)?ESP_OK:ESP_FAIL;
  536. httpd_resp_send_chunk(req, NULL, 0);
  537. fb_len = jchunk.len;
  538. }
  539. }
  540. }
  541. esp_camera_fb_return(fb);
  542. int64_t fr_end = esp_timer_get_time();
  543. ESP_LOGI(TAG, "JPG: %dKB %dms", (int)(fb_len/1024), (int)((fr_end - fr_start)/1000));
  544. if (delay > 0)
  545. LightOnOff(false);
  546. return res;
  547. }
  548. esp_err_t CCamera::CaptureToStream(httpd_req_t *req, bool FlashlightOn)
  549. {
  550. esp_err_t res = ESP_OK;
  551. size_t fb_len = 0;
  552. int64_t fr_start;
  553. char * part_buf[64];
  554. LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Live stream started");
  555. if (FlashlightOn) {
  556. LEDOnOff(true);
  557. LightOnOff(true);
  558. }
  559. //httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*"); //stream is blocking web interface, only serving to local
  560. httpd_resp_set_type(req, _STREAM_CONTENT_TYPE);
  561. httpd_resp_send_chunk(req, _STREAM_BOUNDARY, strlen(_STREAM_BOUNDARY));
  562. while(1)
  563. {
  564. fr_start = esp_timer_get_time();
  565. camera_fb_t *fb = esp_camera_fb_get();
  566. esp_camera_fb_return(fb);
  567. fb = esp_camera_fb_get();
  568. if (!fb) {
  569. LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "CaptureToStream: Camera framebuffer not available");
  570. break;
  571. }
  572. fb_len = fb->len;
  573. if (res == ESP_OK){
  574. size_t hlen = snprintf((char *)part_buf, sizeof(part_buf), _STREAM_PART, fb_len);
  575. res = httpd_resp_send_chunk(req, (const char *)part_buf, hlen);
  576. }
  577. if (res == ESP_OK){
  578. res = httpd_resp_send_chunk(req, (const char *)fb->buf, fb_len);
  579. }
  580. if (res == ESP_OK){
  581. res = httpd_resp_send_chunk(req, _STREAM_BOUNDARY, strlen(_STREAM_BOUNDARY));
  582. }
  583. esp_camera_fb_return(fb);
  584. int64_t fr_end = esp_timer_get_time();
  585. ESP_LOGD(TAG, "JPG: %dKB %dms", (int)(fb_len/1024), (int)((fr_end - fr_start)/1000));
  586. if (res != ESP_OK){ // Exit loop, e.g. also when closing the webpage
  587. break;
  588. }
  589. int64_t fr_delta_ms = (fr_end - fr_start) / 1000;
  590. if (CAM_LIVESTREAM_REFRESHRATE > fr_delta_ms) {
  591. const TickType_t xDelay = (CAM_LIVESTREAM_REFRESHRATE - fr_delta_ms) / portTICK_PERIOD_MS;
  592. ESP_LOGD(TAG, "Stream: sleep for: %ldms", (long) xDelay*10);
  593. vTaskDelay(xDelay);
  594. }
  595. }
  596. LEDOnOff(false);
  597. LightOnOff(false);
  598. LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Live stream stopped");
  599. return res;
  600. }
  601. void CCamera::LightOnOff(bool status)
  602. {
  603. GpioHandler* gpioHandler = gpio_handler_get();
  604. if ((gpioHandler != NULL) && (gpioHandler->isEnabled())) {
  605. ESP_LOGD(TAG, "Use gpioHandler to trigger flashlight");
  606. gpioHandler->flashLightEnable(status);
  607. }
  608. else {
  609. #ifdef USE_PWM_LEDFLASH
  610. if (status) {
  611. ESP_LOGD(TAG, "Internal Flash-LED turn on with PWM %d", led_intensity);
  612. ESP_ERROR_CHECK(ledc_set_duty(LEDC_MODE, LEDC_CHANNEL, led_intensity));
  613. // Update duty to apply the new value
  614. ESP_ERROR_CHECK(ledc_update_duty(LEDC_MODE, LEDC_CHANNEL));
  615. }
  616. else {
  617. ESP_LOGD(TAG, "Internal Flash-LED turn off PWM");
  618. ESP_ERROR_CHECK(ledc_set_duty(LEDC_MODE, LEDC_CHANNEL, 0));
  619. ESP_ERROR_CHECK(ledc_update_duty(LEDC_MODE, LEDC_CHANNEL));
  620. }
  621. #else
  622. // Init the GPIO
  623. gpio_pad_select_gpio(FLASH_GPIO);
  624. // Set the GPIO as a push/pull output
  625. gpio_set_direction(FLASH_GPIO, GPIO_MODE_OUTPUT);
  626. if (status)
  627. gpio_set_level(FLASH_GPIO, 1);
  628. else
  629. gpio_set_level(FLASH_GPIO, 0);
  630. #endif
  631. }
  632. }
  633. void CCamera::LEDOnOff(bool status)
  634. {
  635. if (xHandle_task_StatusLED == NULL) {
  636. // Init the GPIO
  637. gpio_pad_select_gpio(BLINK_GPIO);
  638. /* Set the GPIO as a push/pull output */
  639. gpio_set_direction(BLINK_GPIO, GPIO_MODE_OUTPUT);
  640. if (!status)
  641. gpio_set_level(BLINK_GPIO, 1);
  642. else
  643. gpio_set_level(BLINK_GPIO, 0);
  644. }
  645. }
  646. void CCamera::GetCameraParameter(httpd_req_t *req, int &qual, framesize_t &resol, bool &zoomEnabled, int &zoomMode, int &zoomOffsetX, int &zoomOffsetY)
  647. {
  648. char _query[100];
  649. char _value[10];
  650. resol = ActualResolution;
  651. qual = ActualQuality;
  652. zoomEnabled = imageZoomEnabled;
  653. zoomMode = imageZoomMode;
  654. zoomOffsetX = imageZoomOffsetX;
  655. zoomOffsetY = imageZoomOffsetY;
  656. if (httpd_req_get_url_query_str(req, _query, 100) == ESP_OK)
  657. {
  658. ESP_LOGD(TAG, "Query: %s", _query);
  659. if (httpd_query_key_value(_query, "size", _value, sizeof(_value)) == ESP_OK)
  660. {
  661. #ifdef DEBUG_DETAIL_ON
  662. ESP_LOGD(TAG, "Size: %s", _size);
  663. #endif
  664. if (strcmp(_value, "QVGA") == 0)
  665. resol = FRAMESIZE_QVGA; // 320x240
  666. else if (strcmp(_value, "VGA") == 0)
  667. resol = FRAMESIZE_VGA; // 640x480
  668. else if (strcmp(_value, "SVGA") == 0)
  669. resol = FRAMESIZE_SVGA; // 800x600
  670. else if (strcmp(_value, "XGA") == 0)
  671. resol = FRAMESIZE_XGA; // 1024x768
  672. else if (strcmp(_value, "SXGA") == 0)
  673. resol = FRAMESIZE_SXGA; // 1280x1024
  674. else if (strcmp(_value, "UXGA") == 0)
  675. resol = FRAMESIZE_UXGA; // 1600x1200
  676. }
  677. if (httpd_query_key_value(_query, "quality", _value, sizeof(_value)) == ESP_OK)
  678. {
  679. #ifdef DEBUG_DETAIL_ON
  680. ESP_LOGD(TAG, "Quality: %s", _qual);
  681. #endif
  682. qual = atoi(_value);
  683. if (qual > 63) // Limit to max. 63
  684. qual = 63;
  685. else if (qual < 8) // Limit to min. 8
  686. qual = 8;
  687. }
  688. if (httpd_query_key_value(_query, "z", _value, sizeof(_value)) == ESP_OK)
  689. {
  690. #ifdef DEBUG_DETAIL_ON
  691. ESP_LOGD(TAG, "Zoom: %s", _value);
  692. #endif
  693. if (atoi(_value) != 0)
  694. zoomEnabled = true;
  695. else
  696. zoomEnabled = false;
  697. }
  698. if (httpd_query_key_value(_query, "zm", _value, sizeof(_value)) == ESP_OK)
  699. {
  700. #ifdef DEBUG_DETAIL_ON
  701. ESP_LOGD(TAG, "Zoom mode: %s", _value);
  702. #endif
  703. zoomMode = atoi(_value);
  704. if (zoomMode > 2)
  705. zoomMode = 2;
  706. else if (zoomMode < 0)
  707. zoomMode = 0;
  708. }
  709. if (httpd_query_key_value(_query, "x", _value, sizeof(_value)) == ESP_OK)
  710. {
  711. #ifdef DEBUG_DETAIL_ON
  712. ESP_LOGD(TAG, "X offset: %s", _value);
  713. #endif
  714. zoomOffsetX = atoi(_value);
  715. if (zoomOffsetX < 0)
  716. zoomOffsetX = 0;
  717. }
  718. if (httpd_query_key_value(_query, "y", _value, sizeof(_value)) == ESP_OK)
  719. {
  720. #ifdef DEBUG_DETAIL_ON
  721. ESP_LOGD(TAG, "Y offset: %s", _value);
  722. #endif
  723. zoomOffsetY = atoi(_value);
  724. if (zoomOffsetY < 0)
  725. zoomOffsetY = 0;
  726. }
  727. }
  728. }
  729. framesize_t CCamera::TextToFramesize(const char * _size)
  730. {
  731. if (strcmp(_size, "QVGA") == 0)
  732. return FRAMESIZE_QVGA; // 320x240
  733. else if (strcmp(_size, "VGA") == 0)
  734. return FRAMESIZE_VGA; // 640x480
  735. else if (strcmp(_size, "SVGA") == 0)
  736. return FRAMESIZE_SVGA; // 800x600
  737. else if (strcmp(_size, "XGA") == 0)
  738. return FRAMESIZE_XGA; // 1024x768
  739. else if (strcmp(_size, "SXGA") == 0)
  740. return FRAMESIZE_SXGA; // 1280x1024
  741. else if (strcmp(_size, "UXGA") == 0)
  742. return FRAMESIZE_UXGA; // 1600x1200
  743. return ActualResolution;
  744. }
  745. CCamera::CCamera()
  746. {
  747. #ifdef DEBUG_DETAIL_ON
  748. ESP_LOGD(TAG, "CreateClassCamera");
  749. #endif
  750. brightness = 0;
  751. contrast = 0;
  752. saturation = 0;
  753. isFixedExposure = false;
  754. ledc_init();
  755. }
  756. esp_err_t CCamera::InitCam()
  757. {
  758. ESP_LOGD(TAG, "Init Camera");
  759. ActualQuality = camera_config.jpeg_quality;
  760. ActualResolution = camera_config.frame_size;
  761. //initialize the camera
  762. esp_camera_deinit(); // De-init in case it was already initialized
  763. esp_err_t err = esp_camera_init(&camera_config);
  764. if (err != ESP_OK) {
  765. ESP_LOGE(TAG, "Camera Init Failed");
  766. return err;
  767. }
  768. CameraInitSuccessful = true;
  769. return ESP_OK;
  770. }
  771. void CCamera::SetLEDIntensity(float _intrel)
  772. {
  773. _intrel = min(_intrel, (float) 100);
  774. _intrel = max(_intrel, (float) 0);
  775. _intrel = _intrel / 100;
  776. led_intensity = (int) (_intrel * 8191);
  777. ESP_LOGD(TAG, "Set led_intensity to %d of 8191", led_intensity);
  778. }
  779. bool CCamera::getCameraInitSuccessful()
  780. {
  781. return CameraInitSuccessful;
  782. }
  783. std::vector<std::string> demoFiles;
  784. void CCamera::useDemoMode()
  785. {
  786. char line[50];
  787. FILE *fd = fopen("/sdcard/demo/files.txt", "r");
  788. if (!fd) {
  789. LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Can not start Demo mode, the folder '/sdcard/demo/' does not contain the needed files!");
  790. LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "See Details on https://jomjol.github.io/AI-on-the-edge-device-docs/Demo-Mode!");
  791. return;
  792. }
  793. demoImage = (uint8_t*)malloc(DEMO_IMAGE_SIZE);
  794. if (demoImage == NULL) {
  795. LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Unable to acquire required memory for demo image!");
  796. return;
  797. }
  798. while (fgets(line, sizeof(line), fd) != NULL) {
  799. line[strlen(line) - 1] = '\0';
  800. demoFiles.push_back(line);
  801. }
  802. fclose(fd);
  803. LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Using Demo mode (" + std::to_string(demoFiles.size()) +
  804. " files) instead of real camera image!");
  805. for (auto file : demoFiles) {
  806. LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, file);
  807. }
  808. demoMode = true;
  809. }
  810. bool CCamera::loadNextDemoImage(camera_fb_t *fb) {
  811. char filename[50];
  812. int readBytes;
  813. long fileSize;
  814. snprintf(filename, sizeof(filename), "/sdcard/demo/%s", demoFiles[getCountFlowRounds() % demoFiles.size()].c_str());
  815. LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Using " + std::string(filename) + " as demo image");
  816. /* Inject saved image */
  817. FILE * fp = fopen(filename, "rb");
  818. if (!fp) {
  819. LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Failed to read file: " + std::string(filename) +"!");
  820. return false;
  821. }
  822. fileSize = GetFileSize(filename);
  823. if (fileSize > DEMO_IMAGE_SIZE) {
  824. char buf[100];
  825. snprintf(buf, sizeof(buf), "Demo Image (%d bytes) is larger than provided buffer (%d bytes)!",
  826. (int)fileSize, DEMO_IMAGE_SIZE);
  827. LogFile.WriteToFile(ESP_LOG_ERROR, TAG, std::string(buf));
  828. return false;
  829. }
  830. readBytes = fread(demoImage, 1, DEMO_IMAGE_SIZE, fp);
  831. LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "read " + std::to_string(readBytes) + " bytes");
  832. fclose(fp);
  833. fb->buf = demoImage; // Update pointer
  834. fb->len = readBytes;
  835. // ToDo do we also need to set height, width, format and timestamp?
  836. return true;
  837. }
  838. long CCamera::GetFileSize(std::string filename)
  839. {
  840. struct stat stat_buf;
  841. long rc = stat(filename.c_str(), &stat_buf);
  842. return rc == 0 ? stat_buf.st_size : -1;
  843. }