ClassControllCamera.cpp 30 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012
  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 = 12, //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, bool _negative, bool _aec2)
  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_aec2(s, _aec2 ? 1 : 0);
  140. s->set_ae_level(s, _autoExposureLevel); // -2 to 2
  141. s->set_gainceiling(s, GAINCEILING_2X); // GAINCEILING_2X 4X 8X 16X 32X 64X 128X
  142. // post processing
  143. s->set_saturation(s, _saturation);
  144. s->set_contrast(s, _contrast);
  145. s->set_brightness(s, _brightness);
  146. /* Workaround - bug in cam library - enable bits are set without using bitwise OR logic -> only latest enable setting is used */
  147. /* Library version: https://github.com/espressif/esp32-camera/commit/5c8349f4cf169c8a61283e0da9b8cff10994d3f3 */
  148. /* Reference: https://esp32.com/viewtopic.php?f=19&t=14376#p93178 */
  149. /* The memory structure is as follows for
  150. byte_0 = enable_bits
  151. byte_0->bit0 = enable saturation and hue --> OK
  152. byte_0->bit1 = enable saturation --> OK
  153. byte_0->bit2 = enable brightness and contrast --> OK
  154. byte_0->bit3 = enable green -> blue spitial effect (Antique and blunish and greenish and readdish and b&w) enable
  155. byte_0->bit4 = anable gray -> read spitial effect (Antique and blunish and greenish and readdish and b&w) enable
  156. byte_0->bit5 = remove (UV) in YUV color system
  157. byte_0->bit6 = enable negative
  158. byte_0->bit7 = remove (Y) in YUV color system
  159. byte_1 = saturation1 0-255 --> ?
  160. byte_2 = hue 0-255 --> OK
  161. byte_3 = saturation2 0-255 --> OK
  162. byte_4 = reenter saturation2 in documents --> ?
  163. byte_5 = spital effect green -> blue 0-255 --> ?
  164. byte_6 = spital effect gray -> read 0-255 --> ?
  165. byte_7 = contrast lower byte 0-255 --> OK
  166. byte_8 = contrast higher byte 0-255 --> OK
  167. byte_9 = brightness 0-255 --> OK
  168. byte_10= if byte_10==4 contrast effective --> ?
  169. */
  170. //s->set_reg(s, 0x7C, 0xFF, 2); // Optional feature - hue setting: Select byte 2 in register 0x7C to set hue value
  171. //s->set_reg(s, 0x7D, 0xFF, 0); // Optional feature - hue setting: Hue value 0 - 255
  172. int indirectReg0 = 0x07; // Set bit 0, 1, 2 to enable saturation, contrast, brightness and hue control
  173. if (_grayscale) {
  174. indirectReg0 |= 0x18;
  175. }
  176. if (_negative) {
  177. indirectReg0 |= 0x40;
  178. }
  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, indirectReg0);
  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. else {
  188. LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "SetBrightnessContrastSaturation: Failed to get control structure");
  189. }
  190. if (((_brightness != brightness) || (_contrast != contrast) || (_saturation != saturation)) && isFixedExposure)
  191. EnableAutoExposure(waitbeforepicture_org);
  192. brightness = _brightness;
  193. contrast = _contrast;
  194. saturation = _saturation;
  195. autoExposureLevel = _autoExposureLevel;
  196. imageGrayscale = _grayscale;
  197. imageNegative = _negative;
  198. imageAec2 = _aec2;
  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. stbi_uc* p_target;
  387. stbi_uc* p_source;
  388. int channels = 3;
  389. int width = image_width;
  390. int height = image_height;
  391. #ifdef DEBUG_DETAIL_ON
  392. std::string _zw = "Targetimage: " + std::to_string((int) _Image->rgb_image) + " Size: " + std::to_string(_Image->width) + ", " + std::to_string(_Image->height);
  393. _zw = _zw + " _zwImage: " + std::to_string((int) _zwImage->rgb_image) + " Size: " + std::to_string(_zwImage->width) + ", " + std::to_string(_zwImage->height);
  394. LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, _zw);
  395. #endif
  396. for (int x = 0; x < width; ++x)
  397. for (int y = 0; y < height; ++y)
  398. {
  399. p_target = _Image->rgb_image + (channels * (y * width + x));
  400. p_source = _zwImage->rgb_image + (channels * (y * width + x));
  401. for (int c = 0; c < channels; c++) {
  402. p_target[c] = p_source[c];
  403. }
  404. }
  405. delete _zwImage;
  406. #ifdef DEBUG_DETAIL_ON
  407. LogFile.WriteHeapInfo("CaptureToBasisImage - Done");
  408. #endif
  409. return ESP_OK;
  410. }
  411. esp_err_t CCamera::CaptureToFile(std::string nm, int delay)
  412. {
  413. string ftype;
  414. LEDOnOff(true); // Switched off to save power !
  415. if (delay > 0) {
  416. LightOnOff(true);
  417. const TickType_t xDelay = delay / portTICK_PERIOD_MS;
  418. vTaskDelay( xDelay );
  419. }
  420. camera_fb_t * fb = esp_camera_fb_get();
  421. esp_camera_fb_return(fb);
  422. fb = esp_camera_fb_get();
  423. if (!fb) {
  424. LEDOnOff(false);
  425. LightOnOff(false);
  426. LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "CaptureToFile: Capture Failed. "
  427. "Check camera module and/or proper electrical connection");
  428. //doReboot();
  429. return ESP_FAIL;
  430. }
  431. LEDOnOff(false);
  432. #ifdef DEBUG_DETAIL_ON
  433. ESP_LOGD(TAG, "w %d, h %d, size %d", fb->width, fb->height, fb->len);
  434. #endif
  435. nm = FormatFileName(nm);
  436. #ifdef DEBUG_DETAIL_ON
  437. ESP_LOGD(TAG, "Save Camera to: %s", nm.c_str());
  438. #endif
  439. ftype = toUpper(getFileType(nm));
  440. #ifdef DEBUG_DETAIL_ON
  441. ESP_LOGD(TAG, "Filetype: %s", ftype.c_str());
  442. #endif
  443. uint8_t * buf = NULL;
  444. size_t buf_len = 0;
  445. bool converted = false;
  446. if (ftype.compare("BMP") == 0)
  447. {
  448. frame2bmp(fb, &buf, &buf_len);
  449. converted = true;
  450. }
  451. if (ftype.compare("JPG") == 0)
  452. {
  453. if(fb->format != PIXFORMAT_JPEG){
  454. bool jpeg_converted = frame2jpg(fb, ActualQuality, &buf, &buf_len);
  455. converted = true;
  456. if(!jpeg_converted){
  457. ESP_LOGE(TAG, "JPEG compression failed");
  458. }
  459. } else {
  460. buf_len = fb->len;
  461. buf = fb->buf;
  462. }
  463. }
  464. FILE * fp = fopen(nm.c_str(), "wb");
  465. if (fp == NULL) { // If an error occurs during the file creation
  466. LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "CaptureToFile: Failed to open file " + nm);
  467. }
  468. else {
  469. fwrite(buf, sizeof(uint8_t), buf_len, fp);
  470. fclose(fp);
  471. }
  472. if (converted)
  473. free(buf);
  474. esp_camera_fb_return(fb);
  475. if (delay > 0)
  476. LightOnOff(false);
  477. return ESP_OK;
  478. }
  479. esp_err_t CCamera::CaptureToHTTP(httpd_req_t *req, int delay)
  480. {
  481. esp_err_t res = ESP_OK;
  482. size_t fb_len = 0;
  483. int64_t fr_start = esp_timer_get_time();
  484. LEDOnOff(true);
  485. if (delay > 0) {
  486. LightOnOff(true);
  487. const TickType_t xDelay = delay / portTICK_PERIOD_MS;
  488. vTaskDelay( xDelay );
  489. }
  490. camera_fb_t *fb = esp_camera_fb_get();
  491. esp_camera_fb_return(fb);
  492. fb = esp_camera_fb_get();
  493. if (!fb) {
  494. LEDOnOff(false);
  495. LightOnOff(false);
  496. LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "CaptureToFile: Capture Failed. "
  497. "Check camera module and/or proper electrical connection");
  498. httpd_resp_send_500(req);
  499. // doReboot();
  500. return ESP_FAIL;
  501. }
  502. LEDOnOff(false);
  503. res = httpd_resp_set_type(req, "image/jpeg");
  504. if(res == ESP_OK){
  505. res = httpd_resp_set_hdr(req, "Content-Disposition", "inline; filename=raw.jpg");
  506. }
  507. if(res == ESP_OK){
  508. if (demoMode) { // Use images stored on SD-Card instead of camera image
  509. LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Using Demo image!");
  510. /* Replace Framebuffer with image from SD-Card */
  511. loadNextDemoImage(fb);
  512. res = httpd_resp_send(req, (const char *)fb->buf, fb->len);
  513. }
  514. else {
  515. if(fb->format == PIXFORMAT_JPEG){
  516. fb_len = fb->len;
  517. res = httpd_resp_send(req, (const char *)fb->buf, fb->len);
  518. } else {
  519. jpg_chunking_t jchunk = {req, 0};
  520. res = frame2jpg_cb(fb, 80, jpg_encode_stream, &jchunk)?ESP_OK:ESP_FAIL;
  521. httpd_resp_send_chunk(req, NULL, 0);
  522. fb_len = jchunk.len;
  523. }
  524. }
  525. }
  526. esp_camera_fb_return(fb);
  527. int64_t fr_end = esp_timer_get_time();
  528. ESP_LOGI(TAG, "JPG: %dKB %dms", (int)(fb_len/1024), (int)((fr_end - fr_start)/1000));
  529. if (delay > 0)
  530. LightOnOff(false);
  531. return res;
  532. }
  533. esp_err_t CCamera::CaptureToStream(httpd_req_t *req, bool FlashlightOn)
  534. {
  535. esp_err_t res = ESP_OK;
  536. size_t fb_len = 0;
  537. int64_t fr_start;
  538. char * part_buf[64];
  539. LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Live stream started");
  540. if (FlashlightOn) {
  541. LEDOnOff(true);
  542. LightOnOff(true);
  543. }
  544. //httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*"); //stream is blocking web interface, only serving to local
  545. httpd_resp_set_type(req, _STREAM_CONTENT_TYPE);
  546. httpd_resp_send_chunk(req, _STREAM_BOUNDARY, strlen(_STREAM_BOUNDARY));
  547. while(1)
  548. {
  549. fr_start = esp_timer_get_time();
  550. camera_fb_t *fb = esp_camera_fb_get();
  551. esp_camera_fb_return(fb);
  552. fb = esp_camera_fb_get();
  553. if (!fb) {
  554. LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "CaptureToStream: Camera framebuffer not available");
  555. break;
  556. }
  557. fb_len = fb->len;
  558. if (res == ESP_OK){
  559. size_t hlen = snprintf((char *)part_buf, sizeof(part_buf), _STREAM_PART, fb_len);
  560. res = httpd_resp_send_chunk(req, (const char *)part_buf, hlen);
  561. }
  562. if (res == ESP_OK){
  563. res = httpd_resp_send_chunk(req, (const char *)fb->buf, fb_len);
  564. }
  565. if (res == ESP_OK){
  566. res = httpd_resp_send_chunk(req, _STREAM_BOUNDARY, strlen(_STREAM_BOUNDARY));
  567. }
  568. esp_camera_fb_return(fb);
  569. int64_t fr_end = esp_timer_get_time();
  570. ESP_LOGD(TAG, "JPG: %dKB %dms", (int)(fb_len/1024), (int)((fr_end - fr_start)/1000));
  571. if (res != ESP_OK){ // Exit loop, e.g. also when closing the webpage
  572. break;
  573. }
  574. int64_t fr_delta_ms = (fr_end - fr_start) / 1000;
  575. if (CAM_LIVESTREAM_REFRESHRATE > fr_delta_ms) {
  576. const TickType_t xDelay = (CAM_LIVESTREAM_REFRESHRATE - fr_delta_ms) / portTICK_PERIOD_MS;
  577. ESP_LOGD(TAG, "Stream: sleep for: %ldms", (long) xDelay*10);
  578. vTaskDelay(xDelay);
  579. }
  580. }
  581. LEDOnOff(false);
  582. LightOnOff(false);
  583. LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Live stream stopped");
  584. return res;
  585. }
  586. void CCamera::LightOnOff(bool status)
  587. {
  588. GpioHandler* gpioHandler = gpio_handler_get();
  589. if ((gpioHandler != NULL) && (gpioHandler->isEnabled())) {
  590. ESP_LOGD(TAG, "Use gpioHandler to trigger flashlight");
  591. gpioHandler->flashLightEnable(status);
  592. }
  593. else {
  594. #ifdef USE_PWM_LEDFLASH
  595. if (status) {
  596. ESP_LOGD(TAG, "Internal Flash-LED turn on with PWM %d", led_intensity);
  597. ESP_ERROR_CHECK(ledc_set_duty(LEDC_MODE, LEDC_CHANNEL, led_intensity));
  598. // Update duty to apply the new value
  599. ESP_ERROR_CHECK(ledc_update_duty(LEDC_MODE, LEDC_CHANNEL));
  600. }
  601. else {
  602. ESP_LOGD(TAG, "Internal Flash-LED turn off PWM");
  603. ESP_ERROR_CHECK(ledc_set_duty(LEDC_MODE, LEDC_CHANNEL, 0));
  604. ESP_ERROR_CHECK(ledc_update_duty(LEDC_MODE, LEDC_CHANNEL));
  605. }
  606. #else
  607. // Init the GPIO
  608. gpio_pad_select_gpio(FLASH_GPIO);
  609. // Set the GPIO as a push/pull output
  610. gpio_set_direction(FLASH_GPIO, GPIO_MODE_OUTPUT);
  611. if (status)
  612. gpio_set_level(FLASH_GPIO, 1);
  613. else
  614. gpio_set_level(FLASH_GPIO, 0);
  615. #endif
  616. }
  617. }
  618. void CCamera::LEDOnOff(bool status)
  619. {
  620. if (xHandle_task_StatusLED == NULL) {
  621. // Init the GPIO
  622. gpio_pad_select_gpio(BLINK_GPIO);
  623. /* Set the GPIO as a push/pull output */
  624. gpio_set_direction(BLINK_GPIO, GPIO_MODE_OUTPUT);
  625. if (!status)
  626. gpio_set_level(BLINK_GPIO, 1);
  627. else
  628. gpio_set_level(BLINK_GPIO, 0);
  629. }
  630. }
  631. void CCamera::GetCameraParameter(httpd_req_t *req, int &qual, framesize_t &resol, bool &zoomEnabled, int &zoomMode, int &zoomOffsetX, int &zoomOffsetY)
  632. {
  633. char _query[100];
  634. char _value[10];
  635. resol = ActualResolution;
  636. qual = ActualQuality;
  637. zoomEnabled = imageZoomEnabled;
  638. zoomMode = imageZoomMode;
  639. zoomOffsetX = imageZoomOffsetX;
  640. zoomOffsetY = imageZoomOffsetY;
  641. if (httpd_req_get_url_query_str(req, _query, 100) == ESP_OK)
  642. {
  643. ESP_LOGD(TAG, "Query: %s", _query);
  644. if (httpd_query_key_value(_query, "size", _value, sizeof(_value)) == ESP_OK)
  645. {
  646. #ifdef DEBUG_DETAIL_ON
  647. ESP_LOGD(TAG, "Size: %s", _size);
  648. #endif
  649. if (strcmp(_value, "QVGA") == 0)
  650. resol = FRAMESIZE_QVGA; // 320x240
  651. else if (strcmp(_value, "VGA") == 0)
  652. resol = FRAMESIZE_VGA; // 640x480
  653. else if (strcmp(_value, "SVGA") == 0)
  654. resol = FRAMESIZE_SVGA; // 800x600
  655. else if (strcmp(_value, "XGA") == 0)
  656. resol = FRAMESIZE_XGA; // 1024x768
  657. else if (strcmp(_value, "SXGA") == 0)
  658. resol = FRAMESIZE_SXGA; // 1280x1024
  659. else if (strcmp(_value, "UXGA") == 0)
  660. resol = FRAMESIZE_UXGA; // 1600x1200
  661. }
  662. if (httpd_query_key_value(_query, "quality", _value, sizeof(_value)) == ESP_OK)
  663. {
  664. #ifdef DEBUG_DETAIL_ON
  665. ESP_LOGD(TAG, "Quality: %s", _qual);
  666. #endif
  667. qual = atoi(_value);
  668. if (qual > 63) // Limit to max. 63
  669. qual = 63;
  670. else if (qual < 8) // Limit to min. 8
  671. qual = 8;
  672. }
  673. if (httpd_query_key_value(_query, "z", _value, sizeof(_value)) == ESP_OK)
  674. {
  675. #ifdef DEBUG_DETAIL_ON
  676. ESP_LOGD(TAG, "Zoom: %s", _value);
  677. #endif
  678. if (atoi(_value) != 0)
  679. zoomEnabled = true;
  680. else
  681. zoomEnabled = false;
  682. }
  683. if (httpd_query_key_value(_query, "zm", _value, sizeof(_value)) == ESP_OK)
  684. {
  685. #ifdef DEBUG_DETAIL_ON
  686. ESP_LOGD(TAG, "Zoom mode: %s", _value);
  687. #endif
  688. zoomMode = atoi(_value);
  689. if (zoomMode > 2)
  690. zoomMode = 2;
  691. else if (zoomMode < 0)
  692. zoomMode = 0;
  693. }
  694. if (httpd_query_key_value(_query, "x", _value, sizeof(_value)) == ESP_OK)
  695. {
  696. #ifdef DEBUG_DETAIL_ON
  697. ESP_LOGD(TAG, "X offset: %s", _value);
  698. #endif
  699. zoomOffsetX = atoi(_value);
  700. if (zoomOffsetX < 0)
  701. zoomOffsetX = 0;
  702. }
  703. if (httpd_query_key_value(_query, "y", _value, sizeof(_value)) == ESP_OK)
  704. {
  705. #ifdef DEBUG_DETAIL_ON
  706. ESP_LOGD(TAG, "Y offset: %s", _value);
  707. #endif
  708. zoomOffsetY = atoi(_value);
  709. if (zoomOffsetY < 0)
  710. zoomOffsetY = 0;
  711. }
  712. }
  713. }
  714. framesize_t CCamera::TextToFramesize(const char * _size)
  715. {
  716. if (strcmp(_size, "QVGA") == 0)
  717. return FRAMESIZE_QVGA; // 320x240
  718. else if (strcmp(_size, "VGA") == 0)
  719. return FRAMESIZE_VGA; // 640x480
  720. else if (strcmp(_size, "SVGA") == 0)
  721. return FRAMESIZE_SVGA; // 800x600
  722. else if (strcmp(_size, "XGA") == 0)
  723. return FRAMESIZE_XGA; // 1024x768
  724. else if (strcmp(_size, "SXGA") == 0)
  725. return FRAMESIZE_SXGA; // 1280x1024
  726. else if (strcmp(_size, "UXGA") == 0)
  727. return FRAMESIZE_UXGA; // 1600x1200
  728. return ActualResolution;
  729. }
  730. CCamera::CCamera()
  731. {
  732. #ifdef DEBUG_DETAIL_ON
  733. ESP_LOGD(TAG, "CreateClassCamera");
  734. #endif
  735. brightness = 0;
  736. contrast = 0;
  737. saturation = 0;
  738. isFixedExposure = false;
  739. ledc_init();
  740. }
  741. esp_err_t CCamera::InitCam()
  742. {
  743. ESP_LOGD(TAG, "Init Camera");
  744. ActualQuality = camera_config.jpeg_quality;
  745. ActualResolution = camera_config.frame_size;
  746. //initialize the camera
  747. esp_camera_deinit(); // De-init in case it was already initialized
  748. esp_err_t err = esp_camera_init(&camera_config);
  749. if (err != ESP_OK) {
  750. ESP_LOGE(TAG, "Camera Init Failed");
  751. return err;
  752. }
  753. CameraInitSuccessful = true;
  754. return ESP_OK;
  755. }
  756. void CCamera::SetLEDIntensity(float _intrel)
  757. {
  758. _intrel = min(_intrel, (float) 100);
  759. _intrel = max(_intrel, (float) 0);
  760. _intrel = _intrel / 100;
  761. led_intensity = (int) (_intrel * 8191);
  762. ESP_LOGD(TAG, "Set led_intensity to %d of 8191", led_intensity);
  763. }
  764. bool CCamera::getCameraInitSuccessful()
  765. {
  766. return CameraInitSuccessful;
  767. }
  768. std::vector<std::string> demoFiles;
  769. void CCamera::useDemoMode()
  770. {
  771. char line[50];
  772. FILE *fd = fopen("/sdcard/demo/files.txt", "r");
  773. if (!fd) {
  774. LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Can not start Demo mode, the folder '/sdcard/demo/' does not contain the needed files!");
  775. LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "See Details on https://jomjol.github.io/AI-on-the-edge-device-docs/Demo-Mode!");
  776. return;
  777. }
  778. demoImage = (uint8_t*)malloc(DEMO_IMAGE_SIZE);
  779. if (demoImage == NULL) {
  780. LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Unable to acquire required memory for demo image!");
  781. return;
  782. }
  783. while (fgets(line, sizeof(line), fd) != NULL) {
  784. line[strlen(line) - 1] = '\0';
  785. demoFiles.push_back(line);
  786. }
  787. fclose(fd);
  788. LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Using Demo mode (" + std::to_string(demoFiles.size()) +
  789. " files) instead of real camera image!");
  790. for (auto file : demoFiles) {
  791. LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, file);
  792. }
  793. demoMode = true;
  794. }
  795. bool CCamera::loadNextDemoImage(camera_fb_t *fb) {
  796. char filename[50];
  797. int readBytes;
  798. long fileSize;
  799. snprintf(filename, sizeof(filename), "/sdcard/demo/%s", demoFiles[getCountFlowRounds() % demoFiles.size()].c_str());
  800. LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Using " + std::string(filename) + " as demo image");
  801. /* Inject saved image */
  802. FILE * fp = fopen(filename, "rb");
  803. if (!fp) {
  804. LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Failed to read file: " + std::string(filename) +"!");
  805. return false;
  806. }
  807. fileSize = GetFileSize(filename);
  808. if (fileSize > DEMO_IMAGE_SIZE) {
  809. char buf[100];
  810. snprintf(buf, sizeof(buf), "Demo Image (%d bytes) is larger than provided buffer (%d bytes)!",
  811. (int)fileSize, DEMO_IMAGE_SIZE);
  812. LogFile.WriteToFile(ESP_LOG_ERROR, TAG, std::string(buf));
  813. return false;
  814. }
  815. readBytes = fread(demoImage, 1, DEMO_IMAGE_SIZE, fp);
  816. LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "read " + std::to_string(readBytes) + " bytes");
  817. fclose(fp);
  818. fb->buf = demoImage; // Update pointer
  819. fb->len = readBytes;
  820. // ToDo do we also need to set height, width, format and timestamp?
  821. return true;
  822. }
  823. long CCamera::GetFileSize(std::string filename)
  824. {
  825. struct stat stat_buf;
  826. long rc = stat(filename.c_str(), &stat_buf);
  827. return rc == 0 ? stat_buf.st_size : -1;
  828. }