ClassControllCamera.cpp 31 KB

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