From aa598b0921d081931abcf043dc8e874945919926 Mon Sep 17 00:00:00 2001 From: Mandl Date: Mon, 18 Mar 2024 11:06:11 +0100 Subject: [PATCH 1/8] add push picture --- .gitignore | 4 +- app_httpd.cpp | 6 +- camsend.cpp | 93 +++++ cert.h | 38 ++ esp32-cam-webserver.ino | 780 ++++++++++++++++++++++++---------------- platformio.ini | 13 +- 6 files changed, 612 insertions(+), 322 deletions(-) create mode 100644 camsend.cpp create mode 100644 cert.h diff --git a/.gitignore b/.gitignore index 4dce4cfb..1f41c40a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,9 @@ myconfig.h +myconfig2.h .pioenvs .piolibdeps .clang_complete .gcc-flags.json .pio -.vscode \ No newline at end of file +.vscode +/logs \ No newline at end of file diff --git a/app_httpd.cpp b/app_httpd.cpp index f2b11a90..f3393773 100644 --- a/app_httpd.cpp +++ b/app_httpd.cpp @@ -148,7 +148,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("CPU Freq: %i MHz, Xclk Freq: %i MHz\r\n", ESP.getCpuFreqMHz(), xclk); + Serial.printf("CPU Freq: %i MHz, Xclk Freq: %lu 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()) { @@ -449,7 +449,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, "\"xclk\":%lu,", 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); @@ -598,7 +598,7 @@ static esp_err_t dump_handler(httpd_req_t *req){ 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: %i MHz
\n", ESP.getCpuFreqMHz(), xclk); + d+= sprintf(d,"CPU Freq: %i MHz, Xclk Freq: %lu 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/camsend.cpp b/camsend.cpp new file mode 100644 index 00000000..a4c6eba6 --- /dev/null +++ b/camsend.cpp @@ -0,0 +1,93 @@ +#include +#include "cert.h" +#include "myconfig2.h" +#include + + +// Functions from the main .ino +extern void flashLED(int flashtime); +extern void setLamp(int newVal); + +extern int lampVal; +extern bool autoLamp; +extern bool debugData; + +/* + + send weather data to villa using https + +*/ +esp_err_t SendPictureHttp() +{ + + camera_fb_t *fb = NULL; + esp_err_t res = ESP_OK; + + Serial.print("SendPictureHttp "); + Serial.println(PICTURE); + 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(); + + fb = esp_camera_fb_get(); + if (!fb) + { + Serial.println("CAPTURE: failed to acquire frame"); + if (autoLamp && (lampVal != -1)) + setLamp(0); + esp_camera_return_all(); + return ESP_FAIL; + } + + size_t fb_len = 0; + if (fb->format == PIXFORMAT_JPEG) + { + fb_len = fb->len; + HTTPClient http; + + http.begin(CAMSERVER, root_ca); // Specify the URL and certificate + http.addHeader("Content-Type", "image/jpeg"); + http.addHeader("Content-Length", String(fb_len)); + http.addHeader("Filename", PICTURE); + + int httpCode = http.POST(fb->buf, fb->len); + + if (httpCode > 0) + { // Check for the returning code + + String payload = http.getString(); + Serial.println(httpCode); + } + else + { + Serial.println("Error on HTTP request " + String(httpCode)); + res = ESP_FAIL; + } + http.end(); // Free the resources + } + else + { + res = ESP_FAIL; + 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)); + } + + if (autoLamp && (lampVal != -1)) + { + setLamp(0); + } + + return res; +} \ No newline at end of file diff --git a/cert.h b/cert.h new file mode 100644 index 00000000..c309b9c5 --- /dev/null +++ b/cert.h @@ -0,0 +1,38 @@ +#ifndef __CERTLETS__ +#define __CERTLETS__ + +// lets encrypt R3 +const char *root_ca = + "-----BEGIN CERTIFICATE-----\n" + "MIIFFjCCAv6gAwIBAgIRAJErCErPDBinU/bWLiWnX1owDQYJKoZIhvcNAQELBQAw\n" + "TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh\n" + "cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMjAwOTA0MDAwMDAw\n" + "WhcNMjUwOTE1MTYwMDAwWjAyMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNTGV0J3Mg\n" + "RW5jcnlwdDELMAkGA1UEAxMCUjMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK\n" + "AoIBAQC7AhUozPaglNMPEuyNVZLD+ILxmaZ6QoinXSaqtSu5xUyxr45r+XXIo9cP\n" + "R5QUVTVXjJ6oojkZ9YI8QqlObvU7wy7bjcCwXPNZOOftz2nwWgsbvsCUJCWH+jdx\n" + "sxPnHKzhm+/b5DtFUkWWqcFTzjTIUu61ru2P3mBw4qVUq7ZtDpelQDRrK9O8Zutm\n" + "NHz6a4uPVymZ+DAXXbpyb/uBxa3Shlg9F8fnCbvxK/eG3MHacV3URuPMrSXBiLxg\n" + "Z3Vms/EY96Jc5lP/Ooi2R6X/ExjqmAl3P51T+c8B5fWmcBcUr2Ok/5mzk53cU6cG\n" + "/kiFHaFpriV1uxPMUgP17VGhi9sVAgMBAAGjggEIMIIBBDAOBgNVHQ8BAf8EBAMC\n" + "AYYwHQYDVR0lBBYwFAYIKwYBBQUHAwIGCCsGAQUFBwMBMBIGA1UdEwEB/wQIMAYB\n" + "Af8CAQAwHQYDVR0OBBYEFBQusxe3WFbLrlAJQOYfr52LFMLGMB8GA1UdIwQYMBaA\n" + "FHm0WeZ7tuXkAXOACIjIGlj26ZtuMDIGCCsGAQUFBwEBBCYwJDAiBggrBgEFBQcw\n" + "AoYWaHR0cDovL3gxLmkubGVuY3Iub3JnLzAnBgNVHR8EIDAeMBygGqAYhhZodHRw\n" + "Oi8veDEuYy5sZW5jci5vcmcvMCIGA1UdIAQbMBkwCAYGZ4EMAQIBMA0GCysGAQQB\n" + "gt8TAQEBMA0GCSqGSIb3DQEBCwUAA4ICAQCFyk5HPqP3hUSFvNVneLKYY611TR6W\n" + "PTNlclQtgaDqw+34IL9fzLdwALduO/ZelN7kIJ+m74uyA+eitRY8kc607TkC53wl\n" + "ikfmZW4/RvTZ8M6UK+5UzhK8jCdLuMGYL6KvzXGRSgi3yLgjewQtCPkIVz6D2QQz\n" + "CkcheAmCJ8MqyJu5zlzyZMjAvnnAT45tRAxekrsu94sQ4egdRCnbWSDtY7kh+BIm\n" + "lJNXoB1lBMEKIq4QDUOXoRgffuDghje1WrG9ML+Hbisq/yFOGwXD9RiX8F6sw6W4\n" + "avAuvDszue5L3sz85K+EC4Y/wFVDNvZo4TYXao6Z0f+lQKc0t8DQYzk1OXVu8rp2\n" + "yJMC6alLbBfODALZvYH7n7do1AZls4I9d1P4jnkDrQoxB3UqQ9hVl3LEKQ73xF1O\n" + "yK5GhDDX8oVfGKF5u+decIsH4YaTw7mP3GFxJSqv3+0lUFJoi5Lc5da149p90Ids\n" + "hCExroL1+7mryIkXPeFM5TgO9r0rvZaBFOvV2z0gp35Z0+L4WPlbuEjN/lxPFin+\n" + "HlUjr8gRsI3qfJOQFy/9rKIJR0Y/8Omwt/8oTWgy1mdeHmmjk7j1nYsvC9JSQ6Zv\n" + "MldlTTKB3zhThV1+XWYp6rjd5JW1zbVWEkLNxE7GJThEUG3szgBVGP7pSWTUTsqX\n" + "nLRbwHOoq7hHwg==\n" + "-----END CERTIFICATE-----\n"; + + +#endif \ No newline at end of file diff --git a/esp32-cam-webserver.ino b/esp32-cam-webserver.ino index 64d6483a..8075c74c 100644 --- a/esp32-cam-webserver.ino +++ b/esp32-cam-webserver.ino @@ -10,6 +10,9 @@ #include +SET_LOOP_TASK_STACK_SIZE(12*1024); + + /* This sketch is a extension/expansion/reork of the 'official' ESP32 Camera example * sketch from Expressif: * https://github.com/espressif/arduino-esp32/tree/master/libraries/ESP32/examples/Camera/CameraWebServer @@ -27,7 +30,6 @@ * or another board which has PSRAM enabled to use high resolution camera modes */ - /* * FOR NETWORK AND HARDWARE SETTINGS COPY OR RENAME 'myconfig.sample.h' TO 'myconfig.h' AND EDIT THAT. * @@ -38,14 +40,23 @@ // Primary config, or defaults. #if __has_include("myconfig.h") - struct station { const char ssid[65]; const char password[65]; const bool dhcp;}; // do no edit - #include "myconfig.h" +struct station +{ + const char ssid[65]; + const char password[65]; + const bool dhcp; +}; // do no edit +#include "myconfig.h" #else - #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;} - stationList[] = {{"ESP32-CAM-CONNECT","InsecurePassword", true}}; +#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; +} stationList[] = {{"ESP32-CAM-CONNECT", "InsecurePassword", true}}; #endif // Upstream version string @@ -78,52 +89,58 @@ IPAddress gw; // Declare external function from app_httpd.cpp extern void startCameraServer(int hPort, int sPort); extern void serialDump(); +extern esp_err_t SendPictureHttp(); + +unsigned long previousMillis; + +// Every 5 Minutes +const unsigned long interval = (5 * 1000); // Names for the Camera. (set these in myconfig.h) #if defined(CAM_NAME) - char myName[] = CAM_NAME; +char myName[] = CAM_NAME; #else - char myName[] = "ESP32 camera server"; +char myName[] = "ESP32 camera server"; #endif #if defined(MDNS_NAME) - char mdnsName[] = MDNS_NAME; +char mdnsName[] = MDNS_NAME; #else - char mdnsName[] = "esp32-cam"; +char mdnsName[] = "esp32-cam"; #endif // Ports for http and stream (override in myconfig.h) #if defined(HTTP_PORT) - int httpPort = HTTP_PORT; +int httpPort = HTTP_PORT; #else - int httpPort = 80; +int httpPort = 80; #endif #if defined(STREAM_PORT) - int streamPort = STREAM_PORT; +int streamPort = STREAM_PORT; #else - int streamPort = 81; +int streamPort = 81; #endif #if !defined(WIFI_WATCHDOG) - #define WIFI_WATCHDOG 15000 +#define WIFI_WATCHDOG 15000 #endif // Number of known networks in stationList[] -int stationCount = sizeof(stationList)/sizeof(stationList[0]); +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; +int firstStation = 0; #endif // Select between full and simple index as the default. #if defined(DEFAULT_INDEX_FULL) - char default_index[] = "full"; +char default_index[] = "full"; #else - char default_index[] = "simple"; +char default_index[] = "simple"; #endif // DNS server @@ -145,83 +162,83 @@ unsigned long imagesServed = 0; // Total image requests char myVer[] PROGMEM = __DATE__ " @ " __TIME__; // This will be set to the sensors PID (identifier) during initialisation -//camera_pid_t sensorPID; +// camera_pid_t sensorPID; int sensorPID; // Camera module bus communications frequency. // 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_MHZ) - unsigned long xclk = 8; +#if !defined(XCLK_FREQ_MHZ) +unsigned long xclk = 8; #else - unsigned long xclk = XCLK_FREQ_MHZ; +unsigned long xclk = XCLK_FREQ_MHZ; #endif // initial rotation // can be set in myconfig.h #if !defined(CAM_ROTATION) - #define CAM_ROTATION 0 +#define CAM_ROTATION 0 #endif int myRotation = CAM_ROTATION; // minimal frame duration in ms, effectively 1/maxFPS #if !defined(MIN_FRAME_TIME) - #define MIN_FRAME_TIME 0 +#define MIN_FRAME_TIME 0 #endif int minFrameTime = MIN_FRAME_TIME; // Illumination LAMP and status LED #if defined(LAMP_DISABLE) - int lampVal = -1; // lamp is disabled in config +int lampVal = -1; // lamp is disabled in config #elif defined(LAMP_PIN) - #if defined(LAMP_DEFAULT) - int lampVal = constrain(LAMP_DEFAULT,0,100); // initial lamp value, range 0-100 - #else - int lampVal = 0; //default to off - #endif +#if defined(LAMP_DEFAULT) +int lampVal = constrain(LAMP_DEFAULT, 0, 100); // initial lamp value, range 0-100 +#else +int lampVal = 0; // default to off +#endif #else - int lampVal = -1; // no lamp pin assigned +int lampVal = -1; // no lamp pin assigned #endif #if defined(LED_DISABLE) - #undef LED_PIN // undefining this disables the notification LED +#undef LED_PIN // undefining this disables the notification LED #endif -bool autoLamp = false; // Automatic lamp (auto on while camera running) +bool autoLamp = false; // Automatic lamp (auto on while camera running) -int lampChannel = 7; // a free PWM channel (some channels used by camera) -const int pwmfreq = 50000; // 50K pwm frequency -const int pwmresolution = 9; // duty cycle bit range -const int pwmMax = pow(2,pwmresolution)-1; +int lampChannel = 7; // a free PWM channel (some channels used by camera) +const int pwmfreq = 50000; // 50K pwm frequency +const int pwmresolution = 9; // duty cycle bit range +const int pwmMax = pow(2, pwmresolution) - 1; #if defined(NO_FS) - bool filesystem = false; +bool filesystem = false; #else - bool filesystem = true; +bool filesystem = true; #endif #if defined(NO_OTA) - bool otaEnabled = false; +bool otaEnabled = false; #else - bool otaEnabled = true; +bool otaEnabled = true; #endif #if defined(OTA_PASSWORD) - char otaPassword[] = OTA_PASSWORD; +char otaPassword[] = OTA_PASSWORD; #else - char otaPassword[] = ""; +char otaPassword[] = ""; #endif #if defined(NTPSERVER) - bool haveTime = true; - const char* ntpServer = NTPSERVER; - const long gmtOffset_sec = NTP_GMT_OFFSET; - const int daylightOffset_sec = NTP_DST_OFFSET; +bool haveTime = true; +const char *ntpServer = NTPSERVER; +const long gmtOffset_sec = NTP_GMT_OFFSET; +const int daylightOffset_sec = NTP_DST_OFFSET; #else - bool haveTime = false; - const char* ntpServer = ""; - const long gmtOffset_sec = 0; - const int daylightOffset_sec = 0; +bool haveTime = false; +const char *ntpServer = ""; +const long gmtOffset_sec = 0; +const int daylightOffset_sec = 0; #endif // Critical error string; if set during init (camera hardware failure) it @@ -231,47 +248,60 @@ String critERR = ""; // Debug flag for stream and capture data bool debugData; -void debugOn() { +void debugOn() +{ debugData = true; Serial.println("Camera debug data is enabled (send 'd' for status dump, or any other char to disable debug)"); } -void debugOff() { +void debugOff() +{ debugData = false; Serial.println("Camera debug data is disabled (send 'd' for status dump, or any other char to enable debug)"); } // Serial input (debugging controls) -void handleSerial() { - if (Serial.available()) { +void handleSerial() +{ + if (Serial.available()) + { char cmd = Serial.read(); - if (cmd == 'd' ) { + if (cmd == 'd') + { serialDump(); - } else { - if (debugData) debugOff(); - else debugOn(); + } + else + { + if (debugData) + debugOff(); + else + debugOn(); } } - while (Serial.available()) Serial.read(); // chomp the buffer + while (Serial.available()) + Serial.read(); // chomp the buffer } // Notification LED -void flashLED(int flashtime) { +void flashLED(int flashtime) +{ #if defined(LED_PIN) // If we have it; flash it. digitalWrite(LED_PIN, LED_ON); // On at full power. delay(flashtime); // delay digitalWrite(LED_PIN, LED_OFF); // turn Off #else - return; // No notifcation LED, do nothing, no delay + return; // No notifcation LED, do nothing, no delay #endif } // Lamp Control -void setLamp(int newVal) { +void setLamp(int newVal) +{ #if defined(LAMP_PIN) - if (newVal != -1) { + if (newVal != -1) + { // Apply a logarithmic function to the scale. - int brightness = round((pow(2,(1+(newVal*0.02)))-2)/6*pwmMax); + int brightness = round((pow(2, (1 + (newVal * 0.02))) - 2) / 6 * pwmMax); ledcWrite(lampChannel, brightness); Serial.print("Lamp: "); Serial.print(newVal); @@ -281,39 +311,52 @@ void setLamp(int newVal) { #endif } -void printLocalTime(bool extraData=false) { +void printLocalTime(bool extraData = false) +{ struct tm timeinfo; - if(!getLocalTime(&timeinfo)){ + if (!getLocalTime(&timeinfo)) + { Serial.println("Failed to obtain time"); - } else { + } + else + { Serial.println(&timeinfo, "%H:%M:%S, %A, %B %d %Y"); } - if (extraData) { + if (extraData) + { Serial.printf("NTP Server: %s, GMT Offset: %li(s), DST Offset: %i(s)\r\n", ntpServer, gmtOffset_sec, daylightOffset_sec); } } -void calcURLs() { - // Set the URL's - #if defined(URL_HOSTNAME) - if (httpPort != 80) { - sprintf(httpURL, "http://%s:%d/", URL_HOSTNAME, httpPort); - } else { - sprintf(httpURL, "http://%s/", URL_HOSTNAME); - } - sprintf(streamURL, "http://%s:%d/", URL_HOSTNAME, streamPort); - #else - Serial.println("Setting httpURL"); - if (httpPort != 80) { - sprintf(httpURL, "http://%d.%d.%d.%d:%d/", ip[0], ip[1], ip[2], ip[3], httpPort); - } else { - sprintf(httpURL, "http://%d.%d.%d.%d/", ip[0], ip[1], ip[2], ip[3]); - } - sprintf(streamURL, "http://%d.%d.%d.%d:%d/", ip[0], ip[1], ip[2], ip[3], streamPort); - #endif +void calcURLs() +{ +// Set the URL's +#if defined(URL_HOSTNAME) + if (httpPort != 80) + { + sprintf(httpURL, "http://%s:%d/", URL_HOSTNAME, httpPort); + } + else + { + sprintf(httpURL, "http://%s/", URL_HOSTNAME); + } + sprintf(streamURL, "http://%s:%d/", URL_HOSTNAME, streamPort); +#else + Serial.println("Setting httpURL"); + if (httpPort != 80) + { + sprintf(httpURL, "http://%d.%d.%d.%d:%d/", ip[0], ip[1], ip[2], ip[3], httpPort); + } + else + { + sprintf(httpURL, "http://%d.%d.%d.%d/", ip[0], ip[1], ip[2], ip[3]); + } + sprintf(streamURL, "http://%d.%d.%d.%d:%d/", ip[0], ip[1], ip[2], ip[3], streamPort); +#endif } -void StartCamera() { +void StartCamera() +{ // Populate camera config structure with hardware and other defaults config.ledc_channel = LEDC_CHANNEL_0; config.ledc_timer = LEDC_TIMER_0; @@ -329,8 +372,8 @@ void StartCamera() { config.pin_pclk = PCLK_GPIO_NUM; config.pin_vsync = VSYNC_GPIO_NUM; config.pin_href = HREF_GPIO_NUM; - config.pin_sscb_sda = SIOD_GPIO_NUM; - config.pin_sscb_scl = SIOC_GPIO_NUM; + config.pin_sccb_sda = SIOD_GPIO_NUM; + config.pin_sccb_scl = SIOC_GPIO_NUM; config.pin_pwdn = PWDN_GPIO_NUM; config.pin_reset = RESET_GPIO_NUM; config.xclk_freq_hz = xclk * 1000000; @@ -342,15 +385,16 @@ void StartCamera() { config.fb_count = 2; config.grab_mode = CAMERA_GRAB_LATEST; - #if defined(CAMERA_MODEL_ESP_EYE) - pinMode(13, INPUT_PULLUP); - pinMode(14, INPUT_PULLUP); - #endif +#if defined(CAMERA_MODEL_ESP_EYE) + pinMode(13, INPUT_PULLUP); + pinMode(14, INPUT_PULLUP); +#endif // camera init esp_err_t err = esp_camera_init(&config); - if (err != ESP_OK) { - delay(100); // need a delay here or the next serial o/p gets missed + if (err != ESP_OK) + { + delay(100); // need a delay here or the next serial o/p gets missed Serial.printf("\r\n\r\nCRITICAL FAILURE: Camera sensor failed to initialise.\r\n\r\n"); Serial.printf("A full (hard, power off/on) reboot will probably be needed to recover from this.\r\n"); Serial.printf("Meanwhile; this unit will reboot in 1 minute since these errors sometime clear automatically\r\n"); @@ -363,87 +407,103 @@ void StartCamera() { critERR = "

Error!


Camera module failed to initialise!

Please reset (power off/on) the camera.

"; 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_init(60, true); esp_task_wdt_add(NULL); - } else { + //delay(60 * 1000); + //ESP.restart(); + } + else + { Serial.println("Camera init succeeded"); // Get a reference to the sensor - sensor_t * s = esp_camera_sensor_get(); + sensor_t *s = esp_camera_sensor_get(); // Dump camera module, warn for unsupported modules. sensorPID = s->id.PID; - switch (sensorPID) { - case OV9650_PID: Serial.println("WARNING: OV9650 camera module is not properly supported, will fallback to OV2640 operation"); break; - case OV7725_PID: Serial.println("WARNING: OV7725 camera module is not properly supported, will fallback to OV2640 operation"); break; - case OV2640_PID: Serial.println("OV2640 camera module detected"); break; - case OV3660_PID: Serial.println("OV3660 camera module detected"); break; - default: Serial.println("WARNING: Camera module is unknown and not properly supported, will fallback to OV2640 operation"); + switch (sensorPID) + { + case OV9650_PID: + Serial.println("WARNING: OV9650 camera module is not properly supported, will fallback to OV2640 operation"); + break; + case OV7725_PID: + Serial.println("WARNING: OV7725 camera module is not properly supported, will fallback to OV2640 operation"); + break; + case OV2640_PID: + Serial.println("OV2640 camera module detected"); + break; + case OV3660_PID: + Serial.println("OV3660 camera module detected"); + break; + default: + Serial.println("WARNING: Camera module is unknown and not properly supported, will fallback to OV2640 operation"); } // OV3660 initial sensors are flipped vertically and colors are a bit saturated - if (sensorPID == OV3660_PID) { - s->set_vflip(s, 1); //flip it back - s->set_brightness(s, 1); //up the blightness just a bit - s->set_saturation(s, -2); //lower the saturation + if (sensorPID == OV3660_PID) + { + s->set_vflip(s, 1); // flip it back + s->set_brightness(s, 1); // up the blightness just a bit + s->set_saturation(s, -2); // lower the saturation } - // M5 Stack Wide has special needs - #if defined(CAMERA_MODEL_M5STACK_WIDE) - s->set_vflip(s, 1); - s->set_hmirror(s, 1); - #endif - - // Config can override mirror and flip - #if defined(H_MIRROR) - s->set_hmirror(s, H_MIRROR); - #endif - #if defined(V_FLIP) - s->set_vflip(s, V_FLIP); - #endif - - // set initial frame rate - #if defined(DEFAULT_RESOLUTION) - s->set_framesize(s, DEFAULT_RESOLUTION); - #endif +// M5 Stack Wide has special needs +#if defined(CAMERA_MODEL_M5STACK_WIDE) + s->set_vflip(s, 1); + s->set_hmirror(s, 1); +#endif + +// Config can override mirror and flip +#if defined(H_MIRROR) + s->set_hmirror(s, H_MIRROR); +#endif +#if defined(V_FLIP) + s->set_vflip(s, V_FLIP); +#endif + +// set initial frame rate +#if defined(DEFAULT_RESOLUTION) + s->set_framesize(s, DEFAULT_RESOLUTION); +#endif /* - * 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 - */ - - //s->set_framesize(s, FRAMESIZE_SVGA); // FRAMESIZE_[QQVGA|HQVGA|QVGA|CIF|VGA|SVGA|XGA|SXGA|UXGA|QXGA(ov3660)]); - //s->set_quality(s, val); // 10 to 63 - //s->set_brightness(s, 0); // -2 to 2 - //s->set_contrast(s, 0); // -2 to 2 - //s->set_saturation(s, 0); // -2 to 2 - //s->set_special_effect(s, 0); // 0 to 6 (0 - No Effect, 1 - Negative, 2 - Grayscale, 3 - Red Tint, 4 - Green Tint, 5 - Blue Tint, 6 - Sepia) - //s->set_whitebal(s, 1); // aka 'awb' in the UI; 0 = disable , 1 = enable - //s->set_awb_gain(s, 1); // 0 = disable , 1 = enable - //s->set_wb_mode(s, 0); // 0 to 4 - if awb_gain enabled (0 - Auto, 1 - Sunny, 2 - Cloudy, 3 - Office, 4 - Home) - //s->set_exposure_ctrl(s, 1); // 0 = disable , 1 = enable - //s->set_aec2(s, 0); // 0 = disable , 1 = enable - //s->set_ae_level(s, 0); // -2 to 2 - //s->set_aec_value(s, 300); // 0 to 1200 - //s->set_gain_ctrl(s, 1); // 0 = disable , 1 = enable - //s->set_agc_gain(s, 0); // 0 to 30 - //s->set_gainceiling(s, (gainceiling_t)0); // 0 to 6 - //s->set_bpc(s, 0); // 0 = disable , 1 = enable - //s->set_wpc(s, 1); // 0 = disable , 1 = enable - //s->set_raw_gma(s, 1); // 0 = disable , 1 = enable - //s->set_lenc(s, 1); // 0 = disable , 1 = enable - //s->set_hmirror(s, 0); // 0 = disable , 1 = enable - //s->set_vflip(s, 0); // 0 = disable , 1 = enable - //s->set_dcw(s, 1); // 0 = disable , 1 = enable - //s->set_colorbar(s, 0); // 0 = disable , 1 = enable + * 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 + */ + + // s->set_framesize(s, FRAMESIZE_SVGA); // FRAMESIZE_[QQVGA|HQVGA|QVGA|CIF|VGA|SVGA|XGA|SXGA|UXGA|QXGA(ov3660)]); + // s->set_quality(s, val); // 10 to 63 + // s->set_brightness(s, 0); // -2 to 2 + // s->set_contrast(s, 0); // -2 to 2 + // s->set_saturation(s, 0); // -2 to 2 + // s->set_special_effect(s, 0); // 0 to 6 (0 - No Effect, 1 - Negative, 2 - Grayscale, 3 - Red Tint, 4 - Green Tint, 5 - Blue Tint, 6 - Sepia) + // s->set_whitebal(s, 1); // aka 'awb' in the UI; 0 = disable , 1 = enable + // s->set_awb_gain(s, 1); // 0 = disable , 1 = enable + // s->set_wb_mode(s, 0); // 0 to 4 - if awb_gain enabled (0 - Auto, 1 - Sunny, 2 - Cloudy, 3 - Office, 4 - Home) + // s->set_exposure_ctrl(s, 1); // 0 = disable , 1 = enable + // s->set_aec2(s, 0); // 0 = disable , 1 = enable + // s->set_ae_level(s, 0); // -2 to 2 + // s->set_aec_value(s, 300); // 0 to 1200 + // s->set_gain_ctrl(s, 1); // 0 = disable , 1 = enable + // s->set_agc_gain(s, 0); // 0 to 30 + // s->set_gainceiling(s, (gainceiling_t)0); // 0 to 6 + // s->set_bpc(s, 0); // 0 = disable , 1 = enable + // s->set_wpc(s, 1); // 0 = disable , 1 = enable + // s->set_raw_gma(s, 1); // 0 = disable , 1 = enable + // s->set_lenc(s, 1); // 0 = disable , 1 = enable + // s->set_hmirror(s, 0); // 0 = disable , 1 = enable + // s->set_vflip(s, 0); // 0 = disable , 1 = enable + // s->set_dcw(s, 1); // 0 = disable , 1 = enable + // s->set_colorbar(s, 0); // 0 = disable , 1 = enable } // We now have camera with default init } -void WifiSetup() { +void WifiSetup() +{ // Feedback that we are now attempting to connect flashLED(300); delay(100); @@ -455,13 +515,17 @@ void WifiSetup() { WiFi.setSleep(false); Serial.print("Known external SSIDs: "); - if (stationCount > firstStation) { - for (int i=firstStation; i < stationCount; i++) Serial.printf(" '%s'", stationList[i].ssid); - } else { + if (stationCount > firstStation) + { + for (int i = firstStation; i < stationCount; i++) + Serial.printf(" '%s'", stationList[i].ssid); + } + else + { Serial.print("None"); } Serial.println(); - byte mac[6] = {0,0,0,0,0,0}; + 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]); @@ -469,25 +533,31 @@ void WifiSetup() { long bestRSSI = -1024; char bestSSID[65] = ""; uint8_t bestBSSID[6]; - if (stationCount > firstStation) { + if (stationCount > firstStation) + { // 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); - if (stationsFound > 0) { - for (int i = 0; i < stationsFound; ++i) { + if (stationsFound > 0) + { + for (int i = 0; i < stationsFound; ++i) + { // Print SSID and RSSI for each network found String thisSSID = WiFi.SSID(i); int thisRSSI = WiFi.RSSI(i); String thisBSSID = WiFi.BSSIDstr(i); 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++) { + for (int sta = firstStation; sta < stationCount; sta++) + { if ((strcmp(stationList[sta].ssid, thisSSID.c_str()) == 0) || - (strcmp(stationList[sta].ssid, thisBSSID.c_str()) == 0)) { + (strcmp(stationList[sta].ssid, thisBSSID.c_str()) == 0)) + { Serial.print(" - Known!"); // Chose the strongest RSSI seen - if (thisRSSI > bestRSSI) { + if (thisRSSI > bestRSSI) + { bestStation = sta; strncpy(bestSSID, thisSSID.c_str(), 64); // Convert char bssid[] to a byte array @@ -499,50 +569,59 @@ void WifiSetup() { Serial.println(); } } - } else { + } + else + { // No list to scan, therefore we are an accesspoint accesspoint = true; } - if (bestStation == -1) { - if (!accesspoint) { - #if defined(WIFI_AP_ENABLE) - Serial.println("No known networks found, entering AccessPoint fallback mode"); - accesspoint = true; - #else - Serial.println("No known networks found"); - #endif - } else { + if (bestStation == -1) + { + if (!accesspoint) + { +#if defined(WIFI_AP_ENABLE) + Serial.println("No known networks found, entering AccessPoint fallback mode"); + accesspoint = true; +#else + Serial.println("No known networks found"); +#endif + } + else + { Serial.println("AccessPoint mode selected in config"); } - } else { + } + 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], - bestBSSID[4], bestBSSID[5], bestSSID); + 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) - #error "You must supply both Gateway and NetMask when specifying a static IP address" - #endif - IPAddress staticIP(ST_IP); - IPAddress gateway(ST_GATEWAY); - IPAddress subnet(ST_NETMASK); - #if !defined(ST_DNS1) - WiFi.config(staticIP, gateway, subnet); - #else - IPAddress dns1(ST_DNS1); - #if !defined(ST_DNS2) - WiFi.config(staticIP, gateway, subnet, dns1); - #else - IPAddress dns2(ST_DNS2); - WiFi.config(staticIP, gateway, subnet, dns1, dns2); - #endif - #endif - #else - Serial.println("Static IP settings requested but not defined in config, falling back to dhcp"); - #endif + if (stationList[bestStation].dhcp == false) + { +#if defined(ST_IP) + Serial.println("Applying static IP settings"); +#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); + IPAddress gateway(ST_GATEWAY); + IPAddress subnet(ST_NETMASK); +#if !defined(ST_DNS1) + WiFi.config(staticIP, gateway, subnet); +#else + IPAddress dns1(ST_DNS1); +#if !defined(ST_DNS2) + WiFi.config(staticIP, gateway, subnet, dns1); +#else + IPAddress dns2(ST_DNS2); + WiFi.config(staticIP, gateway, subnet, dns1, dns2); +#endif +#endif +#else + Serial.println("Static IP settings requested but not defined in config, falling back to dhcp"); +#endif } WiFi.setHostname(mdnsName); @@ -552,75 +631,83 @@ void WifiSetup() { // Wait to connect, or timeout unsigned long start = millis(); - while ((millis() - start <= WIFI_WATCHDOG) && (WiFi.status() != WL_CONNECTED)) { + while ((millis() - start <= WIFI_WATCHDOG) && (WiFi.status() != WL_CONNECTED)) + { delay(500); Serial.print('.'); } // If we have connected, inform user - if (WiFi.status() == WL_CONNECTED) { + if (WiFi.status() == WL_CONNECTED) + { Serial.println("Client connection succeeded"); accesspoint = false; // Note IP details ip = WiFi.localIP(); net = WiFi.subnetMask(); gw = WiFi.gatewayIP(); - Serial.printf("IP address: %d.%d.%d.%d\r\n",ip[0],ip[1],ip[2],ip[3]); - Serial.printf("Netmask : %d.%d.%d.%d\r\n",net[0],net[1],net[2],net[3]); - Serial.printf("Gateway : %d.%d.%d.%d\r\n",gw[0],gw[1],gw[2],gw[3]); + Serial.printf("IP address: %d.%d.%d.%d\r\n", ip[0], ip[1], ip[2], ip[3]); + Serial.printf("Netmask : %d.%d.%d.%d\r\n", net[0], net[1], net[2], net[3]); + Serial.printf("Gateway : %d.%d.%d.%d\r\n", gw[0], gw[1], gw[2], gw[3]); calcURLs(); // Flash the LED to show we are connected - for (int i = 0; i < 5; i++) { + for (int i = 0; i < 5; i++) + { flashLED(50); delay(150); } - } else { + } + else + { Serial.println("Client connection Failed"); - WiFi.disconnect(); // (resets the WiFi scan) + WiFi.disconnect(); // (resets the WiFi scan) } } - if (accesspoint && (WiFi.status() != WL_CONNECTED)) { - // The accesspoint has been enabled, and we have not connected to any existing networks - #if defined(AP_CHAN) - Serial.println("Setting up Fixed Channel AccessPoint"); - Serial.print(" SSID : "); - Serial.println(stationList[0].ssid); - Serial.print(" Password : "); - Serial.println(stationList[0].password); - Serial.print(" Channel : "); - Serial.println(AP_CHAN); - WiFi.softAP(stationList[0].ssid, stationList[0].password, AP_CHAN); - # else - Serial.println("Setting up AccessPoint"); - Serial.print(" SSID : "); - Serial.println(stationList[0].ssid); - Serial.print(" Password : "); - Serial.println(stationList[0].password); - WiFi.softAP(stationList[0].ssid, stationList[0].password); - #endif - #if defined(AP_ADDRESS) - // User has specified the AP details; apply them after a short delay - // (https://github.com/espressif/arduino-esp32/issues/985#issuecomment-359157428) - delay(100); - IPAddress local_IP(AP_ADDRESS); - IPAddress gateway(AP_ADDRESS); - IPAddress subnet(255,255,255,0); - WiFi.softAPConfig(local_IP, gateway, subnet); - #endif + if (accesspoint && (WiFi.status() != WL_CONNECTED)) + { +// The accesspoint has been enabled, and we have not connected to any existing networks +#if defined(AP_CHAN) + Serial.println("Setting up Fixed Channel AccessPoint"); + Serial.print(" SSID : "); + Serial.println(stationList[0].ssid); + Serial.print(" Password : "); + Serial.println(stationList[0].password); + Serial.print(" Channel : "); + Serial.println(AP_CHAN); + WiFi.softAP(stationList[0].ssid, stationList[0].password, AP_CHAN); +#else + Serial.println("Setting up AccessPoint"); + Serial.print(" SSID : "); + Serial.println(stationList[0].ssid); + Serial.print(" Password : "); + Serial.println(stationList[0].password); + WiFi.softAP(stationList[0].ssid, stationList[0].password); +#endif +#if defined(AP_ADDRESS) + // User has specified the AP details; apply them after a short delay + // (https://github.com/espressif/arduino-esp32/issues/985#issuecomment-359157428) + delay(100); + IPAddress local_IP(AP_ADDRESS); + IPAddress gateway(AP_ADDRESS); + IPAddress subnet(255, 255, 255, 0); + WiFi.softAPConfig(local_IP, gateway, subnet); +#endif // Note AP details ip = WiFi.softAPIP(); net = WiFi.subnetMask(); gw = WiFi.gatewayIP(); strcpy(apName, stationList[0].ssid); - Serial.printf("IP address: %d.%d.%d.%d\r\n",ip[0],ip[1],ip[2],ip[3]); + Serial.printf("IP address: %d.%d.%d.%d\r\n", ip[0], ip[1], ip[2], ip[3]); calcURLs(); // Flash the LED to show we are connected - for (int i = 0; i < 5; i++) { + for (int i = 0; i < 5; i++) + { flashLED(150); delay(50); } // Start the DNS captive portal if requested - if (stationList[0].dhcp == true) { + if (stationList[0].dhcp == true) + { Serial.println("Starting Captive Portal"); dnsServer.start(DNS_PORT, "*", ip); captivePortal = true; @@ -628,7 +715,8 @@ void WifiSetup() { } } -void setup() { +void setup() +{ Serial.begin(115200); Serial.setDebugOutput(true); Serial.println(); @@ -642,56 +730,66 @@ void setup() { Serial.println(); // Warn if no PSRAM is detected (typically user error with board selection in the IDE) - if(!psramFound()){ + if (!psramFound()) + { Serial.println("\r\nFatal Error; Halting"); - while (true) { + while (true) + { Serial.println("No PSRAM found; camera cannot be initialised: Please check the board config for your module."); delay(5000); } } - if (stationCount == 0) { + if (stationCount == 0) + { Serial.println("\r\nFatal Error; Halting"); - while (true) { + 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 - pinMode(LED_PIN, OUTPUT); - digitalWrite(LED_PIN, LED_ON); - #endif +#if defined(LED_PIN) // If we have a notification LED, set it to output + pinMode(LED_PIN, OUTPUT); + digitalWrite(LED_PIN, LED_ON); +#endif // Start the SPIFFS filesystem before we initialise the camera - if (filesystem) { + if (filesystem) + { filesystemStart(); delay(200); // a short delay to let spi bus settle after SPIFFS init } - // Start (init) the camera + // Start (init) the camera StartCamera(); // Now load and apply any saved preferences - if (filesystem) { + if (filesystem) + { delay(200); // a short delay to let spi bus settle after camera init loadPrefs(SPIFFS); - } else { + } + else + { Serial.println("No Internal Filesystem, cannot load or save preferences"); } /* - * Camera setup complete; initialise the rest of the hardware. - */ + * Camera setup complete; initialise the rest of the hardware. + */ // Start Wifi and loop until we are connected or have started an AccessPoint - while ((WiFi.status() != WL_CONNECTED) && !accesspoint) { + while ((WiFi.status() != WL_CONNECTED) && !accesspoint) + { WifiSetup(); delay(1000); } // Set up OTA - if (otaEnabled) { + if (otaEnabled) + { // Start OTA once connected Serial.println("Setting up OTA"); // Port defaults to 3232 @@ -699,14 +797,18 @@ void setup() { // Hostname defaults to esp3232-[MAC] ArduinoOTA.setHostname(mdnsName); // No authentication by default - if (strlen(otaPassword) != 0) { + if (strlen(otaPassword) != 0) + { ArduinoOTA.setPassword(otaPassword); Serial.printf("OTA Password: %s\n\r", otaPassword); - } else { + } + else + { Serial.printf("\r\nNo OTA password has been set! (insecure)\r\n\r\n"); } ArduinoOTA - .onStart([]() { + .onStart([]() + { String type; if (ArduinoOTA.getCommand() == U_FLASH) type = "sketch"; @@ -717,44 +819,47 @@ void setup() { // Stop the camera since OTA will crash the module if it is running. // the unit will need rebooting to restart it, either by OTA on success, or manually by the user Serial.println("Stopping Camera"); - esp_err_t err = esp_camera_deinit(); + esp_camera_deinit(); critERR = "

OTA Has been started


Camera has Halted!

"; - critERR += "

Wait for OTA to finish and reboot, or reboot manually to recover

"; - }) - .onEnd([]() { - Serial.println("\r\nEnd"); - }) - .onProgress([](unsigned int progress, unsigned int total) { - Serial.printf("Progress: %u%%\r", (progress / (total / 100))); - }) - .onError([](ota_error_t error) { + critERR += "

Wait for OTA to finish and reboot, or reboot manually to recover

"; }) + .onEnd([]() + { Serial.println("\r\nEnd"); }) + .onProgress([](unsigned int progress, unsigned int total) + { Serial.printf("Progress: %u%%\r", (progress / (total / 100))); }) + .onError([](ota_error_t error) + { Serial.printf("Error[%u]: ", error); if (error == OTA_AUTH_ERROR) Serial.println("Auth Failed"); else if (error == OTA_BEGIN_ERROR) Serial.println("Begin Failed"); else if (error == OTA_CONNECT_ERROR) Serial.println("Connect Failed"); else if (error == OTA_RECEIVE_ERROR) Serial.println("Receive Failed"); - else if (error == OTA_END_ERROR) Serial.println("End Failed"); - }); + else if (error == OTA_END_ERROR) Serial.println("End Failed"); }); ArduinoOTA.begin(); - } else { + } + else + { Serial.println("OTA is disabled"); - if (!MDNS.begin(mdnsName)) { - Serial.println("Error setting up MDNS responder!"); + if (!MDNS.begin(mdnsName)) + { + 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 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) { + if (haveTime) + { Serial.print("Time: "); configTime(gmtOffset_sec, daylightOffset_sec, ntpServer); printLocalTime(true); - } else { + } + else + { Serial.println("Time functions disabled"); } @@ -764,78 +869,121 @@ void setup() { sketchMD5 = ESP.getSketchMD5(); // Initialise and set the lamp - if (lampVal != -1) { - #if defined(LAMP_PIN) - 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); - #endif - } else { + if (lampVal != -1) + { +#if defined(LAMP_PIN) + 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); +#endif + } + else + { Serial.println("No lamp, or lamp disabled in config"); } // Start the camera server startCameraServer(httpPort, streamPort); - if (critERR.length() == 0) { + if (critERR.length() == 0) + { Serial.printf("\r\nCamera Ready!\r\nUse '%s' to connect\r\n", httpURL); Serial.printf("Stream viewer available at '%sview'\r\n", streamURL); Serial.printf("Raw stream URL is '%s'\r\n", streamURL); - #if defined(DEBUG_DEFAULT_ON) - debugOn(); - #else - debugOff(); - #endif - } else { +#if defined(DEBUG_DEFAULT_ON) + debugOn(); +#else + debugOff(); +#endif + } + else + { Serial.printf("\r\nCamera unavailable due to initialisation errors.\r\n\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"); + previousMillis = millis(); + // 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 (Serial.available()) + Serial.read(); + + // send picture + SendPictureHttp(); } -void loop() { +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. - */ - if (accesspoint) { + */ + if (accesspoint) + { // Accespoint is permanently up, so just loop, servicing the captive portal as needed // Rather than loop forever, follow the watchdog, in case we later add auto re-scan. unsigned long start = millis(); - while (millis() - start < WIFI_WATCHDOG ) { + while (millis() - start < WIFI_WATCHDOG) + { delay(100); - if (otaEnabled) ArduinoOTA.handle(); + if (otaEnabled) + ArduinoOTA.handle(); handleSerial(); - if (captivePortal) dnsServer.processNextRequest(); + if (captivePortal) + dnsServer.processNextRequest(); } - } else { + } + else + { // client mode can fail; so reconnect as appropriate static bool warned = false; - if (WiFi.status() == WL_CONNECTED) { + if (WiFi.status() == WL_CONNECTED) + { // We are connected, wait a bit and re-check - if (warned) { + if (warned) + { // Tell the user if we have just reconnected Serial.println("WiFi reconnected"); warned = false; } // loop here for WIFI_WATCHDOG, turning debugData true/false depending on serial input.. unsigned long start = millis(); - while (millis() - start < WIFI_WATCHDOG ) { + while (millis() - start < WIFI_WATCHDOG) + { delay(100); - if (otaEnabled) ArduinoOTA.handle(); + if (otaEnabled) + ArduinoOTA.handle(); handleSerial(); } - } else { + if (PUSH_PICTURE == true) + { + if ((start - previousMillis) >= interval) + { + // Save the current time + previousMillis = start; + + // send picture + if(SendPictureHttp()!= ESP_OK) + { + //delay(1000); + //ESP.restart(); + } + } + } + } + else + { // disconnected; attempt to reconnect - if (!warned) { + if (!warned) + { // Tell the user if we just disconnected - WiFi.disconnect(); // ensures disconnect is complete, wifi scan cleared + WiFi.disconnect(); // ensures disconnect is complete, wifi scan cleared Serial.println("WiFi disconnected, retrying"); warned = true; } diff --git a/platformio.ini b/platformio.ini index cabb5b46..e95bdd83 100644 --- a/platformio.ini +++ b/platformio.ini @@ -34,14 +34,23 @@ src_dir = ./ [env:esp32dev] -platform = https://github.com/platformio/platform-espressif32.git#feature/arduino-upstream -platform_packages = framework-arduinoespressif32@https://github.com/espressif/arduino-esp32.git#2.0.3 +platform = espressif32@^6.5.0 board = esp32dev board_build.partitions = min_spiffs.csv framework = arduino +upload_speed = 921600 +monitor_speed = 115200 +monitor_rts = 0 +monitor_dtr = 0 +build_type = debug +monitor_filters = esp32_exception_decoder + ; log2file + time + build_flags = -DBOARD_HAS_PSRAM -mfix-esp32-psram-cache-issue +; -DCORE_DEBUG_LEVEL=5 ; 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 = From 0b757cddcb125d0648a9dbd8cb26de2f81c278d2 Mon Sep 17 00:00:00 2001 From: Mandl Date: Mon, 2 Sep 2024 14:01:21 +0200 Subject: [PATCH 2/8] update cert --- cert.h | 66 ++++++++++++++++++++++++++++++---------------------------- 1 file changed, 34 insertions(+), 32 deletions(-) diff --git a/cert.h b/cert.h index c309b9c5..d0e98688 100644 --- a/cert.h +++ b/cert.h @@ -1,38 +1,40 @@ #ifndef __CERTLETS__ #define __CERTLETS__ -// lets encrypt R3 -const char *root_ca = - "-----BEGIN CERTIFICATE-----\n" - "MIIFFjCCAv6gAwIBAgIRAJErCErPDBinU/bWLiWnX1owDQYJKoZIhvcNAQELBQAw\n" - "TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh\n" - "cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMjAwOTA0MDAwMDAw\n" - "WhcNMjUwOTE1MTYwMDAwWjAyMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNTGV0J3Mg\n" - "RW5jcnlwdDELMAkGA1UEAxMCUjMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK\n" - "AoIBAQC7AhUozPaglNMPEuyNVZLD+ILxmaZ6QoinXSaqtSu5xUyxr45r+XXIo9cP\n" - "R5QUVTVXjJ6oojkZ9YI8QqlObvU7wy7bjcCwXPNZOOftz2nwWgsbvsCUJCWH+jdx\n" - "sxPnHKzhm+/b5DtFUkWWqcFTzjTIUu61ru2P3mBw4qVUq7ZtDpelQDRrK9O8Zutm\n" - "NHz6a4uPVymZ+DAXXbpyb/uBxa3Shlg9F8fnCbvxK/eG3MHacV3URuPMrSXBiLxg\n" - "Z3Vms/EY96Jc5lP/Ooi2R6X/ExjqmAl3P51T+c8B5fWmcBcUr2Ok/5mzk53cU6cG\n" - "/kiFHaFpriV1uxPMUgP17VGhi9sVAgMBAAGjggEIMIIBBDAOBgNVHQ8BAf8EBAMC\n" - "AYYwHQYDVR0lBBYwFAYIKwYBBQUHAwIGCCsGAQUFBwMBMBIGA1UdEwEB/wQIMAYB\n" - "Af8CAQAwHQYDVR0OBBYEFBQusxe3WFbLrlAJQOYfr52LFMLGMB8GA1UdIwQYMBaA\n" - "FHm0WeZ7tuXkAXOACIjIGlj26ZtuMDIGCCsGAQUFBwEBBCYwJDAiBggrBgEFBQcw\n" - "AoYWaHR0cDovL3gxLmkubGVuY3Iub3JnLzAnBgNVHR8EIDAeMBygGqAYhhZodHRw\n" - "Oi8veDEuYy5sZW5jci5vcmcvMCIGA1UdIAQbMBkwCAYGZ4EMAQIBMA0GCysGAQQB\n" - "gt8TAQEBMA0GCSqGSIb3DQEBCwUAA4ICAQCFyk5HPqP3hUSFvNVneLKYY611TR6W\n" - "PTNlclQtgaDqw+34IL9fzLdwALduO/ZelN7kIJ+m74uyA+eitRY8kc607TkC53wl\n" - "ikfmZW4/RvTZ8M6UK+5UzhK8jCdLuMGYL6KvzXGRSgi3yLgjewQtCPkIVz6D2QQz\n" - "CkcheAmCJ8MqyJu5zlzyZMjAvnnAT45tRAxekrsu94sQ4egdRCnbWSDtY7kh+BIm\n" - "lJNXoB1lBMEKIq4QDUOXoRgffuDghje1WrG9ML+Hbisq/yFOGwXD9RiX8F6sw6W4\n" - "avAuvDszue5L3sz85K+EC4Y/wFVDNvZo4TYXao6Z0f+lQKc0t8DQYzk1OXVu8rp2\n" - "yJMC6alLbBfODALZvYH7n7do1AZls4I9d1P4jnkDrQoxB3UqQ9hVl3LEKQ73xF1O\n" - "yK5GhDDX8oVfGKF5u+decIsH4YaTw7mP3GFxJSqv3+0lUFJoi5Lc5da149p90Ids\n" - "hCExroL1+7mryIkXPeFM5TgO9r0rvZaBFOvV2z0gp35Z0+L4WPlbuEjN/lxPFin+\n" - "HlUjr8gRsI3qfJOQFy/9rKIJR0Y/8Omwt/8oTWgy1mdeHmmjk7j1nYsvC9JSQ6Zv\n" - "MldlTTKB3zhThV1+XWYp6rjd5JW1zbVWEkLNxE7GJThEUG3szgBVGP7pSWTUTsqX\n" - "nLRbwHOoq7hHwg==\n" - "-----END CERTIFICATE-----\n"; + +const char* root_ca = +"-----BEGIN CERTIFICATE-----\n" +"MIIFYDCCBEigAwIBAgIQQAF3ITfU6UK47naqPGQKtzANBgkqhkiG9w0BAQsFADA/\n" +"MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT\n" +"DkRTVCBSb290IENBIFgzMB4XDTIxMDEyMDE5MTQwM1oXDTI0MDkzMDE4MTQwM1ow\n" +"TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh\n" +"cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwggIiMA0GCSqGSIb3DQEB\n" +"AQUAA4ICDwAwggIKAoICAQCt6CRz9BQ385ueK1coHIe+3LffOJCMbjzmV6B493XC\n" +"ov71am72AE8o295ohmxEk7axY/0UEmu/H9LqMZshftEzPLpI9d1537O4/xLxIZpL\n" +"wYqGcWlKZmZsj348cL+tKSIG8+TA5oCu4kuPt5l+lAOf00eXfJlII1PoOK5PCm+D\n" +"LtFJV4yAdLbaL9A4jXsDcCEbdfIwPPqPrt3aY6vrFk/CjhFLfs8L6P+1dy70sntK\n" +"4EwSJQxwjQMpoOFTJOwT2e4ZvxCzSow/iaNhUd6shweU9GNx7C7ib1uYgeGJXDR5\n" +"bHbvO5BieebbpJovJsXQEOEO3tkQjhb7t/eo98flAgeYjzYIlefiN5YNNnWe+w5y\n" +"sR2bvAP5SQXYgd0FtCrWQemsAXaVCg/Y39W9Eh81LygXbNKYwagJZHduRze6zqxZ\n" +"Xmidf3LWicUGQSk+WT7dJvUkyRGnWqNMQB9GoZm1pzpRboY7nn1ypxIFeFntPlF4\n" +"FQsDj43QLwWyPntKHEtzBRL8xurgUBN8Q5N0s8p0544fAQjQMNRbcTa0B7rBMDBc\n" +"SLeCO5imfWCKoqMpgsy6vYMEG6KDA0Gh1gXxG8K28Kh8hjtGqEgqiNx2mna/H2ql\n" +"PRmP6zjzZN7IKw0KKP/32+IVQtQi0Cdd4Xn+GOdwiK1O5tmLOsbdJ1Fu/7xk9TND\n" +"TwIDAQABo4IBRjCCAUIwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYw\n" +"SwYIKwYBBQUHAQEEPzA9MDsGCCsGAQUFBzAChi9odHRwOi8vYXBwcy5pZGVudHJ1\n" +"c3QuY29tL3Jvb3RzL2RzdHJvb3RjYXgzLnA3YzAfBgNVHSMEGDAWgBTEp7Gkeyxx\n" +"+tvhS5B1/8QVYIWJEDBUBgNVHSAETTBLMAgGBmeBDAECATA/BgsrBgEEAYLfEwEB\n" +"ATAwMC4GCCsGAQUFBwIBFiJodHRwOi8vY3BzLnJvb3QteDEubGV0c2VuY3J5cHQu\n" +"b3JnMDwGA1UdHwQ1MDMwMaAvoC2GK2h0dHA6Ly9jcmwuaWRlbnRydXN0LmNvbS9E\n" +"U1RST09UQ0FYM0NSTC5jcmwwHQYDVR0OBBYEFHm0WeZ7tuXkAXOACIjIGlj26Ztu\n" +"MA0GCSqGSIb3DQEBCwUAA4IBAQAKcwBslm7/DlLQrt2M51oGrS+o44+/yQoDFVDC\n" +"5WxCu2+b9LRPwkSICHXM6webFGJueN7sJ7o5XPWioW5WlHAQU7G75K/QosMrAdSW\n" +"9MUgNTP52GE24HGNtLi1qoJFlcDyqSMo59ahy2cI2qBDLKobkx/J3vWraV0T9VuG\n" +"WCLKTVXkcGdtwlfFRjlBz4pYg1htmf5X6DYO8A4jqv2Il9DjXA6USbW1FzXSLr9O\n" +"he8Y4IWS6wY7bCkjCWDcRQJMEhg76fsO3txE+FiYruq9RUWhiF1myv4Q6W+CyBFC\n" +"Dfvp7OOGAN6dEOM4+qR9sdjoSYKEBpsr6GtPAQw4dy753ec5\n" +"-----END CERTIFICATE-----\n"; + #endif \ No newline at end of file From 60ff04a1b449e0f11ff37bfb9dee77b2d1d199ea Mon Sep 17 00:00:00 2001 From: Mandl Date: Mon, 2 Sep 2024 14:01:57 +0200 Subject: [PATCH 3/8] update espressif32 --- platformio.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platformio.ini b/platformio.ini index e95bdd83..200af1d8 100644 --- a/platformio.ini +++ b/platformio.ini @@ -34,7 +34,7 @@ src_dir = ./ [env:esp32dev] -platform = espressif32@^6.5.0 +platform = espressif32@^6.8.1 board = esp32dev board_build.partitions = min_spiffs.csv framework = arduino From bbdb29b5979dd7dcccdf7aefff044bc51fe0e9e1 Mon Sep 17 00:00:00 2001 From: Mandl Date: Wed, 30 Oct 2024 09:07:49 +0100 Subject: [PATCH 4/8] disable stream add basic auth update plattform --- app_httpd.cpp | 16 ++++ esp32-cam-webserver.ino | 25 +---- index_other.h | 1 - myconfig.sample.h | 197 ---------------------------------------- platformio.ini | 2 +- 5 files changed, 22 insertions(+), 219 deletions(-) delete mode 100644 myconfig.sample.h diff --git a/app_httpd.cpp b/app_httpd.cpp index f3393773..97a48fcf 100644 --- a/app_httpd.cpp +++ b/app_httpd.cpp @@ -27,6 +27,9 @@ #include "src/favicons.h" #include "src/logo.h" #include "storage.h" +#include "src/httpd_basic_auth.h" +#include "myconfig2.h" + // Functions from the main .ino extern void flashLED(int flashtime); @@ -675,6 +678,19 @@ static esp_err_t index_handler(httpd_req_t *req){ size_t buf_len; char view[32] = {0,}; + if ( HTTP_BASIC_AUTH == true ) + { + + if(httpd_basic_auth(req, "admin", "test") != ESP_OK) { + + httpd_basic_auth_resp_send_401(req); + httpd_resp_sendstr(req, "Not Authorized"); + return ESP_FAIL; + } + } + + + flashLED(75); // See if we have a specific target (full/simple/portal) and serve as appropriate buf_len = httpd_req_get_url_query_len(req) + 1; diff --git a/esp32-cam-webserver.ino b/esp32-cam-webserver.ino index 8075c74c..62522850 100644 --- a/esp32-cam-webserver.ino +++ b/esp32-cam-webserver.ino @@ -38,26 +38,8 @@ SET_LOOP_TASK_STACK_SIZE(12*1024); * */ -// Primary config, or defaults. -#if __has_include("myconfig.h") -struct station -{ - const char ssid[65]; - const char password[65]; - const bool dhcp; -}; // do no edit + #include "myconfig.h" -#else -#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; -} stationList[] = {{"ESP32-CAM-CONNECT", "InsecurePassword", true}}; -#endif // Upstream version string #include "src/version.h" @@ -914,7 +896,10 @@ void setup() Serial.read(); // send picture - SendPictureHttp(); + if (PUSH_PICTURE == true) + { + SendPictureHttp(); + } } void loop() diff --git a/index_other.h b/index_other.h index 1eda91e1..1032e0e6 100644 --- a/index_other.h +++ b/index_other.h @@ -208,7 +208,6 @@ const uint8_t index_simple_html[] = R"=====( hide(waitSettings); show(settings); show(streamButton); - startStream(); }) // Put some helpful text on the 'Still' button diff --git a/myconfig.sample.h b/myconfig.sample.h deleted file mode 100644 index 86efcafe..00000000 --- a/myconfig.sample.h +++ /dev/null @@ -1,197 +0,0 @@ -/* - * 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 */ -#define CAM_NAME "ESP32 camera server" - -/* - * Give the network name - * It will be used as the hostname in ST modes - * This is the name the camera will advertise on the network (mdns) for services and OTA - */ -#define MDNS_NAME "esp32-cam" - -/* - * WiFi Settings - * - * For the simplest connection to an existing network - * just replace your ssid and password in the line below. - */ - -struct station stationList[] = {{"my_ssid","my_password", true}}; - -/* - * You can extend the stationList[] above with additional SSID+Password pairs - -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 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 separated mac address string) in place of - * the ssid to force connections to specific networks even when the ssid's collide, - */ - -/* Extended WiFi Settings */ - -/* - * If defined: URL_HOSTNAME will be used in place of the IP address in internal URL's - */ -// #define URL_HOSTNAME "esp32-cam" - -/* - * 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_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; - * - * Uncomment to enable AP mode; - * - */ -// #define WIFI_AP_ENABLE - -/* AP Mode Notes: - * - * Once enabled the AP ssid and password will be taken from the 1st entry in the stationList[] above. - * - * If there are further entries listed they will be scanned at startup in the normal way and connected to - * if they are found. AP then works as a fallback mode for when there are no 'real' networks available. - * - * Setting the 'dhcp' field to true for the AP enables a captive portal and attempts to send - * all 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) -// 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 -// #define AP_CHAN 1 - -/* - * Port numbers for WebUI and Stream, defaults to 80 and 81. - * Uncomment and edit as appropriate - */ -// #define HTTP_PORT 80 -// #define STREAM_PORT 81 - -/* - * 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. - */ -// #define WIFI_WATCHDOG 15000 - -/* - * Over The Air firmware updates can be disabled by uncommenting the folowing line - * When enabled the device will advertise itself using the MDNS_NAME defined above - */ -// #define NO_OTA - -/* - * OTA can be password protected to prevent the device being hijacked - */ -// #define OTA_PASSWORD "SuperVisor" - -/* NTP - * Uncomment the following to enable the on-board clock - * Pick a nearby pool server from: https://www.ntppool.org/zone/@ - * Set the GMT offset to match your timezone IN SECONDS; - * see https://en.wikipedia.org/wiki/List_of_UTC_time_offsets - * 1hr = 3600 seconds; do the math ;-) - * Default is CET (Central European Time), eg GMT + 1hr - * The DST offset is usually 1 hour (again, in seconds) if used in your country. - */ -//#define NTPSERVER ".pool.ntp.org" -//#define NTP_GMT_OFFSET 3600 -//#define NTP_DST_OFFSET 3600 - -/* - * Camera Defaults - * - */ -// Initial Reslolution, default SVGA -// available values are: FRAMESIZE_[THUMB|QQVGA|HQVGA|QVGA|CIF|HVGA|VGA|SVGA|XGA|HD|SXGA|UXGA] + [FHD|QXGA] for 3Mp Sensors; eg ov3660 -// #define DEFAULT_RESOLUTION FRAMESIZE_SVGA - -// Hardware Horizontal Mirror, 0 or 1 (overrides default board setting) -// #define H_MIRROR 0 - -// Hardware Vertical Flip , 0 or 1 (overrides default board setting) -// #define V_FLIP 1 - -// 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/min_frame_time -// #define MIN_FRAME_TIME 500 - -/* - * Additional Features - * - */ -// Default Page: uncomment to make the full control page the default, otherwise show simple viewer -// #define DEFAULT_INDEX_FULL - -// Uncomment to disable the notification LED on the module -// #define LED_DISABLE - -// Uncomment to disable the illumination lamp features -// #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 -// Uncomment to disable this this, the controls will still be shown in the UI but are inoperative. -// #define NO_FS - -// Uncomment to enable camera debug info on serial by default -// #define DEBUG_DEFAULT_ON - -/* - * Camera Hardware Selectiom - * - * You must uncomment one, and only one, of the lines below to select your board model. - * Remember to also select the board in the Boards Manager - * This is not optional - */ -#define CAMERA_MODEL_AI_THINKER // default -// #define CAMERA_MODEL_WROVER_KIT -// #define CAMERA_MODEL_ESP_EYE -// #define CAMERA_MODEL_M5STACK_PSRAM -// #define CAMERA_MODEL_M5STACK_V2_PSRAM -// #define CAMERA_MODEL_M5STACK_WIDE -// #define CAMERA_MODEL_M5STACK_ESP32CAM // Originally: CAMERA_MODEL_M5STACK_NO_PSRAM -// #define CAMERA_MODEL_TTGO_T_JOURNAL -// #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 diff --git a/platformio.ini b/platformio.ini index 200af1d8..7a882294 100644 --- a/platformio.ini +++ b/platformio.ini @@ -34,7 +34,7 @@ src_dir = ./ [env:esp32dev] -platform = espressif32@^6.8.1 +platform = espressif32@^6.9.0 board = esp32dev board_build.partitions = min_spiffs.csv framework = arduino From 1fce718cf16394c33be014801aedde6dbe377577 Mon Sep 17 00:00:00 2001 From: Mandl Date: Tue, 26 Nov 2024 12:23:48 +0100 Subject: [PATCH 5/8] add basic_auth --- esp32-cam-webserver.ino | 2 +- src/httpd_basic_auth.cpp | 91 ++++++++++++++++++++++++++++++++++++++++ src/httpd_basic_auth.h | 41 ++++++++++++++++++ 3 files changed, 133 insertions(+), 1 deletion(-) create mode 100644 src/httpd_basic_auth.cpp create mode 100644 src/httpd_basic_auth.h diff --git a/esp32-cam-webserver.ino b/esp32-cam-webserver.ino index 62522850..67824a36 100644 --- a/esp32-cam-webserver.ino +++ b/esp32-cam-webserver.ino @@ -76,7 +76,7 @@ extern esp_err_t SendPictureHttp(); unsigned long previousMillis; // Every 5 Minutes -const unsigned long interval = (5 * 1000); +const unsigned long interval = (5 * 60 * 1000); // Names for the Camera. (set these in myconfig.h) #if defined(CAM_NAME) diff --git a/src/httpd_basic_auth.cpp b/src/httpd_basic_auth.cpp new file mode 100644 index 00000000..42897b85 --- /dev/null +++ b/src/httpd_basic_auth.cpp @@ -0,0 +1,91 @@ +#include "src/httpd_basic_auth.h" +#include "mbedtls/base64.h" + +#include + +static const char *TAG = "httpd_basic_auth"; + +esp_err_t httpd_basic_auth_resp_send_401(httpd_req_t* req) { + esp_err_t ret = httpd_resp_set_status(req, HTTPD_401); + + ESP_LOGD(TAG, "httpd_basic_auth_resp_send_401"); + if(ret == ESP_OK) { + ret = httpd_resp_set_hdr(req, "WWW-Authenticate", "Basic realm=\"User Visible Realm\""); + } + + return ret; +} + +esp_err_t httpd_basic_auth(httpd_req_t* req, const char* username, const char* password) { + size_t auth_head_len = 1 + httpd_req_get_hdr_value_len(req, "Authorization"); + size_t n = 0; + size_t out; + + ESP_LOGD(TAG, "httpd_basic_auth"); + + if(auth_head_len <= 1 + 7) { + + ESP_LOGE(TAG, "ESP_ERR_HTTPD_BASIC_AUTH_HEADER_NOT_FOUND"); + return ESP_ERR_HTTPD_BASIC_AUTH_HEADER_NOT_FOUND; + } + + char* auth_head = (char*)malloc(auth_head_len); + + if(auth_head == NULL) { + return ESP_ERR_NO_MEM; + } + + if(httpd_req_get_hdr_value_str(req, "Authorization", auth_head, auth_head_len) != ESP_OK) { + free(auth_head); + return ESP_ERR_HTTPD_BASIC_AUTH_FAIL_TO_GET_HEADER; + } + + ESP_LOGD(TAG, "Header: '%s'", auth_head); + + if(strncmp("Basic", auth_head, 5) != 0) { + free(auth_head); + return ESP_ERR_HTTPD_BASIC_AUTH_HEADER_INVALID; + } + + mbedtls_base64_decode(NULL, 0, &n, (const unsigned char *)(auth_head + 6), auth_head_len - 6 - 1); + + unsigned char* decoded = (unsigned char*) calloc(1, 6 + n + 1); + + if (decoded) { + strcpy((char*)decoded, "Basic "); + int err = mbedtls_base64_decode(decoded, n, &out, (const unsigned char *)(auth_head + 6), auth_head_len - 6 - 1); + if(err != 0) { + free(auth_head); + free(decoded); + ESP_LOGE(TAG, "ESP_ERR_HTTPD_BASIC_AUTH_HEADER_INVALID"); + return ESP_ERR_HTTPD_BASIC_AUTH_HEADER_INVALID; + } + } + + free(auth_head); + + char* colonDelimiter = strchr((const char*) decoded, ':'); + + if(colonDelimiter == NULL) { + free(decoded); + ESP_LOGE(TAG, "ESP_ERR_HTTPD_BASIC_AUTH_HEADER_INVALID2"); + + return ESP_ERR_HTTPD_BASIC_AUTH_HEADER_INVALID; + } + + size_t head_username_len = colonDelimiter - (const char*) decoded; + size_t head_password_len = strlen(colonDelimiter + 1); + + if(strlen(username) != head_username_len + || strlen(password) != head_password_len + || strncmp(username, (const char*) decoded, head_username_len) != 0 + || strncmp(password, colonDelimiter + 1, head_password_len) != 0) { + free(decoded); + ESP_LOGD(TAG, "ESP_ERR_HTTPD_BASIC_AUTH_NOT_AUTHORIZED"); + return ESP_ERR_HTTPD_BASIC_AUTH_NOT_AUTHORIZED; + } + + free(decoded); + + return ESP_OK; +} \ No newline at end of file diff --git a/src/httpd_basic_auth.h b/src/httpd_basic_auth.h new file mode 100644 index 00000000..db2e6dce --- /dev/null +++ b/src/httpd_basic_auth.h @@ -0,0 +1,41 @@ +/** + * @file httpd_basic_auth.h + * @brief Basic Authorization Wrapper around Htppd + */ + +#include + +#define ESP_ERR_HTTPD_BASIC_AUTH_BASE (ESP_ERR_HTTPD_BASE + 200) /*!< Starting number of HTTPD_BASIC_AUTH error codes */ +#define ESP_ERR_HTTPD_BASIC_AUTH_HEADER_NOT_FOUND (ESP_ERR_HTTPD_BASIC_AUTH_BASE + 1) /*!< Authorization header has not found in request */ +#define ESP_ERR_HTTPD_BASIC_AUTH_FAIL_TO_GET_HEADER (ESP_ERR_HTTPD_BASIC_AUTH_BASE + 2) /*!< Fail to read authorization header */ +#define ESP_ERR_HTTPD_BASIC_AUTH_HEADER_INVALID (ESP_ERR_HTTPD_BASIC_AUTH_BASE + 3) /*!< Invalid format of authorization header */ +#define ESP_ERR_HTTPD_BASIC_AUTH_NOT_AUTHORIZED (ESP_ERR_HTTPD_BASIC_AUTH_BASE + 4) /*!< Not authorized */ + +#ifndef HTTPD_401 + #define HTTPD_401 "401 Unauthorized" +#endif + +/** + * @brief Send error code 401 and set "WWW-Authenticate" header + * @param[in] req The request being responded to + * + * @return + * - ESP_OK on success + */ +esp_err_t httpd_basic_auth_resp_send_401(httpd_req_t* req); + +/** + * @brief Read authorization token from request and compare with provided username and password + * @param[in] req The request being responded to + * @param[in] username Username + * @param[in] password Password + * + * @return + * - ESP_OK user is authorized + * - ESP_ERR_NO_MEM no memory + * - ESP_ERR_HTTPD_BASIC_AUTH_HEADER_NOT_FOUND authorization header is not provided in request + * - ESP_ERR_HTTPD_BASIC_AUTH_FAIL_TO_GET_HEADER fail to read authorization header + * - ESP_ERR_HTTPD_BASIC_AUTH_HEADER_INVALID invalid format of authorization header + * - ESP_ERR_HTTPD_BASIC_AUTH_NOT_AUTHORIZED not authorized + */ +esp_err_t httpd_basic_auth(httpd_req_t* req, const char* username, const char* password); From c47f7ff6db60b7c46dd3f6c8636541d03b78dd11 Mon Sep 17 00:00:00 2001 From: Mandl Date: Fri, 7 Feb 2025 11:42:35 +0100 Subject: [PATCH 6/8] change wdt --- camsend.cpp | 41 ++++++++++++++++++++++++++++++++++++++++- esp32-cam-webserver.ino | 18 +++++++++++++----- platformio.ini | 4 +++- 3 files changed, 56 insertions(+), 7 deletions(-) diff --git a/camsend.cpp b/camsend.cpp index a4c6eba6..fa72e7bf 100644 --- a/camsend.cpp +++ b/camsend.cpp @@ -2,6 +2,7 @@ #include "cert.h" #include "myconfig2.h" #include +#include // Functions from the main .ino @@ -11,6 +12,44 @@ extern void setLamp(int newVal); extern int lampVal; extern bool autoLamp; extern bool debugData; +extern char mdnsName; + + +String serverurl = CAMSERVER; + + +/* + send status + +*/ +esp_err_t SendStatusHttp() +{ + HTTPClient http; + + JsonDocument doc; + + doc["esphostname"] = String(mdnsName); + doc["rssi"] = String(WiFi.RSSI()); + + // Serialize JSON document + String json; + serializeJson(doc, json); + + http.begin( serverurl + "/espstatus/", root_ca); // Specify the URL and certificate + http.addHeader("Content-Type", "application/json"); + + int httpCode = http.POST(json); + + http.end(); + + if(httpCode > 0) + { + Serial.println(httpCode); + return ESP_OK; + } + return ESP_FAIL; + +} /* @@ -50,7 +89,7 @@ esp_err_t SendPictureHttp() fb_len = fb->len; HTTPClient http; - http.begin(CAMSERVER, root_ca); // Specify the URL and certificate + http.begin(serverurl + "/muccam/", root_ca); // Specify the URL and certificate http.addHeader("Content-Type", "image/jpeg"); http.addHeader("Content-Length", String(fb_len)); http.addHeader("Filename", PICTURE); diff --git a/esp32-cam-webserver.ino b/esp32-cam-webserver.ino index 67824a36..b3a148dd 100644 --- a/esp32-cam-webserver.ino +++ b/esp32-cam-webserver.ino @@ -8,6 +8,8 @@ #include "src/parsebytes.h" #include "time.h" #include +#include "soc/soc.h" +#include "soc/rtc_cntl_reg.h" SET_LOOP_TASK_STACK_SIZE(12*1024); @@ -75,8 +77,8 @@ extern esp_err_t SendPictureHttp(); unsigned long previousMillis; -// Every 5 Minutes -const unsigned long interval = (5 * 60 * 1000); +// Every 1 Minutes +const unsigned long interval = (1 * 60 * 1000); // Names for the Camera. (set these in myconfig.h) #if defined(CAM_NAME) @@ -391,8 +393,12 @@ void StartCamera() // Start a 60 second watchdog timer esp_task_wdt_init(60, true); esp_task_wdt_add(NULL); - //delay(60 * 1000); - //ESP.restart(); + while (1) + { + flashLED(1500); + delay(1500); + } + } else { @@ -711,6 +717,8 @@ void setup() Serial.println(baseVersion); Serial.println(); + // WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0); + // Warn if no PSRAM is detected (typically user error with board selection in the IDE) if (!psramFound()) { @@ -734,7 +742,7 @@ void setup() #if defined(LED_PIN) // If we have a notification LED, set it to output pinMode(LED_PIN, OUTPUT); - digitalWrite(LED_PIN, LED_ON); + digitalWrite(LED_PIN, LED_OFF); #endif // Start the SPIFFS filesystem before we initialise the camera diff --git a/platformio.ini b/platformio.ini index 7a882294..f5dfc05b 100644 --- a/platformio.ini +++ b/platformio.ini @@ -34,7 +34,7 @@ src_dir = ./ [env:esp32dev] -platform = espressif32@^6.9.0 +platform = espressif32@^6.10.0 board = esp32dev board_build.partitions = min_spiffs.csv framework = arduino @@ -43,6 +43,8 @@ monitor_speed = 115200 monitor_rts = 0 monitor_dtr = 0 build_type = debug +lib_deps = bblanchon/ArduinoJson + monitor_filters = esp32_exception_decoder ; log2file time From 8c373da1a1533c64fa18147f9a542e18c45819c1 Mon Sep 17 00:00:00 2001 From: Mandl Date: Fri, 30 May 2025 11:38:13 +0200 Subject: [PATCH 7/8] add client ip logging --- app_httpd.cpp | 958 ++++++++++++++++++++++++---------------- esp32-cam-webserver.ino | 1 + 2 files changed, 575 insertions(+), 384 deletions(-) diff --git a/app_httpd.cpp b/app_httpd.cpp index 97a48fcf..aee4781c 100644 --- a/app_httpd.cpp +++ b/app_httpd.cpp @@ -19,6 +19,8 @@ #include #include #include +#include +#include #include "index_ov2640.h" #include "index_ov3660.h" @@ -30,7 +32,6 @@ #include "src/httpd_basic_auth.h" #include "myconfig2.h" - // Functions from the main .ino extern void flashLED(int flashtime); extern void setLamp(int newVal); @@ -70,15 +71,16 @@ extern char otaPassword[]; extern unsigned long xclk; extern int sensorPID; -typedef struct { - httpd_req_t *req; - size_t len; +typedef struct +{ + httpd_req_t *req; + size_t len; } jpg_chunking_t; #define PART_BOUNDARY "123456789000000000000987654321" -static const char* _STREAM_CONTENT_TYPE = "multipart/x-mixed-replace;boundary=" PART_BOUNDARY; -static const char* _STREAM_BOUNDARY = "\r\n--" PART_BOUNDARY "\r\n"; -static const char* _STREAM_PART = "Content-Type: image/jpeg\r\nContent-Length: %u\r\n\r\n"; +static const char *_STREAM_CONTENT_TYPE = "multipart/x-mixed-replace;boundary=" PART_BOUNDARY; +static const char *_STREAM_BOUNDARY = "\r\n--" PART_BOUNDARY "\r\n"; +static const char *_STREAM_PART = "Content-Type: image/jpeg\r\nContent-Length: %u\r\n\r\n"; httpd_handle_t stream_httpd = NULL; httpd_handle_t camera_httpd = NULL; @@ -87,18 +89,21 @@ httpd_handle_t camera_httpd = NULL; bool streamKill; #ifdef __cplusplus -extern "C" { +extern "C" +{ #endif -uint8_t temprature_sens_read(); + uint8_t temprature_sens_read(); #ifdef __cplusplus } #endif -void serialDump() { +void serialDump() +{ Serial.println(); // Module Serial.printf("Name: %s\r\n", myName); - if (haveTime) { + if (haveTime) + { Serial.print("Time: "); printLocalTime(true); } @@ -107,24 +112,36 @@ void serialDump() { Serial.printf("Sketch Size: %i (total: %i, %.1f%% used)\r\n", sketchSize, sketchSpace, sketchPct); Serial.printf("MD5: %s\r\n", sketchMD5.c_str()); Serial.printf("ESP sdk: %s\r\n", ESP.getSdkVersion()); - if (otaEnabled) { - if (strlen(otaPassword) != 0) { + if (otaEnabled) + { + if (strlen(otaPassword) != 0) + { Serial.printf("OTA: Enabled, Password: %s\n\r", otaPassword); - } else { + } + else + { Serial.printf("OTA: Enabled, No Password! (insecure)\n\r"); - } - } else { + } + } + else + { Serial.printf("OTA: Disabled\n\r"); } // Network - if (accesspoint) { - if (captivePortal) { + if (accesspoint) + { + if (captivePortal) + { Serial.printf("WiFi Mode: AccessPoint with captive portal\r\n"); - } else { + } + else + { Serial.printf("WiFi Mode: AccessPoint\r\n"); } Serial.printf("WiFi SSID: %s\r\n", apName); - } else { + } + else + { Serial.printf("WiFi Mode: Client\r\n"); String ssidName = WiFi.SSID(); Serial.printf("WiFi Ssid: %s\r\n", ssidName.c_str()); @@ -133,7 +150,8 @@ void serialDump() { Serial.printf("WiFi BSSID: %s\r\n", bssid.c_str()); } Serial.printf("WiFi IP address: %d.%d.%d.%d\r\n", ip[0], ip[1], ip[2], ip[3]); - if (!accesspoint) { + if (!accesspoint) + { Serial.printf("WiFi Netmask: %d.%d.%d.%d\r\n", net[0], net[1], net[2], net[3]); Serial.printf("WiFi Gateway: %d.%d.%d.%d\r\n", gw[0], gw[1], gw[2], gw[3]); } @@ -143,33 +161,40 @@ void serialDump() { Serial.printf("WiFi MAC: %02X:%02X:%02X:%02X:%02X:%02X\r\n", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); // System int64_t sec = esp_timer_get_time() / 1000000; - int64_t upDays = int64_t(floor(sec/86400)); - int upHours = int64_t(floor(sec/3600)) % 24; - int upMin = int64_t(floor(sec/60)) % 60; + int64_t upDays = int64_t(floor(sec / 86400)); + int upHours = int64_t(floor(sec / 3600)) % 24; + int upMin = int64_t(floor(sec / 60)) % 60; int upSec = sec % 60; int McuTc = (temprature_sens_read() - 32) / 1.8; // celsius - int McuTf = temprature_sens_read(); // fahrenheit + 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("CPU Freq: %i MHz, Xclk Freq: %lu 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()) { + 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 { + } + 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 && (SPIFFS.totalBytes() > 0)) { + if (filesystem && (SPIFFS.totalBytes() > 0)) + { Serial.printf("Spiffs: %i, used: %i\r\n", SPIFFS.totalBytes(), SPIFFS.usedBytes()); - } else { + } + 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); - if (critERR.length() > 0) { + if (critERR.length() > 0) + { Serial.printf("\r\n\r\nAn error or halt has occurred with Camera Hardware, see previous messages.\r\n"); Serial.printf("A reboot is required to recover from this.\r\nError message: (html)\r\n %s\r\n\r\n", critERR.c_str()); } @@ -177,12 +202,32 @@ void serialDump() { return; } -static esp_err_t capture_handler(httpd_req_t *req){ - camera_fb_t * fb = NULL; +static esp_err_t capture_handler(httpd_req_t *req) +{ + camera_fb_t *fb = NULL; esp_err_t res = ESP_OK; + int sockfd = httpd_req_to_sockfd(req); + char ipstr[INET6_ADDRSTRLEN]; + struct sockaddr_in6 addr; // esp_http_server uses IPv6 addressing + socklen_t addr_size = sizeof(addr); + + if (getpeername(sockfd, (struct sockaddr *)&addr, &addr_size) < 0) + { + Serial.println("Error getting client IP"); + } + else + { + // Convert to IPv6 string + // inet_ntop(AF_INET, &addr.sin6_addr, ipstr, sizeof(ipstr)); + // Convert to IPv4 string + inet_ntop(AF_INET, &addr.sin6_addr.un.u32_addr[3], ipstr, sizeof(ipstr)); + Serial.print("Client IP => "); + Serial.println(ipstr); + } Serial.println("Capture Requested"); - if (autoLamp && (lampVal != -1)) { + if (autoLamp && (lampVal != -1)) + { setLamp(lampVal); delay(75); // coupled with the status led flash this gives ~150ms for lamp to settle. } @@ -191,10 +236,12 @@ static esp_err_t capture_handler(httpd_req_t *req){ int64_t fr_start = esp_timer_get_time(); fb = esp_camera_fb_get(); - if (!fb) { + if (!fb) + { Serial.println("CAPTURE: failed to acquire frame"); httpd_resp_send_500(req); - if (autoLamp && (lampVal != -1)) setLamp(0); + if (autoLamp && (lampVal != -1)) + setLamp(0); return ESP_FAIL; } @@ -203,10 +250,13 @@ static esp_err_t capture_handler(httpd_req_t *req){ httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*"); size_t fb_len = 0; - if(fb->format == PIXFORMAT_JPEG){ + if (fb->format == PIXFORMAT_JPEG) + { fb_len = fb->len; res = httpd_resp_send(req, (const char *)fb->buf, fb->len); - } else { + } + else + { res = ESP_FAIL; Serial.println("Capture Error: Non-JPEG image returned by camera module"); } @@ -214,90 +264,113 @@ static esp_err_t capture_handler(httpd_req_t *req){ 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)); + 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)) { + if (autoLamp && (lampVal != -1)) + { setLamp(0); } return res; } -static esp_err_t stream_handler(httpd_req_t *req){ - camera_fb_t * fb = NULL; +static esp_err_t stream_handler(httpd_req_t *req) +{ + camera_fb_t *fb = NULL; esp_err_t res = ESP_OK; size_t _jpg_buf_len = 0; - uint8_t * _jpg_buf = NULL; - char * part_buf[64]; + 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.. - flashLED(75); // double flash of status LED + if (autoLamp && (lampVal != -1)) + setLamp(lampVal); + streamCount = 1; // at present we only have one stream handler, so values are 0 or 1.. + flashLED(75); // double flash of status LED delay(75); flashLED(75); static int64_t last_frame = 0; - if(!last_frame) { + if (!last_frame) + { last_frame = esp_timer_get_time(); } res = httpd_resp_set_type(req, _STREAM_CONTENT_TYPE); - if(res != ESP_OK){ + if (res != ESP_OK) + { streamCount = 0; - if (autoLamp && (lampVal != -1)) setLamp(0); + if (autoLamp && (lampVal != -1)) + setLamp(0); Serial.println("STREAM: failed to set HTTP response type"); return res; } httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*"); - if(res == ESP_OK){ + if (res == ESP_OK) + { res = httpd_resp_send_chunk(req, _STREAM_BOUNDARY, strlen(_STREAM_BOUNDARY)); } - while(true){ + while (true) + { fb = esp_camera_fb_get(); - if (!fb) { + if (!fb) + { Serial.println("STREAM: failed to acquire frame"); res = ESP_FAIL; - } else { - if(fb->format != PIXFORMAT_JPEG){ + } + else + { + if (fb->format != PIXFORMAT_JPEG) + { Serial.println("STREAM: Non-JPEG frame returned by camera module"); res = ESP_FAIL; - } else { + } + else + { _jpg_buf_len = fb->len; _jpg_buf = fb->buf; } } - if(res == ESP_OK){ + 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); } - if(res == ESP_OK){ + if (res == ESP_OK) + { res = httpd_resp_send_chunk(req, (const char *)_jpg_buf, _jpg_buf_len); } - if(res == ESP_OK){ + if (res == ESP_OK) + { res = httpd_resp_send_chunk(req, _STREAM_BOUNDARY, strlen(_STREAM_BOUNDARY)); } - if(fb){ + if (fb) + { esp_camera_fb_return(fb); fb = NULL; _jpg_buf = NULL; - } else if(_jpg_buf){ + } + else if (_jpg_buf) + { free(_jpg_buf); _jpg_buf = NULL; } - if(res != ESP_OK){ + if (res != ESP_OK) + { // This is the error 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. Serial.printf("Stream failed, code = %i : %s\r\n", res, esp_err_to_name(res)); break; } - if((res != ESP_OK) || streamKill){ + if ((res != ESP_OK) || streamKill) + { // We end the stream here when a kill is signalled. Serial.printf("Stream killed\r\n"); break; @@ -307,179 +380,251 @@ static esp_err_t stream_handler(httpd_req_t *req){ int32_t frame_delay = (minFrameTime > frame_time) ? minFrameTime - frame_time : 0; delay(frame_delay); - if (debugData) { + if (debugData) + { Serial.printf("MJPG: %uB %ums, delay: %ums, framerate (%.1ffps)\r\n", - (uint32_t)(_jpg_buf_len), - (uint32_t)frame_time, frame_delay, 1000.0 / (uint32_t)(frame_time + frame_delay)); + (uint32_t)(_jpg_buf_len), + (uint32_t)frame_time, frame_delay, 1000.0 / (uint32_t)(frame_time + frame_delay)); } last_frame = esp_timer_get_time(); } streamsServed++; streamCount = 0; - if (autoLamp && (lampVal != -1)) setLamp(0); + if (autoLamp && (lampVal != -1)) + setLamp(0); Serial.println("Stream ended"); last_frame = 0; return res; } -static esp_err_t cmd_handler(httpd_req_t *req){ - char* buf; +static esp_err_t cmd_handler(httpd_req_t *req) +{ + char *buf; size_t buf_len; - char variable[32] = {0,}; - char value[32] = {0,}; + char variable[32] = { + 0, + }; + char value[32] = { + 0, + }; flashLED(75); buf_len = httpd_req_get_url_query_len(req) + 1; - if (buf_len > 1) { - buf = (char*)malloc(buf_len); - if(!buf){ + if (buf_len > 1) + { + buf = (char *)malloc(buf_len); + if (!buf) + { httpd_resp_send_500(req); return ESP_FAIL; } - if (httpd_req_get_url_query_str(req, buf, buf_len) == ESP_OK) { + if (httpd_req_get_url_query_str(req, buf, buf_len) == ESP_OK) + { if (httpd_query_key_value(buf, "var", variable, sizeof(variable)) == ESP_OK && - httpd_query_key_value(buf, "val", value, sizeof(value)) == ESP_OK) { - } else { + httpd_query_key_value(buf, "val", value, sizeof(value)) == ESP_OK) + { + } + else + { free(buf); httpd_resp_send_404(req); return ESP_FAIL; } - } else { + } + else + { free(buf); httpd_resp_send_404(req); return ESP_FAIL; } free(buf); - } else { + } + else + { httpd_resp_send_404(req); return ESP_FAIL; } - if (critERR.length() > 0) return httpd_resp_send_500(req); + if (critERR.length() > 0) + return httpd_resp_send_500(req); int val = atoi(value); - sensor_t * s = esp_camera_sensor_get(); + sensor_t *s = esp_camera_sensor_get(); int res = 0; - if(!strcmp(variable, "framesize")) { - 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); - else if(!strcmp(variable, "gainceiling")) res = s->set_gainceiling(s, (gainceiling_t)val); - else if(!strcmp(variable, "colorbar")) res = s->set_colorbar(s, val); - else if(!strcmp(variable, "awb")) res = s->set_whitebal(s, val); - else if(!strcmp(variable, "agc")) res = s->set_gain_ctrl(s, val); - else if(!strcmp(variable, "aec")) res = s->set_exposure_ctrl(s, val); - else if(!strcmp(variable, "hmirror")) res = s->set_hmirror(s, val); - else if(!strcmp(variable, "vflip")) res = s->set_vflip(s, val); - else if(!strcmp(variable, "awb_gain")) res = s->set_awb_gain(s, val); - else if(!strcmp(variable, "agc_gain")) res = s->set_agc_gain(s, val); - else if(!strcmp(variable, "aec_value")) res = s->set_aec_value(s, val); - else if(!strcmp(variable, "aec2")) res = s->set_aec2(s, val); - else if(!strcmp(variable, "dcw")) res = s->set_dcw(s, val); - else if(!strcmp(variable, "bpc")) res = s->set_bpc(s, val); - else if(!strcmp(variable, "wpc")) res = s->set_wpc(s, val); - else if(!strcmp(variable, "raw_gma")) res = s->set_raw_gma(s, val); - else if(!strcmp(variable, "lenc")) res = s->set_lenc(s, val); - else if(!strcmp(variable, "special_effect")) res = s->set_special_effect(s, val); - 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, "min_frame_time")) minFrameTime = val; - else if(!strcmp(variable, "autolamp") && (lampVal != -1)) { + if (!strcmp(variable, "framesize")) + { + 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); + else if (!strcmp(variable, "gainceiling")) + res = s->set_gainceiling(s, (gainceiling_t)val); + else if (!strcmp(variable, "colorbar")) + res = s->set_colorbar(s, val); + else if (!strcmp(variable, "awb")) + res = s->set_whitebal(s, val); + else if (!strcmp(variable, "agc")) + res = s->set_gain_ctrl(s, val); + else if (!strcmp(variable, "aec")) + res = s->set_exposure_ctrl(s, val); + else if (!strcmp(variable, "hmirror")) + res = s->set_hmirror(s, val); + else if (!strcmp(variable, "vflip")) + res = s->set_vflip(s, val); + else if (!strcmp(variable, "awb_gain")) + res = s->set_awb_gain(s, val); + else if (!strcmp(variable, "agc_gain")) + res = s->set_agc_gain(s, val); + else if (!strcmp(variable, "aec_value")) + res = s->set_aec_value(s, val); + else if (!strcmp(variable, "aec2")) + res = s->set_aec2(s, val); + else if (!strcmp(variable, "dcw")) + res = s->set_dcw(s, val); + else if (!strcmp(variable, "bpc")) + res = s->set_bpc(s, val); + else if (!strcmp(variable, "wpc")) + res = s->set_wpc(s, val); + else if (!strcmp(variable, "raw_gma")) + res = s->set_raw_gma(s, val); + else if (!strcmp(variable, "lenc")) + res = s->set_lenc(s, val); + else if (!strcmp(variable, "special_effect")) + res = s->set_special_effect(s, val); + 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, "min_frame_time")) + minFrameTime = val; + else if (!strcmp(variable, "autolamp") && (lampVal != -1)) + { autoLamp = val; - if (autoLamp) { - if (streamCount > 0) setLamp(lampVal); - else setLamp(0); - } else { + if (autoLamp) + { + if (streamCount > 0) + setLamp(lampVal); + else + setLamp(0); + } + else + { setLamp(lampVal); } } - else if(!strcmp(variable, "lamp") && (lampVal != -1)) { - lampVal = constrain(val,0,100); - if (autoLamp) { - if (streamCount > 0) setLamp(lampVal); - else setLamp(0); - } else { + else if (!strcmp(variable, "lamp") && (lampVal != -1)) + { + lampVal = constrain(val, 0, 100); + if (autoLamp) + { + if (streamCount > 0) + setLamp(lampVal); + else + setLamp(0); + } + else + { setLamp(lampVal); } } - else if(!strcmp(variable, "save_prefs")) { - if (filesystem) savePrefs(SPIFFS); + else if (!strcmp(variable, "save_prefs")) + { + if (filesystem) + savePrefs(SPIFFS); } - else if(!strcmp(variable, "clear_prefs")) { - if (filesystem) removePrefs(SPIFFS); + else if (!strcmp(variable, "clear_prefs")) + { + 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 + 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 periph_module_disable(PERIPH_I2C1_MODULE); periph_module_reset(PERIPH_I2C0_MODULE); periph_module_reset(PERIPH_I2C1_MODULE); Serial.print("REBOOT requested"); - while(true) { - flashLED(50); - delay(150); - Serial.print('.'); + while (true) + { + flashLED(50); + delay(150); + Serial.print('.'); } } - else { + else + { res = -1; } - if(res){ + if (res) + { return httpd_resp_send_500(req); } httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*"); return httpd_resp_send(req, NULL, 0); } -static esp_err_t status_handler(httpd_req_t *req){ +static esp_err_t status_handler(httpd_req_t *req) +{ static char json_response[1024]; - char * p = json_response; + char *p = json_response; *p++ = '{'; // Do not get attempt to get sensor when in error; causes a panic.. - if (critERR.length() == 0) { - sensor_t * s = esp_camera_sensor_get(); - p+=sprintf(p, "\"lamp\":%d,", lampVal); - p+=sprintf(p, "\"autolamp\":%d,", autoLamp); - 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\":%lu,", 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); - p+=sprintf(p, "\"sharpness\":%d,", s->status.sharpness); - p+=sprintf(p, "\"special_effect\":%u,", s->status.special_effect); - p+=sprintf(p, "\"wb_mode\":%u,", s->status.wb_mode); - p+=sprintf(p, "\"awb\":%u,", s->status.awb); - p+=sprintf(p, "\"awb_gain\":%u,", s->status.awb_gain); - p+=sprintf(p, "\"aec\":%u,", s->status.aec); - p+=sprintf(p, "\"aec2\":%u,", s->status.aec2); - p+=sprintf(p, "\"ae_level\":%d,", s->status.ae_level); - p+=sprintf(p, "\"aec_value\":%u,", s->status.aec_value); - p+=sprintf(p, "\"agc\":%u,", s->status.agc); - p+=sprintf(p, "\"agc_gain\":%u,", s->status.agc_gain); - p+=sprintf(p, "\"gainceiling\":%u,", s->status.gainceiling); - p+=sprintf(p, "\"bpc\":%u,", s->status.bpc); - p+=sprintf(p, "\"wpc\":%u,", s->status.wpc); - p+=sprintf(p, "\"raw_gma\":%u,", s->status.raw_gma); - p+=sprintf(p, "\"lenc\":%u,", s->status.lenc); - p+=sprintf(p, "\"vflip\":%u,", s->status.vflip); - p+=sprintf(p, "\"hmirror\":%u,", s->status.hmirror); - p+=sprintf(p, "\"dcw\":%u,", s->status.dcw); - p+=sprintf(p, "\"colorbar\":%u,", s->status.colorbar); - p+=sprintf(p, "\"cam_name\":\"%s\",", myName); - p+=sprintf(p, "\"code_ver\":\"%s\",", myVer); - p+=sprintf(p, "\"rotate\":\"%d\",", myRotation); - p+=sprintf(p, "\"stream_url\":\"%s\"", streamURL); + if (critERR.length() == 0) + { + sensor_t *s = esp_camera_sensor_get(); + p += sprintf(p, "\"lamp\":%d,", lampVal); + p += sprintf(p, "\"autolamp\":%d,", autoLamp); + 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\":%lu,", 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); + p += sprintf(p, "\"sharpness\":%d,", s->status.sharpness); + p += sprintf(p, "\"special_effect\":%u,", s->status.special_effect); + p += sprintf(p, "\"wb_mode\":%u,", s->status.wb_mode); + p += sprintf(p, "\"awb\":%u,", s->status.awb); + p += sprintf(p, "\"awb_gain\":%u,", s->status.awb_gain); + p += sprintf(p, "\"aec\":%u,", s->status.aec); + p += sprintf(p, "\"aec2\":%u,", s->status.aec2); + p += sprintf(p, "\"ae_level\":%d,", s->status.ae_level); + p += sprintf(p, "\"aec_value\":%u,", s->status.aec_value); + p += sprintf(p, "\"agc\":%u,", s->status.agc); + p += sprintf(p, "\"agc_gain\":%u,", s->status.agc_gain); + p += sprintf(p, "\"gainceiling\":%u,", s->status.gainceiling); + p += sprintf(p, "\"bpc\":%u,", s->status.bpc); + p += sprintf(p, "\"wpc\":%u,", s->status.wpc); + p += sprintf(p, "\"raw_gma\":%u,", s->status.raw_gma); + p += sprintf(p, "\"lenc\":%u,", s->status.lenc); + p += sprintf(p, "\"vflip\":%u,", s->status.vflip); + p += sprintf(p, "\"hmirror\":%u,", s->status.hmirror); + p += sprintf(p, "\"dcw\":%u,", s->status.dcw); + p += sprintf(p, "\"colorbar\":%u,", s->status.colorbar); + p += sprintf(p, "\"cam_name\":\"%s\",", myName); + p += sprintf(p, "\"code_ver\":\"%s\",", myVer); + p += sprintf(p, "\"rotate\":\"%d\",", myRotation); + p += sprintf(p, "\"stream_url\":\"%s\"", streamURL); } *p++ = '}'; *p++ = 0; @@ -488,13 +633,14 @@ static esp_err_t status_handler(httpd_req_t *req){ return httpd_resp_send(req, json_response, strlen(json_response)); } -static esp_err_t info_handler(httpd_req_t *req){ +static esp_err_t info_handler(httpd_req_t *req) +{ static char json_response[256]; - char * p = json_response; + char *p = json_response; *p++ = '{'; - p+=sprintf(p, "\"cam_name\":\"%s\",", myName); - p+=sprintf(p, "\"rotate\":\"%d\",", myRotation); - p+=sprintf(p, "\"stream_url\":\"%s\"", streamURL); + p += sprintf(p, "\"cam_name\":\"%s\",", myName); + p += sprintf(p, "\"rotate\":\"%d\",", myRotation); + p += sprintf(p, "\"stream_url\":\"%s\"", streamURL); *p++ = '}'; *p++ = 0; httpd_resp_set_type(req, "application/json"); @@ -502,139 +648,161 @@ static esp_err_t info_handler(httpd_req_t *req){ return httpd_resp_send(req, json_response, strlen(json_response)); } -static esp_err_t favicon_16x16_handler(httpd_req_t *req){ +static esp_err_t favicon_16x16_handler(httpd_req_t *req) +{ httpd_resp_set_type(req, "image/png"); httpd_resp_set_hdr(req, "Content-Encoding", "identity"); return httpd_resp_send(req, (const char *)favicon_16x16_png, favicon_16x16_png_len); } -static esp_err_t favicon_32x32_handler(httpd_req_t *req){ +static esp_err_t favicon_32x32_handler(httpd_req_t *req) +{ httpd_resp_set_type(req, "image/png"); httpd_resp_set_hdr(req, "Content-Encoding", "identity"); return httpd_resp_send(req, (const char *)favicon_32x32_png, favicon_32x32_png_len); } -static esp_err_t favicon_ico_handler(httpd_req_t *req){ +static esp_err_t favicon_ico_handler(httpd_req_t *req) +{ httpd_resp_set_type(req, "image/x-icon"); httpd_resp_set_hdr(req, "Content-Encoding", "identity"); return httpd_resp_send(req, (const char *)favicon_ico, favicon_ico_len); } -static esp_err_t logo_svg_handler(httpd_req_t *req){ +static esp_err_t logo_svg_handler(httpd_req_t *req) +{ httpd_resp_set_type(req, "image/svg+xml"); httpd_resp_set_hdr(req, "Content-Encoding", "identity"); return httpd_resp_send(req, (const char *)logo_svg, logo_svg_len); } -static esp_err_t dump_handler(httpd_req_t *req){ +static esp_err_t dump_handler(httpd_req_t *req) +{ flashLED(75); Serial.println("\r\nDump requested via Web"); serialDump(); static char dumpOut[2000] = ""; - char * d = dumpOut; + char *d = dumpOut; // Header - d+= sprintf(d,"\n"); - d+= sprintf(d,"\n"); - d+= sprintf(d,"%s - Status\n", myName); - d+= sprintf(d,"\n"); - d+= sprintf(d,"\n"); - d+= sprintf(d,"\n"); - d+= sprintf(d,"\n"); - d+= sprintf(d,"\n"); - d+= sprintf(d,"\n"); - if (critERR.length() > 0) { - d+= sprintf(d,"%s
\n", critERR.c_str()); - } - d+= sprintf(d,"

ESP32 Cam Webserver

\n"); + d += sprintf(d, "\n"); + d += sprintf(d, "\n"); + d += sprintf(d, "%s - Status\n", myName); + d += sprintf(d, "\n"); + d += sprintf(d, "\n"); + d += sprintf(d, "\n"); + d += sprintf(d, "\n"); + d += sprintf(d, "\n"); + d += sprintf(d, "\n"); + if (critERR.length() > 0) + { + d += sprintf(d, "%s
\n", critERR.c_str()); + } + d += sprintf(d, "

ESP32 Cam Webserver

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

WiFi

\n"); - if (accesspoint) { - if (captivePortal) { - d+= sprintf(d,"Mode: AccessPoint with captive portal
\n"); - } else { - d+= sprintf(d,"Mode: AccessPoint
\n"); - } - d+= sprintf(d,"SSID: %s
\n", apName); - } else { - d+= sprintf(d,"Mode: Client
\n"); + d += sprintf(d, "

WiFi

\n"); + if (accesspoint) + { + if (captivePortal) + { + d += sprintf(d, "Mode: AccessPoint with captive portal
\n"); + } + else + { + d += sprintf(d, "Mode: AccessPoint
\n"); + } + d += sprintf(d, "SSID: %s
\n", apName); + } + else + { + d += sprintf(d, "Mode: Client
\n"); String ssidName = WiFi.SSID(); - d+= sprintf(d,"SSID: %s
\n", ssidName.c_str()); - d+= sprintf(d,"Rssi: %i
\n", WiFi.RSSI()); + d += sprintf(d, "SSID: %s
\n", ssidName.c_str()); + d += sprintf(d, "Rssi: %i
\n", WiFi.RSSI()); String bssid = WiFi.BSSIDstr(); - d+= sprintf(d,"BSSID: %s
\n", bssid.c_str()); + d += sprintf(d, "BSSID: %s
\n", bssid.c_str()); } - d+= sprintf(d,"IP address: %d.%d.%d.%d
\n", ip[0], ip[1], ip[2], ip[3]); - if (!accesspoint) { - d+= sprintf(d,"Netmask: %d.%d.%d.%d
\n", net[0], net[1], net[2], net[3]); - d+= sprintf(d,"Gateway: %d.%d.%d.%d
\n", gw[0], gw[1], gw[2], gw[3]); + d += sprintf(d, "IP address: %d.%d.%d.%d
\n", ip[0], ip[1], ip[2], ip[3]); + if (!accesspoint) + { + d += sprintf(d, "Netmask: %d.%d.%d.%d
\n", net[0], net[1], net[2], net[3]); + d += sprintf(d, "Gateway: %d.%d.%d.%d
\n", gw[0], gw[1], gw[2], gw[3]); } - d+= sprintf(d,"Http port: %i, Stream port: %i
\n", httpPort, streamPort); + d += sprintf(d, "Http port: %i, Stream port: %i
\n", httpPort, streamPort); byte mac[6]; WiFi.macAddress(mac); - d+= sprintf(d,"MAC: %02X:%02X:%02X:%02X:%02X:%02X
\n", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); + d += sprintf(d, "MAC: %02X:%02X:%02X:%02X:%02X:%02X
\n", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); // System - d+= sprintf(d,"

System

\n"); - if (haveTime) { + d += sprintf(d, "

System

\n"); + if (haveTime) + { struct tm timeinfo; - if(getLocalTime(&timeinfo)){ - char timeStringBuff[50]; //50 chars should be enough + if (getLocalTime(&timeinfo)) + { + char timeStringBuff[50]; // 50 chars should be enough strftime(timeStringBuff, sizeof(timeStringBuff), "%H:%M:%S, %A, %B %d %Y", &timeinfo); - //print like "const char*" - d+= sprintf(d,"Time: %s
\n", timeStringBuff); + // print like "const char*" + d += sprintf(d, "Time: %s
\n", timeStringBuff); } } int64_t sec = esp_timer_get_time() / 1000000; - int64_t upDays = int64_t(floor(sec/86400)); - int upHours = int64_t(floor(sec/3600)) % 24; - int upMin = int64_t(floor(sec/60)) % 60; + int64_t upDays = int64_t(floor(sec / 86400)); + int upHours = int64_t(floor(sec / 3600)) % 24; + int upMin = int64_t(floor(sec / 60)) % 60; int upSec = sec % 60; int McuTc = (temprature_sens_read() - 32) / 1.8; // celsius - int McuTf = temprature_sens_read(); // fahrenheit - - 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: %lu 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()); - 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"); + int McuTf = temprature_sens_read(); // fahrenheit + + 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: %lu 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()); + 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 - d+= sprintf(d,"
\n"); - d+= sprintf(d,"\n"); - d+= sprintf(d,"\n"); - d+= sprintf(d,"\n"); - d+= sprintf(d,"
\n\n"); + 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. - d+= sprintf(d,"\n\n"); + d += sprintf(d, "\n\n"); *d++ = 0; httpd_resp_set_type(req, "text/html"); httpd_resp_set_hdr(req, "Content-Encoding", "identity"); return httpd_resp_send(req, dumpOut, strlen(dumpOut)); } -static esp_err_t stop_handler(httpd_req_t *req){ +static esp_err_t stop_handler(httpd_req_t *req) +{ flashLED(75); Serial.println("\r\nStream stop requested via Web"); streamKill = true; @@ -642,14 +810,15 @@ static esp_err_t stop_handler(httpd_req_t *req){ return httpd_resp_send(req, NULL, 0); } - -static esp_err_t style_handler(httpd_req_t *req){ +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"); return httpd_resp_send(req, (const char *)style_css, style_css_len); } -static esp_err_t streamviewer_handler(httpd_req_t *req){ +static esp_err_t streamviewer_handler(httpd_req_t *req) +{ flashLED(75); Serial.println("Stream viewer requested"); httpd_resp_set_type(req, "text/html"); @@ -657,7 +826,8 @@ static esp_err_t streamviewer_handler(httpd_req_t *req){ return httpd_resp_send(req, (const char *)streamviewer_html, streamviewer_html_len); } -static esp_err_t error_handler(httpd_req_t *req){ +static esp_err_t error_handler(httpd_req_t *req) +{ flashLED(75); Serial.println("Sending error page"); std::string s(error_html); @@ -673,72 +843,93 @@ static esp_err_t error_handler(httpd_req_t *req){ return httpd_resp_send(req, (const char *)s.c_str(), s.length()); } -static esp_err_t index_handler(httpd_req_t *req){ - char* buf; +static esp_err_t index_handler(httpd_req_t *req) +{ + char *buf; size_t buf_len; - char view[32] = {0,}; + char view[32] = { + 0, + }; - if ( HTTP_BASIC_AUTH == true ) + if (HTTP_BASIC_AUTH == true) { - if(httpd_basic_auth(req, "admin", "test") != ESP_OK) { - + if (httpd_basic_auth(req, "admin", "rehleber123") != ESP_OK) + { + httpd_basic_auth_resp_send_401(req); httpd_resp_sendstr(req, "Not Authorized"); return ESP_FAIL; } } - - flashLED(75); // See if we have a specific target (full/simple/portal) and serve as appropriate buf_len = httpd_req_get_url_query_len(req) + 1; - if (buf_len > 1) { - buf = (char*)malloc(buf_len); - if(!buf){ + if (buf_len > 1) + { + buf = (char *)malloc(buf_len); + if (!buf) + { httpd_resp_send_500(req); return ESP_FAIL; } - if (httpd_req_get_url_query_str(req, buf, buf_len) == ESP_OK) { - if (httpd_query_key_value(buf, "view", view, sizeof(view)) == ESP_OK) { - } else { + if (httpd_req_get_url_query_str(req, buf, buf_len) == ESP_OK) + { + if (httpd_query_key_value(buf, "view", view, sizeof(view)) == ESP_OK) + { + } + else + { free(buf); httpd_resp_send_404(req); return ESP_FAIL; } - } else { + } + else + { free(buf); httpd_resp_send_404(req); return ESP_FAIL; } free(buf); - } else { + } + else + { // no target specified; default. - strcpy(view,default_index); + strcpy(view, default_index); // If captive portal is active send that instead - if (captivePortal) { - strcpy(view,"portal"); + if (captivePortal) + { + strcpy(view, "portal"); } } - if (strncmp(view,"simple", sizeof(view)) == 0) { + if (strncmp(view, "simple", sizeof(view)) == 0) + { Serial.println("Simple index page requested"); - if (critERR.length() > 0) return error_handler(req); + if (critERR.length() > 0) + return error_handler(req); httpd_resp_set_type(req, "text/html"); httpd_resp_set_hdr(req, "Content-Encoding", "identity"); return httpd_resp_send(req, (const char *)index_simple_html, index_simple_html_len); - } else if(strncmp(view,"full", sizeof(view)) == 0) { + } + else if (strncmp(view, "full", sizeof(view)) == 0) + { Serial.println("Full index page requested"); - if (critERR.length() > 0) return error_handler(req); + if (critERR.length() > 0) + return error_handler(req); httpd_resp_set_type(req, "text/html"); httpd_resp_set_hdr(req, "Content-Encoding", "identity"); - if (sensorPID == OV3660_PID) { + if (sensorPID == OV3660_PID) + { return httpd_resp_send(req, (const char *)index_ov3660_html, index_ov3660_html_len); } return httpd_resp_send(req, (const char *)index_ov2640_html, index_ov2640_html_len); - } else if(strncmp(view,"portal", sizeof(view)) == 0) { - //Prototype captive portal landing page. + } + else if (strncmp(view, "portal", sizeof(view)) == 0) + { + // Prototype captive portal landing page. Serial.println("Portal page requested"); std::string s(portal_html); size_t index; @@ -751,7 +942,9 @@ static esp_err_t index_handler(httpd_req_t *req){ httpd_resp_set_type(req, "text/html"); httpd_resp_set_hdr(req, "Content-Encoding", "identity"); return httpd_resp_send(req, (const char *)s.c_str(), s.length()); - } else { + } + else + { Serial.print("Unknown page requested: "); Serial.println(view); httpd_resp_send_404(req); @@ -759,115 +952,104 @@ static esp_err_t index_handler(httpd_req_t *req){ } } -void startCameraServer(int hPort, int sPort){ +void startCameraServer(int hPort, int sPort) +{ httpd_config_t config = HTTPD_DEFAULT_CONFIG(); config.max_uri_handlers = 16; // we use more than the default 8 (on port 80) httpd_uri_t index_uri = { - .uri = "/", - .method = HTTP_GET, - .handler = index_handler, - .user_ctx = NULL - }; + .uri = "/", + .method = HTTP_GET, + .handler = index_handler, + .user_ctx = NULL}; httpd_uri_t status_uri = { - .uri = "/status", - .method = HTTP_GET, - .handler = status_handler, - .user_ctx = NULL - }; + .uri = "/status", + .method = HTTP_GET, + .handler = status_handler, + .user_ctx = NULL}; httpd_uri_t cmd_uri = { - .uri = "/control", - .method = HTTP_GET, - .handler = cmd_handler, - .user_ctx = NULL - }; + .uri = "/control", + .method = HTTP_GET, + .handler = cmd_handler, + .user_ctx = NULL}; httpd_uri_t capture_uri = { - .uri = "/capture", - .method = HTTP_GET, - .handler = capture_handler, - .user_ctx = NULL - }; + .uri = "/capture", + .method = HTTP_GET, + .handler = capture_handler, + .user_ctx = NULL}; httpd_uri_t style_uri = { - .uri = "/style.css", - .method = HTTP_GET, - .handler = style_handler, - .user_ctx = NULL - }; + .uri = "/style.css", + .method = HTTP_GET, + .handler = style_handler, + .user_ctx = NULL}; httpd_uri_t favicon_16x16_uri = { - .uri = "/favicon-16x16.png", - .method = HTTP_GET, - .handler = favicon_16x16_handler, - .user_ctx = NULL - }; + .uri = "/favicon-16x16.png", + .method = HTTP_GET, + .handler = favicon_16x16_handler, + .user_ctx = NULL}; httpd_uri_t favicon_32x32_uri = { - .uri = "/favicon-32x32.png", - .method = HTTP_GET, - .handler = favicon_32x32_handler, - .user_ctx = NULL - }; + .uri = "/favicon-32x32.png", + .method = HTTP_GET, + .handler = favicon_32x32_handler, + .user_ctx = NULL}; httpd_uri_t favicon_ico_uri = { - .uri = "/favicon.ico", - .method = HTTP_GET, - .handler = favicon_ico_handler, - .user_ctx = NULL - }; + .uri = "/favicon.ico", + .method = HTTP_GET, + .handler = favicon_ico_handler, + .user_ctx = NULL}; httpd_uri_t logo_svg_uri = { - .uri = "/logo.svg", - .method = HTTP_GET, - .handler = logo_svg_handler, - .user_ctx = NULL - }; + .uri = "/logo.svg", + .method = HTTP_GET, + .handler = logo_svg_handler, + .user_ctx = NULL}; httpd_uri_t dump_uri = { - .uri = "/dump", - .method = HTTP_GET, - .handler = dump_handler, - .user_ctx = NULL - }; + .uri = "/dump", + .method = HTTP_GET, + .handler = dump_handler, + .user_ctx = NULL}; httpd_uri_t stop_uri = { - .uri = "/stop", - .method = HTTP_GET, - .handler = stop_handler, - .user_ctx = NULL - }; + .uri = "/stop", + .method = HTTP_GET, + .handler = stop_handler, + .user_ctx = NULL}; httpd_uri_t stream_uri = { - .uri = "/", - .method = HTTP_GET, - .handler = stream_handler, - .user_ctx = NULL - }; + .uri = "/", + .method = HTTP_GET, + .handler = stream_handler, + .user_ctx = NULL}; httpd_uri_t streamviewer_uri = { - .uri = "/view", - .method = HTTP_GET, - .handler = streamviewer_handler, - .user_ctx = NULL - }; + .uri = "/view", + .method = HTTP_GET, + .handler = streamviewer_handler, + .user_ctx = NULL}; httpd_uri_t info_uri = { - .uri = "/info", - .method = HTTP_GET, - .handler = info_handler, - .user_ctx = NULL - }; + .uri = "/info", + .method = HTTP_GET, + .handler = info_handler, + .user_ctx = NULL}; httpd_uri_t error_uri = { - .uri = "/", - .method = HTTP_GET, - .handler = error_handler, - .user_ctx = NULL - }; + .uri = "/", + .method = HTTP_GET, + .handler = error_handler, + .user_ctx = NULL}; httpd_uri_t viewerror_uri = { - .uri = "/view", - .method = HTTP_GET, - .handler = error_handler, - .user_ctx = NULL - }; + .uri = "/view", + .method = HTTP_GET, + .handler = error_handler, + .user_ctx = NULL}; // Request Handlers; config.max_uri_handlers (above) must be >= the number of handlers config.server_port = hPort; config.ctrl_port = hPort; Serial.printf("Starting web server on port: '%d'\r\n", config.server_port); - if (httpd_start(&camera_httpd, &config) == ESP_OK) { - if (critERR.length() > 0) { + if (httpd_start(&camera_httpd, &config) == ESP_OK) + { + if (critERR.length() > 0) + { httpd_register_uri_handler(camera_httpd, &error_uri); - } else { + } + else + { httpd_register_uri_handler(camera_httpd, &index_uri); httpd_register_uri_handler(camera_httpd, &cmd_uri); httpd_register_uri_handler(camera_httpd, &status_uri); @@ -884,18 +1066,26 @@ void startCameraServer(int hPort, int sPort){ config.server_port = sPort; config.ctrl_port = sPort; - Serial.printf("Starting stream server on port: '%d'\r\n", config.server_port); - if (httpd_start(&stream_httpd, &config) == ESP_OK) { - if (critERR.length() > 0) { - httpd_register_uri_handler(camera_httpd, &error_uri); - httpd_register_uri_handler(camera_httpd, &viewerror_uri); - } else { - httpd_register_uri_handler(stream_httpd, &stream_uri); - httpd_register_uri_handler(stream_httpd, &info_uri); - httpd_register_uri_handler(stream_httpd, &streamviewer_uri); - } - httpd_register_uri_handler(stream_httpd, &favicon_16x16_uri); - httpd_register_uri_handler(stream_httpd, &favicon_32x32_uri); - httpd_register_uri_handler(stream_httpd, &favicon_ico_uri); + + if (HTTP_BASIC_AUTH == false) + { + Serial.printf("Starting stream server on port: '%d'\r\n", config.server_port); + if (httpd_start(&stream_httpd, &config) == ESP_OK) + { + if (critERR.length() > 0) + { + httpd_register_uri_handler(camera_httpd, &error_uri); + httpd_register_uri_handler(camera_httpd, &viewerror_uri); + } + else + { + httpd_register_uri_handler(stream_httpd, &stream_uri); + httpd_register_uri_handler(stream_httpd, &info_uri); + httpd_register_uri_handler(stream_httpd, &streamviewer_uri); + } + httpd_register_uri_handler(stream_httpd, &favicon_16x16_uri); + httpd_register_uri_handler(stream_httpd, &favicon_32x32_uri); + httpd_register_uri_handler(stream_httpd, &favicon_ico_uri); + } } } diff --git a/esp32-cam-webserver.ino b/esp32-cam-webserver.ino index b3a148dd..6ae83782 100644 --- a/esp32-cam-webserver.ino +++ b/esp32-cam-webserver.ino @@ -962,6 +962,7 @@ void loop() previousMillis = start; // send picture + Serial.println("SendPictureHttp"); if(SendPictureHttp()!= ESP_OK) { //delay(1000); From e6bab942170dd91d19daf9591a021a1d7fb1d352 Mon Sep 17 00:00:00 2001 From: Mandl Date: Sat, 18 Oct 2025 13:20:55 +0200 Subject: [PATCH 8/8] new platform --- platformio.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platformio.ini b/platformio.ini index f5dfc05b..31d3c3c0 100644 --- a/platformio.ini +++ b/platformio.ini @@ -34,7 +34,7 @@ src_dir = ./ [env:esp32dev] -platform = espressif32@^6.10.0 +platform = espressif32@^6.12.0 board = esp32dev board_build.partitions = min_spiffs.csv framework = arduino