diff --git a/docs/Commands.txt b/docs/Commands.txt index d862b313..af80ca34 100644 --- a/docs/Commands.txt +++ b/docs/Commands.txt @@ -82,7 +82,10 @@ The json format is { * Get/Set Camera command value / list all values in JSON/plain label can be: light/framesize/quality/contrast/brightness/saturation/gainceiling/colorbar/awb/agc/aec/hmirror/vflip/awb_gain/agc_gain/aec_value/aec2/cw/bpc/wpc/raw_gma/lenc/special_effect/wb_mode/ae_level -[ESP170] json= pwd= +[ESP170] json= pwd= + +* Save frame to target path and filename (default target = today date, default name=timestamp.jpg) +[ESP171] * Get/Set Ftp state which can be ON, OFF, CLOSE [ESP180] json= pwd= diff --git a/esp3d/src/core/commands.cpp b/esp3d/src/core/commands.cpp index 20a44140..d48cd55e 100644 --- a/esp3d/src/core/commands.cpp +++ b/esp3d/src/core/commands.cpp @@ -492,11 +492,16 @@ bool Commands::execute_internal_command (int cmd, const char* cmd_params, level_ #endif //WS_DATA_FEATURE #ifdef CAMERA_DEVICE //Get/Set Camera command value / list all values in JSON/plain - //[ESP170]label=pwd= + //[ESP170]label= pwd= //label can be: light/framesize/quality/contrast/brightness/saturation/gainceiling/colorbar/awb/agc/aec/hmirror/vflip/awb_gain/agc_gain/aec_value/aec2/cw/bpc/wpc/raw_gma/lenc/special_effect/wb_mode/ae_level case 170: response = ESP170(cmd_params, auth_type, output); break; + //Save frame to target path and filename (default target = today date, default name=timestamp.jpg) + //[ESP171]path= filename= pwd= + case 171: + response = ESP171(cmd_params, auth_type, output); + break; #endif //CAMERA_DEVICE #ifdef FTP_FEATURE //Set Ftp state which can be ON, OFF diff --git a/esp3d/src/core/commands.h b/esp3d/src/core/commands.h index 887fde0e..bd66ffbf 100644 --- a/esp3d/src/core/commands.h +++ b/esp3d/src/core/commands.h @@ -85,6 +85,7 @@ public: #endif //WS_DATA_FEATURE #if defined(CAMERA_DEVICE) bool ESP170(const char* cmd_params, level_authenticate_type auth_level, ESP3DOutput * output); + bool ESP171(const char* cmd_params, level_authenticate_type auth_level, ESP3DOutput * output); #endif //CAMERA_DEVICE #if defined(FTP_FEATURE) bool ESP180(const char* cmd_params, level_authenticate_type auth_level, ESP3DOutput * output); diff --git a/esp3d/src/core/espcmd/ESP0.cpp b/esp3d/src/core/espcmd/ESP0.cpp index c765f20b..a7028063 100644 --- a/esp3d/src/core/espcmd/ESP0.cpp +++ b/esp3d/src/core/espcmd/ESP0.cpp @@ -67,7 +67,8 @@ const char * help[]= {"[ESP] (id) - display this help", "[ESP161](Port) - display/set WebSocket port", #endif //WS_DATA_FEATURE #if defined(CAMERA_DEVICE) - "[ESP170](plain) (label=value) - display(JSON/plain)/set Camera commands", + "[ESP170](json) (label=value) - display/set Camera commands", + "[ESP171] (path=) (filename=) Save frame to target path and filename", #endif //CAMERA_DEVICE #if defined(FTP_FEATURE) "[ESP180](State) - display/set FTP state which can be ON, OFF", @@ -203,6 +204,7 @@ const uint cmdlist[]= {0, #endif //WS_DATA_FEATURE #if defined(CAMERA_DEVICE) 170, + 171, #endif //CAMERA_DEVICE #if defined(FTP_FEATURE) 180, diff --git a/esp3d/src/core/espcmd/ESP171.cpp b/esp3d/src/core/espcmd/ESP171.cpp new file mode 100644 index 00000000..00bbf2ac --- /dev/null +++ b/esp3d/src/core/espcmd/ESP171.cpp @@ -0,0 +1,114 @@ +/* + ESP122.cpp - ESP3D command class + + Copyright (c) 2014 Luc Lebosse. All rights reserved. + + This code is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This code 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with This code; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ +#include "../../include/esp3d_config.h" +#if defined (CAMERA_DEVICE) +#include "../commands.h" +#include "../esp3doutput.h" +#include "esp_camera.h" +#include "../settings_esp3d.h" +#include "../../modules/authentication/authentication_service.h" +#include "../../modules/camera/camera.h" +#include +#define COMMANDID 171 +//Save frame to target path and filename (default target = today date, default name=timestamp.jpg) +//[ESP171]path= filename= pwd= +bool Commands::ESP171(const char* cmd_params, level_authenticate_type auth_type, ESP3DOutput * output) +{ + bool noError = true; + bool json = has_tag (cmd_params, "json"); + String response; + String parameter; + int errorCode = 200; //unless it is a server error use 200 as default and set error in json instead + String path; + String filename; +#ifdef AUTHENTICATION_FEATURE + if (auth_type == LEVEL_GUEST) { + response = format_response(COMMANDID, json, false, "Guest user can't use this command"); + noError = false; + errorCode = 401; + } +#else + (void)auth_type; +#endif //AUTHENTICATION_FEATURE + if(noError) { + if (!esp3d_camera.started()) { + response = format_response(COMMANDID, json, false, "No camera initialized"); + noError = false; + } else { + parameter = clean_param(get_param (cmd_params, "path=")); + //get path + if (parameter.length() != 0) { + + path = parameter; + } + parameter = clean_param(get_param (cmd_params, "filename=")); + //get filename + if (parameter.length() != 0) { + filename = parameter; + } + //if nothing provided, use default filename / path + if (path.length()==0) { + struct tm tmstruct; + time_t now; + path = ""; + time(&now); + localtime_r(&now, &tmstruct); + path = String((tmstruct.tm_year)+1900) + "-"; + if (((tmstruct.tm_mon)+1) < 10) { + path +="0"; + } + path += String(( tmstruct.tm_mon)+1) + "-"; + if (tmstruct.tm_mday < 10) { + path +="0"; + } + path += String(tmstruct.tm_mday); + } + if(filename.length()==0) { + struct tm tmstruct; + time_t now; + time(&now); + localtime_r(&now, &tmstruct); + filename = String(now) + ".jpg"; + } + + //now send command + if(noError) { + noError = esp3d_camera.handle_snap(nullptr,path.c_str(), filename.c_str()); + if(noError) { + response = format_response(COMMANDID, json, true, "Snapshot taken"); + } else { + response = format_response(COMMANDID, json, false, "Error taking snapshot"); + } + } + } + } + if (noError) { + if (json) { + output->printLN (response.c_str() ); + } else { + output->printMSG (response.c_str() ); + } + } else { + output->printERROR(response.c_str(), errorCode); + } + return noError; +} + +#endif //CAMERA_DEVICE diff --git a/esp3d/src/include/version.h b/esp3d/src/include/version.h index e4da69b6..57314e66 100644 --- a/esp3d/src/include/version.h +++ b/esp3d/src/include/version.h @@ -22,7 +22,7 @@ #define _VERSION_ESP3D_H //version and sources location -#define FW_VERSION "3.0.0.a200" +#define FW_VERSION "3.0.0.a201" #define REPOSITORY "https://github.com/luc-github/ESP3D/tree/3.0" #endif //_VERSION_ESP3D_H diff --git a/esp3d/src/modules/camera/camera.cpp b/esp3d/src/modules/camera/camera.cpp index 813ff8e4..0fcdd3b9 100644 --- a/esp3d/src/modules/camera/camera.cpp +++ b/esp3d/src/modules/camera/camera.cpp @@ -26,8 +26,10 @@ #include #include //not sure this one is needed #include - #include +#if defined (SD_DEVICE) +#include "../filesystem/esp_sd.h" +#endif //SD_DEVICE #define DEFAULT_FRAME_SIZE FRAMESIZE_SVGA @@ -35,45 +37,54 @@ Camera esp3d_camera; -void Camera::handle_snap(WebServer * webserver) +bool Camera::handle_snap(WebServer * webserver, const char *path, const char* filename) { log_esp3d("Camera stream reached"); if (!_initialised) { log_esp3d("Camera not started"); - webserver->send (500, "text/plain", "Camera not started"); - return; + if (webserver) { + webserver->send (500, "text/plain", "Camera not started"); + } + return false; } sensor_t * s = esp_camera_sensor_get(); - if (webserver->hasArg ("framesize") ) { - if(s->status.framesize != webserver->arg ("framesize").toInt()) { - command("framesize", webserver->arg ("framesize").c_str()); + if (webserver) { + if (webserver->hasArg ("framesize") ) { + if(s->status.framesize != webserver->arg ("framesize").toInt()) { + command("framesize", webserver->arg ("framesize").c_str()); + } + } + if (webserver->hasArg ("hmirror") ) { + command("hmirror", webserver->arg ("hmirror").c_str()); + } + if (webserver->hasArg ("vflip") ) { + command("vflip", webserver->arg ("vflip").c_str()); + } + if (webserver->hasArg ("wb_mode") ) { + command("wb_mode", webserver->arg ("wb_mode").c_str()); } - } - if (webserver->hasArg ("hmirror") ) { - command("hmirror", webserver->arg ("hmirror").c_str()); - } - if (webserver->hasArg ("vflip") ) { - command("vflip", webserver->arg ("vflip").c_str()); - } - if (webserver->hasArg ("wb_mode") ) { - command("wb_mode", webserver->arg ("wb_mode").c_str()); - } #ifdef ESP_ACCESS_CONTROL_ALLOW_ORIGIN - webserver->enableCrossOrigin(true); + webserver->enableCrossOrigin(true); #endif //ESP_ACCESS_CONTROL_ALLOw_ORIGIN + } camera_fb_t * fb = NULL; bool res_error = false; size_t _jpg_buf_len = 0; uint8_t * _jpg_buf = NULL; - webserver->sendHeader(String(F("Content-Type")), String(F("image/jpeg")),true); - webserver->sendHeader(String(F("Content-Disposition")), String(F("inline; filename=capture.jpg")),true); - webserver->setContentLength(CONTENT_LENGTH_UNKNOWN); - webserver->send(200); + if (webserver) { + webserver->sendHeader(String(F("Content-Type")), String(F("image/jpeg")),true); + webserver->sendHeader(String(F("Content-Disposition")), String(F("inline; filename=capture.jpg")),true); + webserver->setContentLength(CONTENT_LENGTH_UNKNOWN); + webserver->send(200); + } log_esp3d("Camera capture ongoing"); fb = esp_camera_fb_get(); if (!fb) { log_esp3d("Camera capture failed"); - webserver->send (500, "text/plain", "Capture failed"); + if (webserver) { + webserver->send (500, "text/plain", "Capture failed"); + } + res_error=true; } else { if(fb->format != PIXFORMAT_JPEG) { bool jpeg_converted = frame2jpg(fb, JPEG_COMPRESSION, &_jpg_buf, &_jpg_buf_len); @@ -89,7 +100,49 @@ void Camera::handle_snap(WebServer * webserver) } } if (!res_error) { - webserver->sendContent_P ((const char *)_jpg_buf, _jpg_buf_len); + if(webserver) { + webserver->sendContent_P ((const char *)_jpg_buf, _jpg_buf_len); + } +#if defined (SD_DEVICE) + if (filename!=nullptr && path!=nullptr) { + if (!ESP_SD::accessFS()) { + res_error = true; + log_esp3d("SD not available"); + } else { + if (ESP_SD::getState(true) == ESP_SDCARD_NOT_PRESENT) { + res_error = true; + log_esp3d("No SD"); + } else { + ESP_SD::setState(ESP_SDCARD_BUSY ); + String wpath = path[0]=='/' ? path : String("/")+path; + if (!ESP_SD::exists(wpath.c_str())) { + res_error = !ESP_SD::mkdir(wpath.c_str()); + } + if (!res_error) { + if (wpath[wpath.length()-1]!='/') { + wpath += "/"; + } + wpath +=filename ; + ESP_SDFile f = ESP_SD::open(wpath.c_str(), ESP_FILE_WRITE); + if (f) { + f.write((const uint8_t *)_jpg_buf, _jpg_buf_len); + f.close(); + log_esp3d("Camera capture done"); + } else { + res_error = true; + log_esp3d("Failed to open file for writing"); + } + } + } + ESP_SD::releaseFS(); + } + + } +#endif //SD_DEVICE + if (!webserver && filename==nullptr && path==nullptr) { + log_esp3d("No output defined"); + res_error = true; + } } if(fb) { @@ -100,7 +153,10 @@ void Camera::handle_snap(WebServer * webserver) free(_jpg_buf); _jpg_buf = NULL; } - webserver->sendContent(""); + if(webserver) { + webserver->sendContent(""); + } + return !res_error; } Camera::Camera() diff --git a/esp3d/src/modules/camera/camera.h b/esp3d/src/modules/camera/camera.h index a03056f9..5de1eecd 100644 --- a/esp3d/src/modules/camera/camera.h +++ b/esp3d/src/modules/camera/camera.h @@ -33,7 +33,7 @@ public: void end(); bool initHardware(); bool stopHardware(); - void handle_snap(WebServer * webserver); + bool handle_snap(WebServer * webserver, const char *path=NULL, const char* filename=NULL); void handle(); int command(const char * param, const char * value); uint8_t GetModel();