From 5ced3090721beafa79bb28676cb18e825590cc55 Mon Sep 17 00:00:00 2001 From: Owen Carter Date: Wed, 7 Oct 2020 02:08:18 +0200 Subject: [PATCH] Change how we serve full and simple indexes, add portal page and logo to that (#68) * Implement one URI for index pages, takes a parameter, ?view=full|simple|etc * Add an access portal page, add a logo to that and the dump page. * Fix default myconfig * fix a stoopid, possibly disruptive bug that I introduced when unpacking html in my 1st commit --- Docs/logo.svg | 33 ++------- app_httpd.cpp | 109 ++++++++++++++++++++++------- css.h | 10 ++- viewers.h => index_other.h | 48 +++++++++---- index_ov2640.h | 12 ++-- index_ov3660.h | 2 +- myconfig.sample.h | 98 +++++++++++++------------- src/logo.h | 137 +++++++++++++++++++++++++++++++++++++ 8 files changed, 321 insertions(+), 128 deletions(-) rename viewers.h => index_other.h (88%) create mode 100644 src/logo.h diff --git a/Docs/logo.svg b/Docs/logo.svg index b5c25fe..b9b2adc 100644 --- a/Docs/logo.svg +++ b/Docs/logo.svg @@ -7,17 +7,13 @@ xmlns="http://www.w3.org/2000/svg" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" - inkscape:export-ydpi="532.84314" - inkscape:export-xdpi="532.84314" - inkscape:export-filename="/home/owen/Arduino/esp32-cam-webserver/Docs/logo-big.png" - id="图层_1" - data-name="图层 1" + id="espeye_1" + data-name="Esp Eye 1" viewBox="0 0 86.471377 86.479481" version="1.1" sodipodi:docname="logo.svg" width="86.471375" - height="86.479485" - inkscape:version="1.0 (4035a4fb49, 2020-05-01)"> + height="86.479485"> @@ -29,34 +25,13 @@ - 导航栏-菜单-logo + id="title6">ESP32 CAM Webserver - logo \n"); @@ -713,6 +720,7 @@ static esp_err_t dump_handler(httpd_req_t *req){ d+= sprintf(d,"\n"); d+= sprintf(d,"\n"); d+= sprintf(d,"\n\n"); + d+= sprintf(d,"\n"); d+= sprintf(d,"

ESP32 Cam Webserver

\n"); // Module d+= sprintf(d,"Name: %s
\n", myName); @@ -804,14 +812,6 @@ static esp_err_t style_handler(httpd_req_t *req){ return httpd_resp_send(req, (const char *)style_css, style_css_len); } -static esp_err_t miniviewer_handler(httpd_req_t *req){ - flashLED(75); - Serial.println("Simple viewer requested"); - httpd_resp_set_type(req, "text/html"); - httpd_resp_set_hdr(req, "Content-Encoding", "identity"); - return httpd_resp_send(req, (const char *)miniviewer_html, miniviewer_html_len); -} - static esp_err_t streamviewer_handler(httpd_req_t *req){ flashLED(75); Serial.println("Stream Viewer requested"); @@ -821,15 +821,74 @@ static esp_err_t streamviewer_handler(httpd_req_t *req){ } static esp_err_t index_handler(httpd_req_t *req){ + char* buf; + size_t buf_len; + char view[32] = {0,}; + flashLED(75); - Serial.println("Index page requested"); - httpd_resp_set_type(req, "text/html"); - httpd_resp_set_hdr(req, "Content-Encoding", "identity"); - sensor_t * s = esp_camera_sensor_get(); - if (s->id.PID == OV3660_PID) { - return httpd_resp_send(req, (const char *)index_ov3660_html, index_ov3660_html_len); + // See if we have a specific target (full/simple/?portal) and serve as appropriate + buf_len = httpd_req_get_url_query_len(req) + 1; + if (buf_len > 1) { + buf = (char*)malloc(buf_len); + if(!buf){ + httpd_resp_send_500(req); + return ESP_FAIL; + } + if (httpd_req_get_url_query_str(req, buf, buf_len) == ESP_OK) { + if (httpd_query_key_value(buf, "view", view, sizeof(view)) == ESP_OK) { + } else { + free(buf); + httpd_resp_send_404(req); + return ESP_FAIL; + } + } else { + free(buf); + httpd_resp_send_404(req); + return ESP_FAIL; + } + free(buf); + } else { + if (captivePortal) { + strcpy(view,"simple"); + } + // no target specified; default. + #if defined(DEFAULT_INDEX_FULL) + strcpy(view,"full"); + #else + strcpy(view,"simple"); + #endif + // If a captive portal page is created, we can use it here + if (captivePortal) { + strcpy(view,"portal"); + } + } + + if (strncmp(view,"simple", sizeof(view)) == 0) { + Serial.println("Simple index page requested"); + httpd_resp_set_type(req, "text/html"); + httpd_resp_set_hdr(req, "Content-Encoding", "identity"); + return httpd_resp_send(req, (const char *)index_simple_html, index_simple_html_len); + } else if(strncmp(view,"full", sizeof(view)) == 0) { + Serial.println("Full index page requested"); + httpd_resp_set_type(req, "text/html"); + httpd_resp_set_hdr(req, "Content-Encoding", "identity"); + sensor_t * s = esp_camera_sensor_get(); + if (s->id.PID == OV3660_PID) { + return httpd_resp_send(req, (const char *)index_ov3660_html, index_ov3660_html_len); + } + return httpd_resp_send(req, (const char *)index_ov2640_html, index_ov2640_html_len); + } else if(strncmp(view,"portal", sizeof(view)) == 0) { + //Prototype captive portal landing page. + Serial.println("Portal page requested"); + httpd_resp_set_type(req, "text/html"); + httpd_resp_set_hdr(req, "Content-Encoding", "identity"); + return httpd_resp_send(req, (const char *)portal_html, portal_html_len); + } else { + Serial.print("Unknown page requested: "); + Serial.println(view); + httpd_resp_send_404(req); + return ESP_FAIL; } - return httpd_resp_send(req, (const char *)index_ov2640_html, index_ov2640_html_len); } void startCameraServer(int hPort, int sPort){ @@ -860,12 +919,6 @@ void startCameraServer(int hPort, int sPort){ .handler = capture_handler, .user_ctx = NULL }; - httpd_uri_t miniviewer_uri = { - .uri = "/view", - .method = HTTP_GET, - .handler = miniviewer_handler, - .user_ctx = NULL - }; httpd_uri_t style_uri = { .uri = "/style.css", .method = HTTP_GET, @@ -890,14 +943,18 @@ void startCameraServer(int hPort, int sPort){ .handler = favicon_ico_handler, .user_ctx = NULL }; - // DEBUG + httpd_uri_t logo_svg_uri = { + .uri = "/logo.svg", + .method = HTTP_GET, + .handler = logo_svg_handler, + .user_ctx = NULL + }; httpd_uri_t dump_uri = { .uri = "/dump", .method = HTTP_GET, .handler = dump_handler, .user_ctx = NULL }; - // DEBUG httpd_uri_t stream_uri = { .uri = "/", .method = HTTP_GET, @@ -942,11 +999,11 @@ void startCameraServer(int hPort, int sPort){ httpd_register_uri_handler(camera_httpd, &cmd_uri); httpd_register_uri_handler(camera_httpd, &status_uri); httpd_register_uri_handler(camera_httpd, &capture_uri); - httpd_register_uri_handler(camera_httpd, &miniviewer_uri); httpd_register_uri_handler(camera_httpd, &style_uri); httpd_register_uri_handler(camera_httpd, &favicon_16x16_uri); httpd_register_uri_handler(camera_httpd, &favicon_32x32_uri); httpd_register_uri_handler(camera_httpd, &favicon_ico_uri); + httpd_register_uri_handler(camera_httpd, &logo_svg_uri); httpd_register_uri_handler(camera_httpd, &dump_uri); } diff --git a/css.h b/css.h index 0d7dc6a..26a35f7 100644 --- a/css.h +++ b/css.h @@ -1,9 +1,8 @@ /* - * Master CSS file included in the camer_index_* and miniviewer HTML + * Master CSS file for the camera pages */ -const uint8_t style_css[] = R"=====( -/* +const uint8_t style_css[] = R"=====(/* * CSS for the esp32 cam webserver */ @@ -358,7 +357,6 @@ select { @keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } -} -)====="; +})====="; -size_t style_css_len = sizeof(style_css); +size_t style_css_len = sizeof(style_css)-1; diff --git a/viewers.h b/index_other.h similarity index 88% rename from viewers.h rename to index_other.h index 644e723..5e307e8 100644 --- a/viewers.h +++ b/index_other.h @@ -1,14 +1,13 @@ /* - * Miniviewer and streamviewer + * simpleviewer and streamviewer */ - const uint8_t miniviewer_html[] = R"=====( - + const uint8_t index_simple_html[] = R"=====( - ESP32-CAM MiniViewer + ESP32-CAM Simplified View @@ -273,20 +272,18 @@ } swapButton.onclick = () => { - window.open('/','_self'); + window.open('/?view=full','_self'); } }) - -)====="; +)====="; -size_t miniviewer_html_len = sizeof(miniviewer_html); +size_t index_simple_html_len = sizeof(index_simple_html)-1; /* Stream Viewer */ - const uint8_t streamviewer_html[] = R"=====( - + const uint8_t streamviewer_html[] = R"=====( @@ -432,7 +429,32 @@ size_t miniviewer_html_len = sizeof(miniviewer_html); } }) - -)====="; +)====="; -size_t streamviewer_html_len = sizeof(streamviewer_html); +size_t streamviewer_html_len = sizeof(streamviewer_html)-1; + +/* Prototype Captive Portal page */ + + const uint8_t portal_html[] = R"=====( + + + + + ESP32-CAM portal + + + + + + +

ESP32 cam access portal

+ +
+ Camera Details
+ ESP32 cam webserver on GitHub +)====="; + +size_t portal_html_len = sizeof(portal_html)-1; diff --git a/index_ov2640.h b/index_ov2640.h index 36affd5..c7f3e23 100644 --- a/index_ov2640.h +++ b/index_ov2640.h @@ -2,8 +2,7 @@ * primary HTML for the OV2640 camera module */ -const uint8_t index_ov2640_html[] = R"=====( - +const uint8_t index_ov2640_html[] = R"=====( @@ -265,7 +264,7 @@ const uint8_t index_ov2640_html[] = R"=====(
@@ -589,7 +588,7 @@ const uint8_t index_ov2640_html[] = R"=====( } swapButton.onclick = () => { - window.open('/view','_self'); + window.open('/?view=simple','_self'); } // saveFaceButton.onclick = () => { @@ -626,7 +625,6 @@ const uint8_t index_ov2640_html[] = R"=====( }) - -)====="; +)====="; -size_t index_ov2640_html_len = sizeof(index_ov2640_html); +size_t index_ov2640_html_len = sizeof(index_ov2640_html)-1; diff --git a/index_ov3660.h b/index_ov3660.h index ce8c9a9..efbe8c0 100644 --- a/index_ov3660.h +++ b/index_ov3660.h @@ -636,4 +636,4 @@ const uint8_t index_ov3660_html[] = R"=====( )====="; -size_t index_ov3660_html_len = sizeof(index_ov3660_html); +size_t index_ov3660_html_len = sizeof(index_ov3660_html)-1; diff --git a/myconfig.sample.h b/myconfig.sample.h index a4e29d8..61b778c 100644 --- a/myconfig.sample.h +++ b/myconfig.sample.h @@ -14,29 +14,30 @@ * WiFi Settings * * Note the the use of commas as seperators in IP addresses! - */ - -// WiFi Credentials -// A structure for each WiFi network entry -struct station -{ - const char ssid[64]; // ssid (max 64 chars) - const char password[64]; // password (max 64 chars) - const bool dhcp; // dhcp -}; -/* - * Extend the list below with additional SSID+Password pairs like this: + * Extend the stationList[] below with additional SSID+Password pairs. + * The first block defines /what/ the structure holds + * The second block is where our list of ssid/passwords live -struct station stationList[] = {{"ssid1", "pass1", true}, - {"ssid2", "pass2", true}, - {"ssid3", "pass3", false}}; +struct station { + const char ssid[64]; // - ssid (max 64 chars) + const char password[64]; // - password (max 64 chars) + const bool dhcp; // - dhcp +} station stationList[] = {{"ssid1", "pass1", true}, + {"ssid2", "pass2", true}, + {"ssid3", "pass3", false}}; - * The first entry will be used for the AccessPoint ssid and password when it is enabled + * The first entry in the stationList[] is special, if WIFI_AP_ENABLE has been uncommented (below) + * it will be used for the AccessPoint ssid and password. + * * The 'dhcp' setting controls wether the station uses static IP settings (if in doubt leave 'true') * Note the use of nested braces '{' and '}' to group each entry, and commas ',' to seperate them. */ -struct station stationList[] = {{"my_ssid","my_password", false}}; +struct station { + const char ssid[64]; // ssid (max 64 chars) + const char password[64]; // password (max 64 chars) + const bool dhcp; // dhcp +} stationList[] = {{"my_ssid","my_password", true}}; /* @@ -45,7 +46,7 @@ struct station stationList[] = {{"my_ssid","my_password", false}}; * Note: The same settings will be applied to all client connections where the dhcp setting is 'false' * You must define all three: IP, Gateway and NetMask */ -// warning - IP addresses must be seperated with commas (,) and not decimals (.) +// warning - IP addresses must be seperated with commas (,) and not decimals (.) here // #define ST_IP 192,168,0,16 // #define ST_GATEWAY 192,168,0,2 // #define ST_NETMASK 255,255,255,0 @@ -59,28 +60,24 @@ struct station stationList[] = {{"my_ssid","my_password", false}}; * Uncomment to enable AP mode; * */ -#define WIFI_AP_ENABLE +// #define WIFI_AP_ENABLE /* AP Mode Notes: * * Once enabled the AP ssid and password will be taken from the 1st entry in the stationList[] above. * - * If there are further entries listed they will be scanned at startup and connected to if they are found. - * Making the AP a fallback mode that happens only when there are no 'real' networks available + * If there are more entries listed they will be scanned at startup in the normal way and connected to + * if they are found. AP then works as a fallback mode for when there are no 'real' networks available. * - * Setting the dhcp field to true for the AP enables a captive portal and attempts to send - * all incoming pages to the webcam page, with varying degrees of success depending on the visitors + * Setting the 'dhcp' field to true for the AP enables a captive portal and attempts to send + * all visitors to the webcam page, with varying degrees of success depending on the visitors * browser and other settings. - * - The Captive Portal really needs a seperate landing page instead of using the 'main' page. - * Browsers and OS's restrict landing page functions, since they have been abused by marketing types - * and other low quality people. Video/Audio playback and Javascript are commonly disabled as a result. */ - -// AccessPoint; optionally change the ip address (default = 192.168.4.1) -// warning - IP addresses must be seperated with commas (,) and not decimals (.) +// Optionally change the AccessPoint ip address (default = 192.168.4.1) +// warning - IP addresses must be seperated with commas (,) and not decimals (.) here // #define AP_ADDRESS 192,168,4,1 -// AccessPoint; Uncomment this to force the channel number, default = 1 +// Uncomment this to force the AccessPoint channel number, default = 1 // #define AP_CHAN 1 /* @@ -98,21 +95,9 @@ struct station stationList[] = {{"my_ssid","my_password", false}}; //#define WIFI_WATCHDOG 5000 /* - * Camera Hardware Settings + * Camera Defaults * - * You must uncomment one, and only one, of the lines below to select your board model. - * Remember to also select the board in the Boards Manager - * This is not optional */ -#define CAMERA_MODEL_AI_THINKER // default -// #define CAMERA_MODEL_WROVER_KIT -// #define CAMERA_MODEL_ESP_EYE -// #define CAMERA_MODEL_M5STACK_PSRAM -// #define CAMERA_MODEL_M5STACK_V2_PSRAM -// #define CAMERA_MODEL_M5STACK_WIDE -// #define CAMERA_MODEL_M5STACK_ESP32CAM // Originally: CAMERA_MODEL_M5STACK_NO_PSRAM -// #define CAMERA_MODEL_TTGO_T_JOURNAL - // Initial Reslolution, default SVGA // available values are: FRAMESIZE_[QQVGA|HQVGA|QVGA|CIF|VGA|SVGA|XGA|SXGA|UXGA|QXGA(ov3660)] // #define DEFAULT_RESOLUTION FRAMESIZE_SVGA @@ -129,7 +114,10 @@ struct station stationList[] = {{"my_ssid","my_password", false}}; /* * Additional Features + * */ +// Default Page: uncomment to make the full control page the default, otherwise show simple viewer +// #define DEFAULT_INDEX_FULL // Uncomment to disable the illumination lamp features // #define LAMP_DISABLE @@ -137,11 +125,29 @@ struct station stationList[] = {{"my_ssid","my_password", false}}; // Define a initial lamp setting as a percentage, defaults to 0% // #define LAMP_DEFAULT 0 -// Assume we have SPIFFS/LittleFS partition, uncomment if not +// Assume we have SPIFFS/LittleFS partition, uncomment to disable this. +// Controls will still be shown in the UI but are inoperative. // #define NO_FS // Uncomment to enable Face Detection (+ Recognition if desired) by default -// Notes: You must set DEFAULT_RESOLUTION, above, to FRAMESIZE_CIF or lower -// Face recognition enrolements will be lost between reboots. +// Notes: You must set DEFAULT_RESOLUTION (above) to FRAMESIZE_CIF or lower before enabling this +// Face recognition enrolements are currently lost between reboots. // #define FACE_DETECTION // #define FACE_RECOGNITION + + +/* + * Camera Hardware Selectiom + * + * You must uncomment one, and only one, of the lines below to select your board model. + * Remember to also select the board in the Boards Manager + * This is not optional + */ +#define CAMERA_MODEL_AI_THINKER // default +// #define CAMERA_MODEL_WROVER_KIT +// #define CAMERA_MODEL_ESP_EYE +// #define CAMERA_MODEL_M5STACK_PSRAM +// #define CAMERA_MODEL_M5STACK_V2_PSRAM +// #define CAMERA_MODEL_M5STACK_WIDE +// #define CAMERA_MODEL_M5STACK_ESP32CAM // Originally: CAMERA_MODEL_M5STACK_NO_PSRAM +// #define CAMERA_MODEL_TTGO_T_JOURNAL diff --git a/src/logo.h b/src/logo.h new file mode 100644 index 0000000..754388e --- /dev/null +++ b/src/logo.h @@ -0,0 +1,137 @@ +/* + * Logo (svg format) + */ + +const uint8_t logo_svg[] = R"=====( + + + + + image/svg+xml + + + + + + + + ESP32 CAM Webserver - logo + + + + + + + + + + + + + + + + + + + + + + + + + + +)====="; + +size_t logo_svg_len = sizeof(logo_svg)-1;