From f66a0ee561fa794119163aeceeb13660a77d4c4a Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Fri, 8 May 2020 17:17:09 +0200 Subject: [PATCH] Add Webcam commands --- tasmota/settings.h | 35 +++++++- tasmota/xdrv_81_webcam.ino | 176 ++++++++++++++++++++++++++----------- 2 files changed, 157 insertions(+), 54 deletions(-) diff --git a/tasmota/settings.h b/tasmota/settings.h index 6b74cb269..3925aa30d 100644 --- a/tasmota/settings.h +++ b/tasmota/settings.h @@ -182,6 +182,35 @@ typedef union { }; } Timer; +typedef union { // Restricted by MISRA-C Rule 18.4 but so useful... + uint32_t data; + struct { + uint32_t stream : 1; + uint32_t mirror : 1; + uint32_t flip : 1; + uint32_t spare3 : 1; + uint32_t spare4 : 1; + uint32_t spare5 : 1; + uint32_t spare6 : 1; + uint32_t spare7 : 1; + uint32_t spare8 : 1; + uint32_t spare9 : 1; + uint32_t spare10 : 1; + uint32_t spare11 : 1; + uint32_t spare12 : 1; + uint32_t spare13 : 1; + uint32_t spare14 : 1; + uint32_t spare15 : 1; + uint32_t spare16 : 1; + uint32_t spare17 : 1; + uint32_t spare18 : 1; + uint32_t contrast : 3; + uint32_t brightness : 3; + uint32_t saturation : 3; + uint32_t resolution : 4; + }; +} WebCamCfg; + typedef union { uint16_t data; struct { @@ -365,9 +394,11 @@ struct { myio my_gp; // 3AC - 2 x 40 bytes (ESP32) mytmplt user_template; // 3FC - 2 x 37 bytes (ESP32) - uint8_t free_esp32_446[10]; // 446 + uint8_t free_esp32_446[6]; // 446 - uint8_t esp32_webcam_resolution; // 450 + WebCamCfg webcam_config; // 44C + + uint8_t free_esp32_450[1]; // 450 #endif // ESP8266 - ESP32 char serial_delimiter; // 451 diff --git a/tasmota/xdrv_81_webcam.ino b/tasmota/xdrv_81_webcam.ino index a317b1aaa..15c2dd4ba 100644 --- a/tasmota/xdrv_81_webcam.ino +++ b/tasmota/xdrv_81_webcam.ino @@ -230,11 +230,19 @@ uint32_t wc_setup(int32_t fsiz) { sensor_t * wc_s = esp_camera_sensor_get(); // initial sensors are flipped vertically and colors are a bit saturated +/* 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 } +*/ + 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 + // drop down frame size for higher initial frame rate wc_s->set_framesize(wc_s, (framesize_t)fsiz); @@ -243,7 +251,6 @@ uint32_t wc_setup(int32_t fsiz) { wc_height = wc_fb->height; esp_camera_fb_return(wc_fb); - #ifdef USE_FACE_DETECT fd_init(); #endif @@ -461,11 +468,11 @@ WiFiClient client; void handleMjpeg(void) { AddLog_P2(WC_LOGLEVEL, PSTR("CAM: Handle camserver")); - //if (!wc_stream_active) { + if (!wc_stream_active) { wc_stream_active = 1; client = CamServer->client(); AddLog_P2(WC_LOGLEVEL, PSTR("CAM: Create client")); - //} + } } #ifdef USE_FACE_DETECT @@ -613,11 +620,9 @@ void handleMjpeg_task(void) { bool jpeg_converted = false; if (!client.connected()) { - wc_stream_active = 0; AddLog_P2(WC_LOGLEVEL, PSTR("CAM: Client fail")); - goto exit; + wc_stream_active = 0; } - if (1 == wc_stream_active) { client.flush(); client.setTimeout(3); @@ -626,15 +631,15 @@ void handleMjpeg_task(void) { "Content-Type: multipart/x-mixed-replace;boundary=" BOUNDARY "\r\n" "\r\n"); wc_stream_active = 2; - } else { + } + if (2 == wc_stream_active) { wc_fb = esp_camera_fb_get(); if (!wc_fb) { - wc_stream_active = 0; AddLog_P2(WC_LOGLEVEL, PSTR("CAM: Frame fail")); - goto exit; + wc_stream_active = 0; } - - + } + if (2 == wc_stream_active) { if (wc_fb->format != PIXFORMAT_JPEG) { jpeg_converted = frame2jpg(wc_fb, 80, &_jpg_buf, &_jpg_buf_len); if (!jpeg_converted){ @@ -673,13 +678,11 @@ void handleMjpeg_task(void) { if (jpeg_converted) { free(_jpg_buf); } esp_camera_fb_return(wc_fb); //AddLog_P2(WC_LOGLEVEL, PSTR("CAM: send frame")); - -exit: - if (!wc_stream_active) { - AddLog_P2(WC_LOGLEVEL, PSTR("CAM: Stream exit")); - client.flush(); - client.stop(); - } + } + if (0 == wc_stream_active) { + AddLog_P2(WC_LOGLEVEL, PSTR("CAM: Stream exit")); + client.flush(); + client.stop(); } } @@ -750,13 +753,6 @@ void detect_motion(void) { } } -void wc_show_stream(void) { - if (CamServer) { - WSContentSend_P(PSTR("

Webcam stream

"), - WiFi.localIP().toString().c_str()); - } -} - uint32_t wc_set_streamserver(uint32_t flag) { if (global_state.wifi_down) { return 0; } @@ -783,17 +779,30 @@ uint32_t wc_set_streamserver(uint32_t flag) { return 0; } -void WcStreamControl(uint32_t resolution) { - wc_set_streamserver(resolution); - /*if (0 == resolution) { - resolution=-1; - }*/ +void WcStreamControl() { + wc_set_streamserver(Settings.webcam_config.stream); + int resolution = (!Settings.webcam_config.stream) ? -1 : Settings.webcam_config.resolution; wc_setup(resolution); } +void WcShowStream(void) { + if (Settings.webcam_config.stream) { + if (!CamServer) { + WcStreamControl(); + delay(50); // Give the webcam webserver some time to prepare the stream + } + if (CamServer) { + WSContentSend_P(PSTR("

Webcam stream

"), + WiFi.localIP().toString().c_str()); + } + } +} + void wc_loop(void) { - if (CamServer) { CamServer->handleClient(); } - if (wc_stream_active) { handleMjpeg_task(); } + if (CamServer) { + CamServer->handleClient(); + if (wc_stream_active) { handleMjpeg_task(); } + } if (motion_detect) { detect_motion(); } #ifdef USE_FACE_DETECT if (face_detect_time) { detect_face(); } @@ -833,34 +842,103 @@ red led = gpio 33 */ void WcInit(void) { - if (Settings.esp32_webcam_resolution > 10) { - Settings.esp32_webcam_resolution = 0; + 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 } } - /*********************************************************************************************\ * Commands \*********************************************************************************************/ -#define D_CMND_WEBCAM "Webcam" +#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 = "|" // no prefix - D_CMND_WEBCAM +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 ; void (* const WCCommand[])(void) PROGMEM = { - &CmndWebcam, + &CmndWebcam, &CmndWebcamStream, &CmndWebcamResolution, &CmndWebcamMirror, &CmndWebcamFlip, + &CmndWebcamSaturation, &CmndWebcamBrightness, &CmndWebcamContrast }; void CmndWebcam(void) { - uint32_t flag = 0; - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 10)) { - Settings.esp32_webcam_resolution=XdrvMailbox.payload; - WcStreamControl(Settings.esp32_webcam_resolution); + 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 } - if (CamServer) { flag = 1; } - Response_P(PSTR("{\"" D_CMND_WEBCAM "\":{\"Streaming\":\"%s\"}"),GetStateText(flag)); + ResponseCmndStateText(Settings.webcam_config.stream); +} + +void CmndWebcamResolution(void) { + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 10)) { + Settings.webcam_config.resolution = XdrvMailbox.payload; + wc_set_options(0, Settings.webcam_config.resolution); + } + ResponseCmndNumber(Settings.webcam_config.resolution); +} + +void CmndWebcamMirror(void) { + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 1)) { + Settings.webcam_config.mirror = XdrvMailbox.payload; + wc_set_options(3, Settings.webcam_config.mirror); + } + ResponseCmndStateText(Settings.webcam_config.mirror); +} + +void CmndWebcamFlip(void) { + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 1)) { + Settings.webcam_config.flip = XdrvMailbox.payload; + wc_set_options(2, Settings.webcam_config.flip); + } + ResponseCmndStateText(Settings.webcam_config.flip); +} + +void CmndWebcamSaturation(void) { + if ((XdrvMailbox.payload >= -2) && (XdrvMailbox.payload <= 2)) { + Settings.webcam_config.saturation = XdrvMailbox.payload +2; + wc_set_options(6, Settings.webcam_config.saturation -2); + } + ResponseCmndNumber(Settings.webcam_config.saturation -2); +} + +void CmndWebcamBrightness(void) { + if ((XdrvMailbox.payload >= -2) && (XdrvMailbox.payload <= 2)) { + Settings.webcam_config.brightness = XdrvMailbox.payload +2; + wc_set_options(5, Settings.webcam_config.brightness -2); + } + ResponseCmndNumber(Settings.webcam_config.brightness -2); +} + +void CmndWebcamContrast(void) { + if ((XdrvMailbox.payload >= -2) && (XdrvMailbox.payload <= 2)) { + Settings.webcam_config.contrast = XdrvMailbox.payload +2; + wc_set_options(4, Settings.webcam_config.contrast -2); + } + ResponseCmndNumber(Settings.webcam_config.contrast -2); } /*********************************************************************************************\ @@ -878,13 +956,7 @@ bool Xdrv81(uint8_t function) { wc_pic_setup(); break; case FUNC_WEB_ADD_MAIN_BUTTON: - if (Settings.esp32_webcam_resolution) { -//#ifndef USE_SCRIPT - WcStreamControl(Settings.esp32_webcam_resolution); - delay(50); // Give the webcam webserver some time to prepare the stream - wc_show_stream(); -//#endif - } + WcShowStream(); break; case FUNC_COMMAND: result = DecodeCommand(kWCCommands, WCCommand);