diff --git a/app_httpd.cpp b/app_httpd.cpp index eeed766..6e75f26 100644 --- a/app_httpd.cpp +++ b/app_httpd.cpp @@ -63,7 +63,7 @@ extern int sketchSpace; extern String sketchMD5; extern bool otaEnabled; extern char otaPassword[]; -extern unsigned long xclkFreqHz; +extern unsigned long xclk; typedef struct { httpd_req_t *req; @@ -78,6 +78,9 @@ static const char* _STREAM_PART = "Content-Type: image/jpeg\r\nContent-Length: % httpd_handle_t stream_httpd = NULL; httpd_handle_t camera_httpd = NULL; +// Flag that can be set to kill all active streams +bool streamKill; + #ifdef __cplusplus extern "C" { #endif @@ -141,10 +144,9 @@ void serialDump() { int upSec = sec % 60; int McuTc = (temprature_sens_read() - 32) / 1.8; // celsius int McuTf = temprature_sens_read(); // fahrenheit - float xclk = xclkFreqHz/1000000; Serial.printf("System up: %" PRId64 ":%02i:%02i:%02i (d:h:m:s)\r\n", upDays, upHours, upMin, upSec); Serial.printf("Active streams: %i, Previous streams: %lu, Images captured: %lu\r\n", streamCount, streamsServed, imagesServed); - Serial.printf("CPU Freq: %i MHz, Xclk Freq: %.1f MHz\r\n", ESP.getCpuFreqMHz(), xclk); + Serial.printf("CPU Freq: %i MHz, Xclk Freq: %i MHz\r\n", ESP.getCpuFreqMHz(), xclk); Serial.printf("MCU temperature : %i C, %i F (approximate)\r\n", McuTc, McuTf); Serial.printf("Heap: %i, free: %i, min free: %i, max block: %i\r\n", ESP.getHeapSize(), ESP.getFreeHeap(), ESP.getMinFreeHeap(), ESP.getMaxAllocHeap()); if(psramFound()) { @@ -223,6 +225,8 @@ static esp_err_t stream_handler(httpd_req_t *req){ uint8_t * _jpg_buf = NULL; char * part_buf[64]; + streamKill = false; + Serial.println("Stream requested"); if (autoLamp && (lampVal != -1)) setLamp(lampVal); streamCount = 1; // at present we only have one stream handler, so values are 0 or 1.. @@ -277,7 +281,7 @@ static esp_err_t stream_handler(httpd_req_t *req){ free(_jpg_buf); _jpg_buf = NULL; } - if(res != ESP_OK){ + if((res != ESP_OK) || streamKill){ // This is the only exit point from the stream loop. // We end the stream here only if a Hard failure has been encountered or the connection has been interrupted. break; @@ -341,6 +345,7 @@ static esp_err_t cmd_handler(httpd_req_t *req){ if(s->pixformat == PIXFORMAT_JPEG) res = s->set_framesize(s, (framesize_t)val); } else if(!strcmp(variable, "quality")) res = s->set_quality(s, val); + else if(!strcmp(variable, "xclk")) { xclk = val; res = s->set_xclk(s, LEDC_TIMER_0, val); } else if(!strcmp(variable, "contrast")) res = s->set_contrast(s, val); else if(!strcmp(variable, "brightness")) res = s->set_brightness(s, val); else if(!strcmp(variable, "saturation")) res = s->set_saturation(s, val); @@ -421,6 +426,7 @@ static esp_err_t status_handler(httpd_req_t *req){ p+=sprintf(p, "\"autolamp\":%d,", autoLamp); p+=sprintf(p, "\"framesize\":%u,", s->status.framesize); p+=sprintf(p, "\"quality\":%u,", s->status.quality); + p+=sprintf(p, "\"xclk\":%u,", xclk); p+=sprintf(p, "\"brightness\":%d,", s->status.brightness); p+=sprintf(p, "\"contrast\":%d,", s->status.contrast); p+=sprintf(p, "\"saturation\":%d,", s->status.saturation); @@ -495,7 +501,7 @@ static esp_err_t logo_svg_handler(httpd_req_t *req){ static esp_err_t dump_handler(httpd_req_t *req){ flashLED(75); - Serial.println("\r\nDump Requested via Web"); + Serial.println("\r\nDump requested via Web"); serialDump(); static char dumpOut[2000] = ""; char * d = dumpOut; @@ -566,11 +572,10 @@ static esp_err_t dump_handler(httpd_req_t *req){ int upSec = sec % 60; int McuTc = (temprature_sens_read() - 32) / 1.8; // celsius int McuTf = temprature_sens_read(); // fahrenheit - float xclk = xclkFreqHz/1000000; d+= sprintf(d,"Up: %" PRId64 ":%02i:%02i:%02i (d:h:m:s)
\n", upDays, upHours, upMin, upSec); d+= sprintf(d,"Active streams: %i, Previous streams: %lu, Images captured: %lu
\n", streamCount, streamsServed, imagesServed); - d+= sprintf(d,"CPU Freq: %i MHz, Xclk Freq: %.1f MHz
\n", ESP.getCpuFreqMHz(), xclk); + d+= sprintf(d,"CPU Freq: %i MHz, Xclk Freq: %i MHz
\n", ESP.getCpuFreqMHz(), xclk); d+= sprintf(d,""); d+= sprintf(d,"MCU temperature : %i °C, %i °F\n
", McuTc, McuTf); d+= sprintf(d,"Heap: %i, free: %i, min free: %i, max block: %i
\n", ESP.getHeapSize(), ESP.getFreeHeap(), ESP.getMinFreeHeap(), ESP.getMaxAllocHeap()); @@ -590,6 +595,7 @@ static esp_err_t dump_handler(httpd_req_t *req){ // Footer d+= sprintf(d,"
\n"); d+= sprintf(d,"\n"); + d+= sprintf(d,"\n"); d+= sprintf(d,"\n"); d+= sprintf(d,"
\n\n"); // A javascript timer to refresh the page every minute. @@ -601,6 +607,15 @@ static esp_err_t dump_handler(httpd_req_t *req){ return httpd_resp_send(req, dumpOut, strlen(dumpOut)); } +static esp_err_t stop_handler(httpd_req_t *req){ + flashLED(75); + Serial.println("\r\nStream stop requested via Web"); + streamKill = true; + httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*"); + return httpd_resp_send(req, NULL, 0); +} + + static esp_err_t style_handler(httpd_req_t *req){ httpd_resp_set_type(req, "text/css"); httpd_resp_set_hdr(req, "Content-Encoding", "identity"); @@ -609,7 +624,7 @@ static esp_err_t style_handler(httpd_req_t *req){ static esp_err_t streamviewer_handler(httpd_req_t *req){ flashLED(75); - Serial.println("Stream Viewer requested"); + Serial.println("Stream viewer requested"); httpd_resp_set_type(req, "text/html"); httpd_resp_set_hdr(req, "Content-Encoding", "identity"); return httpd_resp_send(req, (const char *)streamviewer_html, streamviewer_html_len); @@ -617,7 +632,7 @@ static esp_err_t streamviewer_handler(httpd_req_t *req){ static esp_err_t error_handler(httpd_req_t *req){ flashLED(75); - Serial.println("Sending Error page"); + Serial.println("Sending error page"); std::string s(error_html); size_t index; while ((index = s.find("")) != std::string::npos) @@ -705,7 +720,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 = 12; // we use more than the default 8 (on port 80) + config.max_uri_handlers = 16; // we use more than the default 8 (on port 80) httpd_uri_t index_uri = { .uri = "/", @@ -767,6 +782,12 @@ void startCameraServer(int hPort, int sPort){ .handler = dump_handler, .user_ctx = NULL }; + httpd_uri_t stop_uri = { + .uri = "/stop", + .method = HTTP_GET, + .handler = stop_handler, + .user_ctx = NULL + }; httpd_uri_t stream_uri = { .uri = "/", .method = HTTP_GET, @@ -817,6 +838,7 @@ void startCameraServer(int hPort, int sPort){ 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); + httpd_register_uri_handler(camera_httpd, &stop_uri); } config.server_port = sPort; diff --git a/esp32-cam-webserver.ino b/esp32-cam-webserver.ino index 2ca4b7a..c6851cc 100644 --- a/esp32-cam-webserver.ino +++ b/esp32-cam-webserver.ino @@ -134,12 +134,12 @@ unsigned long imagesServed = 0; // Total image requests char myVer[] PROGMEM = __DATE__ " @ " __TIME__; // Camera module bus communications frequency. -// Originally: config.xclk_freq_hz = 20000000, but this lead to visual artifacts on many modules. +// Originally: config.xclk_freq_mhz = 20000000, but this lead to visual artifacts on many modules. // See https://github.com/espressif/esp32-camera/issues/150#issuecomment-726473652 et al. -#if !defined (XCLK_FREQ_HZ) - unsigned long xclkFreqHz = 16500000; +#if !defined (XCLK_FREQ_MHZ) + unsigned long xclk = 16; #else - unsigned long xclkFreqHz = XCLK_FREQ_HZ; + unsigned long xclk = XCLK_FREQ_MHZ; #endif // initial rotation @@ -524,7 +524,7 @@ void setup() { config.pin_sscb_scl = SIOC_GPIO_NUM; config.pin_pwdn = PWDN_GPIO_NUM; config.pin_reset = RESET_GPIO_NUM; - config.xclk_freq_hz = xclkFreqHz; + config.xclk_freq_hz = xclk * 1000000; config.pixel_format = PIXFORMAT_JPEG; config.grab_mode = CAMERA_GRAB_LATEST; // Pre-allocate large buffers diff --git a/index_ov2640.h b/index_ov2640.h index f923a21..3baa56b 100644 --- a/index_ov2640.h +++ b/index_ov2640.h @@ -74,6 +74,13 @@ const uint8_t index_ov2640_html[] = R"=====(
High
(slow)
+
+ +
+ +
MHz
+
+
-2
@@ -292,6 +299,7 @@ const uint8_t index_ov2640_html[] = R"=====( const closeButton = document.getElementById('close-stream') const streamLink = document.getElementById('stream_link') const framesize = document.getElementById('framesize') + const xclk = document.getElementById('xclk') const swapButton = document.getElementById('swap-viewer') const savePrefsButton = document.getElementById('save_prefs') const clearPrefsButton = document.getElementById('clear_prefs') @@ -395,6 +403,7 @@ const uint8_t index_ov2640_html[] = R"=====( value = el.checked ? 1 : 0 break case 'range': + case 'number': case 'select-one': value = el.value break @@ -562,6 +571,11 @@ const uint8_t index_ov2640_html[] = R"=====( updateConfig(framesize) } + xclk.onchange = () => { + console.log("xclk:" , xclk); + updateConfig(xclk) + } + swapButton.onclick = () => { window.open('/?view=simple','_self'); } diff --git a/index_ov3660.h b/index_ov3660.h index eb7ca74..9992376 100644 --- a/index_ov3660.h +++ b/index_ov3660.h @@ -76,6 +76,13 @@ const uint8_t index_ov3660_html[] = R"=====(
High
(slow)
+
+ +
+ +
MHz
+
+
-3
@@ -306,6 +313,7 @@ const uint8_t index_ov3660_html[] = R"=====( const closeButton = document.getElementById('close-stream') const streamLink = document.getElementById('stream_link') const framesize = document.getElementById('framesize') + const xclk = document.getElementById('xclk') const swapButton = document.getElementById('swap-viewer') const savePrefsButton = document.getElementById('save_prefs') const clearPrefsButton = document.getElementById('clear_prefs') @@ -407,6 +415,7 @@ const uint8_t index_ov3660_html[] = R"=====( value = el.checked ? 1 : 0 break case 'range': + case 'number': case 'select-one': value = el.value break @@ -571,6 +580,11 @@ const uint8_t index_ov3660_html[] = R"=====( updateConfig(framesize) } + xclk.onchange = () => { + console.log("xclk:" , xclk); + updateConfig(xclk) + } + swapButton.onclick = () => { window.open('/?view=simple','_self'); } diff --git a/myconfig.sample.h b/myconfig.sample.h index e463e4b..4388422 100644 --- a/myconfig.sample.h +++ b/myconfig.sample.h @@ -1,6 +1,6 @@ -/* +/* * Rename this example to 'myconfig.h' and fill in your details. - * + * * The local config is in the '.gitignore' file, which helps to keep details secret. */ @@ -11,7 +11,7 @@ /* * WiFi Settings - * + * * For the simplest connection to an existing network * just replace your ssid and password in the line below. */ @@ -31,18 +31,18 @@ struct station stationList[] = {{"ssid1", "pass1", true}, * it will be used for the AccessPoint ssid and password. See the comments there for more. * * The 'dhcp' setting controls whether the station uses DHCP or static IP settings; if in doubt leave 'true' - * + * * You can also use a BSSID (eg: "2F:67:94:F5:BB:6A", a colon separated mac address string) in place of * the ssid to force connections to specific networks even when the ssid's collide, */ /* Extended WiFi Settings */ -/* +/* * Hostname. Optional, uncomment and set if desired * - used in DHCP request when connecting to networks, not used in AP mode * - Most useful when used with a static netwrk config, not all routers respect this setting - * + * * The URL_HOSTNAME will be used in place of the IP address in internal URL's */ @@ -51,22 +51,22 @@ struct station stationList[] = {{"ssid1", "pass1", true}, /* * Static network settings for client mode - * + * * 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 separated with commas (,) and not decimals (.) // #define ST_IP 192,168,0,123 -// #define ST_GATEWAY 192,168,0,2 +// #define ST_GATEWAY 192,168,0,2 // #define ST_NETMASK 255,255,255,0 // One or two DNS servers can be supplied, only the NTP code currently uses them // #define ST_DNS1 192,168,0,2 // #define ST_DNS2 8,8,8,8 -/* - * AccessPoint; +/* + * AccessPoint; * - * Uncomment to enable AP mode; + * Uncomment to enable AP mode; * */ // #define WIFI_AP_ENABLE @@ -79,7 +79,7 @@ struct station stationList[] = {{"ssid1", "pass1", true}, * 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 visitors to the webcam page, with varying degrees of success depending on the visitors + * all visitors to the webcam page, with varying degrees of success depending on the visitors * browser and other settings. */ // Optionally change the AccessPoint ip address (default = 192.168.4.1) @@ -184,9 +184,10 @@ struct station stationList[] = {{"ssid1", "pass1", true}, // #define CAMERA_MODEL_TTGO_T_JOURNAL // #define CAMERA_MODEL_ARDUCAM_ESP32S_UNO -// Camera module bus communications frequency, setting too high can cause visual artifacts. -// Currently defaults to 16.5MHz, but some (non-clone) modules may be able to use the -// original frequency of 20MHz for to allow higher framerates etc. -// #define XCLK_FREQ_HZ 20000000; -// For clone modules that have camera module artifacts and SPIFFS issues; try setting this very low: -// #define XCLK_FREQ_HZ 3000000; +// Initial Camera module bus communications frequency +// Currently defaults to 8MHz +// The post-initialisation (runtime) value can be set and edited by the user in the UI +// For clone modules that have camera module and SPIFFS startup issues try setting +// this very low (start at 2MHZ and increase): +// #define XCLK_FREQ_MHZ 2 + diff --git a/src/version.h b/src/version.h index 453ad7d..4c68dce 100644 --- a/src/version.h +++ b/src/version.h @@ -1,4 +1,4 @@ /* Version of upstream code */ -char baseVersion[] = "4.0.beta3"; +char baseVersion[] = "4.0.rc1"; diff --git a/storage.cpp b/storage.cpp index 8ebc7fa..8a2644a 100644 --- a/storage.cpp +++ b/storage.cpp @@ -7,6 +7,7 @@ extern void flashLED(int flashtime); extern int myRotation; // Rotation extern int lampVal; // The current Lamp value extern int autoLamp; // Automatic lamp mode +extern int xclk; // Camera module clock speed /* * Useful utility when debugging... @@ -90,8 +91,11 @@ void loadPrefs(fs::FS &fs){ // process all the settings lampVal = jsonExtract(prefs, "lamp").toInt(); autoLamp = jsonExtract(prefs, "autolamp").toInt(); + int xclkPref = jsonExtract(prefs, "xclk").toInt(); + if (xclkPref != 0) xclk = xclkPref; s->set_framesize(s, (framesize_t)jsonExtract(prefs, "framesize").toInt()); s->set_quality(s, jsonExtract(prefs, "quality").toInt()); + s->set_xclk(s, LEDC_TIMER_0, xclk); s->set_brightness(s, jsonExtract(prefs, "brightness").toInt()); s->set_contrast(s, jsonExtract(prefs, "contrast").toInt()); s->set_saturation(s, jsonExtract(prefs, "saturation").toInt()); @@ -138,6 +142,7 @@ void savePrefs(fs::FS &fs){ p+=sprintf(p, "\"autolamp\":%u,", autoLamp); p+=sprintf(p, "\"framesize\":%u,", s->status.framesize); p+=sprintf(p, "\"quality\":%u,", s->status.quality); + p+=sprintf(p, "\"xclk\":%u,", xclk); p+=sprintf(p, "\"brightness\":%d,", s->status.brightness); p+=sprintf(p, "\"contrast\":%d,", s->status.contrast); p+=sprintf(p, "\"saturation\":%d,", s->status.saturation);