Merge branch '4-0-rc1' into framerate_limit

This commit is contained in:
Owen Carter 2022-03-09 16:34:18 +01:00 committed by GitHub
commit cfe81b0ff8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 300 additions and 185 deletions

View File

@ -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

View File

@ -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

View File

@ -13,7 +13,7 @@ But expanded with:
* Over The Air firmware updates
* Lots of minor fixes and tweaks, documentation etc.
And 'reduced' by removing the Face Recognition features
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 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 :-)
@ -33,7 +33,7 @@ I have four [AI-THINKER ESP32-CAM](https://github.com/raphaelbs/esp32-cam-ai-thi
https://github.com/raphaelbs/esp32-cam-ai-thinker
* The AI thinker wiki can be quite informative, when run through an online translator and read sensibly:
https://wiki.ai-thinker.com/esp32-cam
* Default pinouts are also included for WRover Kit, ESP Eye and M5Stack esp32 camera modules.
* Default pinouts are also included for WRover Kit, ESP Eye and M5Stack esp32 camera modules.
I do not have any of these boards, so they are untested by me. Please [let me know](https://github.com/easytarget/esp32-cam-webserver/issues) if you find issues or have a board not [in the list](./camera_pins.h).
## Troubleshooting:
@ -48,7 +48,11 @@ The ESP itself is susceptible to the usual list of WiFi problems, not helped by
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.
A lot of common issues with this sketch are discussed and covered in the discussion forums:
https://github.com/easytarget/esp32-cam-webserver/discussions/categories/common-issues
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 or in the forums.
Note that I do not respond to any Private Messages (via github, hackaday, or wherever) for support.
@ -74,7 +78,7 @@ Is pretty simple, You just need jumper wires, no soldering really required, see
Download the latest release of the sketch from https://github.com/easytarget/esp32-cam-webserver/releases/latest
- You can get the latest stable development release by cloning / downloading the `master` branch of the repo.
This will give you an archive file with the Version number in it, eg.`esp32-cam-webserver-3.0.zip`. Tou need to unpack this into your Arduino sketch folder, and then you need to rename the folder you just extracted to remove the version number, eg.`esp32-cam-webserver-3.0` becomes `esp32-cam-webserver`.
This will give you an archive file with the Version number in it, eg.`esp32-cam-webserver-4.0.zip`. You need to unpack this into your Arduino sketch folder, and then you need to **rename the folder you extracted to remove the version number**, eg.`esp32-cam-webserver-4.0` becomes `esp32-cam-webserver`.
Once you have done that you can open the sketch in the IDE by going to the `esp32-cam-webserver` sketch folder and selecting `esp32-cam-webserver.ino`.
@ -84,7 +88,7 @@ By default the sketch assumes you have an AI-THINKER board, it creates an Access
To make a permanent config with your home wifi settings, different defaults or a different board; copy (or rename) the file `myconfig.sample.h` in the sketch folder to `myconfig.h` and edit that, all the usable defaults are in that file. Because this is your private copy of the config it will not get overwritten if you update the main sketch!
### Programming
### Programming
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)
@ -107,7 +111,7 @@ Go to the URL given in the serial output, the web UI should appear with the sett
The WiFi details can be stored in an (optional) header file to allow easier code development, and a camera name for the UI title can be configured. The lamp and status LED's are optional, and the lamp uses a exponential scale for brightness so that the control has some finess.
All of the face recognition code has been removed as of V4.0; this reduces the code size enough to allow OTA programming while improving compile and programming times.
All of the face recognition code has been removed as of V4.0; this reduces the code size enough to allow OTA programming while improving compile and programming times.
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.
@ -141,17 +145,11 @@ Contributions are welcome; please see the [Contribution guidelines](CONTRIBUTING
Time allowing; my Current plan is:
V4
* Remove face recognition entirely;
* **Done**, see the `NoFace` branch :sunglasses:
* Not optional, this is a code and maintenance nightmare. V3 can be maintained on a branch for those who need it.
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.
* Implement a better network stack for remembering multiple AP's, auto-config etc.
* 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
* 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.

View File

@ -64,6 +64,7 @@ extern int sketchSpace;
extern String sketchMD5;
extern bool otaEnabled;
extern char otaPassword[];
extern unsigned long xclk;
typedef struct {
httpd_req_t *req;
@ -78,6 +79,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
@ -143,7 +147,7 @@ void serialDump() {
int McuTf = temprature_sens_read(); // fahrenheit
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: %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()) {
@ -173,7 +177,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();
@ -199,12 +206,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;
}
@ -215,6 +226,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..
@ -273,7 +286,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;
@ -340,6 +353,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);
@ -389,6 +403,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
@ -422,6 +437,7 @@ static esp_err_t status_handler(httpd_req_t *req){
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, "\"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);
@ -496,7 +512,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;
@ -509,7 +525,7 @@ static esp_err_t dump_handler(httpd_req_t *req){
d+= sprintf(d,"<link rel=\"stylesheet\" type=\"text/css\" href=\"/style.css\">\n");
d+= sprintf(d,"</head>\n");
d+= sprintf(d,"<body>\n");
d+= sprintf(d,"<img src=\"/logo.svg\" style=\"position: relative; float: right;\">\n");
d+= sprintf(d,"<img src=\"/logo.svg\" style=\"position: relative; float: right;\">\n");
if (critERR.length() > 0) {
d+= sprintf(d,"<span style=\"color:red;\">%s<hr></span>\n", critERR.c_str());
d+= sprintf(d,"<h2 style=\"color:red;\">(the serial log may give more information)</h2><br>\n");
@ -570,7 +586,7 @@ static esp_err_t dump_handler(httpd_req_t *req){
d+= sprintf(d,"Up: %" PRId64 ":%02i:%02i:%02i (d:h:m:s)<br>\n", upDays, upHours, upMin, upSec);
d+= sprintf(d,"Active streams: %i, Previous streams: %lu, Images captured: %lu<br>\n", streamCount, streamsServed, imagesServed);
d+= sprintf(d,"Freq: %i MHz<br>\n", ESP.getCpuFreqMHz());
d+= sprintf(d,"CPU Freq: %i MHz, Xclk Freq: %i MHz<br>\n", ESP.getCpuFreqMHz(), xclk);
d+= sprintf(d,"<span title=\"NOTE: Internal temperature sensor readings can be innacurate on the ESP32-c1 chipset, and may vary significantly between devices!\">");
d+= sprintf(d,"MCU temperature : %i &deg;C, %i &deg;F</span>\n<br>", McuTc, McuTf);
d+= sprintf(d,"Heap: %i, free: %i, min free: %i, max block: %i<br>\n", ESP.getHeapSize(), ESP.getFreeHeap(), ESP.getMinFreeHeap(), ESP.getMaxAllocHeap());
@ -590,6 +606,7 @@ static esp_err_t dump_handler(httpd_req_t *req){
// Footer
d+= sprintf(d,"<br><div class=\"input-group\">\n");
d+= sprintf(d,"<button title=\"Instant Refresh; the page reloads every minute anyway\" onclick=\"location.replace(document.URL)\">Refresh</button>\n");
d+= sprintf(d,"<button title=\"Stop any active streams\" onclick=\"let throwaway = fetch('stop');setTimeout(function(){\nlocation.replace(document.URL);\n}, 200);\">Stop Stream</button>\n");
d+= sprintf(d,"<button title=\"Close this page\" onclick=\"javascript:window.close()\">Close</button>\n");
d+= sprintf(d,"</div>\n</body>\n");
// A javascript timer to refresh the page every minute.
@ -601,6 +618,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 +635,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 +643,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("<APPURL>")) != std::string::npos)
@ -705,7 +731,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 +793,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 +849,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;

View File

@ -1,13 +1,39 @@
/*
/*
* 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_WROVER_KIT)
#if defined(CAMERA_MODEL_AI_THINKER)
//
// ESP WROVER
// 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
//
#define PWDN_GPIO_NUM -1
@ -161,35 +187,9 @@
// #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.. :-(
// 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
@ -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...

2
css.h
View File

@ -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
*/

View File

@ -6,6 +6,7 @@
#include <ArduinoOTA.h>
#include "src/parsebytes.h"
#include "time.h"
#include <ESPmDNS.h>
/* This sketch is a extension/expansion/reork of the 'official' ESP32 Camera example
@ -16,7 +17,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 +27,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 +43,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
@ -95,7 +96,7 @@ extern void serialDump();
#endif
#if !defined(WIFI_WATCHDOG)
#define WIFI_WATCHDOG 8000
#define WIFI_WATCHDOG 15000
#endif
// Number of known networks in stationList[]
@ -103,7 +104,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
@ -134,10 +135,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)
#define XCLK_FREQ_HZ 16500000;
#if !defined (XCLK_FREQ_MHZ)
unsigned long xclk = 16;
#else
unsigned long xclk = XCLK_FREQ_MHZ;
#endif
// initial rotation
@ -162,7 +165,7 @@ int minFrameTime = MIN_FRAME_TIME;
#else
int lampVal = 0; //default to off
#endif
#else
#else
int lampVal = -1; // no lamp pin assigned
#endif
@ -238,7 +241,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.
@ -301,7 +304,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);
@ -315,13 +318,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);
@ -334,7 +337,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
@ -342,7 +345,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;
}
}
@ -352,11 +355,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;
@ -367,14 +370,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);
@ -404,7 +407,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('.');
@ -482,8 +485,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();
@ -494,13 +495,23 @@ 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()){
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
@ -508,7 +519,13 @@ void setup() {
digitalWrite(LED_PIN, LED_ON);
#endif
// Create camera config structure; and populate with hardware and other defaults
// 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;
config.ledc_timer = LEDC_TIMER_0;
@ -528,13 +545,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 = xclk * 1000000;
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;
@ -563,7 +581,7 @@ void setup() {
critERR += "<p>We will continue to reboot once per minute since this error sometimes clears automatically.</p>";
// 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");
@ -610,7 +628,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
*/
@ -644,7 +662,7 @@ void setup() {
// check for saved preferences and apply them
if (filesystem) {
filesystemStart();
delay(200); // a short delay to let spi bus settle after camera init
loadPrefs(SPIFFS);
} else {
Serial.println("No Internal Filesystem, cannot load or save preferences");
@ -655,29 +673,18 @@ 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");
// Port defaults to 3232
// ArduinoOTA.setPort(3232);
// ArduinoOTA.setPort(3232);
// Hostname defaults to esp3232-[MAC]
ArduinoOTA.setHostname(myName);
// No authentication by default
@ -714,8 +721,17 @@ void setup() {
ArduinoOTA.begin();
} else {
Serial.println("OTA is disabled");
if (!MDNS.begin(myName)) {
Serial.println("Error setting up MDNS responder!");
}
Serial.println("mDNS responder started");
}
//MDNS Config -- note that if OTA is NOT enabled this needs prior steps!
MDNS.addService("http", "tcp", 80);
Serial.println("Added HTTP service to MDNS server");
// Set time via NTP server when enabled
if (haveTime) {
Serial.print("Time: ");
@ -725,7 +741,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) {
@ -741,26 +772,15 @@ 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();
// 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");
}
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.
@ -776,12 +796,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;
}

View File

@ -36,11 +36,11 @@ const uint8_t index_simple_html[] = R"=====(<!doctype html>
<div class="hidden" id="sidebar">
<input type="checkbox" id="nav-toggle-cb">
<nav id="menu" style="width:24em;">
<div class="input-group hidden" id="lamp-group">
<div class="input-group hidden" id="lamp-group" title="Brightness of flashlight LED. Warning: Very bright! Be careful when increasing. Avoid looking directly at LED!>
<label for="lamp">Light</label>
<div class="range-min">Off</div>
<input type="range" id="lamp" min="0" max="100" value="0" class="action-setting">
<div class="range-max">Full</div>
<div class="range-max">Full&#9888;</div>
</div>
<div class="input-group" id="framesize-group">
<label for="framesize">Resolution</label>
@ -125,7 +125,7 @@ const uint8_t index_simple_html[] = R"=====(<!doctype html>
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"=====(<!doctype html>
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"=====(<!doctype html>
} 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"=====(<!doctype html>
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"=====(<!doctype html>
@ -489,7 +489,7 @@ const std::string portal_html = R"=====(<!doctype html>
</body>
</html>)=====";
/* Error page
/* Error page
we replace the <> delimited strings with correct values as it is served */
const std::string error_html = R"=====(<!doctype html>

View File

@ -35,16 +35,16 @@ const uint8_t index_ov2640_html[] = R"=====(<!doctype html>
<div class="hidden" id="sidebar">
<input type="checkbox" id="nav-toggle-cb" checked="checked">
<nav id="menu">
<div class="input-group hidden" id="lamp-group">
<div class="input-group hidden" id="lamp-group" title="Brightness of flashlight LED. Warning: Very bright! Be careful when increasing. Avoid looking directly at LED!">
<label for="lamp">Light</label>
<div class="range-min">Off</div>
<input type="range" id="lamp" min="0" max="100" value="0" class="default-action">
<div class="range-max">Full</div>
<div class="range-max">Full&#9888;</div>
</div>
<div class="input-group hidden" id="autolamp-group">
<div class="input-group hidden" id="autolamp-group" title="Lamp only on when camera active">
<label for="autolamp">Auto Lamp</label>
<div class="switch">
<input id="autolamp" type="checkbox" class="default-action" title="Lamp only on when camera active">
<input id="autolamp" type="checkbox" class="default-action">
<label class="slider" for="autolamp"></label>
</div>
</div>
@ -74,6 +74,13 @@ const uint8_t index_ov2640_html[] = R"=====(<!doctype html>
<input type="range" id="quality" min="6" max="63" value="10" class="default-action">
<div class="range-max">High<br><span style="font-size: 80%;">(slow)</span></div>
</div>
<div class="input-group" id="set-xclk-group">
<label for="set-xclk">XCLK</label>
<div class="text">
<input id="xclk" type="number" min="2" max="32" size="4" step="1" class="default-action">
<div class="range-max">MHz</div>
</div>
</div>
<div class="input-group" id="brightness-group">
<label for="brightness">Brightness</label>
<div class="range-min">-2</div>
@ -252,7 +259,7 @@ const uint8_t index_ov2640_html[] = R"=====(<!doctype html>
</select>
</div>
<div class="input-group" id="preferences-group">
<label for="reboot" style="line-height: 2em;">Preferences</label>
<label for="prefs" style="line-height: 2em;">Preferences</label>
<button id="reboot" title="Reboot the camera module">Reboot</button>
<button id="save_prefs" title="Save Preferences on camera module">Save</button>
<button id="clear_prefs" title="Erase saved Preferences on camera module">Erase</button>
@ -306,6 +313,7 @@ const uint8_t index_ov2640_html[] = R"=====(<!doctype html>
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')
@ -357,7 +365,7 @@ const uint8_t index_ov2640_html[] = R"=====(<!doctype html>
} 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 {
@ -387,7 +395,7 @@ const uint8_t index_ov2640_html[] = R"=====(<!doctype html>
show(streamGroup)
console.log('Stream URL set to: ' + streamURL);
console.log('Stream Viewer URL set to: ' + viewerURL);
}
}
}
}
@ -412,6 +420,7 @@ const uint8_t index_ov2640_html[] = R"=====(<!doctype html>
value = el.checked ? 1 : 0
break
case 'range':
case 'number':
case 'select-one':
value = el.value
break
@ -496,7 +505,7 @@ const uint8_t index_ov2640_html[] = R"=====(<!doctype html>
}
// Attach actions to controls
streamLink.onclick = () => {
stopStream();
window.open(viewerURL, "_blank");
@ -581,12 +590,16 @@ const uint8_t index_ov2640_html[] = R"=====(<!doctype html>
minFrameTime.onchange = () => {
updateConfig(minFrameTime)
xclk.onchange = () => {
console.log("xclk:" , xclk);
updateConfig(xclk)
}
swapButton.onclick = () => {
window.open('/?view=simple','_self');
}
savePrefsButton.onclick = () => {
if (confirm("Save the current preferences?")) {
updateConfig(savePrefsButton);

View File

@ -35,16 +35,16 @@ const uint8_t index_ov3660_html[] = R"=====(<!doctype html>
<div class="hidden" id="sidebar">
<input type="checkbox" id="nav-toggle-cb" checked="checked">
<nav id="menu">
<div class="input-group hidden" id="lamp-group">
<div class="input-group hidden" id="lamp-group" title="Brightness of flashlight LED. Warning: Very bright! Be careful when increasing. Avoid looking directly at LED!">
<label for="lamp">Light</label>
<div class="range-min">Off</div>
<input type="range" id="lamp" min="0" max="100" value="0" class="default-action">
<div class="range-max">Full</div>
<div class="range-max">Full&#9888;</div>
</div>
<div class="input-group hidden" id="autolamp-group">
<div class="input-group hidden" id="autolamp-group" title="Lamp only on when camera active">
<label for="autolamp">Auto Lamp</label>
<div class="switch">
<input id="autolamp" type="checkbox" class="default-action" title="Lamp only on when camera active">
<input id="autolamp" type="checkbox" class="default-action">
<label class="slider" for="autolamp"></label>
</div>
</div>
@ -76,6 +76,13 @@ const uint8_t index_ov3660_html[] = R"=====(<!doctype html>
<input type="range" id="quality" min="4" max="63" value="10" class="default-action">
<div class="range-max">High<br><span style="font-size: 80%;">(slow)</span></div>
</div>
<div class="input-group" id="set-xclk-group">
<label for="set-xclk">XCLK</label>
<div class="text">
<input id="xclk" type="number" min="2" max="32" size="4" step="1" class="default-action">
<div class="range-max">MHz</div>
</div>
</div>
<div class="input-group" id="brightness-group">
<label for="brightness">Brightness</label>
<div class="range-min">-3</div>
@ -266,7 +273,7 @@ const uint8_t index_ov3660_html[] = R"=====(<!doctype html>
</select>
</div>
<div class="input-group" id="preferences-group">
<label for="reboot" style="line-height: 2em;">Preferences</label>
<label for="prefs" style="line-height: 2em;">Preferences</label>
<button id="reboot" title="Reboot the camera module">Reboot</button>
<button id="save_prefs" title="Save Preferences on camera module">Save</button>
<button id="clear_prefs" title="Erase saved Preferences on camera module">Erase</button>
@ -320,6 +327,7 @@ const uint8_t index_ov3660_html[] = R"=====(<!doctype html>
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')
@ -369,7 +377,7 @@ const uint8_t index_ov3660_html[] = R"=====(<!doctype html>
} 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 {
@ -399,7 +407,7 @@ const uint8_t index_ov3660_html[] = R"=====(<!doctype html>
show(streamGroup)
console.log('Stream URL set to: ' + streamURL);
console.log('Stream Viewer URL set to: ' + viewerURL);
}
}
}
}
@ -424,6 +432,7 @@ const uint8_t index_ov3660_html[] = R"=====(<!doctype html>
value = el.checked ? 1 : 0
break
case 'range':
case 'number':
case 'select-one':
value = el.value
break
@ -508,7 +517,7 @@ const uint8_t index_ov3660_html[] = R"=====(<!doctype html>
}
// Attach actions to controls
streamLink.onclick = () => {
stopStream();
window.open(viewerURL, "_blank");
@ -590,12 +599,16 @@ const uint8_t index_ov3660_html[] = R"=====(<!doctype html>
minFrameTime.onchange = () => {
updateConfig(minFrameTime)
xclk.onchange = () => {
console.log("xclk:" , xclk);
updateConfig(xclk)
}
swapButton.onclick = () => {
window.open('/?view=simple','_self');
}
savePrefsButton.onclick = () => {
if (confirm("Save the current preferences?")) {
updateConfig(savePrefsButton);

View File

@ -1,17 +1,20 @@
/*
/*
* 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.
*/
/* Give the camera a name for the web interface */
/* Give the camera a name for the web interface
* A word of warning: This name is also used for OTA updates and MDNS addressing.
* Pick something convenient!
*/
#define CAM_NAME "ESP32 camera server"
/*
* WiFi Settings
*
*
* For the simplest connection to an existing network
* just replace your ssid and password in the line below.
*/
@ -31,18 +34,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 +54,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 +82,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)
@ -99,9 +102,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
@ -161,6 +164,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
@ -185,8 +189,12 @@ 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
// 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
// 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;

View File

@ -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

View File

@ -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,

View File

@ -1,4 +1,4 @@
/* Version of upstream code */
char baseVersion[] = "4.0.beta2";
char baseVersion[] = "4.0.rc1";

View File

@ -4,13 +4,13 @@
// 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 minFrameTime; // Minimal frame duration
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...
* Useful utility when debugging...
*/
void listDir(fs::FS &fs, const char * dirname, uint8_t levels){
@ -90,10 +90,13 @@ 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();
if (jsonExtract(prefs, "autolamp").toInt() == 0) autoLamp = false; else autoLamp = true;
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());
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());
s->set_saturation(s, jsonExtract(prefs, "saturation").toInt());
@ -141,6 +144,7 @@ void savePrefs(fs::FS &fs){
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, "\"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);
@ -183,10 +187,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 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
@ -195,6 +200,5 @@ void filesystemStart(){
delay(1000);
Serial.println("Retrying..");
}
Serial.println("Internal filesystem contents");
listDir(SPIFFS, "/", 0);
}