From d410420110d0e804d66678c876d94c2ee2caa398 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Sun, 3 May 2020 18:37:12 +0200 Subject: [PATCH] Tasmotify ESP32 webcam --- tasmota/support_tasmota.ino | 13 +- tasmota/tasmota_template_ESP32.h | 3 +- tasmota/xdrv_39_webcam.ino | 447 ++++++++++++++++--------------- 3 files changed, 246 insertions(+), 217 deletions(-) diff --git a/tasmota/support_tasmota.ino b/tasmota/support_tasmota.ino index c335bd9cb..e789d0cc9 100644 --- a/tasmota/support_tasmota.ino +++ b/tasmota/support_tasmota.ino @@ -1441,14 +1441,9 @@ void GpioInit(void) #ifdef ESP8266 if ((2 == Pin(GPIO_TXD)) || (H801 == my_module_type)) { Serial.set_tx(2); } -#endif // ESP8266 -#ifdef ESP8266 analogWriteRange(Settings.pwm_range); // Default is 1023 (Arduino.h) analogWriteFreq(Settings.pwm_frequency); // Default is 1000 (core_esp8266_wiring_pwm.c) -#else - analogWriteFreqRange(0,Settings.pwm_frequency,Settings.pwm_range); -#endif #ifdef USE_SPI spi_flg = (((PinUsed(GPIO_SPI_CS) && (Pin(GPIO_SPI_CS) > 14)) || (Pin(GPIO_SPI_CS) < 12)) || ((PinUsed(GPIO_SPI_DC) && (Pin(GPIO_SPI_DC) > 14)) || (Pin(GPIO_SPI_DC) < 12))); @@ -1462,6 +1457,14 @@ void GpioInit(void) } soft_spi_flg = (PinUsed(GPIO_SSPI_CS) && PinUsed(GPIO_SSPI_SCLK) && (PinUsed(GPIO_SSPI_MOSI) || PinUsed(GPIO_SSPI_MISO))); #endif // USE_SPI +#else // ESP32 + analogWriteFreqRange(0, Settings.pwm_frequency, Settings.pwm_range); + +#ifdef USE_SPI + spi_flg = (PinUsed(GPIO_SPI_CLK) && (PinUsed(GPIO_SPI_MOSI) || PinUsed(GPIO_SPI_MISO))); + soft_spi_flg = (PinUsed(GPIO_SSPI_SCLK) && (PinUsed(GPIO_SSPI_MOSI) || PinUsed(GPIO_SSPI_MISO))); +#endif // USE_SPI +#endif // ESP8266 - ESP32 // Set any non-used GPIO to INPUT - Related to resetPins() in support_legacy_cores.ino // Doing it here solves relay toggles at restart. diff --git a/tasmota/tasmota_template_ESP32.h b/tasmota/tasmota_template_ESP32.h index 2a356a46a..80f4b56dd 100644 --- a/tasmota/tasmota_template_ESP32.h +++ b/tasmota/tasmota_template_ESP32.h @@ -197,8 +197,7 @@ enum UserSelectablePins { ADC0_BUTTON_INV, ADC0_RANGE, // Range ADC0_CT_POWER, // Current - // webcam interface - GPIO_WEBCAM_PWDN_GPIO_NUM, + GPIO_WEBCAM_PWDN_GPIO_NUM, // Webcam interface GPIO_WEBCAM_RESET_GPIO_NUM, GPIO_WEBCAM_XCLK_GPIO_NUM, GPIO_WEBCAM_SIOD_GPIO_NUM, diff --git a/tasmota/xdrv_39_webcam.ino b/tasmota/xdrv_39_webcam.ino index a223ddfa6..ffda441ee 100644 --- a/tasmota/xdrv_39_webcam.ino +++ b/tasmota/xdrv_39_webcam.ino @@ -1,5 +1,45 @@ +/* + xdrv_39_webcam.ino - ESP32 webcam support for Tasmota -#if defined(ESP32) && defined(USE_WEBCAM) + Copyright (C) 2020 Gerhard Mutz and Theo Arends + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifdef ESP32 +#ifdef USE_WEBCAM +/*********************************************************************************************\ + * ESP32 webcam based on example in Arduino-ESP32 library + * + * Template as used on ESP32-CAM WiFi + bluetooth Camera Module Development Board ESP32 With Camera Module OV2640 Geekcreit for Arduino + * {"NAME":"AITHINKER CAM No SPI","GPIO":[4992,65504,65504,65504,5472,5312,65504,65504,5504,5536,65504,65504,5568,5440,5280,5248,0,5216,5408,5376,0,5344,5024,5056,0,0,0,0,4928,65504,5120,5088,5184,0,0,5152],"FLAG":0,"BASE":1} + * Template with SPI configured. This needs define USE_SPI + * {"NAME":"AITHINKER CAM","GPIO":[4992,65504,672,65504,5472,5312,65504,65504,5504,5536,736,704,5568,5440,5280,5248,0,5216,5408,5376,0,5344,5024,5056,0,0,0,0,4928,65504,5120,5088,5184,0,0,5152],"FLAG":0,"BASE":1} + * + * Command: Webcam + * 0 = Stop streaming + * 1 = FRAMESIZE_QQVGA2 (128x160) + * 2 = FRAMESIZE_QCIF (176x144) + * 3 = FRAMESIZE_HQVGA (240x176) + * 4 = FRAMESIZE_QVGA (320x240) + * 5 = FRAMESIZE_CIF (400x296) + * 6 = FRAMESIZE_VGA (640x480) + * 7 = FRAMESIZE_SVGA (800x600) + * 8 = FRAMESIZE_XGA (1024x768) + * 9 = FRAMESIZE_SXGA (1280x1024) + * 10 = FRAMESIZE_UXGA (1600x1200) +\*********************************************************************************************/ #define XDRV_39 39 @@ -38,13 +78,11 @@ uint16_t wc_height; uint8_t wc_stream_active; uint32_t wc_setup(int32_t fsiz) { -bool psram; + if (fsiz > 10) { fsiz = 10; } - if (fsiz>10) fsiz=10; + wc_stream_active = 0; - wc_stream_active=0; - - if (fsiz<0) { + if (fsiz < 0) { esp_camera_deinit(); return 0; } @@ -56,7 +94,7 @@ bool psram; //esp_log_level_set("*", ESP_LOG_VERBOSE); -camera_config_t config; + camera_config_t config; config.ledc_channel = LEDC_CHANNEL_0; config.ledc_timer = LEDC_TIMER_0; config.xclk_freq_hz = 20000000; @@ -65,55 +103,6 @@ camera_config_t config; // config.pixel_format = PIXFORMAT_RGB565; #ifndef USE_TEMPLATE - -config.pin_d0 = Y2_GPIO_NUM; -config.pin_d1 = Y3_GPIO_NUM; -config.pin_d2 = Y4_GPIO_NUM; -config.pin_d3 = Y5_GPIO_NUM; -config.pin_d4 = Y6_GPIO_NUM; -config.pin_d5 = Y7_GPIO_NUM; -config.pin_d6 = Y8_GPIO_NUM; -config.pin_d7 = Y9_GPIO_NUM; -config.pin_xclk = XCLK_GPIO_NUM; -config.pin_pclk = PCLK_GPIO_NUM; -config.pin_vsync = VSYNC_GPIO_NUM; -config.pin_href = HREF_GPIO_NUM; -config.pin_sscb_sda = SIOD_GPIO_NUM; -config.pin_sscb_scl = SIOC_GPIO_NUM; -config.pin_pwdn = PWDN_GPIO_NUM; -config.pin_reset = RESET_GPIO_NUM; - -#else - -if (PinUsed(GPIO_WEBCAM_Y2_GPIO_NUM) && PinUsed(GPIO_WEBCAM_Y3_GPIO_NUM) && PinUsed(GPIO_WEBCAM_Y4_GPIO_NUM) && PinUsed(GPIO_WEBCAM_Y5_GPIO_NUM)\ - && PinUsed(GPIO_WEBCAM_Y6_GPIO_NUM) && PinUsed(GPIO_WEBCAM_Y7_GPIO_NUM) && PinUsed(GPIO_WEBCAM_Y8_GPIO_NUM) && PinUsed(GPIO_WEBCAM_Y9_GPIO_NUM)\ - && PinUsed(GPIO_WEBCAM_XCLK_GPIO_NUM) && PinUsed(GPIO_WEBCAM_PCLK_GPIO_NUM) && PinUsed(GPIO_WEBCAM_VSYNC_GPIO_NUM) && PinUsed(GPIO_WEBCAM_HREF_GPIO_NUM)\ - && PinUsed(GPIO_WEBCAM_SIOD_GPIO_NUM) && PinUsed(GPIO_WEBCAM_SIOC_GPIO_NUM)) { - - config.pin_d0 = Pin(GPIO_WEBCAM_Y2_GPIO_NUM); //Y2_GPIO_NUM; - config.pin_d1 = Pin(GPIO_WEBCAM_Y3_GPIO_NUM); //Y3_GPIO_NUM; - config.pin_d2 = Pin(GPIO_WEBCAM_Y4_GPIO_NUM); //Y4_GPIO_NUM; - config.pin_d3 = Pin(GPIO_WEBCAM_Y5_GPIO_NUM); //Y5_GPIO_NUM; - config.pin_d4 = Pin(GPIO_WEBCAM_Y6_GPIO_NUM); //Y6_GPIO_NUM; - config.pin_d5 = Pin(GPIO_WEBCAM_Y7_GPIO_NUM); //Y7_GPIO_NUM; - config.pin_d6 = Pin(GPIO_WEBCAM_Y8_GPIO_NUM); //Y8_GPIO_NUM; - config.pin_d7 = Pin(GPIO_WEBCAM_Y9_GPIO_NUM); //Y9_GPIO_NUM; - config.pin_xclk = Pin(GPIO_WEBCAM_XCLK_GPIO_NUM); //XCLK_GPIO_NUM; - config.pin_pclk = Pin(GPIO_WEBCAM_PCLK_GPIO_NUM); //PCLK_GPIO_NUM; - config.pin_vsync = Pin(GPIO_WEBCAM_VSYNC_GPIO_NUM); //VSYNC_GPIO_NUM; - config.pin_href = Pin(GPIO_WEBCAM_HREF_GPIO_NUM); //HREF_GPIO_NUM; - config.pin_sscb_sda = Pin(GPIO_WEBCAM_SIOD_GPIO_NUM); //SIOD_GPIO_NUM; - config.pin_sscb_scl = Pin(GPIO_WEBCAM_SIOC_GPIO_NUM); //SIOC_GPIO_NUM; - - int16_t xpin; - xpin=Pin(GPIO_WEBCAM_PWDN_GPIO_NUM); - if (xpin==99) xpin=-1; - config.pin_pwdn = xpin; //PWDN_GPIO_NUM; - xpin=Pin(GPIO_WEBCAM_RESET_GPIO_NUM); - if (xpin==99) xpin=-1; - config.pin_reset = xpin; //RESET_GPIO_NUM; -} else { - // defaults to AI THINKER config.pin_d0 = Y2_GPIO_NUM; config.pin_d1 = Y3_GPIO_NUM; config.pin_d2 = Y4_GPIO_NUM; @@ -130,9 +119,51 @@ if (PinUsed(GPIO_WEBCAM_Y2_GPIO_NUM) && PinUsed(GPIO_WEBCAM_Y3_GPIO_NUM) && PinU config.pin_sscb_scl = SIOC_GPIO_NUM; config.pin_pwdn = PWDN_GPIO_NUM; config.pin_reset = RESET_GPIO_NUM; -} - - +#else + if (PinUsed(GPIO_WEBCAM_Y2_GPIO_NUM) && PinUsed(GPIO_WEBCAM_Y3_GPIO_NUM) && PinUsed(GPIO_WEBCAM_Y4_GPIO_NUM) && PinUsed(GPIO_WEBCAM_Y5_GPIO_NUM)\ + && PinUsed(GPIO_WEBCAM_Y6_GPIO_NUM) && PinUsed(GPIO_WEBCAM_Y7_GPIO_NUM) && PinUsed(GPIO_WEBCAM_Y8_GPIO_NUM) && PinUsed(GPIO_WEBCAM_Y9_GPIO_NUM)\ + && PinUsed(GPIO_WEBCAM_XCLK_GPIO_NUM) && PinUsed(GPIO_WEBCAM_PCLK_GPIO_NUM) && PinUsed(GPIO_WEBCAM_VSYNC_GPIO_NUM) && PinUsed(GPIO_WEBCAM_HREF_GPIO_NUM)\ + && PinUsed(GPIO_WEBCAM_SIOD_GPIO_NUM) && PinUsed(GPIO_WEBCAM_SIOC_GPIO_NUM)) { + config.pin_d0 = Pin(GPIO_WEBCAM_Y2_GPIO_NUM); //Y2_GPIO_NUM; + config.pin_d1 = Pin(GPIO_WEBCAM_Y3_GPIO_NUM); //Y3_GPIO_NUM; + config.pin_d2 = Pin(GPIO_WEBCAM_Y4_GPIO_NUM); //Y4_GPIO_NUM; + config.pin_d3 = Pin(GPIO_WEBCAM_Y5_GPIO_NUM); //Y5_GPIO_NUM; + config.pin_d4 = Pin(GPIO_WEBCAM_Y6_GPIO_NUM); //Y6_GPIO_NUM; + config.pin_d5 = Pin(GPIO_WEBCAM_Y7_GPIO_NUM); //Y7_GPIO_NUM; + config.pin_d6 = Pin(GPIO_WEBCAM_Y8_GPIO_NUM); //Y8_GPIO_NUM; + config.pin_d7 = Pin(GPIO_WEBCAM_Y9_GPIO_NUM); //Y9_GPIO_NUM; + config.pin_xclk = Pin(GPIO_WEBCAM_XCLK_GPIO_NUM); //XCLK_GPIO_NUM; + config.pin_pclk = Pin(GPIO_WEBCAM_PCLK_GPIO_NUM); //PCLK_GPIO_NUM; + config.pin_vsync = Pin(GPIO_WEBCAM_VSYNC_GPIO_NUM); //VSYNC_GPIO_NUM; + config.pin_href = Pin(GPIO_WEBCAM_HREF_GPIO_NUM); //HREF_GPIO_NUM; + config.pin_sscb_sda = Pin(GPIO_WEBCAM_SIOD_GPIO_NUM); //SIOD_GPIO_NUM; + config.pin_sscb_scl = Pin(GPIO_WEBCAM_SIOC_GPIO_NUM); //SIOC_GPIO_NUM; + int16_t xpin; + xpin = Pin(GPIO_WEBCAM_PWDN_GPIO_NUM); + if (99 == xpin) { xpin = -1; } + config.pin_pwdn = xpin; //PWDN_GPIO_NUM; + xpin = Pin(GPIO_WEBCAM_RESET_GPIO_NUM); + if (99 == xpin) { xpin=-1; } + config.pin_reset = xpin; //RESET_GPIO_NUM; + } else { + // defaults to AI THINKER + config.pin_d0 = Y2_GPIO_NUM; + config.pin_d1 = Y3_GPIO_NUM; + config.pin_d2 = Y4_GPIO_NUM; + config.pin_d3 = Y5_GPIO_NUM; + config.pin_d4 = Y6_GPIO_NUM; + config.pin_d5 = Y7_GPIO_NUM; + config.pin_d6 = Y8_GPIO_NUM; + config.pin_d7 = Y9_GPIO_NUM; + config.pin_xclk = XCLK_GPIO_NUM; + config.pin_pclk = PCLK_GPIO_NUM; + config.pin_vsync = VSYNC_GPIO_NUM; + config.pin_href = HREF_GPIO_NUM; + config.pin_sscb_sda = SIOD_GPIO_NUM; + config.pin_sscb_scl = SIOC_GPIO_NUM; + config.pin_pwdn = PWDN_GPIO_NUM; + config.pin_reset = RESET_GPIO_NUM; + } #endif //ESP.getPsramSize() @@ -143,17 +174,17 @@ if (PinUsed(GPIO_WEBCAM_Y2_GPIO_NUM) && PinUsed(GPIO_WEBCAM_Y3_GPIO_NUM) && PinU // if PSRAM IC present, init with UXGA resolution and higher JPEG quality // for larger pre-allocated frame buffer. - psram=psramFound(); + bool psram = psramFound(); if (psram) { config.frame_size = FRAMESIZE_UXGA; config.jpeg_quality = 10; config.fb_count = 2; - AddLog_P(WC_LOGLEVEL,"PSRAM found!"); + AddLog_P2(WC_LOGLEVEL, PSTR("CAM: PSRAM found")); } else { config.frame_size = FRAMESIZE_VGA; config.jpeg_quality = 12; config.fb_count = 1; - AddLog_P(WC_LOGLEVEL,"PSRAM not found!"); + AddLog_P2(WC_LOGLEVEL, PSTR("CAM: PSRAM not found")); } // stupid workaround camera diver eats up static ram should prefer PSRAM @@ -161,75 +192,70 @@ if (PinUsed(GPIO_WEBCAM_Y2_GPIO_NUM) && PinUsed(GPIO_WEBCAM_Y3_GPIO_NUM) && PinU //ESP.getMaxAllocHeap() // void *x=malloc(70000); -void *x=0; - + void *x = 0; esp_err_t err = esp_camera_init(&config); - - if (x) free(x); + if (x) { free(x); } if (err != ESP_OK) { - AddLog_P2(WC_LOGLEVEL,"Camera init failed with error 0x%x", err); + AddLog_P2(WC_LOGLEVEL, PSTR("CAM: Init failed with error 0x%x"), err); return 0; } sensor_t * wc_s = esp_camera_sensor_get(); // initial sensors are flipped vertically and colors are a bit saturated - if (wc_s->id.PID == OV3660_PID) { - wc_s->set_vflip(wc_s, 1); // flip it back - wc_s->set_brightness(wc_s, 1); // up the brightness just a bit - wc_s->set_saturation(wc_s, -2); // lower the saturation + if (OV3660_PID == wc_s->id.PID) { + wc_s->set_vflip(wc_s, 1); // flip it back + wc_s->set_brightness(wc_s, 1); // up the brightness just a bit + wc_s->set_saturation(wc_s, -2); // lower the saturation } // drop down frame size for higher initial frame rate wc_s->set_framesize(wc_s, (framesize_t)fsiz); camera_fb_t *wc_fb = esp_camera_fb_get(); - wc_width=wc_fb->width; - wc_height=wc_fb->height; + wc_width = wc_fb->width; + wc_height = wc_fb->height; esp_camera_fb_return(wc_fb); - AddLog_P(WC_LOGLEVEL,"Camera successfully initialized!"); + AddLog_P2(WC_LOGLEVEL, PSTR("CAM: Initialized")); - wc_up=1; + wc_up = 1; + if (psram) { wc_up=2; } - if (psram) { - wc_up=2; - } return wc_up; } - -int32_t wc_set_options(uint32_t sel,int32_t value) { - int32_t res=0; +int32_t wc_set_options(uint32_t sel, int32_t value) { + int32_t res = 0; sensor_t *s = esp_camera_sensor_get(); - if (!s) return -99; + if (!s) { return -99; } switch (sel) { case 0: - if (value>=0) s->set_framesize(s,(framesize_t)value); + if (value >= 0) { s->set_framesize(s, (framesize_t)value); } res = s->status.framesize; break; case 1: - if (value>=0) s->set_special_effect(s,value); + if (value >= 0) { s->set_special_effect(s, value); } res = s->status.special_effect; break; case 2: - if (value>=0) s->set_vflip(s,value); + if (value >= 0) { s->set_vflip(s, value); } res = s->status.vflip; break; case 3: - if (value>=0) s->set_hmirror(s,value); + if (value >= 0) { s->set_hmirror(s, value); } res = s->status.hmirror; break; case 4: - if (value>=-4) s->set_contrast(s,value); + if (value >= -4) { s->set_contrast(s, value); } res = s->status.contrast; break; case 5: - if (value>=-4) s->set_brightness(s,value); + if (value >= -4) { s->set_brightness(s, value); } res = s->status.brightness; break; case 6: - if (value>=-4) s->set_saturation(s,value); + if (value >= -4) { s->set_saturation(s,value); } res = s->status.saturation; break; } @@ -239,16 +265,16 @@ int32_t wc_set_options(uint32_t sel,int32_t value) { uint32_t wc_get_width(void) { camera_fb_t *wc_fb = esp_camera_fb_get(); - if (!wc_fb) return 0; - wc_width=wc_fb->width; + if (!wc_fb) { return 0; } + wc_width = wc_fb->width; esp_camera_fb_return(wc_fb); return wc_width; } uint32_t wc_get_height(void) { camera_fb_t *wc_fb = esp_camera_fb_get(); - if (!wc_fb) return 0; - wc_height=wc_fb->height; + if (!wc_fb) { return 0; } + wc_height = wc_fb->height; esp_camera_fb_return(wc_fb); return wc_height; } @@ -268,61 +294,60 @@ struct PICSTORE tmp_picstore; #endif uint32_t get_picstore(int32_t num, uint8_t **buff) { - if (num<0) return MAX_PICSTORE; - *buff=picstore[num].buff; + if (num<0) { return MAX_PICSTORE; } + *buff = picstore[num].buff; return picstore[num].len; } uint32_t wc_get_jpeg(uint8_t **buff) { -size_t _jpg_buf_len = 0; -uint8_t * _jpg_buf = NULL; -camera_fb_t *wc_fb; + size_t _jpg_buf_len = 0; + uint8_t * _jpg_buf = NULL; + camera_fb_t *wc_fb; wc_fb = esp_camera_fb_get(); - if (!wc_fb) return 0; - if (wc_fb->format!=PIXFORMAT_JPEG) { + if (!wc_fb) { return 0; } + if (wc_fb->format != PIXFORMAT_JPEG) { bool jpeg_converted = frame2jpg(wc_fb, 80, &_jpg_buf, &_jpg_buf_len); - if (!jpeg_converted){ - _jpg_buf_len = wc_fb->len; - _jpg_buf = wc_fb->buf; + if (!jpeg_converted) { + _jpg_buf_len = wc_fb->len; + _jpg_buf = wc_fb->buf; } } else { _jpg_buf_len = wc_fb->len; _jpg_buf = wc_fb->buf; } esp_camera_fb_return(wc_fb); - *buff=_jpg_buf; + *buff = _jpg_buf; return _jpg_buf_len; } - uint32_t wc_get_frame(int32_t bnum) { size_t _jpg_buf_len = 0; uint8_t * _jpg_buf = NULL; - camera_fb_t *wc_fb=0; - bool jpeg_converted=false; + camera_fb_t *wc_fb = 0; + bool jpeg_converted = false; - if (bnum<0) { - if (bnum<-MAX_PICSTORE) bnum=-1; - bnum=-bnum; + if (bnum < 0) { + if (bnum < -MAX_PICSTORE) { bnum=-1; } + bnum = -bnum; bnum--; - if (picstore[bnum].buff) free(picstore[bnum].buff); - picstore[bnum].len=0; + if (picstore[bnum].buff) { free(picstore[bnum].buff); } + picstore[bnum].len = 0; return 0; } #ifdef COPYFRAME - if (bnum&0x10) { - bnum&=0xf; - _jpg_buf=tmp_picstore.buff; - _jpg_buf_len=tmp_picstore.len; - if (!_jpg_buf_len) return 0; + if (bnum & 0x10) { + bnum &= 0xf; + _jpg_buf = tmp_picstore.buff; + _jpg_buf_len = tmp_picstore.len; + if (!_jpg_buf_len) { return 0; } goto pcopy; } #endif wc_fb = esp_camera_fb_get(); if (!wc_fb) { - AddLog_P(WC_LOGLEVEL, "cant get frame"); + AddLog_P2(WC_LOGLEVEL, PSTR("CAM: Can't get frame")); return 0; } if (!bnum) { @@ -332,12 +357,12 @@ uint32_t wc_get_frame(int32_t bnum) { return 0; } - if (wc_fb->format!=PIXFORMAT_JPEG) { + if (wc_fb->format != PIXFORMAT_JPEG) { jpeg_converted = frame2jpg(wc_fb, 80, &_jpg_buf, &_jpg_buf_len); if (!jpeg_converted){ - //Serial.println("JPEG compression failed"); - _jpg_buf_len = wc_fb->len; - _jpg_buf = wc_fb->buf; + //Serial.println("JPEG compression failed"); + _jpg_buf_len = wc_fb->len; + _jpg_buf = wc_fb->buf; } } else { _jpg_buf_len = wc_fb->len; @@ -345,20 +370,21 @@ uint32_t wc_get_frame(int32_t bnum) { } pcopy: - if (bnum<1 || bnum>MAX_PICSTORE) bnum=1; + if ((bnum < 1) || (bnum > MAX_PICSTORE)) { bnum = 1; } bnum--; - if (picstore[bnum].buff) free(picstore[bnum].buff); - picstore[bnum].buff = (uint8_t *)heap_caps_malloc(_jpg_buf_len+4,MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); + if (picstore[bnum].buff) { free(picstore[bnum].buff); } + picstore[bnum].buff = (uint8_t *)heap_caps_malloc(_jpg_buf_len+4, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); if (picstore[bnum].buff) { - memcpy(picstore[bnum].buff,_jpg_buf,_jpg_buf_len); - picstore[bnum].len=_jpg_buf_len; + memcpy(picstore[bnum].buff, _jpg_buf, _jpg_buf_len); + picstore[bnum].len = _jpg_buf_len; } else { - AddLog_P(WC_LOGLEVEL, "cant allocate picstore"); - picstore[bnum].len=0; + AddLog_P2(WC_LOGLEVEL, PSTR("CAM: Can't allocate picstore")); + picstore[bnum].len = 0; } - if (wc_fb) esp_camera_fb_return(wc_fb); - if (jpeg_converted) free(_jpg_buf); - if (!picstore[bnum].buff) return 0; + if (wc_fb) { esp_camera_fb_return(wc_fb); } + if (jpeg_converted) { free(_jpg_buf); } + if (!picstore[bnum].buff) { return 0; } + return _jpg_buf_len; } @@ -369,7 +395,7 @@ void HandleImage(void) { if (!HttpCheckPriviledgedAccess(true)) { return; } uint32_t bnum = Webserver->arg(F("p")).toInt(); - if (bnum<0 || bnum>MAX_PICSTORE) bnum=1; + if ((bnum < 0) || (bnum > MAX_PICSTORE)) { bnum= 1; } WiFiClient client = Webserver->client(); String response = "HTTP/1.1 200 OK\r\n"; response += "Content-disposition: inline; filename=cap.jpg\r\n"; @@ -379,7 +405,7 @@ void HandleImage(void) { if (!bnum) { uint8_t *buff; uint32_t len; - len=wc_get_jpeg(&buff); + len = wc_get_jpeg(&buff); if (len) { client.write(buff,len); free(buff); @@ -387,14 +413,13 @@ void HandleImage(void) { } else { bnum--; if (!picstore[bnum].len) { - AddLog_P2(WC_LOGLEVEL, PSTR("no image #: %d"), bnum); + AddLog_P2(WC_LOGLEVEL, PSTR("CAM: No image #: %d"), bnum); return; } client.write((char *)picstore[bnum].buff, picstore[bnum].len); } - AddLog_P2(WC_LOGLEVEL, PSTR("sending image #: %d"), bnum+1); - + AddLog_P2(WC_LOGLEVEL, PSTR("CAM: Sending image #: %d"), bnum+1); } ESP8266WebServer *CamServer; @@ -403,17 +428,14 @@ ESP8266WebServer *CamServer; WiFiClient client; void handleMjpeg(void) { - AddLog_P(WC_LOGLEVEL, "handle camserver"); + AddLog_P2(WC_LOGLEVEL, PSTR("CAM: Handle camserver")); //if (!wc_stream_active) { - wc_stream_active=1; + wc_stream_active = 1; client = CamServer->client(); - AddLog_P(WC_LOGLEVEL, "create client"); + AddLog_P2(WC_LOGLEVEL, PSTR("CAM: Create client")); //} } - - - void handleMjpeg_task(void) { camera_fb_t *wc_fb; size_t _jpg_buf_len = 0; @@ -421,35 +443,34 @@ void handleMjpeg_task(void) { //WiFiClient client = CamServer->client(); uint32_t tlen; - bool jpeg_converted=false; + bool jpeg_converted = false; if (!client.connected()) { - wc_stream_active=0; - AddLog_P(WC_LOGLEVEL,"client fail"); + wc_stream_active = 0; + AddLog_P2(WC_LOGLEVEL, PSTR("CAM: Client fail")); goto exit; } - if (wc_stream_active==1) { + if (1 == wc_stream_active) { client.flush(); client.setTimeout(3); - AddLog_P(WC_LOGLEVEL, "start stream"); + AddLog_P2(WC_LOGLEVEL, PSTR("CAM: Start stream")); client.print("HTTP/1.1 200 OK\r\n" - "Content-Type: multipart/x-mixed-replace;boundary=" BOUNDARY "\r\n" - "\r\n"); - wc_stream_active=2; + "Content-Type: multipart/x-mixed-replace;boundary=" BOUNDARY "\r\n" + "\r\n"); + wc_stream_active = 2; } else { - wc_fb = esp_camera_fb_get(); if (!wc_fb) { - wc_stream_active=0; - AddLog_P(WC_LOGLEVEL, "frame fail"); + wc_stream_active = 0; + AddLog_P2(WC_LOGLEVEL, PSTR("CAM: Frame fail")); goto exit; } - if (wc_fb->format!=PIXFORMAT_JPEG) { + if (wc_fb->format != PIXFORMAT_JPEG) { jpeg_converted = frame2jpg(wc_fb, 80, &_jpg_buf, &_jpg_buf_len); if (!jpeg_converted){ - AddLog_P(WC_LOGLEVEL, "JPEG compression failed"); + AddLog_P2(WC_LOGLEVEL, PSTR("CAM: JPEG compression failed")); _jpg_buf_len = wc_fb->len; _jpg_buf = wc_fb->buf; } @@ -460,34 +481,34 @@ void handleMjpeg_task(void) { client.printf("Content-Type: image/jpeg\r\n" "Content-Length: %d\r\n" - "\r\n", static_cast(_jpg_buf_len)); - tlen=client.write(_jpg_buf, _jpg_buf_len); + "\r\n", static_cast(_jpg_buf_len)); + tlen = client.write(_jpg_buf, _jpg_buf_len); /* if (tlen!=_jpg_buf_len) { esp_camera_fb_return(wc_fb); wc_stream_active=0; - AddLog_P(WC_LOGLEVEL, "send fail"); + AddLog_P2(WC_LOGLEVEL, PSTR("CAM: Send fail")); }*/ client.print("\r\n--" BOUNDARY "\r\n"); #ifdef COPYFRAME - if (tmp_picstore.buff) free(tmp_picstore.buff); - tmp_picstore.buff = (uint8_t *)heap_caps_malloc(_jpg_buf_len+4,MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); + if (tmp_picstore.buff) { free(tmp_picstore.buff); } + tmp_picstore.buff = (uint8_t *)heap_caps_malloc(_jpg_buf_len+4, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); if (tmp_picstore.buff) { - memcpy(tmp_picstore.buff,_jpg_buf,_jpg_buf_len); - tmp_picstore.len=_jpg_buf_len; + memcpy(tmp_picstore.buff, _jpg_buf, _jpg_buf_len); + tmp_picstore.len = _jpg_buf_len; } else { - tmp_picstore.len=0; + tmp_picstore.len = 0; } #endif - if (jpeg_converted) free(_jpg_buf); + if (jpeg_converted) { free(_jpg_buf); } esp_camera_fb_return(wc_fb); - //AddLog_P(WC_LOGLEVEL, "send frame"); + //AddLog_P2(WC_LOGLEVEL, PSTR("CAM: send frame")); exit: if (!wc_stream_active) { - AddLog_P(WC_LOGLEVEL, "stream exit"); + AddLog_P2(WC_LOGLEVEL, PSTR("CAM: Stream exit")); client.flush(); client.stop(); } @@ -508,8 +529,8 @@ uint32_t motion_brightness; uint8_t *last_motion_buffer; uint32_t wc_set_motion_detect(int32_t value) { - if (value>=0) motion_detect=value; - if (value==-1) { + if (value >= 0) { motion_detect=value; } + if (-1 == value) { return motion_trigger; } else { return motion_brightness; @@ -518,41 +539,41 @@ uint32_t wc_set_motion_detect(int32_t value) { // optional motion detector void detect_motion(void) { -camera_fb_t *wc_fb; -uint8_t *out_buf=0; + camera_fb_t *wc_fb; + uint8_t *out_buf=0; - if ((millis()-motion_ltime)>motion_detect) { - motion_ltime=millis(); + if ((millis()-motion_ltime) > motion_detect) { + motion_ltime = millis(); wc_fb = esp_camera_fb_get(); - if (!wc_fb) return; + if (!wc_fb) { return; } if (!last_motion_buffer) { - last_motion_buffer=(uint8_t *)heap_caps_malloc((wc_fb->width*wc_fb->height)+4,MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); + last_motion_buffer=(uint8_t *)heap_caps_malloc((wc_fb->width*wc_fb->height)+4, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); } if (last_motion_buffer) { - if (wc_fb->format==PIXFORMAT_JPEG) { - out_buf=(uint8_t *)heap_caps_malloc((wc_fb->width*wc_fb->height*3)+4,MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); + if (PIXFORMAT_JPEG == wc_fb->format) { + out_buf = (uint8_t *)heap_caps_malloc((wc_fb->width*wc_fb->height*3)+4, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); if (out_buf) { fmt2rgb888(wc_fb->buf, wc_fb->len, wc_fb->format, out_buf); - uint32_t x,y; - uint8_t *pxi=out_buf; - uint8_t *pxr=last_motion_buffer; + uint32_t x, y; + uint8_t *pxi = out_buf; + uint8_t *pxr = last_motion_buffer; // convert to bw - uint64_t accu=0; - uint64_t bright=0; - for (y=0;yheight;y++) { - for (x=0;xwidth;x++) { - int32_t gray=(pxi[0]+pxi[1]+pxi[2])/3; - int32_t lgray=pxr[0]; - pxr[0]=gray; - pxi+=3; + uint64_t accu = 0; + uint64_t bright = 0; + for (y = 0; y < wc_fb->height; y++) { + for (x = 0; x < wc_fb->width; x++) { + int32_t gray = (pxi[0] + pxi[1] + pxi[2]) / 3; + int32_t lgray = pxr[0]; + pxr[0] = gray; + pxi += 3; pxr++; - accu+=abs(gray-lgray); - bright+=gray; + accu += abs(gray - lgray); + bright += gray; } } - motion_trigger=accu/((wc_fb->height*wc_fb->width)/100); - motion_brightness=bright/((wc_fb->height*wc_fb->width)/100); + motion_trigger = accu / ((wc_fb->height * wc_fb->width) / 100); + motion_brightness = bright / ((wc_fb->height * wc_fb->width) / 100); free(out_buf); } } @@ -563,16 +584,17 @@ uint8_t *out_buf=0; void wc_show_stream(void) { #ifndef USE_SCRIPT - if (CamServer) WSContentSend_P("

webcam stream",WiFi.localIP().toString().c_str()); + if (CamServer) { + WSContentSend_P(PSTR("

Webcam stream


"), + WiFi.localIP().toString().c_str()); + } #endif } - uint32_t wc_set_streamserver(uint32_t flag) { + if (global_state.wifi_down) { return 0; } - if (global_state.wifi_down) return 0; - - wc_stream_active=0; + wc_stream_active = 0; if (flag) { if (!CamServer) { @@ -581,24 +603,24 @@ uint32_t wc_set_streamserver(uint32_t flag) { CamServer->on("/cam.mjpeg", handleMjpeg); CamServer->on("/cam.jpg", handleMjpeg); CamServer->on("/stream", handleMjpeg); - AddLog_P(WC_LOGLEVEL, "cam stream init"); + AddLog_P2(WC_LOGLEVEL, PSTR("CAM: Stream init")); CamServer->begin(); } } else { if (CamServer) { CamServer->stop(); delete CamServer; - CamServer=NULL; - AddLog_P(WC_LOGLEVEL, "cam stream exit"); + CamServer = NULL; + AddLog_P2(WC_LOGLEVEL, PSTR("CAM: Stream exit")); } } return 0; } void wc_loop(void) { - if (CamServer) CamServer->handleClient(); - if (wc_stream_active) handleMjpeg_task(); - if (motion_detect) detect_motion(); + if (CamServer) { CamServer->handleClient(); } + if (wc_stream_active) { handleMjpeg_task(); } + if (motion_detect) { detect_motion(); } } void wc_pic_setup(void) { @@ -634,9 +656,10 @@ red led = gpio 33 */ /*********************************************************************************************\ - * Interface + * Commands \*********************************************************************************************/ -#define D_CMND_WC "webcam" + +#define D_CMND_WC "Webcam" const char kWCCommands[] PROGMEM = "|" // no prefix D_CMND_WC @@ -647,15 +670,18 @@ void (* const WCCommand[])(void) PROGMEM = { }; void CmndWC(void) { - uint8_t flag=0; - if (XdrvMailbox.data_len > 0) { + uint32_t flag = 0; + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 10)) { wc_set_streamserver(XdrvMailbox.payload); wc_setup(flag); } - if (CamServer) flag=1; - Response_P(PSTR("{\"" D_CMND_WC "\":{\"streaming\":%d}"),flag); + if (CamServer) { flag = 1; } + Response_P(PSTR("{\"" D_CMND_WC "\":{\"Streaming\":\"%s\"}"),GetStateText(flag)); } +/*********************************************************************************************\ + * Interface +\*********************************************************************************************/ bool Xdrv39(uint8_t function) { bool result = false; @@ -677,4 +703,5 @@ bool Xdrv39(uint8_t function) { return result; } -#endif +#endif // USE_WEBCAM +#endif // ESP32