From 4d253bf1800efa85d312de882be023c9f5db20d6 Mon Sep 17 00:00:00 2001 From: Owen Carter Date: Fri, 24 Sep 2021 12:45:16 +0200 Subject: [PATCH 01/52] Platformio OTA partitioning and upload (#160) (#165) --- platformio.ini | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/platformio.ini b/platformio.ini index d8f71ba..624baf5 100644 --- a/platformio.ini +++ b/platformio.ini @@ -11,8 +11,16 @@ [platformio] src_dir = ./ -[env:esp32cam] +[env:esp32dev] platform = espressif32 -board = esp32cam -board_build.partitions = default.csv +board = esp32dev +board_build.partitions = min_spiffs.csv framework = arduino +build_flags = + -DBOARD_HAS_PSRAM + -mfix-esp32-psram-cache-issue +; For OTA uploading uncomment the next lines and add the IP address or mDNS name of the camera module, and the OTA password +;upload_protocol = espota +;upload_port = +;upload_flags = +; --auth= From f6709d0ce89740cd2ecb52ecc1d4fd9089c35244 Mon Sep 17 00:00:00 2001 From: Owen Date: Sat, 25 Sep 2021 15:13:29 +0200 Subject: [PATCH 02/52] Increase default WiFi Connect --- esp32-cam-webserver.ino | 2 +- myconfig.sample.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/esp32-cam-webserver.ino b/esp32-cam-webserver.ino index 44155aa..7e52667 100644 --- a/esp32-cam-webserver.ino +++ b/esp32-cam-webserver.ino @@ -95,7 +95,7 @@ extern void serialDump(); #endif #if !defined(WIFI_WATCHDOG) - #define WIFI_WATCHDOG 5000 + #define WIFI_WATCHDOG 8000 #endif // Number of known networks in stationList[] diff --git a/myconfig.sample.h b/myconfig.sample.h index 2b909eb..e0d79be 100644 --- a/myconfig.sample.h +++ b/myconfig.sample.h @@ -59,7 +59,7 @@ struct station stationList[] = {{"ssid1", "pass1", true}, // #define ST_IP 192,168,0,123 // #define ST_GATEWAY 192,168,0,2 // #define ST_NETMASK 255,255,255,0 -// One or two optional DNS servers can be supplied, but the current firmware never uses them ;-) +// 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 @@ -101,7 +101,7 @@ struct station stationList[] = {{"ssid1", "pass1", true}, * and how often we check to see if we are still connected, milliseconds * You may wish to increase this if your WiFi is slow at conencting, */ -// #define WIFI_WATCHDOG 5000 +// #define WIFI_WATCHDOG 8000 /* * Over The Air firmware updates can be disabled by uncommenting the folowing line From d1928a2050e9c0d2db757954be9e2de27cc3e9a8 Mon Sep 17 00:00:00 2001 From: Owen Carter Date: Sun, 26 Sep 2021 03:00:15 +0200 Subject: [PATCH 03/52] Interactive slider improvements (#166) Send slider (range input) updates immediately so that slider become more responsive. Rate limit sending the slider updates since rapid moves could overwhelm the connection Co-authored-by: Eric Fontaine --- app_httpd.cpp | 1 + index_other.h | 21 +++++++++++++++++++++ index_ov2640.h | 21 +++++++++++++++++++++ index_ov3660.h | 21 +++++++++++++++++++++ 4 files changed, 64 insertions(+) diff --git a/app_httpd.cpp b/app_httpd.cpp index 54100ca..0bc24c8 100644 --- a/app_httpd.cpp +++ b/app_httpd.cpp @@ -288,6 +288,7 @@ static esp_err_t cmd_handler(httpd_req_t *req){ size_t buf_len; char variable[32] = {0,}; char value[32] = {0,}; + flashLED(75); buf_len = httpd_req_get_url_query_len(req) + 1; diff --git a/index_other.h b/index_other.h index 71b6971..ba4a297 100644 --- a/index_other.h +++ b/index_other.h @@ -139,6 +139,20 @@ const uint8_t index_simple_html[] = R"=====( } } + var rangeUpdateScheduled = false + var latestRangeConfig + + function updateRangeConfig (el) { + latestRangeConfig = el + if (!rangeUpdateScheduled) { + rangeUpdateScheduled = true; + setTimeout(function(){ + rangeUpdateScheduled = false + updateConfig(latestRangeConfig) + }, 150); + } + } + function updateConfig (el) { let value switch (el.type) { @@ -259,6 +273,13 @@ const uint8_t index_simple_html[] = R"=====( el.onchange = () => updateConfig(el) }) + // Update range sliders as they are being moved + document + .querySelectorAll('input[type="range"]') + .forEach(el => { + el.oninput = () => updateRangeConfig(el) + }) + // Custom actions // Detection and framesize rotate.onchange = () => { diff --git a/index_ov2640.h b/index_ov2640.h index 00724cf..f923a21 100644 --- a/index_ov2640.h +++ b/index_ov2640.h @@ -374,6 +374,20 @@ const uint8_t index_ov2640_html[] = R"=====( } } + var rangeUpdateScheduled = false + var latestRangeConfig + + function updateRangeConfig (el) { + latestRangeConfig = el + if (!rangeUpdateScheduled) { + rangeUpdateScheduled = true; + setTimeout(function(){ + rangeUpdateScheduled = false + updateConfig(latestRangeConfig) + }, 150); + } + } + function updateConfig (el) { let value switch (el.type) { @@ -499,6 +513,13 @@ const uint8_t index_ov2640_html[] = R"=====( el.onchange = () => updateConfig(el) }) + // Update range sliders as they are being moved + document + .querySelectorAll('input[type="range"]') + .forEach(el => { + el.oninput = () => updateRangeConfig(el) + }) + // Custom actions // Gain const agc = document.getElementById('agc') diff --git a/index_ov3660.h b/index_ov3660.h index ff4b88d..eb7ca74 100644 --- a/index_ov3660.h +++ b/index_ov3660.h @@ -386,6 +386,20 @@ const uint8_t index_ov3660_html[] = R"=====( } } + var rangeUpdateScheduled = false + var latestRangeConfig + + function updateRangeConfig (el) { + latestRangeConfig = el + if (!rangeUpdateScheduled) { + rangeUpdateScheduled = true; + setTimeout(function(){ + rangeUpdateScheduled = false + updateConfig(latestRangeConfig) + }, 150); + } + } + function updateConfig (el) { let value switch (el.type) { @@ -511,6 +525,13 @@ const uint8_t index_ov3660_html[] = R"=====( el.onchange = () => updateConfig(el) }) + // Update range sliders as they are being moved + document + .querySelectorAll('input[type="range"]') + .forEach(el => { + el.oninput = () => updateRangeConfig(el) + }) + // Custom actions // Gain const agc = document.getElementById('agc') From fb2fc1bcb452e090de1fb1d2210d719fea54c714 Mon Sep 17 00:00:00 2001 From: Owen Date: Sun, 26 Sep 2021 11:27:05 +0200 Subject: [PATCH 04/52] Better feedback for no psram/filesystem errors --- app_httpd.cpp | 28 ++++++++++++++++++++++------ esp32-cam-webserver.ino | 2 +- 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/app_httpd.cpp b/app_httpd.cpp index 0bc24c8..5b4c8bd 100644 --- a/app_httpd.cpp +++ b/app_httpd.cpp @@ -145,10 +145,18 @@ void serialDump() { Serial.printf("Freq: %i MHz\r\n", ESP.getCpuFreqMHz()); 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()); - Serial.printf("Psram: %i, free: %i, min free: %i, max block: %i\r\n", ESP.getPsramSize(), ESP.getFreePsram(), ESP.getMinFreePsram(), ESP.getMaxAllocPsram()); + if(psramFound()) { + Serial.printf("Psram: %i, free: %i, min free: %i, max block: %i\r\n", ESP.getPsramSize(), ESP.getFreePsram(), ESP.getMinFreePsram(), ESP.getMaxAllocPsram()); + } else { + Serial.printf("Psram: Not found; please check your board configuration.\r\n"); + Serial.printf("- High resolution/quality settings will show incomplete frames to low memory.\r\n"); + } // Filesystems - if (filesystem) { + if (filesystem && (SPIFFS.totalBytes() > 0)) { Serial.printf("Spiffs: %i, used: %i\r\n", SPIFFS.totalBytes(), SPIFFS.usedBytes()); + } else { + Serial.printf("Spiffs: No filesystem found, please check your board configuration.\r\n"); + Serial.printf("- Saving and restoring camera settings will not function without this.\r\n"); } Serial.println("Preferences file: "); dumpPrefs(SPIFFS); @@ -493,8 +501,8 @@ static esp_err_t dump_handler(httpd_req_t *req){ d+= sprintf(d,"\n"); d+= sprintf(d,"\n"); if (critERR.length() > 0) { - d+= sprintf(d,"Hardware Error Detected!\n(the serial log may give more information)\n"); - d+= sprintf(d,"%s
\n", critERR.c_str()); + d+= sprintf(d,"%s
\n", critERR.c_str()); + d+= sprintf(d,"

(the serial log may give more information)


\n"); } d+= sprintf(d,"

ESP32 Cam Webserver

\n"); // Module @@ -556,9 +564,17 @@ static esp_err_t dump_handler(httpd_req_t *req){ 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()); - d+= sprintf(d,"Psram: %i, free: %i, min free: %i, max block: %i
\n", ESP.getPsramSize(), ESP.getFreePsram(), ESP.getMinFreePsram(), ESP.getMaxAllocPsram()); - if (filesystem) { + if (psramFound()) { + d+= sprintf(d,"Psram: %i, free: %i, min free: %i, max block: %i
\n", ESP.getPsramSize(), ESP.getFreePsram(), ESP.getMinFreePsram(), ESP.getMaxAllocPsram()); + } else { + d+= sprintf(d,"Psram: Not found, please check your board configuration.
\n"); + d+= sprintf(d,"- High resolution/quality images & streams will show incomplete frames due to low memory.
\n"); + } + if (filesystem && (SPIFFS.totalBytes() > 0)) { d+= sprintf(d,"Spiffs: %i, used: %i
\n", SPIFFS.totalBytes(), SPIFFS.usedBytes()); + } else { + d+= sprintf(d,"Spiffs: No filesystem found, please check your board configuration.
\n"); + d+= sprintf(d,"- saving and restoring camera settings will not function without this.
\n"); } // Footer diff --git a/esp32-cam-webserver.ino b/esp32-cam-webserver.ino index 7e52667..7c85dd4 100644 --- a/esp32-cam-webserver.ino +++ b/esp32-cam-webserver.ino @@ -636,7 +636,7 @@ void setup() { filesystemStart(); loadPrefs(SPIFFS); } else { - Serial.println("No Internal Filesystem, cannot save preferences"); + Serial.println("No Internal Filesystem, cannot load or save preferences"); } } From 88d07a963f1fc7c1ddde93f12e39e6d579cb7f50 Mon Sep 17 00:00:00 2001 From: Owen Date: Sun, 26 Sep 2021 11:52:39 +0200 Subject: [PATCH 05/52] Fix compiling on modules with no LAMP_PIN (#138) --- esp32-cam-webserver.ino | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/esp32-cam-webserver.ino b/esp32-cam-webserver.ino index 7c85dd4..8ba8a3a 100644 --- a/esp32-cam-webserver.ino +++ b/esp32-cam-webserver.ino @@ -649,7 +649,9 @@ void setup() { ledcSetup(lampChannel, pwmfreq, pwmresolution); // configure LED PWM channel if (autoLamp) setLamp(0); // set default value else setLamp(lampVal); - ledcAttachPin(LAMP_PIN, lampChannel); // attach the GPIO pin to the channel + #if defined(LAMP_PIN) + ledcAttachPin(LAMP_PIN, lampChannel); // attach the GPIO pin to the channel + #endif } else { Serial.println("No lamp, or lamp disabled in config"); } From 03b0982c7fb46762a68fce37ff1f91e4b2710b15 Mon Sep 17 00:00:00 2001 From: Owen Date: Sun, 26 Sep 2021 13:02:49 +0200 Subject: [PATCH 06/52] Option to disable onboard notification led (also #138) --- esp32-cam-webserver.ino | 7 ++++++- myconfig.sample.h | 3 +++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/esp32-cam-webserver.ino b/esp32-cam-webserver.ino index 8ba8a3a..d084c28 100644 --- a/esp32-cam-webserver.ino +++ b/esp32-cam-webserver.ino @@ -147,7 +147,7 @@ char myVer[] PROGMEM = __DATE__ " @ " __TIME__; #endif int myRotation = CAM_ROTATION; -// Illumination LAMP/LED +// Illumination LAMP and status LED #if defined(LAMP_DISABLE) int lampVal = -1; // lamp is disabled in config #elif defined(LAMP_PIN) @@ -159,6 +159,11 @@ int myRotation = CAM_ROTATION; #else int lampVal = -1; // no lamp pin assigned #endif + +#if defined(LED_DISABLE) + #undef LED_PIN // undefining this disables the notification LED +#endif + bool autoLamp = false; // Automatic lamp (auto on while camera running) int lampChannel = 7; // a free PWM channel (some channels used by camera) diff --git a/myconfig.sample.h b/myconfig.sample.h index e0d79be..f87e4bf 100644 --- a/myconfig.sample.h +++ b/myconfig.sample.h @@ -151,6 +151,9 @@ struct station stationList[] = {{"ssid1", "pass1", true}, // Default Page: uncomment to make the full control page the default, otherwise show simple viewer // #define DEFAULT_INDEX_FULL +// Uncomment to disable the notification LED on the module +// #define LED_DISABLE + // Uncomment to disable the illumination lamp features // #define LAMP_DISABLE From 44f11768dae5b27a640b86bc9bdc9454007035e8 Mon Sep 17 00:00:00 2001 From: Owen Carter Date: Mon, 27 Sep 2021 16:33:14 +0200 Subject: [PATCH 07/52] Some minor fix-ups that I forgot to check-in (#168) * Some minor fixups that I forgot to checkin * fewer branch builds --- .travis.yml | 19 ++++++++++++------- README.md | 16 ++++++++++++---- 2 files changed, 24 insertions(+), 11 deletions(-) diff --git a/.travis.yml b/.travis.yml index 693813a..08e1b19 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,18 +7,23 @@ os: dist: focal +branches: + only: + - master + + before_script: - "export DISPLAY=:99.0" - sleep 3 # give xvfb some time to start - - wget http://downloads.arduino.cc/arduino-1.8.13-linux64.tar.xz - - tar xf arduino-1.8.13-linux64.tar.xz - - mv arduino-1.8.13 $HOME/arduino_ide + - wget http://downloads.arduino.cc/arduino-1.8.15-linux64.tar.xz + - tar xf arduino-1.8.15-linux64.tar.xz + - mv arduino-1.8.15 $HOME/arduino_ide - cd $HOME/arduino_ide/hardware - mkdir esp32 - cd esp32 - - wget https://github.com/espressif/arduino-esp32/archive/refs/tags/1.0.6.tar.gz - - tar -xzf 1.0.6.tar.gz - - mv arduino-esp32-1.0.6/ esp32 + - wget https://github.com/espressif/arduino-esp32/archive/refs/tags/2.0.0.tar.gz + - tar -xzf 2.0.0.tar.gz + - mv arduino-esp32-2.0.0/ esp32 - cd esp32/tools - python --version - python get.py @@ -28,7 +33,7 @@ before_script: script: - cd $TRAVIS_BUILD_DIR - export PATH="$HOME/arduino_ide:$PATH" - - arduino --board esp32:esp32:esp32:PartitionScheme=default,FlashFreq=80 --pref compiler.warning_level=all --save-prefs + - arduino --board esp32:esp32:esp32:PSRAM=enabled,PartitionScheme=min_spiffs,CPUFreq=240,FlashMode=qio,FlashFreq=80,DebugLevel=none --pref compiler.warning_level=all --save-prefs - arduino --verbose --verify esp32-cam-webserver.ino - cp --preserve --verbose myconfig.sample.h myconfig.h - arduino --verbose --verify esp32-cam-webserver.ino diff --git a/README.md b/README.md index 933ee92..64ae52d 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# ESP32-CAM example revisited.     [![CI Status](https://travis-ci.org/easytarget/esp32-cam-webserver.svg?branch=master)](https://travis-ci.org/github/easytarget/esp32-cam-webserver)    ![ESP-EYE logo](Docs/logo.svg) +# ESP32-CAM example revisited.     [![CI Status](https://travis-ci.com/easytarget/esp32-cam-webserver.svg?branch=master)](https://travis-ci.com/github/easytarget/esp32-cam-webserver)    ![ESP-EYE logo](Docs/logo.svg) ## Taken from the ESP examples, and expanded This sketch is a extension/expansion/rework of the 'official' ESP32 Camera example sketch from Espressif: @@ -46,13 +46,17 @@ https://randomnerdtutorials.com/esp32-cam-troubleshooting-guide/ The ESP itself is susceptable to the usual list of WiFi problems, not helped by having small antennas, older designs, congested airwaves and demanding users. The majority of disconnects, stutters and other comms problems are simply due to 'WiFi issues'. The AI-THINKER camera module & esp32 combination is quite susceptable to power supply problems affecting both WiFi conctivity and Video quality; short cabling and decent power supplies are your friend here; also well cooled cases and, if you have the time, decoupling capacitors on the power lines. A basic limitation of the sketch is that it can can only support one stream at a time. If you try to connect to a cam that is already streaming (or attempting to stream) you will get no response and, eventually, a timeout. The stream itself is a [MJPEG stream](https://en.wikipedia.org/wiki/Motion_JPEG), which relies on the client (the web browser) to hold the connection open and request each new frame in turn via javascript. This can cause errors when browsers run into Javascript or caching problem, fail to request new frames or refuse to close the connection. +* You can check the `/dump` page of the cam to see if it currently reports the camera as streaming or not. -The existing [issues list](https://github.com/easytarget/esp32-cam-webserver/issues?q=is%3Aissue) on Github is a good place to start if you have a specific issue not covered above +The existing [issues list](https://github.com/easytarget/esp32-cam-webserver/issues?q=is%3Aissue) on Github is a good place to start if you have a specific issue not covered above. + +Note that I do not respond to any Private Messages (via github, hackaday, or wherever) for support. ## Setup: * For programming you will need a suitable development environment, I use the Arduino IDE, but this code should work in the Espressif development environment too. -* Make sure you are using the [latest version](https://www.arduino.cc/en/main/software#download) of the IDE and then follow [This Guide](https://github.com/espressif/arduino-esp32/blob/master/docs/arduino-ide/boards_manager.md) to set up the Espressif Arduino core for the IDE. +* Make sure you are using the [latest version](https://www.arduino.cc/en/main/software#download) of the IDE and then follow [This Guide](https://docs.espressif.com/projects/arduino-esp32/en/latest/installing.html) to set up the Espressif Arduino core for the IDE. +* _I do not recommend or support running with development builds of either the IDE or the ESP arduino core._ * If you have a development board (anything that can be programmed via a standard USB cable/jack on the board itself) you are in luck. Just plug it in and skip ahead to the [config](#config) section. Remember to set your board model. * The AI-THINKER board requires use of an external **3.3v** serial adapter to program; I use a `FTDI Friend` adapter, for more about this read AdaFruits excellent [FTDI Friend guide](https://learn.adafruit.com/ftdi-friend). * Be careful not to use a 5v serial adapter since this will damage the ESP32. @@ -115,7 +119,11 @@ I would also like to shoutout to @jmfloyd; who suggested rotating the image in t ![The stream viewer](Docs/streamview.png)
*Standalone StreamViewer; No decoration or controls, the image is resizable, and you can doubleclick it for fullscreen* -![The info page](Docs/infodump.png)
*Boring Details* +![The info page](Docs/infodump.png)
*Boring Details, useful when debugging or if you want to check stats* + +### API +The communications between the web browser and the camera module can also be used to send commands directly to the camera (eg to automate it, etc) and form, in effect, an API for the camera. +* I have [documented this here](https://github.com/easytarget/esp32-cam-webserver/blob/master/API.md). ## Notes: From 4031ab2530aa8acdcbb04bc34cfd7eddc891a34e Mon Sep 17 00:00:00 2001 From: Armin Date: Mon, 11 Oct 2021 12:44:58 +0200 Subject: [PATCH 08/52] Fix rotate -90 degree bug (#169) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adding a style with flex-direction: column; and align-items: flex-start; does the job 😀 --- index_other.h | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/index_other.h b/index_other.h index ba4a297..06f5646 100644 --- a/index_other.h +++ b/index_other.h @@ -12,7 +12,14 @@ const uint8_t index_simple_html[] = R"=====( From a40e92470a27749a8e4f8ed4defa09e94a720b04 Mon Sep 17 00:00:00 2001 From: Owen Date: Mon, 11 Oct 2021 14:30:13 +0200 Subject: [PATCH 09/52] Add a default text color to menus to assist customisers [skip travis] --- css.h | 1 + 1 file changed, 1 insertion(+) diff --git a/css.h b/css.h index f0106ba..1c329b6 100644 --- a/css.h +++ b/css.h @@ -33,6 +33,7 @@ section.main { #menu { display: none; flex-wrap: nowrap; + color: #EFEFEF; width: 380px; background: #363636; padding: 8px; From 0ef997e21bfe597ad25d366f9acef99891dd0d09 Mon Sep 17 00:00:00 2001 From: Owen Carter Date: Fri, 15 Oct 2021 04:21:11 +0200 Subject: [PATCH 10/52] correct partition scheme in readme, fix #172 --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 64ae52d..bad94b1 100644 --- a/README.md +++ b/README.md @@ -89,7 +89,7 @@ To make a permanent config with your home wifi settings, different defaults or a Assuming you are using the latest Espressif Arduino core the `ESP32 Dev Module` board will appear in the ESP32 Arduino section of the boards list. Select this (do not use the `AI-THINKER` entry listed in the boiards menu, it is not OTA compatible, and will caus the module to crash and reboot rather than updating if you use it. ![IDE board config](Docs/ota-board-selection.png) -Make sure you select the `Default 4MB with Spiffs` partition scheme and turn `PSRAM` on. +Make sure you select the `Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS)` partition scheme and turn `PSRAM` on. The first time you program (or if OTA is failing) you need to compile and upload the code from the IDE, and when the `Connecting...` appears in the console reboot the ESP32 module while keeping **GPIO0** grounded. You can release GPO0 once the sketch is uploading, most boards have a 'boot' button to trigger a reboot. @@ -148,7 +148,7 @@ V4 * Investigate using SD card to capture images * Implement OTA and a better network stack for remembering multiple AP's, auto-config etc. * **Basic OTA is Done**, see the `NoFace` branch. - * Advanced (web upload) OTA might be nice to have is possible + * Advanced (web upload) OTA might be nice to have if possible * For the Network setup I want to implement https://github.com/Hieromon/AutoConnect * UI Skinning/Theming * OSD From 36aee903521d75d07f3eaeebdc55fa2364d22028 Mon Sep 17 00:00:00 2001 From: Tim Gates Date: Fri, 17 Dec 2021 02:10:45 +1100 Subject: [PATCH 11/52] docs: Fix a few typos (#184) There are small typos in: - Docs/linearled/README.md - README.md - myconfig.sample.h --- Docs/linearled/README.md | 2 +- README.md | 10 +++++----- myconfig.sample.h | 8 ++++---- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/Docs/linearled/README.md b/Docs/linearled/README.md index f5163a7..ab87d94 100644 --- a/Docs/linearled/README.md +++ b/Docs/linearled/README.md @@ -38,7 +38,7 @@ int main(int argc, char **argv) { } ``` -Adjust the width as necesscary and compile with: +Adjust the width as necessary and compile with: $ gcc linearled.c -o linearled -lm diff --git a/README.md b/README.md index bad94b1..a2e8ce2 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ But expanded with: * Lots of minor fixes and tweaks, documentation etc. And 'reduced' by removing the Face Recognition features -* **If you want to try the Face Recognition features** please use the [`3.x` maintenance branch](https://github.com/easytarget/esp32-cam-webserver/tree/3.x), which still recieves bugfixes, but is not reciving any further development. +* **If you want to try the Face Recognition features** please use the [`3.x` maintenance branch](https://github.com/easytarget/esp32-cam-webserver/tree/3.x), which still recieves bugfixes, but is not receiving any further development. * They were a demo, only worked in low resolution modes, did not preserve the face database between power cycles, and were of little use in real-world applications. * There are other (specialised) sketches for the ESP-CAM that do use face recognitioni more effectively, if this is your thing :-) @@ -43,7 +43,7 @@ https://randomnerdtutorials.com/esp32-cam-troubleshooting-guide/ ### Known Issues -The ESP itself is susceptable to the usual list of WiFi problems, not helped by having small antennas, older designs, congested airwaves and demanding users. The majority of disconnects, stutters and other comms problems are simply due to 'WiFi issues'. The AI-THINKER camera module & esp32 combination is quite susceptable to power supply problems affecting both WiFi conctivity and Video quality; short cabling and decent power supplies are your friend here; also well cooled cases and, if you have the time, decoupling capacitors on the power lines. +The ESP itself is susceptible to the usual list of WiFi problems, not helped by having small antennas, older designs, congested airwaves and demanding users. The majority of disconnects, stutters and other comms problems are simply due to 'WiFi issues'. The AI-THINKER camera module & esp32 combination is quite susceptible to power supply problems affecting both WiFi conctivity and Video quality; short cabling and decent power supplies are your friend here; also well cooled cases and, if you have the time, decoupling capacitors on the power lines. A basic limitation of the sketch is that it can can only support one stream at a time. If you try to connect to a cam that is already streaming (or attempting to stream) you will get no response and, eventually, a timeout. The stream itself is a [MJPEG stream](https://en.wikipedia.org/wiki/Motion_JPEG), which relies on the client (the web browser) to hold the connection open and request each new frame in turn via javascript. This can cause errors when browsers run into Javascript or caching problem, fail to request new frames or refuse to close the connection. * You can check the `/dump` page of the cam to see if it currently reports the camera as streaming or not. @@ -111,7 +111,7 @@ All of the face recognition code has been removed as of V4.0; this reduces the c The compressed and binary encoded HTML used in the example has been unpacked to raw text, this makes it much easier to access and modify the Javascript and UI elements. Given the relatively small size of the index page there is very little benefit from compressing it. -The streamviewer, lamp control, and all the other new features have been added. I have tried to retain the basic structure of the original example,extending where necesscary. +The streamviewer, lamp control, and all the other new features have been added. I have tried to retain the basic structure of the original example,extending where necessary. The web UI has had changes to add the lamp control (only when enabled) and make the streamm window rotate and resize appropriately. I also made the 'Start Stream' and 'Snapshot' controls more prominent, and added feedback of the camera name + firmware. @@ -152,6 +152,6 @@ V4 * For the Network setup I want to implement https://github.com/Hieromon/AutoConnect * UI Skinning/Theming * OSD - * Temperature/humidity/pressure sensor suport (bme20,dht11) -You can check the [enhancement list](https://github.com/easytarget/esp32-cam-webserver/issues?q=is%3Aissue+label%3Aenhancement) (past and present), and add any thoghts you may have there. + * Temperature/humidity/pressure sensor support (bme20,dht11) +You can check the [enhancement list](https://github.com/easytarget/esp32-cam-webserver/issues?q=is%3Aissue+label%3Aenhancement) (past and present), and add any thoughts you may have there. diff --git a/myconfig.sample.h b/myconfig.sample.h index f87e4bf..215f9ec 100644 --- a/myconfig.sample.h +++ b/myconfig.sample.h @@ -25,14 +25,14 @@ struct station stationList[] = {{"ssid1", "pass1", true}, {"ssid2", "pass2", true}, {"ssid3", "pass3", false}}; - * Note the use of nested braces '{' and '}' to group each entry, and commas ',' to seperate them. + * Note the use of nested braces '{' and '}' to group each entry, and commas ',' to separate them. * * The first entry (ssid1, above) in the stationList is special, if WIFI_AP_ENABLE has been uncommented (below) * 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 seperated mac address string) in place of + * 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, */ @@ -55,7 +55,7 @@ struct station stationList[] = {{"ssid1", "pass1", true}, * 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 separated with commas (,) and not decimals (.) // #define ST_IP 192,168,0,123 // #define ST_GATEWAY 192,168,0,2 // #define ST_NETMASK 255,255,255,0 @@ -83,7 +83,7 @@ struct station stationList[] = {{"ssid1", "pass1", true}, * browser and other settings. */ // Optionally change the AccessPoint ip address (default = 192.168.4.1) -// warning - IP addresses must be seperated with commas (,) and not decimals (.) +// warning - IP addresses must be separated with commas (,) and not decimals (.) // #define AP_ADDRESS 192,168,4,1 // Uncomment this to force the AccessPoint channel number, default = 1 From ba7c20a2b67d6d07ff1b232fe91ace89416332a7 Mon Sep 17 00:00:00 2001 From: Armin Date: Thu, 16 Dec 2021 16:14:30 +0100 Subject: [PATCH 12/52] replaced \n\r with \r\n (#174) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Fix rotate -90 degree bu Adding a style with flex-direction: column; and align-items: flex-start; does the job 😀 * replaced \n\r with \r\n \n\r gives strange results in other than Arduino serial monitors. --- esp32-cam-webserver.ino | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/esp32-cam-webserver.ino b/esp32-cam-webserver.ino index d084c28..507030d 100644 --- a/esp32-cam-webserver.ino +++ b/esp32-cam-webserver.ino @@ -679,7 +679,7 @@ void setup() { ArduinoOTA.setPassword(otaPassword); Serial.printf("OTA Password: %s\n\r", otaPassword); } else { - Serial.printf("\n\rNo OTA password has been set! (insecure)\n\r\n\r"); + Serial.printf("\r\nNo OTA password has been set! (insecure)\r\n\r\n"); } ArduinoOTA .onStart([]() { @@ -692,7 +692,7 @@ void setup() { Serial.println("Start updating " + type); }) .onEnd([]() { - Serial.println("\nEnd"); + Serial.println("\r\nEnd"); }) .onProgress([](unsigned int progress, unsigned int total) { Serial.printf("Progress: %u%%\r", (progress / (total / 100))); From ae3797951479c710ed1c60d3b072de512549c99f Mon Sep 17 00:00:00 2001 From: Owen Date: Thu, 16 Dec 2021 16:20:55 +0100 Subject: [PATCH 13/52] Latest toolchain --- .travis.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.travis.yml b/.travis.yml index 08e1b19..15b1cc6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -15,15 +15,15 @@ branches: before_script: - "export DISPLAY=:99.0" - sleep 3 # give xvfb some time to start - - wget http://downloads.arduino.cc/arduino-1.8.15-linux64.tar.xz - - tar xf arduino-1.8.15-linux64.tar.xz - - mv arduino-1.8.15 $HOME/arduino_ide + - wget https://downloads.arduino.cc/arduino-1.8.18-linux64.tar.xz + - tar xf arduino-1.8.18-linux64.tar.xz + - mv arduino-1.8.18 $HOME/arduino_ide - cd $HOME/arduino_ide/hardware - mkdir esp32 - cd esp32 - - wget https://github.com/espressif/arduino-esp32/archive/refs/tags/2.0.0.tar.gz - - tar -xzf 2.0.0.tar.gz - - mv arduino-esp32-2.0.0/ esp32 + - wget https://github.com/espressif/arduino-esp32/archive/refs/tags/2.0.1.tar.gz + - tar -xzf 2.0.1.tar.gz + - mv arduino-esp32-2.0.1/ esp32 - cd esp32/tools - python --version - python get.py From 158575affe1545ea893a24b59693bd3efd3652a9 Mon Sep 17 00:00:00 2001 From: 15498th Date: Fri, 17 Dec 2021 00:14:44 +0300 Subject: [PATCH 14/52] Add framerate limit, print delay in debug dump. Add global variable and #define in config to set default. Add new parameter in /control handler and preferences. --- app_httpd.cpp | 12 +++++++++--- esp32-cam-webserver.ino | 6 ++++++ myconfig.sample.h | 3 +++ storage.cpp | 3 +++ 4 files changed, 21 insertions(+), 3 deletions(-) diff --git a/app_httpd.cpp b/app_httpd.cpp index 5b4c8bd..8280983 100644 --- a/app_httpd.cpp +++ b/app_httpd.cpp @@ -52,6 +52,7 @@ extern int8_t streamCount; extern unsigned long streamsServed; extern unsigned long imagesServed; extern int myRotation; +extern int framerateLimit; extern int lampVal; extern bool autoLamp; extern bool filesystem; @@ -274,13 +275,16 @@ static esp_err_t stream_handler(httpd_req_t *req){ break; } int64_t frame_time = esp_timer_get_time() - last_frame; - last_frame = esp_timer_get_time();; frame_time /= 1000; + int32_t frame_delay = (framerateLimit > frame_time) ? framerateLimit - frame_time : 0; + delay(frame_delay); + if (debugData) { - Serial.printf("MJPG: %uB %ums (%.1ffps)\r\n", + Serial.printf("MJPG: %uB %ums, delay: %ums, framerate (%.1ffps)\r\n", (uint32_t)(_jpg_buf_len), - (uint32_t)frame_time, 1000.0 / (uint32_t)frame_time); + (uint32_t)frame_time, frame_delay, 1000.0 / (uint32_t)(frame_time + frame_delay)); } + last_frame = esp_timer_get_time(); } streamsServed++; @@ -355,6 +359,7 @@ static esp_err_t cmd_handler(httpd_req_t *req){ else if(!strcmp(variable, "wb_mode")) res = s->set_wb_mode(s, val); else if(!strcmp(variable, "ae_level")) res = s->set_ae_level(s, val); else if(!strcmp(variable, "rotate")) myRotation = val; + else if(!strcmp(variable, "framerate_limit")) framerateLimit = val; else if(!strcmp(variable, "autolamp") && (lampVal != -1)) { autoLamp = val; if (autoLamp) { @@ -410,6 +415,7 @@ static esp_err_t status_handler(httpd_req_t *req){ *p++ = '{'; p+=sprintf(p, "\"lamp\":%d,", lampVal); p+=sprintf(p, "\"autolamp\":%d,", autoLamp); + p+=sprintf(p, "\"framerate_limit\":%d,", framerateLimit); p+=sprintf(p, "\"framesize\":%u,", s->status.framesize); p+=sprintf(p, "\"quality\":%u,", s->status.quality); p+=sprintf(p, "\"brightness\":%d,", s->status.brightness); diff --git a/esp32-cam-webserver.ino b/esp32-cam-webserver.ino index 507030d..50a5dc2 100644 --- a/esp32-cam-webserver.ino +++ b/esp32-cam-webserver.ino @@ -147,6 +147,12 @@ char myVer[] PROGMEM = __DATE__ " @ " __TIME__; #endif int myRotation = CAM_ROTATION; +// minimal frame duration in ms, effectively 1/maxFPS +#if !defined(FRAMERATE_LIMIT) + #define FRAMERATE_LIMIT 0 +#endif +int framerateLimit = FRAMERATE_LIMIT; + // Illumination LAMP and status LED #if defined(LAMP_DISABLE) int lampVal = -1; // lamp is disabled in config diff --git a/myconfig.sample.h b/myconfig.sample.h index 215f9ec..f40dcdb 100644 --- a/myconfig.sample.h +++ b/myconfig.sample.h @@ -143,6 +143,9 @@ struct station stationList[] = {{"ssid1", "pass1", true}, // Browser Rotation (one of: -90,0,90, default 0) // #define CAM_ROTATION 0 +// Minimal frame duration in ms, used to limit max FPS +// max_fps = 1000/framerate_limit +// #define FRAMERATE_LIMIT 500 /* * Additional Features diff --git a/storage.cpp b/storage.cpp index 8ebc7fa..c463c97 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 framerateLimit; // Minimal frame duration /* * Useful utility when debugging... @@ -90,6 +91,7 @@ void loadPrefs(fs::FS &fs){ // process all the settings lampVal = jsonExtract(prefs, "lamp").toInt(); autoLamp = jsonExtract(prefs, "autolamp").toInt(); + framerateLimit = jsonExtract(prefs, "framerate_limit").toInt(); s->set_framesize(s, (framesize_t)jsonExtract(prefs, "framesize").toInt()); s->set_quality(s, jsonExtract(prefs, "quality").toInt()); s->set_brightness(s, jsonExtract(prefs, "brightness").toInt()); @@ -136,6 +138,7 @@ void savePrefs(fs::FS &fs){ *p++ = '{'; p+=sprintf(p, "\"lamp\":%i,", lampVal); p+=sprintf(p, "\"autolamp\":%u,", autoLamp); + p+=sprintf(p, "\"framerate_limit\":%d,", framerateLimit); p+=sprintf(p, "\"framesize\":%u,", s->status.framesize); p+=sprintf(p, "\"quality\":%u,", s->status.quality); p+=sprintf(p, "\"brightness\":%d,", s->status.brightness); From 1cda0e2ef71ae46fa9c0768c07b644c10c914020 Mon Sep 17 00:00:00 2001 From: 15498th Date: Fri, 17 Dec 2021 00:15:01 +0300 Subject: [PATCH 15/52] Add max fps control in full index page for ovh2640. --- index_ov2640.h | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/index_ov2640.h b/index_ov2640.h index f923a21..49a4980 100644 --- a/index_ov2640.h +++ b/index_ov2640.h @@ -237,6 +237,20 @@ const uint8_t index_ov2640_html[] = R"=====( +
+ + +
@@ -296,6 +310,7 @@ const uint8_t index_ov2640_html[] = R"=====( const savePrefsButton = document.getElementById('save_prefs') const clearPrefsButton = document.getElementById('clear_prefs') const rebootButton = document.getElementById('reboot') + const framerateLimit = document.getElementById('framerate_limit') const hide = el => { el.classList.add('hidden') @@ -359,6 +374,8 @@ const uint8_t index_ov2640_html[] = R"=====( } else if(el.id === "rotate"){ rotate.value = value; applyRotation(); + } else if(el.id === "framerate_limit"){ + framerate_limit.value = value; } else if(el.id === "stream_url"){ streamURL = value; viewerURL = value + 'view'; @@ -562,6 +579,10 @@ const uint8_t index_ov2640_html[] = R"=====( updateConfig(framesize) } + framerateLimit.onchange = () => { + updateConfig(framerateLimit) + } + swapButton.onclick = () => { window.open('/?view=simple','_self'); } From a63bcc35c81a9421f094a8336805edbdda5e1a31 Mon Sep 17 00:00:00 2001 From: 15498th Date: Fri, 17 Dec 2021 00:15:05 +0300 Subject: [PATCH 16/52] Add max fps control for ov3660 index page. --- index_ov3660.h | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/index_ov3660.h b/index_ov3660.h index eb7ca74..ef33baa 100644 --- a/index_ov3660.h +++ b/index_ov3660.h @@ -251,6 +251,20 @@ const uint8_t index_ov3660_html[] = R"=====(
+
+ + +
@@ -310,6 +324,7 @@ const uint8_t index_ov3660_html[] = R"=====( const savePrefsButton = document.getElementById('save_prefs') const clearPrefsButton = document.getElementById('clear_prefs') const rebootButton = document.getElementById('reboot') + const framerateLimit = document.getElementById('framerate_limit') const hide = el => { el.classList.add('hidden') @@ -371,6 +386,8 @@ const uint8_t index_ov3660_html[] = R"=====( } else if(el.id === "rotate"){ rotate.value = value; applyRotation(); + } else if(el.id === "framerate_limit"){ + framerate_limit.value = value; } else if(el.id === "stream_url"){ streamURL = value; viewerURL = value + 'view'; @@ -571,6 +588,10 @@ const uint8_t index_ov3660_html[] = R"=====( updateConfig(framesize) } + framerateLimit.onchange = () => { + updateConfig(framerateLimit) + } + swapButton.onclick = () => { window.open('/?view=simple','_self'); } From d19dd25dd3435e705eeee45c29d8e00839f9b16b Mon Sep 17 00:00:00 2001 From: 15498th <70429579+15498th@users.noreply.github.com> Date: Fri, 17 Dec 2021 16:15:43 +0300 Subject: [PATCH 17/52] Fix index selection example using wrong keyword. (#194) --- API.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/API.md b/API.md index 8e0be09..b165181 100644 --- a/API.md +++ b/API.md @@ -6,7 +6,7 @@ The WebUI and camera server communicate entirely via HTTP requests and responses ## URI's ### Http Port * `/` - Default index -* `/?target=full|simple|portal` - Go direct to specific index +* `/?view=full|simple|portal` - Go direct to specific index * `/capture` - Return a Jpeg snapshot image * `/status` - Returns a JSON string with all camera status / pairs listed * `/control?var=&val=` - Set `` to `` From 69888bd6d81b86aa7145054e03797a498d618869 Mon Sep 17 00:00:00 2001 From: 15498th Date: Sat, 18 Dec 2021 14:07:23 +0300 Subject: [PATCH 18/52] Update API documentation. --- API.md | 1 + 1 file changed, 1 insertion(+) diff --git a/API.md b/API.md index 8e0be09..9a434f2 100644 --- a/API.md +++ b/API.md @@ -26,6 +26,7 @@ Call `/control?var=&val=` with a settings key and value to set camera ``` lamp - Lamp value in percent; integer, 0 - 100 (-1 = disabled) framesize - See below +framerate_limit - Minimal frame duration in ms, used to limit max FPS. Must be positive integer quality - 10 to 63 (ov3660: 4 to 10) contrast - -2 to 2 (ov3660: -3 to 3) brightness - -2 to 2 (ov3660: -3 to 3) From 721e825daaaca67c274fd185ad1daf2862b09f39 Mon Sep 17 00:00:00 2001 From: 15498th Date: Sat, 18 Dec 2021 14:29:43 +0300 Subject: [PATCH 19/52] Use better name for framerate limit variables. --- API.md | 2 +- app_httpd.cpp | 8 ++++---- esp32-cam-webserver.ino | 6 +++--- index_ov2640.h | 16 ++++++++-------- index_ov3660.h | 16 ++++++++-------- myconfig.sample.h | 4 ++-- storage.cpp | 6 +++--- 7 files changed, 29 insertions(+), 29 deletions(-) diff --git a/API.md b/API.md index 9a434f2..f7d1640 100644 --- a/API.md +++ b/API.md @@ -26,7 +26,7 @@ Call `/control?var=&val=` with a settings key and value to set camera ``` lamp - Lamp value in percent; integer, 0 - 100 (-1 = disabled) framesize - See below -framerate_limit - Minimal frame duration in ms, used to limit max FPS. Must be positive integer +min_frame_time - Minimal frame duration in ms, used to limit max FPS. Must be positive integer quality - 10 to 63 (ov3660: 4 to 10) contrast - -2 to 2 (ov3660: -3 to 3) brightness - -2 to 2 (ov3660: -3 to 3) diff --git a/app_httpd.cpp b/app_httpd.cpp index 8280983..2026c4b 100644 --- a/app_httpd.cpp +++ b/app_httpd.cpp @@ -52,7 +52,7 @@ extern int8_t streamCount; extern unsigned long streamsServed; extern unsigned long imagesServed; extern int myRotation; -extern int framerateLimit; +extern int minFrameTime; extern int lampVal; extern bool autoLamp; extern bool filesystem; @@ -276,7 +276,7 @@ static esp_err_t stream_handler(httpd_req_t *req){ } int64_t frame_time = esp_timer_get_time() - last_frame; frame_time /= 1000; - int32_t frame_delay = (framerateLimit > frame_time) ? framerateLimit - frame_time : 0; + int32_t frame_delay = (minFrameTime > frame_time) ? minFrameTime - frame_time : 0; delay(frame_delay); if (debugData) { @@ -359,7 +359,7 @@ static esp_err_t cmd_handler(httpd_req_t *req){ else if(!strcmp(variable, "wb_mode")) res = s->set_wb_mode(s, val); else if(!strcmp(variable, "ae_level")) res = s->set_ae_level(s, val); else if(!strcmp(variable, "rotate")) myRotation = val; - else if(!strcmp(variable, "framerate_limit")) framerateLimit = val; + else if(!strcmp(variable, "min_frame_time")) minFrameTime = val; else if(!strcmp(variable, "autolamp") && (lampVal != -1)) { autoLamp = val; if (autoLamp) { @@ -415,7 +415,7 @@ static esp_err_t status_handler(httpd_req_t *req){ *p++ = '{'; p+=sprintf(p, "\"lamp\":%d,", lampVal); p+=sprintf(p, "\"autolamp\":%d,", autoLamp); - p+=sprintf(p, "\"framerate_limit\":%d,", framerateLimit); + p+=sprintf(p, "\"min_frame_time\":%d,", minFrameTime); p+=sprintf(p, "\"framesize\":%u,", s->status.framesize); p+=sprintf(p, "\"quality\":%u,", s->status.quality); p+=sprintf(p, "\"brightness\":%d,", s->status.brightness); diff --git a/esp32-cam-webserver.ino b/esp32-cam-webserver.ino index 50a5dc2..06a0eb8 100644 --- a/esp32-cam-webserver.ino +++ b/esp32-cam-webserver.ino @@ -148,10 +148,10 @@ char myVer[] PROGMEM = __DATE__ " @ " __TIME__; int myRotation = CAM_ROTATION; // minimal frame duration in ms, effectively 1/maxFPS -#if !defined(FRAMERATE_LIMIT) - #define FRAMERATE_LIMIT 0 +#if !defined(MIN_FRAME_TIME) + #define MIN_FRAME_TIME 0 #endif -int framerateLimit = FRAMERATE_LIMIT; +int minFrameTime = MIN_FRAME_TIME; // Illumination LAMP and status LED #if defined(LAMP_DISABLE) diff --git a/index_ov2640.h b/index_ov2640.h index 49a4980..1195f70 100644 --- a/index_ov2640.h +++ b/index_ov2640.h @@ -237,9 +237,9 @@ const uint8_t index_ov2640_html[] = R"=====(
-
- - @@ -310,7 +310,7 @@ const uint8_t index_ov2640_html[] = R"=====( const savePrefsButton = document.getElementById('save_prefs') const clearPrefsButton = document.getElementById('clear_prefs') const rebootButton = document.getElementById('reboot') - const framerateLimit = document.getElementById('framerate_limit') + const minFrameTime = document.getElementById('min_frame_time') const hide = el => { el.classList.add('hidden') @@ -374,8 +374,8 @@ const uint8_t index_ov2640_html[] = R"=====( } else if(el.id === "rotate"){ rotate.value = value; applyRotation(); - } else if(el.id === "framerate_limit"){ - framerate_limit.value = value; + } else if(el.id === "min_frame_time"){ + min_frame_time.value = value; } else if(el.id === "stream_url"){ streamURL = value; viewerURL = value + 'view'; @@ -579,8 +579,8 @@ const uint8_t index_ov2640_html[] = R"=====( updateConfig(framesize) } - framerateLimit.onchange = () => { - updateConfig(framerateLimit) + minFrameTime.onchange = () => { + updateConfig(minFrameTime) } swapButton.onclick = () => { diff --git a/index_ov3660.h b/index_ov3660.h index ef33baa..e28ceaa 100644 --- a/index_ov3660.h +++ b/index_ov3660.h @@ -251,9 +251,9 @@ const uint8_t index_ov3660_html[] = R"=====(
-
- - @@ -324,7 +324,7 @@ const uint8_t index_ov3660_html[] = R"=====( const savePrefsButton = document.getElementById('save_prefs') const clearPrefsButton = document.getElementById('clear_prefs') const rebootButton = document.getElementById('reboot') - const framerateLimit = document.getElementById('framerate_limit') + const minFrameTime = document.getElementById('min_frame_time') const hide = el => { el.classList.add('hidden') @@ -386,8 +386,8 @@ const uint8_t index_ov3660_html[] = R"=====( } else if(el.id === "rotate"){ rotate.value = value; applyRotation(); - } else if(el.id === "framerate_limit"){ - framerate_limit.value = value; + } else if(el.id === "min_frame_time"){ + min_frame_time.value = value; } else if(el.id === "stream_url"){ streamURL = value; viewerURL = value + 'view'; @@ -588,8 +588,8 @@ const uint8_t index_ov3660_html[] = R"=====( updateConfig(framesize) } - framerateLimit.onchange = () => { - updateConfig(framerateLimit) + minFrameTime.onchange = () => { + updateConfig(minFrameTime) } swapButton.onclick = () => { diff --git a/myconfig.sample.h b/myconfig.sample.h index f40dcdb..5958b62 100644 --- a/myconfig.sample.h +++ b/myconfig.sample.h @@ -144,8 +144,8 @@ struct station stationList[] = {{"ssid1", "pass1", true}, // #define CAM_ROTATION 0 // Minimal frame duration in ms, used to limit max FPS -// max_fps = 1000/framerate_limit -// #define FRAMERATE_LIMIT 500 +// max_fps = 1000/min_frame_time +// #define MIN_FRAME_TIME 500 /* * Additional Features diff --git a/storage.cpp b/storage.cpp index c463c97..1e202f1 100644 --- a/storage.cpp +++ b/storage.cpp @@ -7,7 +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 framerateLimit; // Minimal frame duration +extern int minFrameTime; // Minimal frame duration /* * Useful utility when debugging... @@ -91,7 +91,7 @@ void loadPrefs(fs::FS &fs){ // process all the settings lampVal = jsonExtract(prefs, "lamp").toInt(); autoLamp = jsonExtract(prefs, "autolamp").toInt(); - framerateLimit = jsonExtract(prefs, "framerate_limit").toInt(); + minFrameTime = jsonExtract(prefs, "min_frame_time").toInt(); s->set_framesize(s, (framesize_t)jsonExtract(prefs, "framesize").toInt()); s->set_quality(s, jsonExtract(prefs, "quality").toInt()); s->set_brightness(s, jsonExtract(prefs, "brightness").toInt()); @@ -138,7 +138,7 @@ void savePrefs(fs::FS &fs){ *p++ = '{'; p+=sprintf(p, "\"lamp\":%i,", lampVal); p+=sprintf(p, "\"autolamp\":%u,", autoLamp); - p+=sprintf(p, "\"framerate_limit\":%d,", framerateLimit); + p+=sprintf(p, "\"min_frame_time\":%d,", minFrameTime); p+=sprintf(p, "\"framesize\":%u,", s->status.framesize); p+=sprintf(p, "\"quality\":%u,", s->status.quality); p+=sprintf(p, "\"brightness\":%d,", s->status.brightness); From fdd920910e361149da470fff6c90cf66d0c41117 Mon Sep 17 00:00:00 2001 From: 15498th Date: Sat, 18 Dec 2021 14:39:52 +0300 Subject: [PATCH 20/52] Change UI to show the same values of framerate control as used in command interface. --- index_ov2640.h | 18 +++++++++--------- index_ov3660.h | 18 +++++++++--------- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/index_ov2640.h b/index_ov2640.h index 1195f70..2af2846 100644 --- a/index_ov2640.h +++ b/index_ov2640.h @@ -238,16 +238,16 @@ const uint8_t index_ov2640_html[] = R"=====(
- +
diff --git a/index_ov3660.h b/index_ov3660.h index e28ceaa..d7eab9b 100644 --- a/index_ov3660.h +++ b/index_ov3660.h @@ -252,16 +252,16 @@ const uint8_t index_ov3660_html[] = R"=====(
- +
From 1b836c5479f3ed3e13408b4f502f7cf770752a86 Mon Sep 17 00:00:00 2001 From: 15498th Date: Sun, 19 Dec 2021 00:08:16 +0300 Subject: [PATCH 21/52] Apply framerate limiting delay after serving content boundary. Putting delay after finishing serving the frame causes image to appear in stream with a lag. The reason for this appears to be that browser relies on next frame content boundary in order to determine the end of image. That means that in order for browser to show frame right after receiving it, server needs to send next frame content boundary right after image itself, and delay to limit framerate should be applied after content boundary, not before it. According to https://www.w3.org/Protocols/rfc1341/7_2_Multipart.html sending first frame without content boundary will likely cause its content to be ignored, which seems to be acceptable price for not treating first iteration of the loop as special case. --- app_httpd.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app_httpd.cpp b/app_httpd.cpp index 2026c4b..36654bb 100644 --- a/app_httpd.cpp +++ b/app_httpd.cpp @@ -251,9 +251,6 @@ static esp_err_t stream_handler(httpd_req_t *req){ _jpg_buf = fb->buf; } } - if(res == ESP_OK){ - res = httpd_resp_send_chunk(req, _STREAM_BOUNDARY, strlen(_STREAM_BOUNDARY)); - } if(res == ESP_OK){ size_t hlen = snprintf((char *)part_buf, 64, _STREAM_PART, _jpg_buf_len); res = httpd_resp_send_chunk(req, (const char *)part_buf, hlen); @@ -261,6 +258,9 @@ static esp_err_t stream_handler(httpd_req_t *req){ if(res == ESP_OK){ res = httpd_resp_send_chunk(req, (const char *)_jpg_buf, _jpg_buf_len); } + if(res == ESP_OK){ + res = httpd_resp_send_chunk(req, _STREAM_BOUNDARY, strlen(_STREAM_BOUNDARY)); + } if(fb){ esp_camera_fb_return(fb); fb = NULL; From 20f4c24dae61f178d3c7da8f7349774ad5f98aa2 Mon Sep 17 00:00:00 2001 From: 15498th Date: Sun, 19 Dec 2021 02:12:10 +0300 Subject: [PATCH 22/52] Fix delay before first frame by sending first request boundary before entering the loop. --- app_httpd.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app_httpd.cpp b/app_httpd.cpp index 36654bb..665de3a 100644 --- a/app_httpd.cpp +++ b/app_httpd.cpp @@ -237,6 +237,10 @@ static esp_err_t stream_handler(httpd_req_t *req){ httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*"); + if(res == ESP_OK){ + res = httpd_resp_send_chunk(req, _STREAM_BOUNDARY, strlen(_STREAM_BOUNDARY)); + } + while(true){ fb = esp_camera_fb_get(); if (!fb) { From 152eec7be60723cb7d9789ea2bbcc79a8a6f5fc5 Mon Sep 17 00:00:00 2001 From: Owen Carter Date: Fri, 31 Dec 2021 02:47:58 +0100 Subject: [PATCH 23/52] Framebuffer fix (#199) * Slightly longer flash delay, #188 * Framebuffer delayed frames * Fix Travis, again * show XCLK in dump output * comment on slow XCLK for slow clones * pump up the wifi watchdog default * latest IDE and esp core --- .travis.yml | 12 ++++++------ app_httpd.cpp | 18 ++++++++++++++---- esp32-cam-webserver.ino | 12 ++++++++---- myconfig.sample.h | 6 ++++-- platformio.ini | 3 ++- 5 files changed, 34 insertions(+), 17 deletions(-) diff --git a/.travis.yml b/.travis.yml index 15b1cc6..f6bdf9b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -15,15 +15,15 @@ branches: before_script: - "export DISPLAY=:99.0" - sleep 3 # give xvfb some time to start - - wget https://downloads.arduino.cc/arduino-1.8.18-linux64.tar.xz - - tar xf arduino-1.8.18-linux64.tar.xz - - mv arduino-1.8.18 $HOME/arduino_ide + - wget https://downloads.arduino.cc/arduino-1.8.19-linux64.tar.xz + - tar xf arduino-1.8.19-linux64.tar.xz + - mv arduino-1.8.19 $HOME/arduino_ide - cd $HOME/arduino_ide/hardware - mkdir esp32 - cd esp32 - - wget https://github.com/espressif/arduino-esp32/archive/refs/tags/2.0.1.tar.gz - - tar -xzf 2.0.1.tar.gz - - mv arduino-esp32-2.0.1/ esp32 + - wget https://github.com/espressif/arduino-esp32/archive/refs/tags/2.0.2.tar.gz + - tar -xzf 2.0.2.tar.gz + - mv arduino-esp32-2.0.2/ esp32 - cd esp32/tools - python --version - python get.py diff --git a/app_httpd.cpp b/app_httpd.cpp index 5b4c8bd..eeed766 100644 --- a/app_httpd.cpp +++ b/app_httpd.cpp @@ -63,6 +63,7 @@ extern int sketchSpace; extern String sketchMD5; extern bool otaEnabled; extern char otaPassword[]; +extern unsigned long xclkFreqHz; typedef struct { httpd_req_t *req; @@ -140,9 +141,10 @@ 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("Freq: %i MHz\r\n", ESP.getCpuFreqMHz()); + Serial.printf("CPU Freq: %i MHz, Xclk Freq: %.1f 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()) { @@ -172,7 +174,10 @@ static esp_err_t capture_handler(httpd_req_t *req){ esp_err_t res = ESP_OK; Serial.println("Capture Requested"); - if (autoLamp && (lampVal != -1)) setLamp(lampVal); + if (autoLamp && (lampVal != -1)) { + setLamp(lampVal); + delay(75); // coupled with the status led flash this gives ~150ms for lamp to settle. + } flashLED(75); // little flash of status LED int64_t fr_start = esp_timer_get_time(); @@ -198,12 +203,16 @@ static esp_err_t capture_handler(httpd_req_t *req){ Serial.println("Capture Error: Non-JPEG image returned by camera module"); } esp_camera_fb_return(fb); + fb = NULL; + int64_t fr_end = esp_timer_get_time(); if (debugData) { Serial.printf("JPG: %uB %ums\r\n", (uint32_t)(fb_len), (uint32_t)((fr_end - fr_start)/1000)); } imagesServed++; - if (autoLamp && (lampVal != -1)) setLamp(0); + if (autoLamp && (lampVal != -1)) { + setLamp(0); + } return res; } @@ -557,10 +566,11 @@ 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,"Freq: %i MHz
\n", ESP.getCpuFreqMHz()); + d+= sprintf(d,"CPU Freq: %i MHz, Xclk Freq: %.1f 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()); diff --git a/esp32-cam-webserver.ino b/esp32-cam-webserver.ino index 507030d..2ca4b7a 100644 --- a/esp32-cam-webserver.ino +++ b/esp32-cam-webserver.ino @@ -95,7 +95,7 @@ extern void serialDump(); #endif #if !defined(WIFI_WATCHDOG) - #define WIFI_WATCHDOG 8000 + #define WIFI_WATCHDOG 15000 #endif // Number of known networks in stationList[] @@ -137,7 +137,9 @@ char myVer[] PROGMEM = __DATE__ " @ " __TIME__; // Originally: config.xclk_freq_hz = 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) - #define XCLK_FREQ_HZ 16500000; + unsigned long xclkFreqHz = 16500000; +#else + unsigned long xclkFreqHz = XCLK_FREQ_HZ; #endif // initial rotation @@ -522,13 +524,14 @@ 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 = XCLK_FREQ_HZ; + config.xclk_freq_hz = xclkFreqHz; config.pixel_format = PIXFORMAT_JPEG; + config.grab_mode = CAMERA_GRAB_LATEST; // Pre-allocate large buffers if(psramFound()){ config.frame_size = FRAMESIZE_UXGA; config.jpeg_quality = 10; - config.fb_count = 6; // We can be generous since we are not using facedetect anymore, allows for bigger jpeg frame size (data) + config.fb_count = 2; } else { config.frame_size = FRAMESIZE_SVGA; config.jpeg_quality = 12; @@ -638,6 +641,7 @@ void setup() { // check for saved preferences and apply them if (filesystem) { + delay(200); // a short delay to let spi bus settle after camera init filesystemStart(); loadPrefs(SPIFFS); } else { diff --git a/myconfig.sample.h b/myconfig.sample.h index 215f9ec..46bf3fd 100644 --- a/myconfig.sample.h +++ b/myconfig.sample.h @@ -99,9 +99,9 @@ struct station stationList[] = {{"ssid1", "pass1", true}, /* * Wifi Watchdog defines how long we spend waiting for a connection before retrying, * and how often we check to see if we are still connected, milliseconds - * You may wish to increase this if your WiFi is slow at conencting, + * You may wish to increase this if your WiFi is slow at conencting. */ -// #define WIFI_WATCHDOG 8000 +// #define WIFI_WATCHDOG 15000 /* * Over The Air firmware updates can be disabled by uncommenting the folowing line @@ -187,3 +187,5 @@ struct station stationList[] = {{"ssid1", "pass1", true}, // 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; diff --git a/platformio.ini b/platformio.ini index 624baf5..a8c88b4 100644 --- a/platformio.ini +++ b/platformio.ini @@ -12,7 +12,8 @@ src_dir = ./ [env:esp32dev] -platform = espressif32 +platform = https://github.com/platformio/platform-espressif32.git#feature/arduino-upstream +platform_packages = framework-arduinoespressif32@https://github.com/espressif/arduino-esp32.git#2.0.2 board = esp32dev board_build.partitions = min_spiffs.csv framework = arduino From 395c4c70bbbc710076e383b6fec2b5959f79b082 Mon Sep 17 00:00:00 2001 From: Owen Date: Fri, 31 Dec 2021 10:36:10 +0100 Subject: [PATCH 24/52] Update Version [ci skip] --- src/version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/version.h b/src/version.h index 25581dc..453ad7d 100644 --- a/src/version.h +++ b/src/version.h @@ -1,4 +1,4 @@ /* Version of upstream code */ -char baseVersion[] = "4.0.beta2"; +char baseVersion[] = "4.0.beta3"; From 4b6cd9a56ef75c3e744c50b91043bca84f79cd92 Mon Sep 17 00:00:00 2001 From: Owen Date: Fri, 31 Dec 2021 11:09:51 +0100 Subject: [PATCH 25/52] Add definition for ArduCAM_ESP32S_UNO [ci skip] --- camera_pins.h | 79 +++++++++++++++++++++++++++++++---------------- myconfig.sample.h | 1 + 2 files changed, 53 insertions(+), 27 deletions(-) diff --git a/camera_pins.h b/camera_pins.h index 3853863..68c4284 100644 --- a/camera_pins.h +++ b/camera_pins.h @@ -5,7 +5,33 @@ * Defaults to AI-THINKER CAM module * */ -#if defined(CAMERA_MODEL_WROVER_KIT) +#if defined(CAMERA_MODEL_AI_THINKER) + // + // AI Thinker + // https://github.com/SeeedDocument/forum_doc/raw/master/reg/ESP32_CAM_V1.6.pdf + // + #define PWDN_GPIO_NUM 32 + #define RESET_GPIO_NUM -1 + #define XCLK_GPIO_NUM 0 + #define SIOD_GPIO_NUM 26 + #define SIOC_GPIO_NUM 27 + #define Y9_GPIO_NUM 35 + #define Y8_GPIO_NUM 34 + #define Y7_GPIO_NUM 39 + #define Y6_GPIO_NUM 36 + #define Y5_GPIO_NUM 21 + #define Y4_GPIO_NUM 19 + #define Y3_GPIO_NUM 18 + #define Y2_GPIO_NUM 5 + #define VSYNC_GPIO_NUM 25 + #define HREF_GPIO_NUM 23 + #define PCLK_GPIO_NUM 22 + #define LED_PIN 33 // Status led + #define LED_ON LOW // - Pin is inverted. + #define LED_OFF HIGH // + #define LAMP_PIN 4 // LED FloodLamp. + +#elif defined(CAMERA_MODEL_WROVER_KIT) // // ESP WROVER // https://dl.espressif.com/dl/schematics/ESP-WROVER-KIT_SCH-2.pdf @@ -161,32 +187,6 @@ // #define LED_OFF LOW // // #define LAMP_PIN x // LED FloodLamp. -#elif defined(CAMERA_MODEL_AI_THINKER) - // - // AI Thinker - // https://github.com/SeeedDocument/forum_doc/raw/master/reg/ESP32_CAM_V1.6.pdf - // - #define PWDN_GPIO_NUM 32 - #define RESET_GPIO_NUM -1 - #define XCLK_GPIO_NUM 0 - #define SIOD_GPIO_NUM 26 - #define SIOC_GPIO_NUM 27 - #define Y9_GPIO_NUM 35 - #define Y8_GPIO_NUM 34 - #define Y7_GPIO_NUM 39 - #define Y6_GPIO_NUM 36 - #define Y5_GPIO_NUM 21 - #define Y4_GPIO_NUM 19 - #define Y3_GPIO_NUM 18 - #define Y2_GPIO_NUM 5 - #define VSYNC_GPIO_NUM 25 - #define HREF_GPIO_NUM 23 - #define PCLK_GPIO_NUM 22 - #define LED_PIN 33 // Status led - #define LED_ON LOW // - Pin is inverted. - #define LED_OFF HIGH // - #define LAMP_PIN 4 // LED FloodLamp. - #elif defined(CAMERA_MODEL_TTGO_T_JOURNAL) // // LilyGO TTGO T-Journal ESP32; with OLED! but not used here.. :-( @@ -212,6 +212,31 @@ // #define LED_OFF HIGH // // #define LAMP_PIN 4 // LED FloodLamp. +#elif defined(CAMERA_MODEL_ARDUCAM_ESP32S_UNO) + // Pins from user @rdragonrydr + // https://github.com/ArduCAM/ArduCAM_ESP32S_UNO/ + // Based on AI-THINKER definitions + #define PWDN_GPIO_NUM 32 + #define RESET_GPIO_NUM -1 + #define XCLK_GPIO_NUM 0 + #define SIOD_GPIO_NUM 26 + #define SIOC_GPIO_NUM 27 + #define Y9_GPIO_NUM 35 + #define Y8_GPIO_NUM 34 + #define Y7_GPIO_NUM 39 + #define Y6_GPIO_NUM 36 + #define Y5_GPIO_NUM 21 + #define Y4_GPIO_NUM 19 + #define Y3_GPIO_NUM 18 + #define Y2_GPIO_NUM 5 + #define VSYNC_GPIO_NUM 25 + #define HREF_GPIO_NUM 23 + #define PCLK_GPIO_NUM 22 + #define LED_PIN 2 // Status led + #define LED_ON HIGH // - Pin is not inverted. + #define LED_OFF LOW // + //#define LAMP_PIN x // No LED FloodLamp. + #else // Well. // that went badly... diff --git a/myconfig.sample.h b/myconfig.sample.h index 46bf3fd..e463e4b 100644 --- a/myconfig.sample.h +++ b/myconfig.sample.h @@ -182,6 +182,7 @@ struct station stationList[] = {{"ssid1", "pass1", true}, // #define CAMERA_MODEL_M5STACK_WIDE // #define CAMERA_MODEL_M5STACK_ESP32CAM // Originally: CAMERA_MODEL_M5STACK_NO_PSRAM // #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 From de0154779d9bb497546eadbe1b43cfd3320419b3 Mon Sep 17 00:00:00 2001 From: Owen Date: Tue, 8 Mar 2022 11:34:45 +0100 Subject: [PATCH 26/52] Xclock in gui and stream stop button --- app_httpd.cpp | 42 +++++++++++++++++++++++++++++++---------- esp32-cam-webserver.ino | 10 +++++----- index_ov2640.h | 14 ++++++++++++++ index_ov3660.h | 14 ++++++++++++++ myconfig.sample.h | 37 ++++++++++++++++++------------------ src/version.h | 2 +- storage.cpp | 5 +++++ 7 files changed, 90 insertions(+), 34 deletions(-) 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); From 6d825277cfc37cb8a795fdd6f6186c52b5ff10b9 Mon Sep 17 00:00:00 2001 From: Owen Date: Tue, 8 Mar 2022 13:46:51 +0100 Subject: [PATCH 27/52] Fail if no PSRAM, fix #196 --- esp32-cam-webserver.ino | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/esp32-cam-webserver.ino b/esp32-cam-webserver.ino index c6851cc..a0a5290 100644 --- a/esp32-cam-webserver.ino +++ b/esp32-cam-webserver.ino @@ -478,8 +478,6 @@ void WifiSetup() { } void setup() { - // This might reduce boot loops caused by camera init failures when soft rebooting - // See, for instance, https://esp32.com/viewtopic.php?t=3152 Serial.begin(115200); Serial.setDebugOutput(true); Serial.println(); @@ -491,12 +489,21 @@ void setup() { Serial.print("Base Release: "); Serial.println(baseVersion); + // Warn if no PSRAM is detected (typically user error with board selection in the IDE) + if(!psramFound()){ + Serial.println("\r\nFatal Error; Halting"); + while (true) { + Serial.println("No PSRAM found; camera cannot be initialised: Please check the board config for your module."); + delay(5000); + } + } + if (stationCount == 0) { - Serial.println("\r\nFatal Error; Halting"); - while (true) { - Serial.println("No wifi details have been configured; we cannot connect to existing WiFi or start our own AccessPoint, there is no point in proceeding."); - delay(5000); - } + Serial.println("\r\nFatal Error; Halting"); + while (true) { + Serial.println("No wifi details have been configured; we cannot connect to existing WiFi or start our own AccessPoint, there is no point in proceeding."); + delay(5000); + } } #if defined(LED_PIN) // If we have a notification LED, set it to output @@ -747,12 +754,6 @@ void setup() { // As a final init step chomp out the serial buffer in case we have recieved mis-keys or garbage during startup while (Serial.available()) Serial.read(); - // Warn if no PSRAM is detected (typically user error with board selection in the IDE) - if(!psramFound()){ - Serial.printf("\r\nNo PSRAM found.\r\nPlease check the board config for your module.\r\n"); - Serial.printf("High resolution/quality images & streams will show incomplete frames due to low memory.\r\n"); - } - // While in Beta; Warn! Serial.print("\r\nThis is the 4.0 alpha\r\n - Face detection has been removed!\r\n"); } From 9402cdf4b713cb1f9cecb35a9692fd2c12c3e1ea Mon Sep 17 00:00:00 2001 From: Owen Date: Tue, 8 Mar 2022 13:51:56 +0100 Subject: [PATCH 28/52] remove the beta warning --- esp32-cam-webserver.ino | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/esp32-cam-webserver.ino b/esp32-cam-webserver.ino index a0a5290..5b46b0f 100644 --- a/esp32-cam-webserver.ino +++ b/esp32-cam-webserver.ino @@ -754,8 +754,8 @@ void setup() { // As a final init step chomp out the serial buffer in case we have recieved mis-keys or garbage during startup while (Serial.available()) Serial.read(); - // While in Beta; Warn! - Serial.print("\r\nThis is the 4.0 alpha\r\n - Face detection has been removed!\r\n"); + // Info line; use for Info messages; eg 'This is a Beta!' warnings, etc. as necesscary + // Serial.print("\r\nThis is the 4.1 beta\r\n"); } void loop() { From 2be6d0bdd20a5f15bfd037bd1dfd3c0c2081891e Mon Sep 17 00:00:00 2001 From: Owen Date: Tue, 8 Mar 2022 14:56:25 +0100 Subject: [PATCH 29/52] Start SPIFFS before camera module, #214 --- esp32-cam-webserver.ino | 8 +++++++- storage.cpp | 4 ++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/esp32-cam-webserver.ino b/esp32-cam-webserver.ino index 5b46b0f..bc94b59 100644 --- a/esp32-cam-webserver.ino +++ b/esp32-cam-webserver.ino @@ -488,6 +488,7 @@ void setup() { Serial.println(myVer); Serial.print("Base Release: "); Serial.println(baseVersion); + Serial.println(); // Warn if no PSRAM is detected (typically user error with board selection in the IDE) if(!psramFound()){ @@ -511,6 +512,12 @@ void setup() { digitalWrite(LED_PIN, LED_ON); #endif + // Start the SPIFFS filesystem before we initialise the camera + if (filesystem) { + filesystemStart(); + delay(200); // a short delay to let spi bus settle after SPIFFS init + } + // Create camera config structure; and populate with hardware and other defaults camera_config_t config; config.ledc_channel = LEDC_CHANNEL_0; @@ -649,7 +656,6 @@ void setup() { if (filesystem) { delay(200); // a short delay to let spi bus settle after camera init - filesystemStart(); loadPrefs(SPIFFS); } else { Serial.println("No Internal Filesystem, cannot load or save preferences"); diff --git a/storage.cpp b/storage.cpp index 8a2644a..e740878 100644 --- a/storage.cpp +++ b/storage.cpp @@ -185,10 +185,11 @@ void removePrefs(fs::FS &fs) { } void filesystemStart(){ + Serial.println("Starting internal SPIFFS filesystem"); while ( !SPIFFS.begin(FORMAT_SPIFFS_IF_FAILED) ) { // if we sit in this loop something is wrong; // if no existing spiffs partition exists one should be automagically created. - Serial.println("SPIFFS Mount failed, this can happen on first-run initialisation."); + Serial.println("SPIFFS Mount failed, this can happen on first-run initialisation"); Serial.println("If it happens repeatedly check if a SPIFFS partition is present for your board?"); for (int i=0; i<10; i++) { flashLED(100); // Show SPIFFS failure @@ -197,6 +198,5 @@ void filesystemStart(){ delay(1000); Serial.println("Retrying.."); } - Serial.println("Internal filesystem contents"); listDir(SPIFFS, "/", 0); } From 3fc9389c5721eefe13c6890672f480fe62ac2b63 Mon Sep 17 00:00:00 2001 From: Owen Date: Wed, 9 Mar 2022 08:27:29 +0100 Subject: [PATCH 30/52] fix doc --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index cc8b8d6..2efd161 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -16,7 +16,7 @@ Pull requests are the best way to propose changes to the codebase (I use [Github 1. Fork the repo and create your branch from `master`. 2. Give your branch a clear descriptive name and do your changes there. 3. If you've changed the HTTP APIs, update the documentation. -4. Issue a pull request against a branch *of the same name* in the main repo. +4. Issue a pull request against the master branch in the main repo. 5. Clearly describe your changes and the reason for them in the pull request. ## Any contributions you make will be under the GNU Lesser General Public License v2.1 From 322187ced98f991ca593fb4eb0f79df562d01cf7 Mon Sep 17 00:00:00 2001 From: Owen Date: Wed, 9 Mar 2022 08:40:00 +0100 Subject: [PATCH 31/52] Strip trailing whitespace everywhere --- app_httpd.cpp | 2 +- camera_pins.h | 10 ++++----- css.h | 2 +- esp32-cam-webserver.ino | 48 ++++++++++++++++++++--------------------- index_other.h | 10 ++++----- index_ov2640.h | 8 +++---- index_ov3660.h | 8 +++---- src/favicons.h | 2 +- storage.cpp | 4 ++-- 9 files changed, 47 insertions(+), 47 deletions(-) diff --git a/app_httpd.cpp b/app_httpd.cpp index 6e75f26..de7da05 100644 --- a/app_httpd.cpp +++ b/app_httpd.cpp @@ -514,7 +514,7 @@ static esp_err_t dump_handler(httpd_req_t *req){ d+= sprintf(d,"\n"); d+= sprintf(d,"\n"); d+= sprintf(d,"\n"); - d+= sprintf(d,"\n"); + d+= sprintf(d,"\n"); if (critERR.length() > 0) { d+= sprintf(d,"%s
\n", critERR.c_str()); d+= sprintf(d,"

(the serial log may give more information)


\n"); diff --git a/camera_pins.h b/camera_pins.h index 68c4284..ce4b93d 100644 --- a/camera_pins.h +++ b/camera_pins.h @@ -1,9 +1,9 @@ -/* +/* * Pin definitions for some common ESP-CAM modules - * + * * Select the module to use in myconfig.h * Defaults to AI-THINKER CAM module - * + * */ #if defined(CAMERA_MODEL_AI_THINKER) // @@ -33,7 +33,7 @@ #elif defined(CAMERA_MODEL_WROVER_KIT) // - // ESP WROVER + // ESP WROVER // https://dl.espressif.com/dl/schematics/ESP-WROVER-KIT_SCH-2.pdf // #define PWDN_GPIO_NUM -1 @@ -189,7 +189,7 @@ #elif defined(CAMERA_MODEL_TTGO_T_JOURNAL) // - // LilyGO TTGO T-Journal ESP32; with OLED! but not used here.. :-( + // LilyGO TTGO T-Journal ESP32; with OLED! but not used here.. :-( #define PWDN_GPIO_NUM 0 #define RESET_GPIO_NUM 15 #define XCLK_GPIO_NUM 27 diff --git a/css.h b/css.h index 1c329b6..66a0deb 100644 --- a/css.h +++ b/css.h @@ -2,7 +2,7 @@ * 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 */ diff --git a/esp32-cam-webserver.ino b/esp32-cam-webserver.ino index bc94b59..0ebbfd8 100644 --- a/esp32-cam-webserver.ino +++ b/esp32-cam-webserver.ino @@ -16,7 +16,7 @@ * greater feedback via a status LED, and the HTML contents are present in plain text * for easy modification. * - * A camera name can now be configured, and wifi details can be stored in an optional + * 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 changes to add the lamp control, rotation, a standalone viewer, @@ -26,7 +26,7 @@ */ -/* +/* * 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 @@ -42,7 +42,7 @@ #warning "Using Defaults: Copy myconfig.sample.h to myconfig.h and edit that to use your own settings" #define WIFI_AP_ENABLE #define CAMERA_MODEL_AI_THINKER - struct station { const char ssid[65]; const char password[65]; const bool dhcp;} + struct station { const char ssid[65]; const char password[65]; const bool dhcp;} stationList[] = {{"ESP32-CAM-CONNECT","InsecurePassword", true}}; #endif @@ -103,7 +103,7 @@ int stationCount = sizeof(stationList)/sizeof(stationList[0]); // If we have AP mode enabled, ignore first entry in the stationList[] #if defined(WIFI_AP_ENABLE) - int firstStation = 1; + int firstStation = 1; #else int firstStation = 0; #endif @@ -158,7 +158,7 @@ int myRotation = CAM_ROTATION; #else int lampVal = 0; //default to off #endif -#else +#else int lampVal = -1; // no lamp pin assigned #endif @@ -234,7 +234,7 @@ void handleSerial() { while (Serial.available()) Serial.read(); // chomp the buffer } -// Notification LED +// Notification LED void flashLED(int flashtime) { #ifdef LED_PIN // If we have it; flash it. digitalWrite(LED_PIN, LED_ON); // On at full power. @@ -297,7 +297,7 @@ void WifiSetup() { flashLED(300); Serial.println("Starting WiFi"); - // Disable power saving on WiFi to improve responsiveness + // Disable power saving on WiFi to improve responsiveness // (https://github.com/espressif/arduino-esp32/issues/1484) WiFi.setSleep(false); @@ -311,13 +311,13 @@ void WifiSetup() { byte mac[6] = {0,0,0,0,0,0}; WiFi.macAddress(mac); Serial.printf("MAC address: %02X:%02X:%02X:%02X:%02X:%02X\r\n", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); - + int bestStation = -1; long bestRSSI = -1024; char bestSSID[65] = ""; uint8_t bestBSSID[6]; if (stationCount > firstStation) { - // We have a list to scan + // We have a list to scan Serial.printf("Scanning local Wifi Networks\r\n"); int stationsFound = WiFi.scanNetworks(); Serial.printf("%i networks found\r\n", stationsFound); @@ -330,7 +330,7 @@ void WifiSetup() { Serial.printf("%3i : [%s] %s (%i)", i + 1, thisBSSID.c_str(), thisSSID.c_str(), thisRSSI); // Scan our list of known external stations for (int sta = firstStation; sta < stationCount; sta++) { - if ((strcmp(stationList[sta].ssid, thisSSID.c_str()) == 0) || + if ((strcmp(stationList[sta].ssid, thisSSID.c_str()) == 0) || (strcmp(stationList[sta].ssid, thisBSSID.c_str()) == 0)) { Serial.print(" - Known!"); // Chose the strongest RSSI seen @@ -338,7 +338,7 @@ void WifiSetup() { bestStation = sta; strncpy(bestSSID, thisSSID.c_str(), 64); // Convert char bssid[] to a byte array - parseBytes(thisBSSID.c_str(), ':', bestBSSID, 6, 16); + parseBytes(thisBSSID.c_str(), ':', bestBSSID, 6, 16); bestRSSI = thisRSSI; } } @@ -348,11 +348,11 @@ void WifiSetup() { } } else { // No list to scan, therefore we are an accesspoint - accesspoint = true; + accesspoint = true; } if (bestStation == -1) { - if (!accesspoint) { + if (!accesspoint) { #if defined(WIFI_AP_ENABLE) Serial.println("No known networks found, entering AccessPoint fallback mode"); accesspoint = true; @@ -363,14 +363,14 @@ void WifiSetup() { Serial.println("AccessPoint mode selected in config"); } } else { - Serial.printf("Connecting to Wifi Network %d: [%02X:%02X:%02X:%02X:%02X:%02X] %s \r\n", - bestStation, bestBSSID[0], bestBSSID[1], bestBSSID[2], bestBSSID[3], + Serial.printf("Connecting to Wifi Network %d: [%02X:%02X:%02X:%02X:%02X:%02X] %s \r\n", + bestStation, bestBSSID[0], bestBSSID[1], bestBSSID[2], bestBSSID[3], bestBSSID[4], bestBSSID[5], bestSSID); // Apply static settings if necesscary if (stationList[bestStation].dhcp == false) { #if defined(ST_IP) Serial.println("Applying static IP settings"); - #if !defined (ST_GATEWAY) || !defined (ST_NETMASK) + #if !defined (ST_GATEWAY) || !defined (ST_NETMASK) #error "You must supply both Gateway and NetMask when specifying a static IP address" #endif IPAddress staticIP(ST_IP); @@ -400,7 +400,7 @@ void WifiSetup() { WiFi.begin(bestSSID, stationList[bestStation].password, 0, bestBSSID); // Wait to connect, or timeout - unsigned long start = millis(); + unsigned long start = millis(); while ((millis() - start <= WIFI_WATCHDOG) && (WiFi.status() != WL_CONNECTED)) { delay(500); Serial.print('.'); @@ -518,7 +518,7 @@ void setup() { delay(200); // a short delay to let spi bus settle after SPIFFS init } - // Create camera config structure; and populate with hardware and other defaults + // Create camera config structure; and populate with hardware and other defaults camera_config_t config; config.ledc_channel = LEDC_CHANNEL_0; config.ledc_timer = LEDC_TIMER_0; @@ -574,7 +574,7 @@ void setup() { critERR += "

We will continue to reboot once per minute since this error sometimes clears automatically.

"; // Start a 60 second watchdog timer esp_task_wdt_init(60,true); - esp_task_wdt_add(NULL); + esp_task_wdt_add(NULL); } else { Serial.println("Camera init succeeded"); @@ -621,7 +621,7 @@ void setup() { /* * Add any other defaults you want to apply at startup here: * uncomment the line and set the value as desired (see the comments) - * + * * these are defined in the esp headers here: * https://github.com/espressif/esp32-camera/blob/master/driver/include/sensor.h#L149 */ @@ -688,7 +688,7 @@ void setup() { // Start OTA once connected Serial.println("Setting up OTA"); // Port defaults to 3232 - // ArduinoOTA.setPort(3232); + // ArduinoOTA.setPort(3232); // Hostname defaults to esp3232-[MAC] ArduinoOTA.setHostname(myName); // No authentication by default @@ -765,7 +765,7 @@ void setup() { } void loop() { - /* + /* * Just loop forever, reconnecting Wifi As necesscary in client mode * The stream and URI handler processes initiated by the startCameraServer() call at the * end of setup() will handle the camera and UI processing from now on. @@ -781,12 +781,12 @@ void loop() { if (captivePortal) dnsServer.processNextRequest(); } } else { - // client mode can fail; so reconnect as appropriate + // client mode can fail; so reconnect as appropriate static bool warned = false; if (WiFi.status() == WL_CONNECTED) { // We are connected, wait a bit and re-check if (warned) { - // Tell the user if we have just reconnected + // Tell the user if we have just reconnected Serial.println("WiFi reconnected"); warned = false; } diff --git a/index_other.h b/index_other.h index 06f5646..409d8d4 100644 --- a/index_other.h +++ b/index_other.h @@ -125,7 +125,7 @@ const uint8_t index_simple_html[] = R"=====( updateConfig(el); } else if(!updateRemote){ if(el.id === "lamp"){ - if (value == -1) { + if (value == -1) { hide(lampGroup) } else { show(lampGroup) @@ -142,7 +142,7 @@ const uint8_t index_simple_html[] = R"=====( streamURL = value; streamButton.setAttribute("title", `Start the stream :: {streamURL}`); console.log('Stream URL set to:' + value); - } + } } } @@ -408,7 +408,7 @@ const uint8_t streamviewer_html[] = R"=====( } else if(el.id === "stream_url"){ streamURL = value; console.log('Stream URL set to:' + value); - } + } } } @@ -460,7 +460,7 @@ const uint8_t streamviewer_html[] = R"=====( size_t streamviewer_html_len = sizeof(streamviewer_html)-1; -/* Captive Portal page +/* Captive Portal page we replace the <> delimited strings with correct values as it is served */ const std::string portal_html = R"=====( @@ -489,7 +489,7 @@ const std::string portal_html = R"=====( )====="; -/* Error page +/* Error page we replace the <> delimited strings with correct values as it is served */ const std::string error_html = R"=====( diff --git a/index_ov2640.h b/index_ov2640.h index 3baa56b..5d54c8b 100644 --- a/index_ov2640.h +++ b/index_ov2640.h @@ -350,7 +350,7 @@ const uint8_t index_ov2640_html[] = R"=====( } else if(el.id === "awb_gain"){ value ? show(wb) : hide(wb) } else if(el.id === "lamp"){ - if (value == -1) { + if (value == -1) { hide(lampGroup) hide(autolampGroup) } else { @@ -378,7 +378,7 @@ const uint8_t index_ov2640_html[] = R"=====( show(streamGroup) console.log('Stream URL set to: ' + streamURL); console.log('Stream Viewer URL set to: ' + viewerURL); - } + } } } @@ -488,7 +488,7 @@ const uint8_t index_ov2640_html[] = R"=====( } // Attach actions to controls - + streamLink.onclick = () => { stopStream(); window.open(viewerURL, "_blank"); @@ -579,7 +579,7 @@ const uint8_t index_ov2640_html[] = R"=====( swapButton.onclick = () => { window.open('/?view=simple','_self'); } - + savePrefsButton.onclick = () => { if (confirm("Save the current preferences?")) { updateConfig(savePrefsButton); diff --git a/index_ov3660.h b/index_ov3660.h index 9992376..8c1a082 100644 --- a/index_ov3660.h +++ b/index_ov3660.h @@ -362,7 +362,7 @@ const uint8_t index_ov3660_html[] = R"=====( } else if(el.id === "awb_gain"){ value ? show(wb) : hide(wb) } else if(el.id === "lamp"){ - if (value == -1) { + if (value == -1) { hide(lampGroup) hide(autolampGroup) } else { @@ -390,7 +390,7 @@ const uint8_t index_ov3660_html[] = R"=====( show(streamGroup) console.log('Stream URL set to: ' + streamURL); console.log('Stream Viewer URL set to: ' + viewerURL); - } + } } } @@ -500,7 +500,7 @@ const uint8_t index_ov3660_html[] = R"=====( } // Attach actions to controls - + streamLink.onclick = () => { stopStream(); window.open(viewerURL, "_blank"); @@ -588,7 +588,7 @@ const uint8_t index_ov3660_html[] = R"=====( swapButton.onclick = () => { window.open('/?view=simple','_self'); } - + savePrefsButton.onclick = () => { if (confirm("Save the current preferences?")) { updateConfig(savePrefsButton); diff --git a/src/favicons.h b/src/favicons.h index e5a5880..e513e1a 100644 --- a/src/favicons.h +++ b/src/favicons.h @@ -155,7 +155,7 @@ unsigned char favicon_32x32_png[] = { }; unsigned int favicon_32x32_png_len = 1051; -/* 32x32 .ico format */ +/* 32x32 .ico format */ unsigned char favicon_ico[] = { 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x20, 0x20, 0x00, 0x00, 0x01, 0x00, diff --git a/storage.cpp b/storage.cpp index e740878..fbba969 100644 --- a/storage.cpp +++ b/storage.cpp @@ -10,7 +10,7 @@ extern int autoLamp; // Automatic lamp mode extern int xclk; // Camera module clock speed /* - * Useful utility when debugging... + * Useful utility when debugging... */ void listDir(fs::FS &fs, const char * dirname, uint8_t levels){ @@ -187,7 +187,7 @@ void removePrefs(fs::FS &fs) { void filesystemStart(){ Serial.println("Starting internal SPIFFS filesystem"); while ( !SPIFFS.begin(FORMAT_SPIFFS_IF_FAILED) ) { - // if we sit in this loop something is wrong; + // if we sit in this loop something is wrong; // if no existing spiffs partition exists one should be automagically created. Serial.println("SPIFFS Mount failed, this can happen on first-run initialisation"); Serial.println("If it happens repeatedly check if a SPIFFS partition is present for your board?"); From cb78162ea4ba3f5056a4c8b168f3a98ecbc4ef86 Mon Sep 17 00:00:00 2001 From: Owen Date: Wed, 9 Mar 2022 11:40:40 +0100 Subject: [PATCH 32/52] lamp default state fix #204 --- app_httpd.cpp | 1 + esp32-cam-webserver.ino | 41 ++++++++++++++++++++--------------------- myconfig.sample.h | 2 +- 3 files changed, 22 insertions(+), 22 deletions(-) diff --git a/app_httpd.cpp b/app_httpd.cpp index de7da05..a8e0ea8 100644 --- a/app_httpd.cpp +++ b/app_httpd.cpp @@ -394,6 +394,7 @@ static esp_err_t cmd_handler(httpd_req_t *req){ if (filesystem) removePrefs(SPIFFS); } else if(!strcmp(variable, "reboot")) { + if (lampVal != -1) setLamp(0); // kill the lamp; otherwise it can remain on during the soft-reboot esp_task_wdt_init(3,true); // schedule a a watchdog panic event for 3 seconds in the future esp_task_wdt_add(NULL); periph_module_disable(PERIPH_I2C0_MODULE); // try to shut I2C down properly diff --git a/esp32-cam-webserver.ino b/esp32-cam-webserver.ino index 0ebbfd8..81644db 100644 --- a/esp32-cam-webserver.ino +++ b/esp32-cam-webserver.ino @@ -666,24 +666,13 @@ void setup() { * Camera setup complete; initialise the rest of the hardware. */ - // Initialise and set the lamp - if (lampVal != -1) { - ledcSetup(lampChannel, pwmfreq, pwmresolution); // configure LED PWM channel - if (autoLamp) setLamp(0); // set default value - else setLamp(lampVal); - #if defined(LAMP_PIN) - ledcAttachPin(LAMP_PIN, lampChannel); // attach the GPIO pin to the channel - #endif - } else { - Serial.println("No lamp, or lamp disabled in config"); - } - - // Having got this far; start Wifi and loop until we are connected or have started an AccessPoint + // Start Wifi and loop until we are connected or have started an AccessPoint while ((WiFi.status() != WL_CONNECTED) && !accesspoint) { WifiSetup(); delay(1000); } + // Set up OTA if (otaEnabled) { // Start OTA once connected Serial.println("Setting up OTA"); @@ -736,7 +725,22 @@ void setup() { Serial.println("Time functions disabled"); } - // Now we have a network we can start the two http handlers for the UI and Stream. + // Gather static values used when dumping status; these are slow functions, so just do them once during startup + sketchSize = ESP.getSketchSize(); + sketchSpace = ESP.getFreeSketchSpace(); + sketchMD5 = ESP.getSketchMD5(); + + // Initialise and set the lamp + if (lampVal != -1) { + ledcSetup(lampChannel, pwmfreq, pwmresolution); // configure LED PWM channel + ledcAttachPin(LAMP_PIN, lampChannel); // attach the GPIO pin to the channel + if (autoLamp) setLamp(0); // set default value + else setLamp(lampVal); + } else { + Serial.println("No lamp, or lamp disabled in config"); + } + + // Start the camera server startCameraServer(httpPort, streamPort); if (critERR.length() == 0) { @@ -752,16 +756,11 @@ void setup() { Serial.printf("\r\nCamera unavailable due to initialisation errors.\r\n\r\n"); } - // Used when dumping status; these are slow functions, so just do them once during startup - sketchSize = ESP.getSketchSize(); - sketchSpace = ESP.getFreeSketchSpace(); - sketchMD5 = ESP.getSketchMD5(); + // Info line; use for Info messages; eg 'This is a Beta!' warnings, etc. as necesscary + // Serial.print("\r\nThis is the 4.1 beta\r\n"); // As a final init step chomp out the serial buffer in case we have recieved mis-keys or garbage during startup while (Serial.available()) Serial.read(); - - // Info line; use for Info messages; eg 'This is a Beta!' warnings, etc. as necesscary - // Serial.print("\r\nThis is the 4.1 beta\r\n"); } void loop() { diff --git a/myconfig.sample.h b/myconfig.sample.h index 4388422..851f7f8 100644 --- a/myconfig.sample.h +++ b/myconfig.sample.h @@ -158,6 +158,7 @@ struct station stationList[] = {{"ssid1", "pass1", true}, // #define LAMP_DISABLE // Define the startup lamp power setting (as a percentage, defaults to 0%) +// Saved (SPIFFS) user settings will override this // #define LAMP_DEFAULT 0 // Assume the module used has a SPIFFS/LittleFS partition, and use that for persistent setting storage @@ -190,4 +191,3 @@ struct station stationList[] = {{"ssid1", "pass1", true}, // 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 - From f4bde8b77caa10d3b2758233909197843e7b316d Mon Sep 17 00:00:00 2001 From: Owen Date: Wed, 9 Mar 2022 11:41:40 +0100 Subject: [PATCH 33/52] autolamp value in prefs should be bool --- storage.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/storage.cpp b/storage.cpp index fbba969..56a1d75 100644 --- a/storage.cpp +++ b/storage.cpp @@ -4,10 +4,10 @@ // These are defined in the main .ino file 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 +extern int myRotation; // Rotation +extern int lampVal; // The current Lamp value +extern bool autoLamp; // Automatic lamp mode +extern int xclk; // Camera module clock speed /* * Useful utility when debugging... @@ -90,11 +90,11 @@ void loadPrefs(fs::FS &fs){ sensor_t * s = esp_camera_sensor_get(); // 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; + if (jsonExtract(prefs, "autolamp").toInt() == 0) autoLamp = false; else autoLamp = true; s->set_framesize(s, (framesize_t)jsonExtract(prefs, "framesize").toInt()); s->set_quality(s, jsonExtract(prefs, "quality").toInt()); + int xclkPref = jsonExtract(prefs, "xclk").toInt(); + if (xclkPref != 0) xclk = xclkPref; s->set_xclk(s, LEDC_TIMER_0, xclk); s->set_brightness(s, jsonExtract(prefs, "brightness").toInt()); s->set_contrast(s, jsonExtract(prefs, "contrast").toInt()); From b5c2293ceb1d112126eed4340d0a76a08d0130a1 Mon Sep 17 00:00:00 2001 From: Eric Fontaine Date: Wed, 9 Mar 2022 05:55:03 -0500 Subject: [PATCH 34/52] fix autolamp tooltip (#146) Properly apply autolamp tooltip --- index_ov2640.h | 4 ++-- index_ov3660.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/index_ov2640.h b/index_ov2640.h index f923a21..8165522 100644 --- a/index_ov2640.h +++ b/index_ov2640.h @@ -41,10 +41,10 @@ const uint8_t index_ov2640_html[] = R"=====(
Full
- -