diff --git a/app_httpd.cpp b/app_httpd.cpp index c324f85..f83305d 100644 --- a/app_httpd.cpp +++ b/app_httpd.cpp @@ -16,6 +16,7 @@ #include "esp_camera.h" #include "img_converters.h" #include "Arduino.h" +#include #include "index_ov2640.h" #include "index_ov3660.h" @@ -31,14 +32,22 @@ extern void flashLED(int flashtime); extern void setLamp(int newVal); // External variables declared in main .ino -extern char myName[]; // Camera Name -extern char myVer[]; // Firmware Build Info -extern int myRotation; // Rotation -extern int lampVal; // The current Lamp value -extern char streamURL[]; // Stream URL -extern int8_t detection_enabled; // Face detection enable -extern int8_t recognition_enabled; // Face recognition enable -extern bool filesystem; // Save/restore features enabled +extern char myName[]; +extern char myVer[]; +extern int myRotation; +extern int lampVal; +extern char streamURL[]; +extern int8_t detection_enabled; +extern int8_t recognition_enabled; +extern bool filesystem; +extern int httpPort; +extern int streamPort; +extern IPAddress ip; +extern IPAddress net; +extern IPAddress gw; +extern int sketchSize; +extern int sketchSpace; +extern String sketchMD5; #include "fb_gfx.h" #include "fd_forward.h" @@ -659,7 +668,6 @@ static esp_err_t info_handler(httpd_req_t *req){ char * p = json_response; *p++ = '{'; p+=sprintf(p, "\"cam_name\":\"%s\",", myName); - p+=sprintf(p, "\"code_ver\":\"%s\",", myVer); p+=sprintf(p, "\"rotate\":\"%d\",", myRotation); p+=sprintf(p, "\"stream_url\":\"%s\"", streamURL); *p++ = '}'; @@ -687,19 +695,81 @@ static esp_err_t favicon_ico_handler(httpd_req_t *req){ return httpd_resp_send(req, (const char *)favicon_ico, favicon_ico_len); } -// DEBUG -extern void dumpPrefs(fs::FS &fs); -static esp_err_t dump_prefs_handler(httpd_req_t *req){ +static esp_err_t dump_handler(httpd_req_t *req){ flashLED(75); + Serial.println("\nDump Requested"); + Serial.print("Preferences file: "); dumpPrefs(SPIFFS); - Serial.printf("mtmn_config size: %u :: ra_filter size: %u :: id_list %u\n", sizeof(mtmn_config), sizeof(ra_filter), sizeof(id_list)); - httpd_resp_set_type(req, "text/css"); + static char dumpOut[1200] = "n"; + char * d = dumpOut; + // Header + d+= sprintf(d,"\n"); + d+= sprintf(d,"\n"); + d+= sprintf(d,"%s - Status\n", myName); + d+= sprintf(d,"\n"); + d+= sprintf(d,"\n"); + d+= sprintf(d,"\n"); + d+= sprintf(d,"\n\n"); + d+= sprintf(d,"

ESP32 Cam Webserver

\n"); + // Module + d+= sprintf(d,"Name: %s
\n", myName); + Serial.printf("Name: %s\n", myName); + d+= sprintf(d,"Firmware: %s
\n", myVer); + Serial.printf("Firmware: %s\n", myVer); + float sketchPct = 100 * sketchSize / sketchSpace; + d+= sprintf(d,"Sketch Size: %i (total: %i, %.1f%% used)
\n", sketchSize, sketchSpace, sketchPct); + Serial.printf("Sketch Size: %i (total: %i, %.1f%% used)\n", sketchSize, sketchSpace, sketchPct); + d+= sprintf(d,"MD5: %s
\n", sketchMD5.c_str()); + Serial.printf("MD5: %s\n", sketchMD5.c_str()); + d+= sprintf(d,"ESP sdk: %s
\n", ESP.getSdkVersion()); + Serial.printf("ESP sdk: %s\n", ESP.getSdkVersion()); + // Network + d+= sprintf(d,"

WiFi

\n"); + String ssidName = WiFi.SSID(); + d+= sprintf(d,"SSID: %s
\n", ssidName.c_str()); + Serial.printf("Ssid: %s\n", ssidName.c_str()); + d+= sprintf(d,"Rssi: %i
\n", WiFi.RSSI()); + Serial.printf("Rssi: %i\n", WiFi.RSSI()); + d+= sprintf(d,"Http port: %i, Stream port: %i
\n", httpPort, streamPort); + Serial.printf("Http port: %i, Stream port: %i\n", httpPort, streamPort); + d+= sprintf(d,"IP address: %d.%d.%d.%d
\n", ip[0], ip[1], ip[2], ip[3]); + Serial.printf("IP address: %d.%d.%d.%d\n", ip[0], ip[1], ip[2], ip[3]); + d+= sprintf(d,"Netmask: %d.%d.%d.%d
\n", net[0], net[1], net[2], net[3]); + Serial.printf("Netmask: %d.%d.%d.%d\n", net[0], net[1], net[2], net[3]); + d+= sprintf(d,"Gateway: %d.%d.%d.%d
\n", gw[0], gw[1], gw[2], gw[3]); + Serial.printf("Gateway: %d.%d.%d.%d\n", gw[0], gw[1], gw[2], gw[3]); + // System + d+= sprintf(d,"

System

\n"); + int64_t sec = esp_timer_get_time() / 1000000; + int64_t upDays = int64_t(floor(sec/86400)); + int upHours = int64_t(floor(sec/3600)) % 24; + int upMin = int64_t(floor(sec/60)) % 60; + int upSec = sec % 60; + d+= sprintf(d,"Up: %" PRId64 ":%02i:%02i:%02i (d:h:m:s)
\n", upDays, upHours, upMin, upSec); + Serial.printf("Up: %" PRId64 ":%02i:%02i:%02i (d:h:m:s)\n", upDays, upHours, upMin, upSec); + d+= sprintf(d,"Freq: %i MHz
\n", ESP.getCpuFreqMHz()); + Serial.printf("Freq: %i MHz\n", ESP.getCpuFreqMHz()); + d+= sprintf(d,"Heap: %i, free: %i, min free: %i, max block: %i
\n", ESP.getHeapSize(), ESP.getFreeHeap(), ESP.getMinFreeHeap(), ESP.getMaxAllocHeap()); + Serial.printf("Heap: %i, free: %i, min free: %i, max block: %i\n", ESP.getHeapSize(), ESP.getFreeHeap(), ESP.getMinFreeHeap(), ESP.getMaxAllocHeap()); + d+= sprintf(d,"Psram: %i, free: %i, min free: %i, max block: %i
\n", ESP.getPsramSize(), ESP.getFreePsram(), ESP.getMinFreePsram(), ESP.getMaxAllocPsram()); + Serial.printf("Psram: %i, free: %i, min free: %i, max block: %i\n", ESP.getPsramSize(), ESP.getFreePsram(), ESP.getMinFreePsram(), ESP.getMaxAllocPsram()); + if (filesystem) { + d+= sprintf(d,"Spiffs: %i, used: %i
\n", SPIFFS.totalBytes(), SPIFFS.usedBytes()); + Serial.printf("Spiffs: %i, used: %i\n", SPIFFS.totalBytes(), SPIFFS.usedBytes()); + } + // Following is debug while implementing FaceDB dump + // d+= sprintf(d,"mtmn_config size: %u
ra_filter size: %u
id_list %u
\n", sizeof(mtmn_config), sizeof(ra_filter), sizeof(id_list)); + // Serial.printf("mtmn_config size: %u
ra_filter size: %u
id_list %u\n", sizeof(mtmn_config), sizeof(ra_filter), sizeof(id_list)); + // Footer + d+= sprintf(d,"
\n"); + d+= sprintf(d,"\n"); + d+= sprintf(d,"\n"); + d+= sprintf(d,"
\n\n\n"); + *d++ = 0; + httpd_resp_set_type(req, "text/html"); httpd_resp_set_hdr(req, "Content-Encoding", "identity"); - char resp[] = "DEBUG: Preferences file Dumped to serial"; - return httpd_resp_send(req, resp, strlen(resp)); + return httpd_resp_send(req, dumpOut, strlen(dumpOut)); } -// /DEBUG - static esp_err_t style_handler(httpd_req_t *req){ httpd_resp_set_type(req, "text/css"); @@ -737,7 +807,7 @@ static esp_err_t index_handler(httpd_req_t *req){ void startCameraServer(int hPort, int sPort){ httpd_config_t config = HTTPD_DEFAULT_CONFIG(); - config.max_uri_handlers = 10; // we use more than the default 8 (on port 80) + config.max_uri_handlers = 12; // we use more than the default 8 (on port 80) httpd_uri_t index_uri = { .uri = "/", @@ -794,10 +864,10 @@ void startCameraServer(int hPort, int sPort){ .user_ctx = NULL }; // DEBUG - httpd_uri_t dump_prefs_uri = { + httpd_uri_t dump_uri = { .uri = "/dump", .method = HTTP_GET, - .handler = dump_prefs_handler, + .handler = dump_handler, .user_ctx = NULL }; // DEBUG @@ -835,11 +905,12 @@ void startCameraServer(int hPort, int sPort){ mtmn_config.o_threshold.nms = 0.7; mtmn_config.o_threshold.candidate_number = 1; face_id_init(&id_list, FACE_ID_SAVE_NUMBER, ENROLL_CONFIRM_TIMES); - + config.server_port = hPort; config.ctrl_port = hPort; Serial.printf("Starting web server on port: '%d'\n", config.server_port); if (httpd_start(&camera_httpd, &config) == ESP_OK) { + // Note; config.max_uri_handlers (above) must be >= the number of handlers httpd_register_uri_handler(camera_httpd, &index_uri); httpd_register_uri_handler(camera_httpd, &cmd_uri); httpd_register_uri_handler(camera_httpd, &status_uri); @@ -849,7 +920,7 @@ void startCameraServer(int hPort, int sPort){ 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, &dump_prefs_uri); // DEBUG + httpd_register_uri_handler(camera_httpd, &dump_uri); } @@ -864,6 +935,4 @@ void startCameraServer(int hPort, int sPort){ httpd_register_uri_handler(stream_httpd, &favicon_32x32_uri); httpd_register_uri_handler(stream_httpd, &favicon_ico_uri); } - - Serial.printf("mtmn_config size: %u :: ra_filter size: %u :: id_list %u\n", sizeof(mtmn_config), sizeof(ra_filter), sizeof(id_list)); } diff --git a/css.h b/css.h index 479961c..0d7dc6a 100644 --- a/css.h +++ b/css.h @@ -51,13 +51,9 @@ section.main { figure { padding: 0px; margin: 0; - -webkit-margin-before: 0; margin-block-start: 0; - -webkit-margin-after: 0; margin-block-end: 0; - -webkit-margin-start: 0; margin-inline-start: 0; - -webkit-margin-end: 0; margin-inline-end: 0 } diff --git a/esp32-cam-webserver.ino b/esp32-cam-webserver.ino index e4003dd..9c452ec 100644 --- a/esp32-cam-webserver.ino +++ b/esp32-cam-webserver.ino @@ -4,24 +4,23 @@ /* This sketch is a extension/expansion/reork of the 'official' ESP32 Camera example * sketch from Expressif: * https://github.com/espressif/arduino-esp32/tree/master/libraries/ESP32/examples/Camera/CameraWebServer - * + * * It is modified to allow control of Illumination LED Lamps's (present on some modules), * greater feedback via a status LED, and the HTML contents are present in plain text - * for easy modification. - * + * for easy modification. + * * A camera name can now be configured, and wifi details can be stored in an optional * header file to allow easier updated of the repo. - * - * The web UI has had minor changes to add the lamp control when present, I have made the - * 'Start Stream' controls more accessible, and add feedback of the camera name/firmware. - * + * + * The web UI has had changes to add the lamp control, rotation, a standalone viewer, + * more feeedback, new controls and other tweaks and changes, * note: Make sure that you have either selected ESP32 AI Thinker, * or another board which has PSRAM enabled to use high resolution camera modes */ /* - * FOR NETWORK AND HARDWARE SETTINGS COPY OR RENAME 'myconfig.sample.h' to 'myconfig.h' AND EDIT THAT. + * FOR NETWORK AND HARDWARE SETTINGS COPY OR RENAME 'myconfig.sample.h' TO 'myconfig.h' AND EDIT THAT. * * By default this sketch will assume an AI-THINKER ESP-CAM and create * an accesspoint called "ESP32-CAM-CONNECT" (password: "InsecurePassword") @@ -51,6 +50,16 @@ // used for non-volatile camera settings and face DB store #include "storage.h" +// Sketch Info +int sketchSize; +int sketchSpace; +String sketchMD5; + +// IP address, netmask and gateway +IPAddress ip; +IPAddress net; +IPAddress gw; + // Declare external function from app_httpd.cpp extern void startCameraServer(int hPort, int sPort); @@ -102,7 +111,7 @@ int myRotation = CAM_ROTATION; #endif #else int lampVal = -1; // no lamp pin assigned -#endif +#endif int lampChannel = 7; // a free PWM channel (some channels used by camera) const int pwmfreq = 50000; // 50K pwm frequency @@ -213,7 +222,7 @@ void WifiSetup(){ delay(WIFI_WATCHDOG / 10); Serial.print('.'); } - + // If we have connected, show details if (WiFi.status() == WL_CONNECTED) { Serial.println(" Succeeded"); @@ -392,25 +401,30 @@ void setup() { startCameraServer(httpPort, streamPort); // find our IP address - IPAddress ip; char httpURL[64] = {"Unknown"}; #if defined(WIFI_AP_ENABLE) ip = WiFi.softAPIP(); #else ip = WiFi.localIP(); #endif + net = WiFi.subnetMask(); + gw = WiFi.gatewayIP(); // Construct the App URL if (httpPort != 80) { sprintf(httpURL, "http://%d.%d.%d.%d:%d/", ip[0], ip[1], ip[2], ip[3], httpPort); } else { sprintf(httpURL, "http://%d.%d.%d.%d/", ip[0], ip[1], ip[2], ip[3]); } + Serial.printf("\nCamera Ready!\nUse '%s' to connect\n", httpURL); // Construct the Stream URL sprintf(streamURL, "http://%d.%d.%d.%d:%d/", ip[0], ip[1], ip[2], ip[3], streamPort); - // Inform the user - Serial.printf("\nCamera Ready!\nUse '%s' to connect\n", httpURL); + Serial.printf("Stream viewer available at '%s/view'\n", streamURL); Serial.printf("Raw stream URL is '%s'\n", streamURL); - Serial.printf("Stream viewer available at '%sview'\n", streamURL); + + // Used when dumpung status; slow functions, so do them here + sketchSize = ESP.getSketchSize(); + sketchSpace = ESP.getFreeSketchSpace(); + sketchMD5 = ESP.getSketchMD5(); } void loop() { diff --git a/index_ov2640.h b/index_ov2640.h index 5633ce9..36affd5 100644 --- a/index_ov2640.h +++ b/index_ov2640.h @@ -19,26 +19,6 @@ const uint8_t index_ov2640_html[] = R"=====( flex-wrap: nowrap; align-items: stretch } - - figure img { - display: block; - max-width: 100%; - width: auto; - height: auto - } - - figure { - padding: 0 0 0 0px; - margin: 0; - -webkit-margin-before: 0; - margin-block-start: 0; - -webkit-margin-after: 0; - margin-block-end: 0; - -webkit-margin-start: 0; - margin-inline-start: 0; - -webkit-margin-end: 0; - margin-inline-end: 0 - } } @@ -274,7 +254,8 @@ const uint8_t index_ov2640_html[] = R"=====(
- +
@@ -303,6 +284,7 @@ const uint8_t index_ov2640_html[] = R"=====( document.addEventListener('DOMContentLoaded', function (event) { var baseHost = document.location.origin; var streamURL = 'Undefined'; + var viewerURL = 'Undefined'; const settings = document.getElementById('sidebar') const waitSettings = document.getElementById('wait-settings') @@ -391,14 +373,16 @@ const uint8_t index_ov2640_html[] = R"=====( rotate.value = value; applyRotation(); } else if(el.id === "stream_url"){ + streamURL = value; + viewerURL = value + 'view'; stream_url.innerHTML = value; - stream_link.setAttribute("title", "Open stream viewer ( " + value + "view )"); + stream_link.setAttribute("title", "Open stream viewer (" + viewerURL + ")"); stream_link.style.textDecoration = "underline"; stream_link.style.cursor = "pointer"; - streamURL = value; streamButton.setAttribute("title", `You can also browse to '${streamURL}' for a raw stream`); show(streamGroup) - console.log('Stream URL set to:' + value); + console.log('Stream URL set to: ' + streamURL); + console.log('Stream Viewer URL set to: ' + viewerURL); } } } @@ -495,7 +479,7 @@ const uint8_t index_ov2640_html[] = R"=====( streamLink.onclick = () => { stopStream(); - window.open(streamURL + "view", "_blank"); + window.open(viewerURL, "_blank"); } stillButton.onclick = () => { diff --git a/index_ov3660.h b/index_ov3660.h index c31d64b..ce8c9a9 100644 --- a/index_ov3660.h +++ b/index_ov3660.h @@ -19,26 +19,6 @@ const uint8_t index_ov3660_html[] = R"=====( flex-wrap: nowrap; align-items: stretch } - - figure img { - display: block; - max-width: 100%; - width: auto; - height: auto - } - - figure { - padding: 0 0 0 0px; - margin: 0; - -webkit-margin-before: 0; - margin-block-start: 0; - -webkit-margin-after: 0; - margin-block-end: 0; - -webkit-margin-start: 0; - margin-inline-start: 0; - -webkit-margin-end: 0; - margin-inline-end: 0 - } } @@ -316,6 +296,7 @@ const uint8_t index_ov3660_html[] = R"=====( document.addEventListener('DOMContentLoaded', function (event) { var baseHost = document.location.origin; var streamURL = 'Undefined'; + var viewerURL = 'Undefined'; const settings = document.getElementById('sidebar') const waitSettings = document.getElementById('wait-settings') @@ -402,14 +383,16 @@ const uint8_t index_ov3660_html[] = R"=====( rotate.value = value; applyRotation(); } else if(el.id === "stream_url"){ + streamURL = value; + viewerURL = value + 'view'; stream_url.innerHTML = value; - stream_link.setAttribute("title", "Open stream viewer ( " + value + "view )"); + stream_link.setAttribute("title", "Open stream viewer (" + viewerURL + ")"); stream_link.style.textDecoration = "underline"; stream_link.style.cursor = "pointer"; - streamURL = value; streamButton.setAttribute("title", `You can also browse to '${streamURL}' for a raw stream`); show(streamGroup) - console.log('Stream URL set to:' + value); + console.log('Stream URL set to: ' + streamURL); + console.log('Stream Viewer URL set to: ' + viewerURL); } } } @@ -506,7 +489,7 @@ const uint8_t index_ov3660_html[] = R"=====( streamLink.onclick = () => { stopStream(); - window.open(streamURL + "view", "_blank"); + window.open(viewerURL, "_blank"); } stillButton.onclick = () => { diff --git a/storage.cpp b/storage.cpp index f3df7c5..3f671ea 100644 --- a/storage.cpp +++ b/storage.cpp @@ -44,7 +44,6 @@ void listDir(fs::FS &fs, const char * dirname, uint8_t levels){ } } -// DEBUG void dumpPrefs(fs::FS &fs){ if (fs.exists(PREFERENCES_FILE)) { // Dump contents for debug @@ -56,7 +55,6 @@ void dumpPrefs(fs::FS &fs){ Serial.printf("%s not found, nothing to dump.\n", PREFERENCES_FILE); } } -// /DEBUG void loadPrefs(fs::FS &fs){ if (fs.exists(PREFERENCES_FILE)) { diff --git a/storage.h b/storage.h index dfcb998..e5ae34c 100644 --- a/storage.h +++ b/storage.h @@ -6,6 +6,7 @@ #define PREFERENCES_FILE "/esp32cam-preferences.json" #define FACE_DB_FILE "/esp32cam-facedb" +extern void dumpPrefs(fs::FS &fs); extern void loadPrefs(fs::FS &fs); extern void removePrefs(fs::FS &fs); extern void savePrefs(fs::FS &fs); diff --git a/viewers.h b/viewers.h index 3276510..644e723 100644 --- a/viewers.h +++ b/viewers.h @@ -302,33 +302,18 @@ size_t miniviewer_html_len = sizeof(miniviewer_html); color: #EFEFEF; font-size: 16px; margin: 0px; + overflow:hidden; } - figure { - padding: 0px; - margin: 0px; - -webkit-margin-before: 0; - margin-block-start: 0; - -webkit-margin-after: 0; - margin-block-end: 0; - -webkit-margin-start: 0; - margin-inline-start: 0; - -webkit-margin-end: 0; - margin-inline-end: 0 - } - - figure img { + img { object-fit: contain; display: block; + margin: 0px; + padding: 0px; width: 100vw; height: 100vh; } - .image-container { - position: relative; - transform-origin: top left - } - .loader { border: 0.5em solid #f3f3f3; border-top: 0.5em solid #000000; @@ -353,34 +338,25 @@ size_t miniviewer_html_len = sizeof(miniviewer_html);
- -
-
- - - - - -
-
- -
+
+
+ + + +
+