2020-05-03 17:37:12 +01:00
/*
2020-05-07 15:53:56 +01:00
xdrv_81_webcam . ino - ESP32 webcam support for Tasmota
2020-05-03 17:37:12 +01:00
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 .
2020-05-02 07:10:23 +01:00
2020-05-03 17:37:12 +01:00
You should have received a copy of the GNU General Public License
along with this program . If not , see < http : //www.gnu.org/licenses/>.
*/
# 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
2020-05-05 17:36:12 +01:00
* { " NAME " : " AITHINKER CAM No SPI " , " GPIO " : [ 4992 , 65504 , 65504 , 65504 , 65504 , 5088 , 65504 , 65504 , 65504 , 65504 , 65504 , 65504 , 65504 , 65504 , 5089 , 5090 , 0 , 5091 , 5184 , 5152 , 0 , 5120 , 5024 , 5056 , 0 , 0 , 0 , 0 , 4928 , 65504 , 5094 , 5095 , 5092 , 0 , 0 , 5093 ] , " FLAG " : 0 , " BASE " : 1 }
2020-05-03 17:37:12 +01:00
*
2020-05-12 13:30:07 +01:00
* Supported commands :
* WcStream = Control streaming , 0 = stop , 1 = start
* WcResolution = Set resolution
* 0 = FRAMESIZE_96x96 , ( 96 x96 )
* 1 = FRAMESIZE_QQVGA2 ( 128 x160 )
* 2 = FRAMESIZE_QCIF ( 176 x144 )
* 3 = FRAMESIZE_HQVGA ( 240 x176 )
* 4 = FRAMESIZE_QVGA ( 320 x240 )
* 5 = FRAMESIZE_CIF ( 400 x296 )
* 6 = FRAMESIZE_VGA ( 640 x480 )
* 7 = FRAMESIZE_SVGA ( 800 x600 )
* 8 = FRAMESIZE_XGA ( 1024 x768 )
* 9 = FRAMESIZE_SXGA ( 1280 x1024 )
* 10 = FRAMESIZE_UXGA ( 1600 x1200 )
* WcMirror = Mirror picture , 0 = no , 1 = yes
* WcFlip = Flip picture , 0 = no , 1 = yes
* WcSaturation = Set picture Saturation - 2 . . . + 2
* WcBrightness = Set picture Brightness - 2 . . . + 2
* WcContrast = Set picture Contrast - 2 . . . + 2
2020-05-04 11:48:42 +01:00
*
* Only boards with PSRAM should be used . To enable PSRAM board should be se set to esp32cam in common32 of platform_override . ini
* board = esp32cam
* To speed up cam processing cpu frequency should be better set to 240 Mhz in common32 of platform_override . ini
* board_build . f_cpu = 240000000L
2020-05-12 07:14:58 +01:00
* remarks for AI - THINKER
* GPIO0 zero must be disconnected from any wire after programming because this pin drives the cam clock and does
* not tolerate any capictive load
* flash led = gpio 4
* red led = gpio 33
*/
2020-05-02 07:10:23 +01:00
2020-05-12 07:14:58 +01:00
/*********************************************************************************************/
2020-05-02 07:10:23 +01:00
2020-05-12 07:14:58 +01:00
# define XDRV_81 81
2020-05-02 07:10:23 +01:00
2020-05-12 13:30:07 +01:00
# include "esp_camera.h"
# include "sensor.h"
2020-05-05 08:36:43 +01:00
# include "fb_gfx.h"
# include "fd_forward.h"
# include "fr_forward.h"
2020-05-02 07:10:23 +01:00
2020-05-12 13:30:07 +01:00
bool HttpCheckPriviledgedAccess ( bool ) ;
extern ESP8266WebServer * Webserver ;
ESP8266WebServer * CamServer ;
# define BOUNDARY "e8b8c539-047d-4777-a985-fbba6edff11e"
WiFiClient client ;
2020-05-12 07:14:58 +01:00
// CAMERA_MODEL_AI_THINKER default template pins
2020-05-02 07:10:23 +01:00
# define PWDN_GPIO_NUM 32
# define RESET_GPIO_NUM -1
# define XCLK_GPIO_NUM 0
# define SIOD_GPIO_NUM 26
# define SIOC_GPIO_NUM 27
# define Y9_GPIO_NUM 35
# define Y8_GPIO_NUM 34
# define Y7_GPIO_NUM 39
# define Y6_GPIO_NUM 36
# define Y5_GPIO_NUM 21
# define Y4_GPIO_NUM 19
# define Y3_GPIO_NUM 18
# define Y2_GPIO_NUM 5
# define VSYNC_GPIO_NUM 25
# define HREF_GPIO_NUM 23
# define PCLK_GPIO_NUM 22
2020-05-25 16:25:47 +01:00
struct {
uint8_t up ;
uint16_t width ;
uint16_t height ;
uint8_t stream_active ;
2020-05-05 08:36:43 +01:00
# ifdef USE_FACE_DETECT
2020-05-25 16:25:47 +01:00
uint8_t faces ;
uint16_t face_detect_time ;
2020-05-05 08:36:43 +01:00
# endif
2020-05-25 16:25:47 +01:00
} Wc ;
2020-05-02 07:10:23 +01:00
2020-05-12 13:30:07 +01:00
/*********************************************************************************************/
2020-05-05 17:36:12 +01:00
bool WcPinUsed ( void ) {
bool pin_used = true ;
for ( uint32_t i = 0 ; i < MAX_WEBCAM_DATA ; i + + ) {
if ( ! PinUsed ( GPIO_WEBCAM_DATA , i ) ) {
pin_used = false ;
}
// if (i < MAX_WEBCAM_HSD) {
// if (!PinUsed(GPIO_WEBCAM_HSD, i)) {
// pin_used = false;
// }
// }
}
if ( ! PinUsed ( GPIO_WEBCAM_XCLK ) | | ! PinUsed ( GPIO_WEBCAM_PCLK ) | |
! PinUsed ( GPIO_WEBCAM_VSYNC ) | | ! PinUsed ( GPIO_WEBCAM_HREF ) | |
! PinUsed ( GPIO_WEBCAM_SIOD ) | | ! PinUsed ( GPIO_WEBCAM_SIOC ) ) {
pin_used = false ;
}
return pin_used ;
}
2020-05-12 13:30:07 +01:00
uint32_t WcSetup ( int32_t fsiz ) {
2020-05-03 17:37:12 +01:00
if ( fsiz > 10 ) { fsiz = 10 ; }
2020-05-02 07:10:23 +01:00
2020-05-25 16:25:47 +01:00
Wc . stream_active = 0 ;
2020-05-02 07:10:23 +01:00
2020-05-03 17:37:12 +01:00
if ( fsiz < 0 ) {
2020-05-02 07:10:23 +01:00
esp_camera_deinit ( ) ;
2020-05-25 16:25:47 +01:00
Wc . up = 0 ;
2020-05-02 07:10:23 +01:00
return 0 ;
}
2020-05-25 16:25:47 +01:00
if ( Wc . up ) {
2020-05-02 07:10:23 +01:00
esp_camera_deinit ( ) ;
2020-05-12 13:30:07 +01:00
AddLog_P2 ( LOG_LEVEL_DEBUG , PSTR ( " CAM: Deinit " ) ) ;
2020-05-25 16:25:47 +01:00
//return Wc.up;
2020-05-02 07:10:23 +01:00
}
2020-05-25 16:52:31 +01:00
Wc . up = 0 ;
2020-05-02 07:10:23 +01:00
//esp_log_level_set("*", ESP_LOG_VERBOSE);
2020-05-03 17:37:12 +01:00
camera_config_t config ;
2020-05-02 07:10:23 +01:00
config . ledc_channel = LEDC_CHANNEL_0 ;
config . ledc_timer = LEDC_TIMER_0 ;
config . xclk_freq_hz = 20000000 ;
config . pixel_format = PIXFORMAT_JPEG ;
// config.pixel_format = PIXFORMAT_GRAYSCALE;
// config.pixel_format = PIXFORMAT_RGB565;
2020-05-05 17:36:12 +01:00
if ( WcPinUsed ( ) ) {
config . pin_d0 = Pin ( GPIO_WEBCAM_DATA ) ; // Y2_GPIO_NUM;
config . pin_d1 = Pin ( GPIO_WEBCAM_DATA , 1 ) ; // Y3_GPIO_NUM;
config . pin_d2 = Pin ( GPIO_WEBCAM_DATA , 2 ) ; // Y4_GPIO_NUM;
config . pin_d3 = Pin ( GPIO_WEBCAM_DATA , 3 ) ; // Y5_GPIO_NUM;
config . pin_d4 = Pin ( GPIO_WEBCAM_DATA , 4 ) ; // Y6_GPIO_NUM;
config . pin_d5 = Pin ( GPIO_WEBCAM_DATA , 5 ) ; // Y7_GPIO_NUM;
config . pin_d6 = Pin ( GPIO_WEBCAM_DATA , 6 ) ; // Y8_GPIO_NUM;
config . pin_d7 = Pin ( GPIO_WEBCAM_DATA , 7 ) ; // Y9_GPIO_NUM;
config . pin_xclk = Pin ( GPIO_WEBCAM_XCLK ) ; // XCLK_GPIO_NUM;
config . pin_pclk = Pin ( GPIO_WEBCAM_PCLK ) ; // PCLK_GPIO_NUM;
config . pin_vsync = Pin ( GPIO_WEBCAM_VSYNC ) ; // VSYNC_GPIO_NUM;
config . pin_href = Pin ( GPIO_WEBCAM_HREF ) ; // HREF_GPIO_NUM;
config . pin_sscb_sda = Pin ( GPIO_WEBCAM_SIOD ) ; // SIOD_GPIO_NUM;
config . pin_sscb_scl = Pin ( GPIO_WEBCAM_SIOC ) ; // SIOC_GPIO_NUM;
config . pin_pwdn = ( PinUsed ( GPIO_WEBCAM_PWDN ) ) ? Pin ( GPIO_WEBCAM_PWDN ) : - 1 ; // PWDN_GPIO_NUM;
config . pin_reset = ( PinUsed ( GPIO_WEBCAM_RESET ) ) ? Pin ( GPIO_WEBCAM_RESET ) : - 1 ; // RESET_GPIO_NUM;
2020-05-12 13:30:07 +01:00
AddLog_P2 ( LOG_LEVEL_DEBUG , PSTR ( " CAM: User template " ) ) ;
2020-05-03 17:37:12 +01:00
} 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 ;
2020-05-12 13:30:07 +01:00
AddLog_P2 ( LOG_LEVEL_DEBUG , PSTR ( " CAM: Default template " ) ) ;
2020-05-03 17:37:12 +01:00
}
2020-05-02 07:10:23 +01:00
//ESP.getPsramSize()
//esp_log_level_set("*", ESP_LOG_INFO);
// if PSRAM IC present, init with UXGA resolution and higher JPEG quality
// for larger pre-allocated frame buffer.
2020-05-03 17:37:12 +01:00
bool psram = psramFound ( ) ;
2020-05-02 07:10:23 +01:00
if ( psram ) {
config . frame_size = FRAMESIZE_UXGA ;
config . jpeg_quality = 10 ;
config . fb_count = 2 ;
2020-05-12 13:30:07 +01:00
AddLog_P2 ( LOG_LEVEL_DEBUG , PSTR ( " CAM: PSRAM found " ) ) ;
2020-05-02 07:10:23 +01:00
} else {
config . frame_size = FRAMESIZE_VGA ;
config . jpeg_quality = 12 ;
config . fb_count = 1 ;
2020-05-12 13:30:07 +01:00
AddLog_P2 ( LOG_LEVEL_DEBUG , PSTR ( " CAM: PSRAM not found " ) ) ;
2020-05-02 07:10:23 +01:00
}
// stupid workaround camera diver eats up static ram should prefer PSRAM
// so we steal static ram to force driver to alloc PSRAM
//ESP.getMaxAllocHeap()
// void *x=malloc(70000);
2020-05-03 17:37:12 +01:00
void * x = 0 ;
2020-05-02 07:10:23 +01:00
esp_err_t err = esp_camera_init ( & config ) ;
2020-05-03 17:37:12 +01:00
if ( x ) { free ( x ) ; }
2020-05-02 07:10:23 +01:00
if ( err ! = ESP_OK ) {
2020-05-25 16:52:31 +01:00
AddLog_P2 ( LOG_LEVEL_INFO , PSTR ( " CAM: Init failed with error 0x%x " ) , err ) ;
2020-05-02 07:10:23 +01:00
return 0 ;
}
sensor_t * wc_s = esp_camera_sensor_get ( ) ;
2020-05-12 07:14:58 +01:00
2020-05-08 16:17:09 +01:00
wc_s - > set_vflip ( wc_s , Settings . webcam_config . flip ) ;
wc_s - > set_hmirror ( wc_s , Settings . webcam_config . mirror ) ;
wc_s - > set_brightness ( wc_s , Settings . webcam_config . brightness - 2 ) ; // up the brightness just a bit
wc_s - > set_saturation ( wc_s , Settings . webcam_config . saturation - 2 ) ; // lower the saturation
wc_s - > set_contrast ( wc_s , Settings . webcam_config . contrast - 2 ) ; // keep contrast
2020-05-02 07:10:23 +01:00
// 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 ( ) ;
2020-05-25 16:25:47 +01:00
if ( ! wc_fb ) {
2020-05-25 16:52:31 +01:00
AddLog_P2 ( LOG_LEVEL_INFO , PSTR ( " CAM: Init failed to get the frame on time " ) ) ;
2020-05-25 16:25:47 +01:00
return 0 ;
}
Wc . width = wc_fb - > width ;
Wc . height = wc_fb - > height ;
2020-05-02 07:10:23 +01:00
esp_camera_fb_return ( wc_fb ) ;
2020-05-05 08:36:43 +01:00
# ifdef USE_FACE_DETECT
fd_init ( ) ;
# endif
2020-05-25 16:52:31 +01:00
AddLog_P2 ( LOG_LEVEL_INFO , PSTR ( " CAM: Initialized " ) ) ;
2020-05-02 07:10:23 +01:00
2020-05-25 16:25:47 +01:00
Wc . up = 1 ;
2020-05-25 16:52:31 +01:00
if ( psram ) { Wc . up = 2 ; }
2020-05-02 07:10:23 +01:00
2020-05-25 16:25:47 +01:00
return Wc . up ;
2020-05-02 07:10:23 +01:00
}
2020-05-12 13:30:07 +01:00
/*********************************************************************************************/
int32_t WcSetOptions ( uint32_t sel , int32_t value ) {
2020-05-03 17:37:12 +01:00
int32_t res = 0 ;
2020-05-02 07:10:23 +01:00
sensor_t * s = esp_camera_sensor_get ( ) ;
2020-05-03 17:37:12 +01:00
if ( ! s ) { return - 99 ; }
2020-05-02 07:10:23 +01:00
switch ( sel ) {
case 0 :
2020-05-03 17:37:12 +01:00
if ( value > = 0 ) { s - > set_framesize ( s , ( framesize_t ) value ) ; }
2020-05-02 07:10:23 +01:00
res = s - > status . framesize ;
break ;
case 1 :
2020-05-03 17:37:12 +01:00
if ( value > = 0 ) { s - > set_special_effect ( s , value ) ; }
2020-05-02 07:10:23 +01:00
res = s - > status . special_effect ;
break ;
case 2 :
2020-05-03 17:37:12 +01:00
if ( value > = 0 ) { s - > set_vflip ( s , value ) ; }
2020-05-02 07:10:23 +01:00
res = s - > status . vflip ;
break ;
case 3 :
2020-05-03 17:37:12 +01:00
if ( value > = 0 ) { s - > set_hmirror ( s , value ) ; }
2020-05-02 07:10:23 +01:00
res = s - > status . hmirror ;
break ;
case 4 :
2020-05-03 17:37:12 +01:00
if ( value > = - 4 ) { s - > set_contrast ( s , value ) ; }
2020-05-02 07:10:23 +01:00
res = s - > status . contrast ;
break ;
case 5 :
2020-05-03 17:37:12 +01:00
if ( value > = - 4 ) { s - > set_brightness ( s , value ) ; }
2020-05-02 07:10:23 +01:00
res = s - > status . brightness ;
break ;
case 6 :
2020-05-03 17:37:12 +01:00
if ( value > = - 4 ) { s - > set_saturation ( s , value ) ; }
2020-05-02 07:10:23 +01:00
res = s - > status . saturation ;
break ;
}
return res ;
}
2020-05-12 13:30:07 +01:00
uint32_t WcGetWidth ( void ) {
2020-05-02 07:10:23 +01:00
camera_fb_t * wc_fb = esp_camera_fb_get ( ) ;
2020-05-03 17:37:12 +01:00
if ( ! wc_fb ) { return 0 ; }
2020-05-25 16:25:47 +01:00
Wc . width = wc_fb - > width ;
2020-05-02 07:10:23 +01:00
esp_camera_fb_return ( wc_fb ) ;
2020-05-25 16:25:47 +01:00
return Wc . width ;
2020-05-02 07:10:23 +01:00
}
2020-05-12 13:30:07 +01:00
uint32_t WcGetHeight ( void ) {
2020-05-02 07:10:23 +01:00
camera_fb_t * wc_fb = esp_camera_fb_get ( ) ;
2020-05-03 17:37:12 +01:00
if ( ! wc_fb ) { return 0 ; }
2020-05-25 16:25:47 +01:00
Wc . height = wc_fb - > height ;
2020-05-02 07:10:23 +01:00
esp_camera_fb_return ( wc_fb ) ;
2020-05-25 16:25:47 +01:00
return Wc . height ;
2020-05-02 07:10:23 +01:00
}
2020-05-12 13:30:07 +01:00
/*********************************************************************************************/
uint16_t motion_detect ;
uint32_t motion_ltime ;
uint32_t motion_trigger ;
uint32_t motion_brightness ;
uint8_t * last_motion_buffer ;
uint32_t WcSetMotionDetect ( int32_t value ) {
if ( value > = 0 ) { motion_detect = value ; }
if ( - 1 = = value ) {
return motion_trigger ;
} else {
return motion_brightness ;
}
}
// optional motion detector
void WcDetectMotion ( void ) {
camera_fb_t * wc_fb ;
uint8_t * out_buf = 0 ;
if ( ( millis ( ) - motion_ltime ) > motion_detect ) {
motion_ltime = millis ( ) ;
wc_fb = esp_camera_fb_get ( ) ;
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 ) ;
}
if ( last_motion_buffer ) {
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 ;
// convert to bw
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 ;
}
}
motion_trigger = accu / ( ( wc_fb - > height * wc_fb - > width ) / 100 ) ;
motion_brightness = bright / ( ( wc_fb - > height * wc_fb - > width ) / 100 ) ;
free ( out_buf ) ;
}
}
}
esp_camera_fb_return ( wc_fb ) ;
}
}
/*********************************************************************************************/
# ifdef USE_FACE_DETECT
static mtmn_config_t mtmn_config = { 0 } ;
void fd_init ( void ) {
mtmn_config . type = FAST ;
mtmn_config . min_face = 80 ;
mtmn_config . pyramid = 0.707 ;
mtmn_config . pyramid_times = 4 ;
mtmn_config . p_threshold . score = 0.6 ;
mtmn_config . p_threshold . nms = 0.7 ;
mtmn_config . p_threshold . candidate_number = 20 ;
mtmn_config . r_threshold . score = 0.7 ;
mtmn_config . r_threshold . nms = 0.7 ;
mtmn_config . r_threshold . candidate_number = 10 ;
mtmn_config . o_threshold . score = 0.7 ;
mtmn_config . o_threshold . nms = 0.7 ;
mtmn_config . o_threshold . candidate_number = 1 ;
}
# define FACE_COLOR_WHITE 0x00FFFFFF
# define FACE_COLOR_BLACK 0x00000000
# define FACE_COLOR_RED 0x000000FF
# define FACE_COLOR_GREEN 0x0000FF00
# define FACE_COLOR_BLUE 0x00FF0000
# define FACE_COLOR_YELLOW (FACE_COLOR_RED | FACE_COLOR_GREEN)
# define FACE_COLOR_CYAN (FACE_COLOR_BLUE | FACE_COLOR_GREEN)
# define FACE_COLOR_PURPLE (FACE_COLOR_BLUE | FACE_COLOR_RED)
void draw_face_boxes ( dl_matrix3du_t * image_matrix , box_array_t * boxes , int face_id ) ;
/*
void draw_face_boxes ( dl_matrix3du_t * image_matrix , box_array_t * boxes , int face_id ) {
int x , y , w , h , i ;
uint32_t color = FACE_COLOR_YELLOW ;
if ( face_id < 0 ) {
color = FACE_COLOR_RED ;
} else if ( face_id > 0 ) {
color = FACE_COLOR_GREEN ;
}
fb_data_t fb ;
fb . width = image_matrix - > w ;
fb . height = image_matrix - > h ;
fb . data = image_matrix - > item ;
fb . bytes_per_pixel = 3 ;
fb . format = FB_BGR888 ;
for ( i = 0 ; i < boxes - > len ; i + + ) {
// rectangle box
x = ( int ) boxes - > box [ i ] . box_p [ 0 ] ;
y = ( int ) boxes - > box [ i ] . box_p [ 1 ] ;
w = ( int ) boxes - > box [ i ] . box_p [ 2 ] - x + 1 ;
h = ( int ) boxes - > box [ i ] . box_p [ 3 ] - y + 1 ;
fb_gfx_drawFastHLine ( & fb , x , y , w , color ) ;
fb_gfx_drawFastHLine ( & fb , x , y + h - 1 , w , color ) ;
fb_gfx_drawFastVLine ( & fb , x , y , h , color ) ;
fb_gfx_drawFastVLine ( & fb , x + w - 1 , y , h , color ) ;
#if 0
// landmark
int x0 , y0 , j ;
for ( j = 0 ; j < 10 ; j + = 2 ) {
x0 = ( int ) boxes - > landmark [ i ] . landmark_p [ j ] ;
y0 = ( int ) boxes - > landmark [ i ] . landmark_p [ j + 1 ] ;
fb_gfx_fillRect ( & fb , x0 , y0 , 3 , 3 , color ) ;
}
# endif
}
}
*/
# define DL_SPIRAM_SUPPORT
uint32_t WcSetFaceDetect ( int32_t value ) {
2020-05-25 16:25:47 +01:00
if ( value > = 0 ) { Wc . face_detect_time = value ; }
return Wc . faces ;
2020-05-12 13:30:07 +01:00
}
uint32_t face_ltime ;
uint32_t WcDetectFace ( void ) ;
uint32_t WcDetectFace ( void ) {
dl_matrix3du_t * image_matrix ;
size_t out_len , out_width , out_height ;
uint8_t * out_buf ;
bool s ;
bool detected = false ;
int face_id = 0 ;
camera_fb_t * fb ;
2020-05-25 16:25:47 +01:00
if ( ( millis ( ) - face_ltime ) > Wc . face_detect_time ) {
2020-05-12 13:30:07 +01:00
face_ltime = millis ( ) ;
fb = esp_camera_fb_get ( ) ;
if ( ! fb ) { return ESP_FAIL ; }
image_matrix = dl_matrix3du_alloc ( 1 , fb - > width , fb - > height , 3 ) ;
if ( ! image_matrix ) {
AddLog_P2 ( LOG_LEVEL_DEBUG , PSTR ( " CAM: dl_matrix3du_alloc failed " ) ) ;
esp_camera_fb_return ( fb ) ;
return ESP_FAIL ;
}
out_buf = image_matrix - > item ;
//out_len = fb->width * fb->height * 3;
//out_width = fb->width;
//out_height = fb->height;
s = fmt2rgb888 ( fb - > buf , fb - > len , fb - > format , out_buf ) ;
esp_camera_fb_return ( fb ) ;
if ( ! s ) {
dl_matrix3du_free ( image_matrix ) ;
AddLog_P2 ( LOG_LEVEL_DEBUG , PSTR ( " CAM: to rgb888 failed " ) ) ;
return ESP_FAIL ;
}
box_array_t * net_boxes = face_detect ( image_matrix , & mtmn_config ) ;
if ( net_boxes ) {
detected = true ;
2020-05-25 16:25:47 +01:00
Wc . faces = net_boxes - > len ;
2020-05-12 13:30:07 +01:00
//if(recognition_enabled){
// face_id = run_face_recognition(image_matrix, net_boxes);
//}
//draw_face_boxes(image_matrix, net_boxes, face_id);
free ( net_boxes - > score ) ;
free ( net_boxes - > box ) ;
free ( net_boxes - > landmark ) ;
free ( net_boxes ) ;
} else {
2020-05-25 16:25:47 +01:00
Wc . faces = 0 ;
2020-05-12 13:30:07 +01:00
}
dl_matrix3du_free ( image_matrix ) ;
2020-05-25 16:25:47 +01:00
//Serial.printf("face detected: %d",Wc.faces);
2020-05-12 13:30:07 +01:00
}
}
# endif
/*********************************************************************************************/
2020-05-02 07:10:23 +01:00
# ifndef MAX_PICSTORE
# define MAX_PICSTORE 4
# endif
struct PICSTORE {
uint8_t * buff ;
uint32_t len ;
} ;
struct PICSTORE picstore [ MAX_PICSTORE ] ;
# ifdef COPYFRAME
struct PICSTORE tmp_picstore ;
# endif
2020-05-12 13:30:07 +01:00
uint32_t WcGetPicstore ( int32_t num , uint8_t * * buff ) {
2020-05-03 17:37:12 +01:00
if ( num < 0 ) { return MAX_PICSTORE ; }
* buff = picstore [ num ] . buff ;
2020-05-02 07:10:23 +01:00
return picstore [ num ] . len ;
}
2020-05-12 13:30:07 +01:00
uint32_t WcGetFrame ( int32_t bnum ) {
2020-05-02 07:10:23 +01:00
size_t _jpg_buf_len = 0 ;
uint8_t * _jpg_buf = NULL ;
2020-05-03 17:37:12 +01:00
camera_fb_t * wc_fb = 0 ;
bool jpeg_converted = false ;
2020-05-02 07:10:23 +01:00
2020-05-03 17:37:12 +01:00
if ( bnum < 0 ) {
if ( bnum < - MAX_PICSTORE ) { bnum = - 1 ; }
bnum = - bnum ;
2020-05-02 07:10:23 +01:00
bnum - - ;
2020-05-03 17:37:12 +01:00
if ( picstore [ bnum ] . buff ) { free ( picstore [ bnum ] . buff ) ; }
picstore [ bnum ] . len = 0 ;
2020-05-02 07:10:23 +01:00
return 0 ;
}
# ifdef COPYFRAME
2020-05-03 17:37:12 +01:00
if ( bnum & 0x10 ) {
bnum & = 0xf ;
_jpg_buf = tmp_picstore . buff ;
_jpg_buf_len = tmp_picstore . len ;
if ( ! _jpg_buf_len ) { return 0 ; }
2020-05-02 07:10:23 +01:00
goto pcopy ;
}
# endif
wc_fb = esp_camera_fb_get ( ) ;
if ( ! wc_fb ) {
2020-05-12 13:30:07 +01:00
AddLog_P2 ( LOG_LEVEL_DEBUG , PSTR ( " CAM: Can't get frame " ) ) ;
2020-05-02 07:10:23 +01:00
return 0 ;
}
if ( ! bnum ) {
2020-05-25 16:25:47 +01:00
Wc . width = wc_fb - > width ;
Wc . height = wc_fb - > height ;
2020-05-02 07:10:23 +01:00
esp_camera_fb_return ( wc_fb ) ;
return 0 ;
}
2020-05-03 17:37:12 +01:00
if ( wc_fb - > format ! = PIXFORMAT_JPEG ) {
2020-05-02 07:10:23 +01:00
jpeg_converted = frame2jpg ( wc_fb , 80 , & _jpg_buf , & _jpg_buf_len ) ;
if ( ! jpeg_converted ) {
2020-05-03 17:37:12 +01:00
//Serial.println("JPEG compression failed");
_jpg_buf_len = wc_fb - > len ;
_jpg_buf = wc_fb - > buf ;
2020-05-02 07:10:23 +01:00
}
} else {
_jpg_buf_len = wc_fb - > len ;
_jpg_buf = wc_fb - > buf ;
}
pcopy :
2020-05-03 17:37:12 +01:00
if ( ( bnum < 1 ) | | ( bnum > MAX_PICSTORE ) ) { bnum = 1 ; }
2020-05-02 07:10:23 +01:00
bnum - - ;
2020-05-03 17:37:12 +01:00
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 ) ;
2020-05-02 07:10:23 +01:00
if ( picstore [ bnum ] . buff ) {
2020-05-03 17:37:12 +01:00
memcpy ( picstore [ bnum ] . buff , _jpg_buf , _jpg_buf_len ) ;
picstore [ bnum ] . len = _jpg_buf_len ;
2020-05-02 07:10:23 +01:00
} else {
2020-05-12 13:30:07 +01:00
AddLog_P2 ( LOG_LEVEL_DEBUG , PSTR ( " CAM: Can't allocate picstore " ) ) ;
2020-05-03 17:37:12 +01:00
picstore [ bnum ] . len = 0 ;
2020-05-02 07:10:23 +01:00
}
2020-05-03 17:37:12 +01:00
if ( wc_fb ) { esp_camera_fb_return ( wc_fb ) ; }
if ( jpeg_converted ) { free ( _jpg_buf ) ; }
if ( ! picstore [ bnum ] . buff ) { return 0 ; }
2020-05-02 07:10:23 +01:00
return _jpg_buf_len ;
}
void HandleImage ( void ) {
2020-05-12 13:30:07 +01:00
if ( ! HttpCheckPriviledgedAccess ( ) ) { return ; }
2020-05-02 07:10:23 +01:00
uint32_t bnum = Webserver - > arg ( F ( " p " ) ) . toInt ( ) ;
2020-05-03 17:37:12 +01:00
if ( ( bnum < 0 ) | | ( bnum > MAX_PICSTORE ) ) { bnum = 1 ; }
2020-05-02 07:10:23 +01:00
WiFiClient client = Webserver - > client ( ) ;
String response = " HTTP/1.1 200 OK \r \n " ;
response + = " Content-disposition: inline; filename=cap.jpg \r \n " ;
response + = " Content-type: image/jpeg \r \n \r \n " ;
Webserver - > sendContent ( response ) ;
if ( ! bnum ) {
2020-05-12 07:14:58 +01:00
size_t _jpg_buf_len = 0 ;
uint8_t * _jpg_buf = NULL ;
camera_fb_t * wc_fb = 0 ;
wc_fb = esp_camera_fb_get ( ) ;
if ( ! wc_fb ) { return ; }
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 ;
}
} else {
_jpg_buf_len = wc_fb - > len ;
_jpg_buf = wc_fb - > buf ;
}
if ( _jpg_buf_len ) {
client . write ( ( char * ) _jpg_buf , _jpg_buf_len ) ;
2020-05-02 07:10:23 +01:00
}
2020-05-12 07:14:58 +01:00
if ( wc_fb ) { esp_camera_fb_return ( wc_fb ) ; }
2020-05-02 07:10:23 +01:00
} else {
bnum - - ;
if ( ! picstore [ bnum ] . len ) {
2020-05-12 13:30:07 +01:00
AddLog_P2 ( LOG_LEVEL_DEBUG , PSTR ( " CAM: No image #: %d " ) , bnum ) ;
2020-05-02 07:10:23 +01:00
return ;
}
client . write ( ( char * ) picstore [ bnum ] . buff , picstore [ bnum ] . len ) ;
}
2020-05-12 07:14:58 +01:00
client . stop ( ) ;
2020-05-02 07:10:23 +01:00
2020-05-12 14:42:16 +01:00
AddLog_P2 ( LOG_LEVEL_DEBUG_MORE , PSTR ( " CAM: Sending image #: %d " ) , bnum + 1 ) ;
2020-05-02 07:10:23 +01:00
}
2020-05-12 13:30:07 +01:00
void HandleImageBasic ( void ) {
if ( ! HttpCheckPriviledgedAccess ( ) ) { return ; }
2020-05-10 16:52:28 +01:00
2020-05-12 14:42:16 +01:00
AddLog_P ( LOG_LEVEL_DEBUG_MORE , PSTR ( D_LOG_HTTP " Capture image " ) ) ;
2020-05-10 16:52:28 +01:00
if ( Settings . webcam_config . stream ) {
if ( ! CamServer ) {
WcStreamControl ( ) ;
}
}
camera_fb_t * wc_fb ;
wc_fb = esp_camera_fb_get ( ) ; // Acquire frame
if ( ! wc_fb ) {
2020-05-12 13:30:07 +01:00
AddLog_P2 ( LOG_LEVEL_DEBUG , PSTR ( " CAM: Frame buffer could not be acquired " ) ) ;
2020-05-10 16:52:28 +01:00
return ;
}
size_t _jpg_buf_len = 0 ;
uint8_t * _jpg_buf = NULL ;
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 ;
}
} else {
_jpg_buf_len = wc_fb - > len ;
_jpg_buf = wc_fb - > buf ;
}
if ( _jpg_buf_len ) {
Webserver - > client ( ) . flush ( ) ;
WSHeaderSend ( ) ;
2020-05-12 13:30:07 +01:00
Webserver - > sendHeader ( F ( " Content-disposition " ) , F ( " inline; filename=snapshot.jpg " ) ) ;
2020-05-10 16:52:28 +01:00
Webserver - > send_P ( 200 , " image/jpeg " , ( char * ) _jpg_buf , _jpg_buf_len ) ;
Webserver - > client ( ) . stop ( ) ;
}
esp_camera_fb_return ( wc_fb ) ; // Free frame buffer
2020-05-12 14:42:16 +01:00
AddLog_P2 ( LOG_LEVEL_DEBUG_MORE , PSTR ( " CAM: Image sent " ) ) ;
2020-05-05 08:36:43 +01:00
}
2020-05-12 13:30:07 +01:00
void HandleWebcamMjpeg ( void ) {
AddLog_P2 ( LOG_LEVEL_DEBUG , PSTR ( " CAM: Handle camserver " ) ) ;
2020-05-25 16:25:47 +01:00
// if (!Wc.stream_active) {
2020-05-12 13:30:07 +01:00
// always restart stream
2020-05-25 16:25:47 +01:00
Wc . stream_active = 1 ;
2020-05-12 13:30:07 +01:00
client = CamServer - > client ( ) ;
AddLog_P2 ( LOG_LEVEL_DEBUG , PSTR ( " CAM: Create client " ) ) ;
// }
2020-05-05 08:36:43 +01:00
}
2020-05-12 13:30:07 +01:00
void HandleWebcamMjpegTask ( void ) {
2020-05-02 07:10:23 +01:00
camera_fb_t * wc_fb ;
size_t _jpg_buf_len = 0 ;
uint8_t * _jpg_buf = NULL ;
//WiFiClient client = CamServer->client();
uint32_t tlen ;
2020-05-03 17:37:12 +01:00
bool jpeg_converted = false ;
2020-05-02 07:10:23 +01:00
if ( ! client . connected ( ) ) {
2020-05-12 13:30:07 +01:00
AddLog_P2 ( LOG_LEVEL_DEBUG , PSTR ( " CAM: Client fail " ) ) ;
2020-05-25 16:25:47 +01:00
Wc . stream_active = 0 ;
2020-05-02 07:10:23 +01:00
}
2020-05-25 16:25:47 +01:00
if ( 1 = = Wc . stream_active ) {
2020-05-02 07:10:23 +01:00
client . flush ( ) ;
client . setTimeout ( 3 ) ;
2020-05-12 13:30:07 +01:00
AddLog_P2 ( LOG_LEVEL_DEBUG , PSTR ( " CAM: Start stream " ) ) ;
2020-05-02 07:10:23 +01:00
client . print ( " HTTP/1.1 200 OK \r \n "
2020-05-03 17:37:12 +01:00
" Content-Type: multipart/x-mixed-replace;boundary= " BOUNDARY " \r \n "
" \r \n " ) ;
2020-05-25 16:25:47 +01:00
Wc . stream_active = 2 ;
2020-05-08 16:17:09 +01:00
}
2020-05-25 16:25:47 +01:00
if ( 2 = = Wc . stream_active ) {
2020-05-02 07:10:23 +01:00
wc_fb = esp_camera_fb_get ( ) ;
if ( ! wc_fb ) {
2020-05-12 13:30:07 +01:00
AddLog_P2 ( LOG_LEVEL_DEBUG , PSTR ( " CAM: Frame fail " ) ) ;
2020-05-25 16:25:47 +01:00
Wc . stream_active = 0 ;
2020-05-02 07:10:23 +01:00
}
2020-05-08 16:17:09 +01:00
}
2020-05-25 16:25:47 +01:00
if ( 2 = = Wc . stream_active ) {
2020-05-03 17:37:12 +01:00
if ( wc_fb - > format ! = PIXFORMAT_JPEG ) {
2020-05-02 07:10:23 +01:00
jpeg_converted = frame2jpg ( wc_fb , 80 , & _jpg_buf , & _jpg_buf_len ) ;
if ( ! jpeg_converted ) {
2020-05-12 13:30:07 +01:00
AddLog_P2 ( LOG_LEVEL_DEBUG , PSTR ( " CAM: JPEG compression failed " ) ) ;
2020-05-02 07:10:23 +01:00
_jpg_buf_len = wc_fb - > len ;
_jpg_buf = wc_fb - > buf ;
}
} else {
_jpg_buf_len = wc_fb - > len ;
_jpg_buf = wc_fb - > buf ;
}
client . printf ( " Content-Type: image/jpeg \r \n "
" Content-Length: %d \r \n "
2020-05-03 17:37:12 +01:00
" \r \n " , static_cast < int > ( _jpg_buf_len ) ) ;
tlen = client . write ( _jpg_buf , _jpg_buf_len ) ;
2020-05-02 07:10:23 +01:00
/*
if ( tlen ! = _jpg_buf_len ) {
esp_camera_fb_return ( wc_fb ) ;
2020-05-25 16:25:47 +01:00
Wc . stream_active = 0 ;
2020-05-12 13:30:07 +01:00
AddLog_P2 ( LOG_LEVEL_DEBUG , PSTR ( " CAM: Send fail " ) ) ;
2020-05-02 07:10:23 +01:00
} */
client . print ( " \r \n -- " BOUNDARY " \r \n " ) ;
# ifdef COPYFRAME
2020-05-03 17:37:12 +01:00
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 ) ;
2020-05-02 07:10:23 +01:00
if ( tmp_picstore . buff ) {
2020-05-03 17:37:12 +01:00
memcpy ( tmp_picstore . buff , _jpg_buf , _jpg_buf_len ) ;
tmp_picstore . len = _jpg_buf_len ;
2020-05-02 07:10:23 +01:00
} else {
2020-05-03 17:37:12 +01:00
tmp_picstore . len = 0 ;
2020-05-02 07:10:23 +01:00
}
# endif
2020-05-03 17:37:12 +01:00
if ( jpeg_converted ) { free ( _jpg_buf ) ; }
2020-05-02 07:10:23 +01:00
esp_camera_fb_return ( wc_fb ) ;
2020-05-12 13:30:07 +01:00
//AddLog_P2(LOG_LEVEL_DEBUG, PSTR("CAM: send frame"));
2020-05-08 16:17:09 +01:00
}
2020-05-25 16:25:47 +01:00
if ( 0 = = Wc . stream_active ) {
2020-05-12 13:30:07 +01:00
AddLog_P2 ( LOG_LEVEL_DEBUG , PSTR ( " CAM: Stream exit " ) ) ;
2020-05-08 16:17:09 +01:00
client . flush ( ) ;
client . stop ( ) ;
2020-05-02 07:10:23 +01:00
}
}
2020-05-12 13:30:07 +01:00
void HandleWebcamRoot ( void ) {
2020-05-02 07:10:23 +01:00
//CamServer->redirect("http://" + String(ip) + ":81/cam.mjpeg");
CamServer - > sendHeader ( " Location " , WiFi . localIP ( ) . toString ( ) + " :81/cam.mjpeg " ) ;
CamServer - > send ( 302 , " " , " " ) ;
2020-05-12 13:30:07 +01:00
AddLog_P2 ( LOG_LEVEL_DEBUG , PSTR ( " CAM: Root called " ) ) ;
2020-05-02 07:10:23 +01:00
}
2020-05-12 13:30:07 +01:00
/*********************************************************************************************/
2020-05-02 07:10:23 +01:00
2020-05-12 13:30:07 +01:00
uint32_t WcSetStreamserver ( uint32_t flag ) {
2020-05-03 17:37:12 +01:00
if ( global_state . wifi_down ) { return 0 ; }
2020-05-02 07:10:23 +01:00
2020-05-25 16:25:47 +01:00
Wc . stream_active = 0 ;
2020-05-02 07:10:23 +01:00
if ( flag ) {
if ( ! CamServer ) {
CamServer = new ESP8266WebServer ( 81 ) ;
2020-05-12 13:30:07 +01:00
CamServer - > on ( " / " , HandleWebcamRoot ) ;
CamServer - > on ( " /cam.mjpeg " , HandleWebcamMjpeg ) ;
CamServer - > on ( " /cam.jpg " , HandleWebcamMjpeg ) ;
CamServer - > on ( " /stream " , HandleWebcamMjpeg ) ;
AddLog_P2 ( LOG_LEVEL_DEBUG , PSTR ( " CAM: Stream init " ) ) ;
2020-05-02 07:10:23 +01:00
CamServer - > begin ( ) ;
}
} else {
if ( CamServer ) {
CamServer - > stop ( ) ;
delete CamServer ;
2020-05-03 17:37:12 +01:00
CamServer = NULL ;
2020-05-12 13:30:07 +01:00
AddLog_P2 ( LOG_LEVEL_DEBUG , PSTR ( " CAM: Stream exit " ) ) ;
2020-05-02 07:10:23 +01:00
}
}
return 0 ;
}
2020-05-08 16:17:09 +01:00
void WcStreamControl ( ) {
2020-05-12 13:30:07 +01:00
WcSetStreamserver ( Settings . webcam_config . stream ) ;
2020-05-08 16:17:09 +01:00
int resolution = ( ! Settings . webcam_config . stream ) ? - 1 : Settings . webcam_config . resolution ;
2020-05-12 13:30:07 +01:00
WcSetup ( resolution ) ;
2020-05-04 16:13:14 +01:00
}
2020-05-12 13:30:07 +01:00
/*********************************************************************************************/
2020-05-08 16:17:09 +01:00
2020-05-12 13:30:07 +01:00
void WcLoop ( void ) {
2020-05-08 16:17:09 +01:00
if ( CamServer ) {
CamServer - > handleClient ( ) ;
2020-05-25 16:25:47 +01:00
if ( Wc . stream_active ) { HandleWebcamMjpegTask ( ) ; }
2020-05-08 16:17:09 +01:00
}
2020-05-12 13:30:07 +01:00
if ( motion_detect ) { WcDetectMotion ( ) ; }
2020-05-05 08:36:43 +01:00
# ifdef USE_FACE_DETECT
2020-05-25 16:25:47 +01:00
if ( Wc . face_detect_time ) { WcDetectFace ( ) ; }
2020-05-05 08:36:43 +01:00
# endif
2020-05-02 07:10:23 +01:00
}
2020-05-12 13:30:07 +01:00
void WcPicSetup ( void ) {
2020-05-02 07:10:23 +01:00
Webserver - > on ( " /wc.jpg " , HandleImage ) ;
Webserver - > on ( " /wc.mjpeg " , HandleImage ) ;
2020-05-12 13:30:07 +01:00
Webserver - > on ( " /snapshot.jpg " , HandleImageBasic ) ;
2020-05-02 07:10:23 +01:00
}
2020-05-12 13:30:07 +01:00
void WcShowStream ( void ) {
if ( Settings . webcam_config . stream ) {
2020-05-25 16:52:31 +01:00
// if (!CamServer || !Wc.up) {
2020-05-12 13:30:07 +01:00
if ( ! CamServer ) {
WcStreamControl ( ) ;
delay ( 50 ) ; // Give the webcam webserver some time to prepare the stream
}
2020-05-25 16:25:47 +01:00
if ( CamServer & & Wc . up ) {
2020-05-12 13:30:07 +01:00
WSContentSend_P ( PSTR ( " <p></p><center><img src='http://%s:81/stream' alt='Webcam stream' style='width:99%%;'></center><p></p> " ) ,
WiFi . localIP ( ) . toString ( ) . c_str ( ) ) ;
}
}
}
2020-05-02 07:10:23 +01:00
2020-05-04 16:13:14 +01:00
void WcInit ( void ) {
2020-05-08 16:17:09 +01:00
if ( ! Settings . webcam_config . data ) {
Settings . webcam_config . stream = 1 ;
Settings . webcam_config . resolution = 5 ;
Settings . webcam_config . flip = 0 ;
Settings . webcam_config . mirror = 0 ;
Settings . webcam_config . saturation = 0 ; // -2
Settings . webcam_config . brightness = 3 ; // 1
Settings . webcam_config . contrast = 2 ; // 0
2020-05-04 16:13:14 +01:00
}
}
2020-05-02 07:10:23 +01:00
/*********************************************************************************************\
2020-05-03 17:37:12 +01:00
* Commands
2020-05-02 07:10:23 +01:00
\ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2020-05-03 17:37:12 +01:00
2020-05-08 16:17:09 +01:00
# define D_PRFX_WEBCAM "WC"
# define D_CMND_WC_STREAM "Stream"
# define D_CMND_WC_RESOLUTION "Resolution"
# define D_CMND_WC_MIRROR "Mirror"
# define D_CMND_WC_FLIP "Flip"
# define D_CMND_WC_SATURATION "Saturation"
# define D_CMND_WC_BRIGHTNESS "Brightness"
# define D_CMND_WC_CONTRAST "Contrast"
const char kWCCommands [ ] PROGMEM = D_PRFX_WEBCAM " | " // Prefix
" | " D_CMND_WC_STREAM " | " D_CMND_WC_RESOLUTION " | " D_CMND_WC_MIRROR " | " D_CMND_WC_FLIP " | "
D_CMND_WC_SATURATION " | " D_CMND_WC_BRIGHTNESS " | " D_CMND_WC_CONTRAST
2020-05-02 15:44:44 +01:00
;
void ( * const WCCommand [ ] ) ( void ) PROGMEM = {
2020-05-08 16:17:09 +01:00
& CmndWebcam , & CmndWebcamStream , & CmndWebcamResolution , & CmndWebcamMirror , & CmndWebcamFlip ,
& CmndWebcamSaturation , & CmndWebcamBrightness , & CmndWebcamContrast
2020-05-02 15:44:44 +01:00
} ;
2020-05-04 16:13:14 +01:00
void CmndWebcam ( void ) {
2020-05-08 16:17:09 +01:00
Response_P ( PSTR ( " { \" " D_PRFX_WEBCAM " \" :{ \" " D_CMND_WC_STREAM " \" :%d, \" " D_CMND_WC_RESOLUTION " \" :%d, \" " D_CMND_WC_MIRROR " \" :%d, \" "
D_CMND_WC_FLIP " \" :%d, \" "
D_CMND_WC_SATURATION " \" :%d, \" " D_CMND_WC_BRIGHTNESS " \" :%d, \" " D_CMND_WC_CONTRAST " \" :%d}} " ) ,
Settings . webcam_config . stream , Settings . webcam_config . resolution , Settings . webcam_config . mirror ,
Settings . webcam_config . flip ,
Settings . webcam_config . saturation - 2 , Settings . webcam_config . brightness - 2 , Settings . webcam_config . contrast - 2 ) ;
}
void CmndWebcamStream ( void ) {
if ( ( XdrvMailbox . payload > = 0 ) & & ( XdrvMailbox . payload < = 1 ) ) {
Settings . webcam_config . stream = XdrvMailbox . payload ;
if ( ! Settings . webcam_config . stream ) { WcStreamControl ( ) ; } // Stop stream
}
ResponseCmndStateText ( Settings . webcam_config . stream ) ;
}
void CmndWebcamResolution ( void ) {
2020-05-03 17:37:12 +01:00
if ( ( XdrvMailbox . payload > = 0 ) & & ( XdrvMailbox . payload < = 10 ) ) {
2020-05-08 16:17:09 +01:00
Settings . webcam_config . resolution = XdrvMailbox . payload ;
2020-05-12 13:30:07 +01:00
WcSetOptions ( 0 , Settings . webcam_config . resolution ) ;
2020-05-08 16:17:09 +01:00
}
ResponseCmndNumber ( Settings . webcam_config . resolution ) ;
}
void CmndWebcamMirror ( void ) {
if ( ( XdrvMailbox . payload > = 0 ) & & ( XdrvMailbox . payload < = 1 ) ) {
Settings . webcam_config . mirror = XdrvMailbox . payload ;
2020-05-12 13:30:07 +01:00
WcSetOptions ( 3 , Settings . webcam_config . mirror ) ;
2020-05-08 16:17:09 +01:00
}
ResponseCmndStateText ( Settings . webcam_config . mirror ) ;
}
void CmndWebcamFlip ( void ) {
if ( ( XdrvMailbox . payload > = 0 ) & & ( XdrvMailbox . payload < = 1 ) ) {
Settings . webcam_config . flip = XdrvMailbox . payload ;
2020-05-12 13:30:07 +01:00
WcSetOptions ( 2 , Settings . webcam_config . flip ) ;
2020-05-08 16:17:09 +01:00
}
ResponseCmndStateText ( Settings . webcam_config . flip ) ;
}
void CmndWebcamSaturation ( void ) {
if ( ( XdrvMailbox . payload > = - 2 ) & & ( XdrvMailbox . payload < = 2 ) ) {
Settings . webcam_config . saturation = XdrvMailbox . payload + 2 ;
2020-05-12 13:30:07 +01:00
WcSetOptions ( 6 , Settings . webcam_config . saturation - 2 ) ;
2020-05-08 16:17:09 +01:00
}
ResponseCmndNumber ( Settings . webcam_config . saturation - 2 ) ;
}
void CmndWebcamBrightness ( void ) {
if ( ( XdrvMailbox . payload > = - 2 ) & & ( XdrvMailbox . payload < = 2 ) ) {
Settings . webcam_config . brightness = XdrvMailbox . payload + 2 ;
2020-05-12 13:30:07 +01:00
WcSetOptions ( 5 , Settings . webcam_config . brightness - 2 ) ;
2020-05-08 16:17:09 +01:00
}
ResponseCmndNumber ( Settings . webcam_config . brightness - 2 ) ;
}
void CmndWebcamContrast ( void ) {
if ( ( XdrvMailbox . payload > = - 2 ) & & ( XdrvMailbox . payload < = 2 ) ) {
Settings . webcam_config . contrast = XdrvMailbox . payload + 2 ;
2020-05-12 13:30:07 +01:00
WcSetOptions ( 4 , Settings . webcam_config . contrast - 2 ) ;
2020-05-02 15:44:44 +01:00
}
2020-05-08 16:17:09 +01:00
ResponseCmndNumber ( Settings . webcam_config . contrast - 2 ) ;
2020-05-02 15:44:44 +01:00
}
2020-05-03 17:37:12 +01:00
/*********************************************************************************************\
* Interface
\ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2020-05-02 07:10:23 +01:00
2020-05-07 15:53:56 +01:00
bool Xdrv81 ( uint8_t function ) {
2020-05-02 07:10:23 +01:00
bool result = false ;
switch ( function ) {
case FUNC_LOOP :
2020-05-12 13:30:07 +01:00
WcLoop ( ) ;
2020-05-02 07:10:23 +01:00
break ;
case FUNC_WEB_ADD_HANDLER :
2020-05-12 13:30:07 +01:00
WcPicSetup ( ) ;
2020-05-02 07:10:23 +01:00
break ;
2020-05-02 15:44:44 +01:00
case FUNC_WEB_ADD_MAIN_BUTTON :
2020-05-08 16:17:09 +01:00
WcShowStream ( ) ;
2020-05-05 08:36:43 +01:00
break ;
2020-05-02 15:44:44 +01:00
case FUNC_COMMAND :
result = DecodeCommand ( kWCCommands , WCCommand ) ;
break ;
2020-05-04 16:13:14 +01:00
case FUNC_PRE_INIT :
WcInit ( ) ;
break ;
2020-05-05 08:36:43 +01:00
2020-05-02 07:10:23 +01:00
}
return result ;
}
2020-05-03 17:37:12 +01:00
# endif // USE_WEBCAM
# endif // ESP32