From 6e0ed3db6fd191c73740add6640758ca1a2fa358 Mon Sep 17 00:00:00 2001 From: Owen Date: Sun, 4 Oct 2020 15:17:55 +0200 Subject: [PATCH 01/19] Better AP/Wifi split, prepared for 'soft' enabling wifi and give better feedback --- app_httpd.cpp | 4 +-- esp32-cam-webserver.ino | 80 ++++++++++++++++++++++++++--------------- myconfig.sample.h | 16 +++++---- 3 files changed, 63 insertions(+), 37 deletions(-) diff --git a/app_httpd.cpp b/app_httpd.cpp index f83305d..db0014d 100644 --- a/app_httpd.cpp +++ b/app_httpd.cpp @@ -602,10 +602,10 @@ static esp_err_t cmd_handler(httpd_req_t *req){ Serial.print("REBOOT requested"); for (int i=0; i<20; i++) { flashLED(50); - delay(50); + delay(150); Serial.print('.'); } - Serial.printf("\nThats all folks...\n\n"); + Serial.printf("\n..Thats all folks!\n\n"); ESP.restart(); } else { diff --git a/esp32-cam-webserver.ino b/esp32-cam-webserver.ino index 9c452ec..bcfa796 100644 --- a/esp32-cam-webserver.ino +++ b/esp32-cam-webserver.ino @@ -29,15 +29,12 @@ // Primary config, or defaults. #if __has_include("myconfig.h") - // I keep my settings in a seperate header file #include "myconfig.h" #else #warning "Using Default Settings: Copy myconfig.sample.h to myconfig.h and edit that to set your personal defaults" // These are the defaults.. dont edit these. // copy myconfig.sample.h to myconfig.h and edit that instead // SSID, Password and Mode - const char* ssid = "ESP32-CAM-CONNECT"; - const char* password = "InsecurePassword"; #define WIFI_AP_ENABLE // Default Board and Camera: #define CAMERA_MODEL_AI_THINKER @@ -55,7 +52,7 @@ int sketchSize; int sketchSpace; String sketchMD5; -// IP address, netmask and gateway +// IP address, Netmask and Gateway, populated when connected IPAddress ip; IPAddress net; IPAddress gw; @@ -83,6 +80,18 @@ extern void startCameraServer(int hPort, int sPort); int streamPort = 81; #endif +#if defined(AP_SSID) + const char* ssidAP = AP_SSID; +#else + const char* ssidAP = "ESP32-CAM-CONNECT"; +#endif +#if defined(AP_PASS) + const char* passwordAP = AP_PASS; +#else + const char* passwordAP = "InsecurePassword"; +#endif + + #if !defined(WIFI_WATCHDOG) #define WIFI_WATCHDOG 5000 #endif @@ -139,9 +148,9 @@ const int pwmMax = pow(2,pwmresolution)-1; // Notification LED void flashLED(int flashtime) { #ifdef LED_PIN // If we have it; flash it. - digitalWrite(LED_PIN, LED_ON); // On at full power. + digitalWrite(LED_PIN, LED_OFF); // On at full power. delay(flashtime); // delay - digitalWrite(LED_PIN, LED_OFF); // turn Off + digitalWrite(LED_PIN, LED_ON); // turn Off #else return; // No notifcation LED, do nothing, no delay #endif @@ -175,21 +184,21 @@ void WifiSetup(){ WiFi.softAPConfig(local_IP, gateway, subnet); #endif #if defined(AP_CHAN) - WiFi.softAP(ssid, password, AP_CHAN); + WiFi.softAP(ssidAP, passwordAP, AP_CHAN); Serial.println("Setting up Fixed Channel AccessPoint"); Serial.print("SSID : "); - Serial.println(ssid); + Serial.println(ssidAP); Serial.print("Password : "); - Serial.println(password); + Serial.println(passwordAP); Serial.print("Channel : "); Serial.println(AP_CHAN); # else - WiFi.softAP(ssid, password); + WiFi.softAP(ssidAP, passwordAP); Serial.println("Setting up AccessPoint"); Serial.print("SSID : "); - Serial.println(ssid); + Serial.println(ssidAP); Serial.print("Password : "); - Serial.println(password); + Serial.println(passwordAP); #endif #else Serial.printf("Connecting to Wifi Network: %s ", ssid); @@ -226,6 +235,20 @@ void WifiSetup(){ // If we have connected, show details if (WiFi.status() == WL_CONNECTED) { Serial.println(" Succeeded"); + + // find our IP details + #if defined(WIFI_AP_ENABLE) + ip = WiFi.softAPIP(); + #else + ip = WiFi.localIP(); + #endif + net = WiFi.subnetMask(); + gw = WiFi.gatewayIP(); + + Serial.printf("IP address: %d.%d.%d.%d\n",ip[0],ip[1],ip[2],ip[3]); + Serial.printf("Netmask : %d.%d.%d.%d\n",net[0],net[1],net[2],net[3]); + Serial.printf("Gateway : %d.%d.%d.%d\n",gw[0],gw[1],gw[2],gw[3]); + // Burst flash the LED to show we are connected for (int i = 0; i < 5; i++) { flashLED(80); @@ -249,7 +272,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_OFF); + digitalWrite(LED_PIN, LED_ON); #endif // Create camera config structure; and populate with hardware and other defaults @@ -291,13 +314,18 @@ void setup() { #endif // camera init + sensor_t * s; esp_err_t err = esp_camera_init(&config); - if (err != ESP_OK) { - Serial.printf("Camera init failed with error 0x%x", err); - return; + if (err == ESP_OK) { + Serial.println("Camera init succeeded"); + s = esp_camera_sensor_get(); + } else { + Serial.printf("\nCamera init failed with error 0x%x\n", err); + // Code originally used to essentially halt at this point. + // But it is useful to still go on and complete network connecting etc. while debugging. + // Camera will give lots of errors on serial and no stream, of course. } - - sensor_t * s = esp_camera_sensor_get(); +/* // Dump camera module, warn for unsupported modules. switch (s->id.PID) { case OV9650_PID: Serial.println("WARNING: OV9650 camera module is not properly supported, will fallback to OV2640 operation"); break; @@ -335,11 +363,13 @@ void setup() { #else s->set_framesize(s, FRAMESIZE_SVGA); #endif - +*/ /* * Add any other defaults you want to apply at startup here: * uncomment the line and set the value as desired (see the comments) */ +/* //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 @@ -365,7 +395,7 @@ void setup() { // We now have camera with default init // check for saved preferences and apply them - +*/ if (filesystem) { filesystemStart(); loadPrefs(SPIFFS); @@ -400,16 +430,8 @@ void setup() { // Start the two http handlers for the HTTP UI and Stream. startCameraServer(httpPort, streamPort); - // find our IP address + // Construct the app and stream URLs char httpURL[64] = {"Unknown"}; - #if defined(WIFI_AP_ENABLE) - ip = WiFi.softAPIP(); - #else - ip = WiFi.localIP(); - #endif - net = WiFi.subnetMask(); - gw = WiFi.gatewayIP(); - // Construct the App URL if (httpPort != 80) { sprintf(httpURL, "http://%d.%d.%d.%d:%d/", ip[0], ip[1], ip[2], ip[3], httpPort); } else { diff --git a/myconfig.sample.h b/myconfig.sample.h index 471193a..4b288be 100644 --- a/myconfig.sample.h +++ b/myconfig.sample.h @@ -16,15 +16,18 @@ * Note the the use of commas as seperators in IP addresses! */ -// Credentials +// WiFi Credentials const char* ssid = "my-ssid"; const char* password = "my-password"; - // AccessPoint; uncomment to enable AP mode, -// otherwise we will attempt to connect to an existing network. +// otherwise we will attempt to connect to an existing network from the list above // #define WIFI_AP_ENABLE +// AccessPoint SSID and Password +// #define AP_SSID = "ESP32-CAM-CONNECT" +// #define AP_PASS = "InsecurePassword" + // AccessPoint; change the ip address (optional, default = 192.168.4.1) // #define AP_ADDRESS 192,168,4,1 @@ -32,11 +35,13 @@ const char* password = "my-password"; // #define AP_CHAN 1 // Static network settings for use when connected to existing network when DHCP is unavailable/unreliable -// You must define all three: IP, Gateway and NetMask +// Note: This applies to all client connections; you cannot set it per-ssid, so it's OK for multiple AP's +// in the same network; but otherwise only define one ssid in the list. +// You must define all three: IP, Gateway and NetMask // #define ST_IP 192,168,0,16 // #define ST_GATEWAY 192,168,0,2 // #define ST_NETMASK 255,255,255,0 -// one or two optional DNS servers can be supplied, but these are not used by current code. +// One or two optional DNS servers can be supplied, but the current firmware never uses them ;-) // #define ST_DNS1 192,168,0,2 // #define ST_DNS2 8,8,8,8 @@ -45,7 +50,6 @@ const char* password = "my-password"; // You may wish to increase this if your WiFi is slow at conencting, // #define WIFI_WATCHDOG = 5000 - /* * Port numbers for WebUI and Stream, defaults to 80 and 81. * Uncomment and edit as appropriate From f09a33c3c6cd221e3574a84c2664e10640dfb77e Mon Sep 17 00:00:00 2001 From: Owen Date: Sun, 4 Oct 2020 16:56:23 +0200 Subject: [PATCH 02/19] Not quite working, but oooh so close --- esp32-cam-webserver.ino | 37 +++++++++++++++++++++++++++++++++++-- myconfig.sample.h | 7 +++++-- 2 files changed, 40 insertions(+), 4 deletions(-) diff --git a/esp32-cam-webserver.ino b/esp32-cam-webserver.ino index bcfa796..52496d8 100644 --- a/esp32-cam-webserver.ino +++ b/esp32-cam-webserver.ino @@ -201,7 +201,40 @@ void WifiSetup(){ Serial.println(passwordAP); #endif #else - Serial.printf("Connecting to Wifi Network: %s ", ssid); + int stationCount = sizeof(stationList)/sizeof(stationList[0]); + Serial.printf("Scanning local Wifi Stations\n", stationCount); + int stationsFound = WiFi.scanNetworks(); + Serial.println("scan done"); + if (stationsFound == 0) { + Serial.println("no networks found"); + } else { + Serial.print(stationsFound); + Serial.println(" networks found"); + for (int i = 0; i < stationsFound; ++i) { + // Print SSID and RSSI for each network found + Serial.print(i + 1); + Serial.print(": "); + Serial.print(WiFi.SSID(i)); + Serial.print(" ("); + Serial.print(WiFi.RSSI(i)); + Serial.print(")"); + Serial.println((WiFi.encryptionType(i) == WIFI_AUTH_OPEN)?" ":"*"); + delay(10); + } + } + Serial.println("Station scan complete\n"); + long rssi; + for (int sta = 0; sta < stationCount; sta++) { + String thisSSID = WiFi.SSID(sta); + if (strcmp(stationList[sta].ssid, thisSSID.c_str())) { + rssi = WiFi.RSSI(sta); + } else { + rssi = 0; + } + Serial.printf("%i : %s : %i\n", sta, stationList[sta].ssid, rssi); + } + int bestStation = 1; + Serial.printf("Connecting to Wifi Network: %s\n", stationList[bestStation].ssid); #if defined(ST_IP) #if !defined (ST_GATEWAY) || !defined (ST_NETMASK) #error "You must supply both Gateway and NetMask when specifying a static IP address" @@ -223,7 +256,7 @@ void WifiSetup(){ #endif // Initiate network connection request - WiFi.begin(ssid, password); + WiFi.begin(stationList[bestStation].ssid, stationList[bestStation].password); // Wait to connect, or timeout unsigned long start = millis(); diff --git a/myconfig.sample.h b/myconfig.sample.h index 4b288be..dda4638 100644 --- a/myconfig.sample.h +++ b/myconfig.sample.h @@ -17,8 +17,11 @@ */ // WiFi Credentials -const char* ssid = "my-ssid"; -const char* password = "my-password"; +struct station +{ + const char ssid[64]; + const char password[64]; +} stationList[] = {{"my_ssid","my_password"}}; // AccessPoint; uncomment to enable AP mode, // otherwise we will attempt to connect to an existing network from the list above From 5df685825626d294495363ae761cc688330e8d64 Mon Sep 17 00:00:00 2001 From: Owen Date: Mon, 5 Oct 2020 01:24:57 +0200 Subject: [PATCH 03/19] Multi-Wifi scanning and failover --- app_httpd.cpp | 9 +- esp32-cam-webserver.ino | 754 ++++++++++++++++++++-------------------- myconfig.sample.h | 60 ++-- 3 files changed, 427 insertions(+), 396 deletions(-) diff --git a/app_httpd.cpp b/app_httpd.cpp index db0014d..aafdcca 100644 --- a/app_httpd.cpp +++ b/app_httpd.cpp @@ -180,7 +180,7 @@ static void draw_face_boxes(dl_matrix3du_t *image_matrix, box_array_t *boxes, in fb_gfx_drawFastHLine(&fb, x, y+h-1, w, color); fb_gfx_drawFastVLine(&fb, x, y, h, color); fb_gfx_drawFastVLine(&fb, x+w-1, y, h, color); -#if 0 + #if 0 // landmark int x0, y0, j; for (j = 0; j < 10; j+=2) { @@ -188,7 +188,7 @@ static void draw_face_boxes(dl_matrix3du_t *image_matrix, box_array_t *boxes, in y0 = (int)boxes->landmark[i].landmark_p[j+1]; fb_gfx_fillRect(&fb, x0, y0, 3, 3, color); } -#endif + #endif } } @@ -738,6 +738,11 @@ static esp_err_t dump_handler(httpd_req_t *req){ Serial.printf("Netmask: %d.%d.%d.%d\n", net[0], net[1], net[2], net[3]); d+= sprintf(d,"Gateway: %d.%d.%d.%d
\n", gw[0], gw[1], gw[2], gw[3]); Serial.printf("Gateway: %d.%d.%d.%d\n", gw[0], gw[1], gw[2], gw[3]); + 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]); + Serial.printf("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"); int64_t sec = esp_timer_get_time() / 1000000; diff --git a/esp32-cam-webserver.ino b/esp32-cam-webserver.ino index 52496d8..3aae87a 100644 --- a/esp32-cam-webserver.ino +++ b/esp32-cam-webserver.ino @@ -29,15 +29,15 @@ // Primary config, or defaults. #if __has_include("myconfig.h") - #include "myconfig.h" + #include "myconfig.h" #else - #warning "Using Default Settings: Copy myconfig.sample.h to myconfig.h and edit that to set your personal defaults" - // These are the defaults.. dont edit these. - // copy myconfig.sample.h to myconfig.h and edit that instead - // SSID, Password and Mode - #define WIFI_AP_ENABLE - // Default Board and Camera: - #define CAMERA_MODEL_AI_THINKER + #warning "Using Default Settings: Copy myconfig.sample.h to myconfig.h and edit that to set your personal defaults" + // These are the defaults.. dont edit these. + // copy myconfig.sample.h to myconfig.h and edit that instead + // SSID, Password and Mode + #define WIFI_AP_ENABLE + // Default Board and Camera: + #define CAMERA_MODEL_AI_THINKER #endif // Pin Mappings @@ -62,38 +62,26 @@ extern void startCameraServer(int hPort, int sPort); // A Name for the Camera. (set 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 // 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(AP_SSID) - const char* ssidAP = AP_SSID; -#else - const char* ssidAP = "ESP32-CAM-CONNECT"; -#endif -#if defined(AP_PASS) - const char* passwordAP = AP_PASS; -#else - const char* passwordAP = "InsecurePassword"; -#endif - - #if !defined(WIFI_WATCHDOG) - #define WIFI_WATCHDOG 5000 + #define WIFI_WATCHDOG 5000 #endif // The stream URL @@ -105,21 +93,21 @@ char myVer[] PROGMEM = __DATE__ " @ " __TIME__; // 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; // Illumination LAMP/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 int lampChannel = 7; // a free PWM channel (some channels used by camera) @@ -127,382 +115,400 @@ const int pwmfreq = 50000; // 50K pwm frequency const int pwmresolution = 9; // duty cycle bit range const int pwmMax = pow(2,pwmresolution)-1; -#if defined(HAS_FS) - bool filesystem = true; +#if defined(NO_FS) + bool filesystem = false; #else - bool filesystem = false; + bool filesystem = true; #endif #if defined(FACE_DETECTION) - int8_t detection_enabled = 1; - #if defined(FACE_RECOGNITION) - int8_t recognition_enabled = 1; - #else - int8_t recognition_enabled = 0; - #endif + int8_t detection_enabled = 1; + #if defined(FACE_RECOGNITION) + int8_t recognition_enabled = 1; + #else + int8_t recognition_enabled = 0; + #endif #else - int8_t detection_enabled = 0; - int8_t recognition_enabled = 0; + int8_t detection_enabled = 0; + int8_t recognition_enabled = 0; #endif // Notification LED void flashLED(int flashtime) { #ifdef LED_PIN // If we have it; flash it. - digitalWrite(LED_PIN, LED_OFF); // On at full power. - delay(flashtime); // delay - digitalWrite(LED_PIN, LED_ON); // turn Off + digitalWrite(LED_PIN, LED_OFF); // On at full power. + delay(flashtime); // delay + digitalWrite(LED_PIN, LED_ON); // 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) { - if (newVal != -1) { - // Apply a logarithmic function to the scale. - int brightness = round((pow(2,(1+(newVal*0.02)))-2)/6*pwmMax); - ledcWrite(lampChannel, brightness); - Serial.print("Lamp: "); - Serial.print(newVal); - Serial.print("%, pwm = "); - Serial.println(brightness); - } + if (newVal != -1) { + // Apply a logarithmic function to the scale. + int brightness = round((pow(2,(1+(newVal*0.02)))-2)/6*pwmMax); + ledcWrite(lampChannel, brightness); + Serial.print("Lamp: "); + Serial.print(newVal); + Serial.print("%, pwm = "); + Serial.println(brightness); + } } -void WifiSetup(){ - // Feedback that we are now attempting to connect - flashLED(300); - delay(100); - flashLED(300); - - #if defined(WIFI_AP_ENABLE) - #if defined(AP_ADDRESS) - // User has specified the AP details, pre-configure AP - IPAddress local_IP(AP_ADDRESS); - IPAddress gateway(AP_ADDRESS); - IPAddress subnet(255,255,255,0); - WiFi.softAPConfig(local_IP, gateway, subnet); +void WifiSetup() { + // Feedback that we are now attempting to connect + flashLED(300); + delay(100); + flashLED(300); + + #if defined(WIFI_AP_ENABLE) + #if defined(AP_ADDRESS) + // User has specified the AP details, pre-configure AP + IPAddress local_IP(AP_ADDRESS); + IPAddress gateway(AP_ADDRESS); + IPAddress subnet(255,255,255,0); + WiFi.softAPConfig(local_IP, gateway, subnet); + #endif + #if defined(AP_CHAN) + WiFi.softAP(stationList[0].ssid, stationList[0].password, AP_CHAN); + Serial.println("Setting up Fixed Channel AccessPoint"); + Serial.print("SSID : "); + Serial.println(ssidAP); + Serial.print("Password : "); + Serial.println(passwordAP); + Serial.print("Channel : "); + Serial.println(AP_CHAN); + # else + WiFi.softAP(stationList[0].ssid, stationList[0].password); + Serial.println("Setting up AccessPoint"); + Serial.print("SSID : "); + Serial.println(ssidAP); + Serial.print("Password : "); + Serial.println(passwordAP); + #endif + #else + int stationCount = sizeof(stationList)/sizeof(stationList[0]); + int bestStation = -1; + long bestRSSI = -1024; + Serial.printf("Scanning local Wifi Stations\n"); + int stationsFound = WiFi.scanNetworks(); + if (stationsFound > 0) { + Serial.printf("%i networks found\n", stationsFound); + for (int i = 0; i < stationsFound; ++i) { + // Print SSID and RSSI for each network found + String thisSSID = WiFi.SSID(i); + long thisRSSI = WiFi.RSSI(i); + Serial.printf("%3i : %s (%i)", i + 1, thisSSID.c_str(), thisRSSI); + // Scan our list of known stations. + for (int sta = 0; sta < stationCount; sta++) { + if (strcmp(stationList[sta].ssid, thisSSID.c_str()) == 0) { + Serial.print(" - Known!"); + // Chose the strongest RSSI seen + if (thisRSSI > bestRSSI) { + bestStation = sta; + bestRSSI = thisRSSI; + } + } + } + Serial.println(""); + } + } + if (WiFi.scanComplete() == WIFI_SCAN_FAILED) { + Serial.println("Scan Failed; no networks visible."); + return; + } + + if (bestStation == -1) { + Serial.println("No known networks found."); + return; + } + + Serial.printf("Connecting to Wifi Network: %s\n", stationList[bestStation].ssid); + 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 + } + + // Initiate network connection request + WiFi.begin(stationList[bestStation].ssid, stationList[bestStation].password); + + // Wait to connect, or timeout + unsigned long start = millis(); + while ((millis() - start <= WIFI_WATCHDOG) && (WiFi.status() != WL_CONNECTED)) { + delay(WIFI_WATCHDOG / 10); + Serial.print('.'); + } + + // If we have connected, show details + if (WiFi.status() == WL_CONNECTED) { + Serial.println(" Succeeded"); + + // find our IP details + #if defined(WIFI_AP_ENABLE) + ip = WiFi.softAPIP(); + #else + ip = WiFi.localIP(); + #endif + net = WiFi.subnetMask(); + gw = WiFi.gatewayIP(); + + Serial.printf("IP address: %d.%d.%d.%d\n",ip[0],ip[1],ip[2],ip[3]); + Serial.printf("Netmask : %d.%d.%d.%d\n",net[0],net[1],net[2],net[3]); + Serial.printf("Gateway : %d.%d.%d.%d\n",gw[0],gw[1],gw[2],gw[3]); + + // Burst flash the LED to show we are connected + for (int i = 0; i < 5; i++) { + flashLED(80); + delay(120); + } + } else { + Serial.println(" Failed"); + WiFi.disconnect(); // Nothing to disconnect; but resets the WiFi scan etc. + } #endif - #if defined(AP_CHAN) - WiFi.softAP(ssidAP, passwordAP, AP_CHAN); - Serial.println("Setting up Fixed Channel AccessPoint"); - Serial.print("SSID : "); - Serial.println(ssidAP); - Serial.print("Password : "); - Serial.println(passwordAP); - Serial.print("Channel : "); - Serial.println(AP_CHAN); - # else - WiFi.softAP(ssidAP, passwordAP); - Serial.println("Setting up AccessPoint"); - Serial.print("SSID : "); - Serial.println(ssidAP); - Serial.print("Password : "); - Serial.println(passwordAP); +} + +void setup() { + Serial.begin(115200); + Serial.setDebugOutput(true); + Serial.println(); + Serial.println("===="); + Serial.print("esp32-cam-webserver: "); + Serial.println(myName); + Serial.print("Code Built: "); + Serial.println(myVer); + + #if defined(LED_PIN) // If we have a notification LED, set it to output + pinMode(LED_PIN, OUTPUT); + digitalWrite(LED_PIN, LED_ON); #endif - #else - int stationCount = sizeof(stationList)/sizeof(stationList[0]); - Serial.printf("Scanning local Wifi Stations\n", stationCount); - int stationsFound = WiFi.scanNetworks(); - Serial.println("scan done"); - if (stationsFound == 0) { - Serial.println("no networks found"); + + // Create camera config structure; and populate with hardware and other defaults + camera_config_t config; + config.ledc_channel = LEDC_CHANNEL_0; + config.ledc_timer = LEDC_TIMER_0; + config.pin_d0 = Y2_GPIO_NUM; + config.pin_d1 = Y3_GPIO_NUM; + config.pin_d2 = Y4_GPIO_NUM; + config.pin_d3 = Y5_GPIO_NUM; + config.pin_d4 = Y6_GPIO_NUM; + config.pin_d5 = Y7_GPIO_NUM; + config.pin_d6 = Y8_GPIO_NUM; + config.pin_d7 = Y9_GPIO_NUM; + config.pin_xclk = XCLK_GPIO_NUM; + 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_pwdn = PWDN_GPIO_NUM; + config.pin_reset = RESET_GPIO_NUM; + config.xclk_freq_hz = 20000000; + config.pixel_format = PIXFORMAT_JPEG; + //init with highest supported specs to pre-allocate large buffers + if(psramFound()){ + config.frame_size = FRAMESIZE_UXGA; + config.jpeg_quality = 10; + config.fb_count = 2; } else { - Serial.print(stationsFound); - Serial.println(" networks found"); - for (int i = 0; i < stationsFound; ++i) { - // Print SSID and RSSI for each network found - Serial.print(i + 1); - Serial.print(": "); - Serial.print(WiFi.SSID(i)); - Serial.print(" ("); - Serial.print(WiFi.RSSI(i)); - Serial.print(")"); - Serial.println((WiFi.encryptionType(i) == WIFI_AUTH_OPEN)?" ":"*"); - delay(10); - } + config.frame_size = FRAMESIZE_SVGA; + config.jpeg_quality = 12; + config.fb_count = 1; } - Serial.println("Station scan complete\n"); - long rssi; - for (int sta = 0; sta < stationCount; sta++) { - String thisSSID = WiFi.SSID(sta); - if (strcmp(stationList[sta].ssid, thisSSID.c_str())) { - rssi = WiFi.RSSI(sta); - } else { - rssi = 0; - } - Serial.printf("%i : %s : %i\n", sta, stationList[sta].ssid, rssi); + + #if defined(CAMERA_MODEL_ESP_EYE) + pinMode(13, INPUT_PULLUP); + pinMode(14, INPUT_PULLUP); + #endif + + // camera init + sensor_t * s; + esp_err_t err = esp_camera_init(&config); + if (err == ESP_OK) { + Serial.println("Camera init succeeded"); + s = esp_camera_sensor_get(); + } else { + delay(100); // need a delay here or the next serial o/p gets missed + Serial.println("Halted: Camera sensor failed to initialise"); + Serial.println("Will reboot to try again in 10s\n"); + delay(10000); + ESP.restart(); } - int bestStation = 1; - Serial.printf("Connecting to Wifi Network: %s\n", stationList[bestStation].ssid); - #if defined(ST_IP) - #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 + + // Dump camera module, warn for unsupported modules. + switch (s->id.PID) { + 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 (s->id.PID == 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 - // Initiate network connection request - WiFi.begin(stationList[bestStation].ssid, stationList[bestStation].password); + // Config can override mirror and flip + #if defined(H_MIRROR) + s->seror(s, H_MIRROR); + #endif + #if defined(V_FLIP) + s->set_vflip(s, V_FLIP); + #endif - // Wait to connect, or timeout - unsigned long start = millis(); - while ((millis() - start <= WIFI_WATCHDOG) && (WiFi.status() != WL_CONNECTED)) { - delay(WIFI_WATCHDOG / 10); - Serial.print('.'); + // set initial frame rate + #if defined(DEFAULT_RESOLUTION) + s->set_framesize(s, DEFAULT_RESOLUTION); + #else + s->set_framesize(s, FRAMESIZE_SVGA); + #endif + + /* + * Add any other defaults you want to apply at startup here: + * uncomment the line and set the value as desired (see the comments) + */ + + //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); // 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 + // check for saved preferences and apply them + + if (filesystem) { + filesystemStart(); + loadPrefs(SPIFFS); + loadFaceDB(SPIFFS); + } else { + Serial.println("No Internal Filesystem, cannot save preferences or face DB"); } - // If we have connected, show details - if (WiFi.status() == WL_CONNECTED) { - Serial.println(" Succeeded"); - - // find our IP details - #if defined(WIFI_AP_ENABLE) - ip = WiFi.softAPIP(); - #else - ip = WiFi.localIP(); - #endif - net = WiFi.subnetMask(); - gw = WiFi.gatewayIP(); - - Serial.printf("IP address: %d.%d.%d.%d\n",ip[0],ip[1],ip[2],ip[3]); - Serial.printf("Netmask : %d.%d.%d.%d\n",net[0],net[1],net[2],net[3]); - Serial.printf("Gateway : %d.%d.%d.%d\n",gw[0],gw[1],gw[2],gw[3]); - - // Burst flash the LED to show we are connected - for (int i = 0; i < 5; i++) { - flashLED(80); - delay(120); - } + /* + * Camera setup complete; initialise the rest of the hardware. + */ + + // Initialise and set the lamp + if (lampVal != -1) { + ledcSetup(lampChannel, pwmfreq, pwmresolution); // configure LED PWM channel + setLamp(lampVal); // set default value + ledcAttachPin(LAMP_PIN, lampChannel); // attach the GPIO pin to the channel } else { - Serial.println(" Failed"); + Serial.println("No lamp, or lamp disabled in config"); } - #endif -} -void setup() { - Serial.begin(115200); - Serial.setDebugOutput(true); - Serial.println(); - Serial.println("===="); - Serial.print("esp32-cam-webserver: "); - Serial.println(myName); - Serial.print("Code Built: "); - Serial.println(myVer); - - #if defined(LED_PIN) // If we have a notification LED, set it to output - pinMode(LED_PIN, OUTPUT); - digitalWrite(LED_PIN, LED_ON); - #endif - - // Create camera config structure; and populate with hardware and other defaults - camera_config_t config; - config.ledc_channel = LEDC_CHANNEL_0; - config.ledc_timer = LEDC_TIMER_0; - config.pin_d0 = Y2_GPIO_NUM; - config.pin_d1 = Y3_GPIO_NUM; - config.pin_d2 = Y4_GPIO_NUM; - config.pin_d3 = Y5_GPIO_NUM; - config.pin_d4 = Y6_GPIO_NUM; - config.pin_d5 = Y7_GPIO_NUM; - config.pin_d6 = Y8_GPIO_NUM; - config.pin_d7 = Y9_GPIO_NUM; - config.pin_xclk = XCLK_GPIO_NUM; - 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_pwdn = PWDN_GPIO_NUM; - config.pin_reset = RESET_GPIO_NUM; - config.xclk_freq_hz = 20000000; - config.pixel_format = PIXFORMAT_JPEG; - //init with highest supported specs to pre-allocate large buffers - if(psramFound()){ - config.frame_size = FRAMESIZE_UXGA; - config.jpeg_quality = 10; - config.fb_count = 2; - } else { - config.frame_size = FRAMESIZE_SVGA; - config.jpeg_quality = 12; - config.fb_count = 1; - } - - #if defined(CAMERA_MODEL_ESP_EYE) - pinMode(13, INPUT_PULLUP); - pinMode(14, INPUT_PULLUP); - #endif - - // camera init - sensor_t * s; - esp_err_t err = esp_camera_init(&config); - if (err == ESP_OK) { - Serial.println("Camera init succeeded"); - s = esp_camera_sensor_get(); - } else { - Serial.printf("\nCamera init failed with error 0x%x\n", err); - // Code originally used to essentially halt at this point. - // But it is useful to still go on and complete network connecting etc. while debugging. - // Camera will give lots of errors on serial and no stream, of course. - } -/* - // Dump camera module, warn for unsupported modules. - switch (s->id.PID) { - 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; - // case OV5640_PID: Serial.println("WARNING: OV5640 camera module is not properly supported, will fallback to OV2640 operation"); 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 (s->id.PID == 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); - #else - s->set_framesize(s, FRAMESIZE_SVGA); - #endif -*/ - /* - * Add any other defaults you want to apply at startup here: - * uncomment the line and set the value as desired (see the comments) - */ -/* //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); // 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 - // check for saved preferences and apply them -*/ - if (filesystem) { - filesystemStart(); - loadPrefs(SPIFFS); - loadFaceDB(SPIFFS); - } else { - Serial.println("No Internal Filesystem, cannot save preferences or face DB"); - } - - /* - * Camera setup complete; initialise the rest of the hardware. - */ - - // Initialise and set the lamp - if (lampVal != -1) { - ledcSetup(lampChannel, pwmfreq, pwmresolution); // configure LED PWM channel - setLamp(lampVal); // set default value - ledcAttachPin(LAMP_PIN, lampChannel); // attach the GPIO pin to the channel - } else { - Serial.println("No lamp, or lamp disabled in config"); - } - - // We need a working Wifi before we can start the http handlers - Serial.println("Starting WiFi"); - #if defined(WIFI_AP_ENABLE) - WifiSetup(); - #else - while (WiFi.status() != WL_CONNECTED) { - WifiSetup(); + // We need a working Wifi before we can start the http handlers + Serial.println("Starting WiFi"); + byte mac[6]; + WiFi.macAddress(mac); + Serial.printf("MAC: %02X:%02X:%02X:%02X:%02X:%02X\n", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); + #if defined(WIFI_AP_ENABLE) + WifiSetup(); + #else + while (WiFi.status() != WL_CONNECTED) { + // Loop until we connect + WifiSetup(); + delay(1000); + } + #endif + + // Start the two http handlers for the HTTP UI and Stream. + startCameraServer(httpPort, streamPort); + + // Construct the app and stream URLs + char httpURL[64] = {"Unknown"}; + 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]); } - #endif - - // Start the two http handlers for the HTTP UI and Stream. - startCameraServer(httpPort, streamPort); - - // Construct the app and stream URLs - char httpURL[64] = {"Unknown"}; - if (httpPort != 80) { - sprintf(httpURL, "http://%d.%d.%d.%d:%d/", ip[0], ip[1], ip[2], ip[3], httpPort); - } else { - sprintf(httpURL, "http://%d.%d.%d.%d/", ip[0], ip[1], ip[2], ip[3]); - } - Serial.printf("\nCamera Ready!\nUse '%s' to connect\n", httpURL); - // Construct the Stream URL - sprintf(streamURL, "http://%d.%d.%d.%d:%d/", ip[0], ip[1], ip[2], ip[3], streamPort); - Serial.printf("Stream viewer available at '%s/view'\n", streamURL); - Serial.printf("Raw stream URL is '%s'\n", streamURL); - - // Used when dumpung status; slow functions, so do them here - sketchSize = ESP.getSketchSize(); - sketchSpace = ESP.getFreeSketchSpace(); - sketchMD5 = ESP.getSketchMD5(); + Serial.printf("\nCamera Ready!\nUse '%s' to connect\n", httpURL); + // Construct the Stream URL + sprintf(streamURL, "http://%d.%d.%d.%d:%d/", ip[0], ip[1], ip[2], ip[3], streamPort); + Serial.printf("Stream viewer available at '%sview'\n", streamURL); + Serial.printf("Raw stream URL is '%s'\n", streamURL); + + // Used when dumpung status; slow functions, so do them here + sketchSize = ESP.getSketchSize(); + sketchSpace = ESP.getFreeSketchSpace(); + sketchMD5 = ESP.getSketchMD5(); } void loop() { - // Just loop forever, reconnecting Wifi As necesscary. - // 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 defined(WIFI_AP_ENABLE) - delay(WIFI_WATCHDOG); - #else + // Just loop forever, reconnecting Wifi As necesscary. + // 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 defined(WIFI_AP_ENABLE) + delay(WIFI_WATCHDOG); + #else static bool warned = false; if (WiFi.status() == WL_CONNECTED) { - // We are connected, wait a bit and re-check - if (warned) { - Serial.println("WiFi reconnected"); - warned = false; - } - delay(WIFI_WATCHDOG); + // We are connected, wait a bit and re-check + if (warned) { + // Tell the user if we have just reconnected + Serial.println("WiFi reconnected"); + warned = false; + } + delay(WIFI_WATCHDOG); } else { - if (!warned) { - Serial.println("WiFi disconnected, retrying"); - warned = true; - } - WifiSetup(); + if (!warned) { + // Tell the user if we just disconnected + WiFi.disconnect(); // ensures disconnect is complete, wifi scan cleared + Serial.println("WiFi disconnected, retrying"); + warned = true; + } + WifiSetup(); } - #endif + #endif } diff --git a/myconfig.sample.h b/myconfig.sample.h index dda4638..c5a3feb 100644 --- a/myconfig.sample.h +++ b/myconfig.sample.h @@ -19,28 +19,31 @@ // WiFi Credentials struct station { - const char ssid[64]; - const char password[64]; -} stationList[] = {{"my_ssid","my_password"}}; + const char ssid[64]; // ssid (max 64 chars) + const char password[64]; // ssid (max 64 chars) + const bool dhcp; // Uses DHCP? +}; -// AccessPoint; uncomment to enable AP mode, -// otherwise we will attempt to connect to an existing network from the list above -// #define WIFI_AP_ENABLE +struct station stationList[] = {{"my_ssid","my_password", false}}; -// AccessPoint SSID and Password -// #define AP_SSID = "ESP32-CAM-CONNECT" -// #define AP_PASS = "InsecurePassword" +/* + * Add SSID/Password pairs to the list above like this: -// AccessPoint; change the ip address (optional, default = 192.168.4.1) -// #define AP_ADDRESS 192,168,4,1 +struct station stationList[] = {{"ssid1", "pass1", false}, + {"ssid2", "pass2", false}, + {"ssid3", "pass3", true }}; -// AccessPoint; Uncomment this to force the channel number, default = 1 -// #define AP_CHAN 1 + * The third column controls whether that station uses dhcp; or the optional static IP settings (below) + * Note the use of nested braces { and }, to group each entry, and commas to seperate them + */ -// Static network settings for use when connected to existing network when DHCP is unavailable/unreliable -// Note: This applies to all client connections; you cannot set it per-ssid, so it's OK for multiple AP's -// in the same network; but otherwise only define one ssid in the list. -// You must define all three: IP, Gateway and NetMask +/* + * Static network settings for client mode + * + * Note: These settings will be applied to all client connections where .staticIP is true + * You must define all three: IP, Gateway and NetMask + */ +// warning - IP addresses must be seperated with commas (,) and not decimals (.) // #define ST_IP 192,168,0,16 // #define ST_GATEWAY 192,168,0,2 // #define ST_NETMASK 255,255,255,0 @@ -48,16 +51,31 @@ struct station // #define ST_DNS1 192,168,0,2 // #define ST_DNS2 8,8,8,8 +/* + * AccessPoint; + * + * Uncomment to enable AP mode, disables connecting to existing WiFi networks + */ +// #define WIFI_AP_ENABLE + +// The SSID and Password for the AP are the first entry defined in the stationList[] above + +// AccessPoint; optionally change the ip address (default = 192.168.4.1) +// warning - IP addresses must be seperated with commas (,) and not decimals (.) +// #define AP_ADDRESS 192,168,4,1 + +// AccessPoint; Uncomment this to force the channel number, default = 1 +// #define AP_CHAN 1 + // 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 = 5000 +// #define WIFI_WATCHDOG 5000 /* * Port numbers for WebUI and Stream, defaults to 80 and 81. * Uncomment and edit as appropriate */ - // #define HTTP_PORT 80 // #define STREAM_PORT 81 @@ -68,7 +86,6 @@ struct station * 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 @@ -102,6 +119,9 @@ struct station // Define a initial lamp setting as a percentage, defaults to 0% // #define LAMP_DEFAULT 0 +// Assume we have SPIFFS/LittleFS partition, uncomment if not +// #define NO_FS + // Uncomment to enable Face Detection (+ Recognition if desired) by default // Notes: You must set DEFAULT_RESOLUTION, above, to FRAMESIZE_CIF or lower // Face recognition enrolements will be lost between reboots. From a903e0eee72844d05be5a5b7e049ff834502308b Mon Sep 17 00:00:00 2001 From: Owen Date: Mon, 5 Oct 2020 01:43:26 +0200 Subject: [PATCH 04/19] Correct configless build --- esp32-cam-webserver.ino | 13 +++++++++---- myconfig.sample.h | 4 ++-- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/esp32-cam-webserver.ino b/esp32-cam-webserver.ino index 3aae87a..a249c54 100644 --- a/esp32-cam-webserver.ino +++ b/esp32-cam-webserver.ino @@ -38,6 +38,11 @@ #define WIFI_AP_ENABLE // Default Board and Camera: #define CAMERA_MODEL_AI_THINKER + struct station { + const char ssid[64]; + const char password[64]; + const bool dhcp; + } stationList[] = {{"ESP32-CAM-CONNECT","InsecurePassword", false}}; #endif // Pin Mappings @@ -175,18 +180,18 @@ void WifiSetup() { WiFi.softAP(stationList[0].ssid, stationList[0].password, AP_CHAN); Serial.println("Setting up Fixed Channel AccessPoint"); Serial.print("SSID : "); - Serial.println(ssidAP); + Serial.println(stationList[0].ssid); Serial.print("Password : "); - Serial.println(passwordAP); + Serial.println(stationList[0].password); Serial.print("Channel : "); Serial.println(AP_CHAN); # else WiFi.softAP(stationList[0].ssid, stationList[0].password); Serial.println("Setting up AccessPoint"); Serial.print("SSID : "); - Serial.println(ssidAP); + Serial.println(stationList[0].ssid); Serial.print("Password : "); - Serial.println(passwordAP); + Serial.println(stationList[0].password); #endif #else int stationCount = sizeof(stationList)/sizeof(stationList[0]); diff --git a/myconfig.sample.h b/myconfig.sample.h index c5a3feb..36726a9 100644 --- a/myconfig.sample.h +++ b/myconfig.sample.h @@ -20,8 +20,8 @@ struct station { const char ssid[64]; // ssid (max 64 chars) - const char password[64]; // ssid (max 64 chars) - const bool dhcp; // Uses DHCP? + const char password[64]; // password (max 64 chars) + const bool dhcp; // dhcp }; struct station stationList[] = {{"my_ssid","my_password", false}}; From 209e95d15ce29101e6d9d8209a3c75f3201e81a7 Mon Sep 17 00:00:00 2001 From: Owen Date: Mon, 5 Oct 2020 02:02:17 +0200 Subject: [PATCH 05/19] Build warnings --- esp32-cam-webserver.ino | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/esp32-cam-webserver.ino b/esp32-cam-webserver.ino index a249c54..a056ec9 100644 --- a/esp32-cam-webserver.ino +++ b/esp32-cam-webserver.ino @@ -22,7 +22,7 @@ /* * FOR NETWORK AND HARDWARE SETTINGS COPY OR RENAME 'myconfig.sample.h' TO 'myconfig.h' AND EDIT THAT. * - * By default this sketch will assume an AI-THINKER ESP-CAM and create + * By default this sketch will assume an AI-THINKER ESP-CAM and create * an accesspoint called "ESP32-CAM-CONNECT" (password: "InsecurePassword") * */ @@ -38,11 +38,8 @@ #define WIFI_AP_ENABLE // Default Board and Camera: #define CAMERA_MODEL_AI_THINKER - struct station { - const char ssid[64]; - const char password[64]; - const bool dhcp; - } stationList[] = {{"ESP32-CAM-CONNECT","InsecurePassword", false}}; + struct station { const char ssid[64]; const char password[64]; const bool dhcp;} + stationList[] = {{"ESP32-CAM-CONNECT","InsecurePassword", false}}; #endif // Pin Mappings @@ -204,7 +201,7 @@ void WifiSetup() { for (int i = 0; i < stationsFound; ++i) { // Print SSID and RSSI for each network found String thisSSID = WiFi.SSID(i); - long thisRSSI = WiFi.RSSI(i); + int thisRSSI = WiFi.RSSI(i); Serial.printf("%3i : %s (%i)", i + 1, thisSSID.c_str(), thisRSSI); // Scan our list of known stations. for (int sta = 0; sta < stationCount; sta++) { @@ -349,11 +346,9 @@ void setup() { #endif // camera init - sensor_t * s; esp_err_t err = esp_camera_init(&config); if (err == ESP_OK) { Serial.println("Camera init succeeded"); - s = esp_camera_sensor_get(); } else { delay(100); // need a delay here or the next serial o/p gets missed Serial.println("Halted: Camera sensor failed to initialise"); @@ -361,6 +356,7 @@ void setup() { delay(10000); ESP.restart(); } + sensor_t * s = esp_camera_sensor_get(); // Dump camera module, warn for unsupported modules. switch (s->id.PID) { @@ -373,9 +369,9 @@ void setup() { // OV3660 initial sensors are flipped vertically and colors are a bit saturated if (s->id.PID == 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 + 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 From 4162fd42faad7b5bceea26144a4a3cc5b475f87f Mon Sep 17 00:00:00 2001 From: Owen Date: Mon, 5 Oct 2020 03:44:01 +0200 Subject: [PATCH 06/19] AP mode IP address discovery waswq broben --- esp32-cam-webserver.ino | 76 +++++++++++++++++++++-------------------- 1 file changed, 39 insertions(+), 37 deletions(-) diff --git a/esp32-cam-webserver.ino b/esp32-cam-webserver.ino index a056ec9..bc61f15 100644 --- a/esp32-cam-webserver.ino +++ b/esp32-cam-webserver.ino @@ -31,12 +31,9 @@ #if __has_include("myconfig.h") #include "myconfig.h" #else - #warning "Using Default Settings: Copy myconfig.sample.h to myconfig.h and edit that to set your personal defaults" - // These are the defaults.. dont edit these. - // copy myconfig.sample.h to myconfig.h and edit that instead - // SSID, Password and Mode + #warning "Using Default Settings: " + #warning "Copy myconfig.sample.h to myconfig.h and edit that to set your personal defaults" #define WIFI_AP_ENABLE - // Default Board and Camera: #define CAMERA_MODEL_AI_THINKER struct station { const char ssid[64]; const char password[64]; const bool dhcp;} stationList[] = {{"ESP32-CAM-CONNECT","InsecurePassword", false}}; @@ -190,6 +187,11 @@ void WifiSetup() { Serial.print("Password : "); Serial.println(stationList[0].password); #endif + + // find our IP details + ip = WiFi.softAPIP(); + net = WiFi.subnetMask(); + gw = WiFi.gatewayIP(); #else int stationCount = sizeof(stationList)/sizeof(stationList[0]); int bestStation = -1; @@ -221,12 +223,12 @@ void WifiSetup() { Serial.println("Scan Failed; no networks visible."); return; } - + if (bestStation == -1) { Serial.println("No known networks found."); return; } - + Serial.printf("Connecting to Wifi Network: %s\n", stationList[bestStation].ssid); if (stationList[bestStation].dhcp == false) { #if defined(ST_IP) @@ -252,44 +254,41 @@ void WifiSetup() { Serial.println("Static IP settings requested but not defined in config, falling back to dhcp"); #endif } - + // Initiate network connection request WiFi.begin(stationList[bestStation].ssid, stationList[bestStation].password); - + // Wait to connect, or timeout unsigned long start = millis(); while ((millis() - start <= WIFI_WATCHDOG) && (WiFi.status() != WL_CONNECTED)) { delay(WIFI_WATCHDOG / 10); Serial.print('.'); } - + // If we have connected, show details if (WiFi.status() == WL_CONNECTED) { - Serial.println(" Succeeded"); - + Serial.println(" Client connection succeeded"); + // find our IP details - #if defined(WIFI_AP_ENABLE) - ip = WiFi.softAPIP(); - #else - ip = WiFi.localIP(); - #endif + ip = WiFi.localIP(); net = WiFi.subnetMask(); gw = WiFi.gatewayIP(); - - Serial.printf("IP address: %d.%d.%d.%d\n",ip[0],ip[1],ip[2],ip[3]); - Serial.printf("Netmask : %d.%d.%d.%d\n",net[0],net[1],net[2],net[3]); - Serial.printf("Gateway : %d.%d.%d.%d\n",gw[0],gw[1],gw[2],gw[3]); - - // Burst flash the LED to show we are connected - for (int i = 0; i < 5; i++) { - flashLED(80); - delay(120); - } + } else { Serial.println(" Failed"); WiFi.disconnect(); // Nothing to disconnect; but resets the WiFi scan etc. + return; } - #endif + #endif + Serial.printf("IP address: %d.%d.%d.%d\n",ip[0],ip[1],ip[2],ip[3]); + Serial.printf("Netmask : %d.%d.%d.%d\n",net[0],net[1],net[2],net[3]); + Serial.printf("Gateway : %d.%d.%d.%d\n",gw[0],gw[1],gw[2],gw[3]); + + // Burst flash the LED to show we are connected + for (int i = 0; i < 5; i++) { + flashLED(80); + delay(120); + } } void setup() { @@ -301,12 +300,12 @@ void setup() { Serial.println(myName); Serial.print("Code Built: "); Serial.println(myVer); - + #if defined(LED_PIN) // If we have a notification LED, set it to output pinMode(LED_PIN, OUTPUT); digitalWrite(LED_PIN, LED_ON); #endif - + // Create camera config structure; and populate with hardware and other defaults camera_config_t config; config.ledc_channel = LEDC_CHANNEL_0; @@ -399,7 +398,7 @@ void setup() { * Add any other defaults you want to apply at startup here: * uncomment the line and set the value as desired (see the comments) */ - + //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 @@ -424,10 +423,10 @@ void setup() { //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 // check for saved preferences and apply them - + if (filesystem) { filesystemStart(); loadPrefs(SPIFFS); @@ -479,7 +478,7 @@ void setup() { sprintf(streamURL, "http://%d.%d.%d.%d:%d/", ip[0], ip[1], ip[2], ip[3], streamPort); Serial.printf("Stream viewer available at '%sview'\n", streamURL); Serial.printf("Raw stream URL is '%s'\n", streamURL); - + // Used when dumpung status; slow functions, so do them here sketchSize = ESP.getSketchSize(); sketchSpace = ESP.getFreeSketchSpace(); @@ -487,10 +486,13 @@ void setup() { } void loop() { - // Just loop forever, reconnecting Wifi As necesscary. - // 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. + /* + * 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 defined(WIFI_AP_ENABLE) + // Accespoint is permanently up, so just loop delay(WIFI_WATCHDOG); #else static bool warned = false; From df77520f16d6d0dc34ff588aa460e18135ad6a58 Mon Sep 17 00:00:00 2001 From: Owen Date: Mon, 5 Oct 2020 14:32:03 +0200 Subject: [PATCH 07/19] Fallback AP, fixes from testing, still needs more testing.. --- app_httpd.cpp | 16 +++- esp32-cam-webserver.ino | 158 +++++++++++++++++++++++++--------------- myconfig.sample.h | 51 +++++++++---- 3 files changed, 148 insertions(+), 77 deletions(-) diff --git a/app_httpd.cpp b/app_httpd.cpp index aafdcca..b6798d3 100644 --- a/app_httpd.cpp +++ b/app_httpd.cpp @@ -40,6 +40,7 @@ extern char streamURL[]; extern int8_t detection_enabled; extern int8_t recognition_enabled; extern bool filesystem; +extern bool accesspoint; extern int httpPort; extern int streamPort; extern IPAddress ip; @@ -725,19 +726,30 @@ static esp_err_t dump_handler(httpd_req_t *req){ Serial.printf("ESP sdk: %s\n", ESP.getSdkVersion()); // Network d+= sprintf(d,"

WiFi

\n"); + if (accesspoint) { + d+= sprintf(d,"Mode: AccessPoint
\n"); + Serial.printf("Mode: AccessPoint\n"); + } else { + d+= sprintf(d,"Mode: Client
\n"); + Serial.printf("Mode: Client\n"); + } String ssidName = WiFi.SSID(); d+= sprintf(d,"SSID: %s
\n", ssidName.c_str()); Serial.printf("Ssid: %s\n", ssidName.c_str()); d+= sprintf(d,"Rssi: %i
\n", WiFi.RSSI()); Serial.printf("Rssi: %i\n", WiFi.RSSI()); - d+= sprintf(d,"Http port: %i, Stream port: %i
\n", httpPort, streamPort); - Serial.printf("Http port: %i, Stream port: %i\n", httpPort, streamPort); + byte bs[6]; + WiFi.macAddress(bs); + d+= sprintf(d,"BSSID: %02X:%02X:%02X:%02X:%02X:%02X
\n", bs[0], bs[1], bs[2], bs[3], bs[4], bs[5]); + Serial.printf("BSSID: %02X:%02X:%02X:%02X:%02X:%02X\n", bs[0], bs[1], bs[2], bs[3], bs[4], bs[5]); d+= sprintf(d,"IP address: %d.%d.%d.%d
\n", ip[0], ip[1], ip[2], ip[3]); Serial.printf("IP address: %d.%d.%d.%d\n", ip[0], ip[1], ip[2], ip[3]); d+= sprintf(d,"Netmask: %d.%d.%d.%d
\n", net[0], net[1], net[2], net[3]); Serial.printf("Netmask: %d.%d.%d.%d\n", net[0], net[1], net[2], net[3]); d+= sprintf(d,"Gateway: %d.%d.%d.%d
\n", gw[0], gw[1], gw[2], gw[3]); Serial.printf("Gateway: %d.%d.%d.%d\n", gw[0], gw[1], gw[2], gw[3]); + d+= sprintf(d,"Http port: %i, Stream port: %i
\n", httpPort, streamPort); + Serial.printf("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]); diff --git a/esp32-cam-webserver.ino b/esp32-cam-webserver.ino index bc61f15..2930324 100644 --- a/esp32-cam-webserver.ino +++ b/esp32-cam-webserver.ino @@ -1,3 +1,4 @@ + #include "esp_camera.h" #include @@ -51,6 +52,10 @@ int sketchSize; int sketchSpace; String sketchMD5; +// Start with accesspoint mode disabled, we will enable it later in setup if +// no known networks are found, and WIFI_AP_ENABLE has been defined +bool accesspoint = false; + // IP address, Netmask and Gateway, populated when connected IPAddress ip; IPAddress net; @@ -83,6 +88,15 @@ extern void startCameraServer(int hPort, int sPort); #define WIFI_WATCHDOG 5000 #endif +// Number of stations in stationList[] +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; +#else + int firstStation = 0; +#endif + // The stream URL char streamURL[64] = {"Undefined"}; // Stream URL to pass to the app. @@ -135,9 +149,9 @@ const int pwmMax = pow(2,pwmresolution)-1; // Notification LED void flashLED(int flashtime) { #ifdef LED_PIN // If we have it; flash it. - digitalWrite(LED_PIN, LED_OFF); // On at full power. + digitalWrite(LED_PIN, LED_ON); // On at full power. delay(flashtime); // delay - digitalWrite(LED_PIN, LED_ON); // turn Off + digitalWrite(LED_PIN, LED_OFF); // turn Off #else return; // No notifcation LED, do nothing, no delay #endif @@ -162,14 +176,7 @@ void WifiSetup() { delay(100); flashLED(300); - #if defined(WIFI_AP_ENABLE) - #if defined(AP_ADDRESS) - // User has specified the AP details, pre-configure AP - IPAddress local_IP(AP_ADDRESS); - IPAddress gateway(AP_ADDRESS); - IPAddress subnet(255,255,255,0); - WiFi.softAPConfig(local_IP, gateway, subnet); - #endif + if (accesspoint) { #if defined(AP_CHAN) WiFi.softAP(stationList[0].ssid, stationList[0].password, AP_CHAN); Serial.println("Setting up Fixed Channel AccessPoint"); @@ -187,13 +194,21 @@ void WifiSetup() { Serial.print("Password : "); Serial.println(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 + // find our IP details ip = WiFi.softAPIP(); - net = WiFi.subnetMask(); - gw = WiFi.gatewayIP(); - #else - int stationCount = sizeof(stationList)/sizeof(stationList[0]); + net = WiFi.subnetMask(); // ? = 0,0,0,0 in AP mode ? + gw = WiFi.gatewayIP(); // ? = 0,0,0,0 in AP mode ? + } else { int bestStation = -1; long bestRSSI = -1024; Serial.printf("Scanning local Wifi Stations\n"); @@ -205,18 +220,19 @@ void WifiSetup() { String thisSSID = WiFi.SSID(i); int thisRSSI = WiFi.RSSI(i); Serial.printf("%3i : %s (%i)", i + 1, thisSSID.c_str(), thisRSSI); - // Scan our list of known stations. - for (int sta = 0; sta < stationCount; sta++) { - if (strcmp(stationList[sta].ssid, thisSSID.c_str()) == 0) { - Serial.print(" - Known!"); - // Chose the strongest RSSI seen - if (thisRSSI > bestRSSI) { - bestStation = sta; - bestRSSI = thisRSSI; + // Scan our list of known external stations, if any + if (stationCount > firstStation) { + for (int sta = firstStation; sta < stationCount; sta++) { + if (strcmp(stationList[sta].ssid, thisSSID.c_str()) == 0) { + Serial.print(" - Known!"); + // Chose the strongest RSSI seen + if (thisRSSI > bestRSSI) { + bestStation = sta; + bestRSSI = thisRSSI; + } } } } - Serial.println(""); } } if (WiFi.scanComplete() == WIFI_SCAN_FAILED) { @@ -226,6 +242,10 @@ void WifiSetup() { if (bestStation == -1) { Serial.println("No known networks found."); + // Failover to accesspoint mode if no known networks are visible and WIFI_AP_ENABLE is defined + #if defined(WIFI_AP_ENABLE) + accesspoint = true; + #endif return; } @@ -265,24 +285,28 @@ void WifiSetup() { Serial.print('.'); } - // If we have connected, show details + // We have connected, inform user if (WiFi.status() == WL_CONNECTED) { Serial.println(" Client connection succeeded"); - // find our IP details ip = WiFi.localIP(); net = WiFi.subnetMask(); gw = WiFi.gatewayIP(); - } else { Serial.println(" Failed"); WiFi.disconnect(); // Nothing to disconnect; but resets the WiFi scan etc. return; } - #endif + } + Serial.printf("IP address: %d.%d.%d.%d\n",ip[0],ip[1],ip[2],ip[3]); - Serial.printf("Netmask : %d.%d.%d.%d\n",net[0],net[1],net[2],net[3]); - Serial.printf("Gateway : %d.%d.%d.%d\n",gw[0],gw[1],gw[2],gw[3]); + if (!accesspoint) { + Serial.printf("Netmask : %d.%d.%d.%d\n",net[0],net[1],net[2],net[3]); + Serial.printf("Gateway : %d.%d.%d.%d\n",gw[0],gw[1],gw[2],gw[3]); + } else { + Serial.printf("?? Netmask : %d.%d.%d.%d\n",net[0],net[1],net[2],net[3]); + Serial.printf("?? Gateway : %d.%d.%d.%d\n",gw[0],gw[1],gw[2],gw[3]); + } // Burst flash the LED to show we are connected for (int i = 0; i < 5; i++) { @@ -301,6 +325,12 @@ void setup() { Serial.print("Code Built: "); Serial.println(myVer); + if (stationCount == 0) { + Serial.println("\nFatal Error; Halting"); + Serial.println("No wifi ssid details have been configured; we cannot connect to or start WiFi"); + while (true) delay(1000); + } + #if defined(LED_PIN) // If we have a notification LED, set it to output pinMode(LED_PIN, OUTPUT); digitalWrite(LED_PIN, LED_ON); @@ -450,20 +480,26 @@ void setup() { // We need a working Wifi before we can start the http handlers Serial.println("Starting WiFi"); + Serial.println("Known external SSIDs"); + if (firstStation < stationCount) { + for (int i=firstStation; i < stationCount; i++) Serial.println(stationList[i].ssid); + } else { + Serial.println("None"); + #if defined(AP_MODE_ENABLE) + accesspoint = true; // we can safely assume a dedicate accesspoint at this point + #endif + } byte mac[6]; WiFi.macAddress(mac); - Serial.printf("MAC: %02X:%02X:%02X:%02X:%02X:%02X\n", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); - #if defined(WIFI_AP_ENABLE) + Serial.printf("MAC address: %02X:%02X:%02X:%02X:%02X:%02X\n", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); + + // Having got this far; start Wifi and loop until we succeed. + while (WiFi.status() != WL_CONNECTED) { WifiSetup(); - #else - while (WiFi.status() != WL_CONNECTED) { - // Loop until we connect - WifiSetup(); - delay(1000); - } - #endif + delay(WIFI_WATCHDOG); + } - // Start the two http handlers for the HTTP UI and Stream. + // Now we have a network we can start the two http handlers for the UI and Stream. startCameraServer(httpPort, streamPort); // Construct the app and stream URLs @@ -479,7 +515,7 @@ void setup() { Serial.printf("Stream viewer available at '%sview'\n", streamURL); Serial.printf("Raw stream URL is '%s'\n", streamURL); - // Used when dumpung status; slow functions, so do them here + // Used when dumpung status; slow functions, so do them here during startup sketchSize = ESP.getSketchSize(); sketchSpace = ESP.getFreeSketchSpace(); sketchMD5 = ESP.getSketchMD5(); @@ -491,27 +527,29 @@ void loop() { * 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 defined(WIFI_AP_ENABLE) - // Accespoint is permanently up, so just loop - delay(WIFI_WATCHDOG); - #else - static bool warned = false; - if (WiFi.status() == WL_CONNECTED) { - // We are connected, wait a bit and re-check - if (warned) { - // Tell the user if we have just reconnected - Serial.println("WiFi reconnected"); - warned = false; - } + if (accesspoint) { + // Accespoint is permanently up, so just loop delay(WIFI_WATCHDOG); } else { - if (!warned) { - // Tell the user if we just disconnected - WiFi.disconnect(); // ensures disconnect is complete, wifi scan cleared - Serial.println("WiFi disconnected, retrying"); - warned = true; + // client mode can fail; so reconnect as appropriate + static bool warned = false; + if (WiFi.status() == WL_CONNECTED) { + // We are connected, wait a bit and re-check + if (warned) { + // Tell the user if we have just reconnected + Serial.println("WiFi reconnected"); + warned = false; + } + delay(WIFI_WATCHDOG); + } else { + // disconnected; attempt to reconnect + if (!warned) { + // Tell the user if we just disconnected + WiFi.disconnect(); // ensures disconnect is complete, wifi scan cleared + Serial.println("WiFi disconnected, retrying"); + warned = true; + } + WifiSetup(); } - WifiSetup(); } - #endif } diff --git a/myconfig.sample.h b/myconfig.sample.h index 36726a9..c631acc 100644 --- a/myconfig.sample.h +++ b/myconfig.sample.h @@ -27,14 +27,13 @@ struct station struct station stationList[] = {{"my_ssid","my_password", false}}; /* - * Add SSID/Password pairs to the list above like this: + * Extend the list above with additional SSID+Password pairs like this: + * The third (dhcp) column controls whether that station uses dhcp; or the optional static IP settings (below) + * Note the use of nested braces { and }, to group each entry, and commas to seperate them. -struct station stationList[] = {{"ssid1", "pass1", false}, - {"ssid2", "pass2", false}, - {"ssid3", "pass3", true }}; - - * The third column controls whether that station uses dhcp; or the optional static IP settings (below) - * Note the use of nested braces { and }, to group each entry, and commas to seperate them +struct station stationList[] = {{"ssid1", "pass1", true}, + {"ssid2", "pass2", true}, + {"ssid3", "pass3", false}}; */ /* @@ -54,12 +53,20 @@ struct station stationList[] = {{"ssid1", "pass1", false}, /* * AccessPoint; * - * Uncomment to enable AP mode, disables connecting to existing WiFi networks + * Uncomment to enable AP mode; + * The AP ssid and password will be taken from the 1st entry in the stationList[] above. + * + * This is a 'fallback' mode. The remaining stationList[] is first scanned; if a matching network + * is found we will loop attempting to connect to that. + * If no matching networks are found during the initial scan the AccessPoint mode will + * be permanently enabled and started; no further scan+connection attempts will be made. + * If you only list one entry in the stationList[] you will effectively create a dedicated + * AccessPoint setup. + * + * ENHANCEMENT: use dhcp field to create a mdns captive portal? currently it is unused. */ // #define WIFI_AP_ENABLE -// The SSID and Password for the AP are the first entry defined in the stationList[] above - // AccessPoint; optionally change the ip address (default = 192.168.4.1) // warning - IP addresses must be seperated with commas (,) and not decimals (.) // #define AP_ADDRESS 192,168,4,1 @@ -67,11 +74,6 @@ struct station stationList[] = {{"ssid1", "pass1", false}, // AccessPoint; Uncomment this to force the channel number, default = 1 // #define AP_CHAN 1 -// 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 5000 - /* * Port numbers for WebUI and Stream, defaults to 80 and 81. * Uncomment and edit as appropriate @@ -79,6 +81,25 @@ struct station stationList[] = {{"ssid1", "pass1", false}, // #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 5000 + +/* + * Http and Stream prefix; useful for embedding and casual snooping + * Prefix will be added to the beginning of the UIR path; + * eg http:/192.168.1.4/ becomes http:/192.168.1.4// etc. + * You must prefix it with '/'. + * + * Handy against casual 'evesdropping', but (important!) this is + * not an effective privacy measure until HTTPS is salso supported! + */ +#define HTTP_PREFIX "/the/http/interface" +#define STREAM_PREFIX "/the-stream-itself" + /* * Camera Hardware Settings * From 0f4eb48415d5dbf209b41a7dfa1a31824aa4c05d Mon Sep 17 00:00:00 2001 From: Owen Date: Mon, 5 Oct 2020 17:59:48 +0200 Subject: [PATCH 08/19] Captive portal, and more testing fixes --- app_httpd.cpp | 33 ++++--- esp32-cam-webserver.ino | 197 +++++++++++++++++++++------------------- 2 files changed, 125 insertions(+), 105 deletions(-) diff --git a/app_httpd.cpp b/app_httpd.cpp index b6798d3..4ed6956 100644 --- a/app_httpd.cpp +++ b/app_httpd.cpp @@ -41,6 +41,8 @@ extern int8_t detection_enabled; extern int8_t recognition_enabled; extern bool filesystem; extern bool accesspoint; +extern bool captivePortal; +extern char apName[]; extern int httpPort; extern int streamPort; extern IPAddress ip; @@ -727,27 +729,36 @@ static esp_err_t dump_handler(httpd_req_t *req){ // Network d+= sprintf(d,"

WiFi

\n"); if (accesspoint) { - d+= sprintf(d,"Mode: AccessPoint
\n"); - Serial.printf("Mode: AccessPoint\n"); + if (captivePortal) { + d+= sprintf(d,"Mode: AccessPoint with captive portal
\n"); + Serial.printf("Mode: AccessPoint with captive portal\n"); + } else { + d+= sprintf(d,"Mode: AccessPoint
\n"); + Serial.printf("Mode: AccessPoint\n"); + } + d+= sprintf(d,"SSID: %s
\n", apName); + Serial.printf("SSID: %s\n", apName); } else { d+= sprintf(d,"Mode: Client
\n"); Serial.printf("Mode: Client\n"); + String ssidName = WiFi.SSID(); + d+= sprintf(d,"SSID: %s
\n", ssidName.c_str()); + Serial.printf("Ssid: %s\n", ssidName.c_str()); + d+= sprintf(d,"Rssi: %i
\n", WiFi.RSSI()); + Serial.printf("Rssi: %i\n", WiFi.RSSI()); } - String ssidName = WiFi.SSID(); - d+= sprintf(d,"SSID: %s
\n", ssidName.c_str()); - Serial.printf("Ssid: %s\n", ssidName.c_str()); - d+= sprintf(d,"Rssi: %i
\n", WiFi.RSSI()); - Serial.printf("Rssi: %i\n", WiFi.RSSI()); byte bs[6]; WiFi.macAddress(bs); d+= sprintf(d,"BSSID: %02X:%02X:%02X:%02X:%02X:%02X
\n", bs[0], bs[1], bs[2], bs[3], bs[4], bs[5]); Serial.printf("BSSID: %02X:%02X:%02X:%02X:%02X:%02X\n", bs[0], bs[1], bs[2], bs[3], bs[4], bs[5]); d+= sprintf(d,"IP address: %d.%d.%d.%d
\n", ip[0], ip[1], ip[2], ip[3]); Serial.printf("IP address: %d.%d.%d.%d\n", ip[0], ip[1], ip[2], ip[3]); - d+= sprintf(d,"Netmask: %d.%d.%d.%d
\n", net[0], net[1], net[2], net[3]); - Serial.printf("Netmask: %d.%d.%d.%d\n", net[0], net[1], net[2], net[3]); - d+= sprintf(d,"Gateway: %d.%d.%d.%d
\n", gw[0], gw[1], gw[2], gw[3]); - Serial.printf("Gateway: %d.%d.%d.%d\n", gw[0], gw[1], gw[2], gw[3]); + if (!accesspoint) { + d+= sprintf(d,"Netmask: %d.%d.%d.%d
\n", net[0], net[1], net[2], net[3]); + Serial.printf("Netmask: %d.%d.%d.%d\n", net[0], net[1], net[2], net[3]); + d+= sprintf(d,"Gateway: %d.%d.%d.%d
\n", gw[0], gw[1], gw[2], gw[3]); + Serial.printf("Gateway: %d.%d.%d.%d\n", gw[0], gw[1], gw[2], gw[3]); + } d+= sprintf(d,"Http port: %i, Stream port: %i
\n", httpPort, streamPort); Serial.printf("Http port: %i, Stream port: %i\n", httpPort, streamPort); byte mac[6]; diff --git a/esp32-cam-webserver.ino b/esp32-cam-webserver.ino index 2930324..ad09c82 100644 --- a/esp32-cam-webserver.ino +++ b/esp32-cam-webserver.ino @@ -1,6 +1,7 @@ #include "esp_camera.h" #include +#include /* This sketch is a extension/expansion/reork of the 'official' ESP32 Camera example * sketch from Expressif: @@ -29,15 +30,14 @@ */ // Primary config, or defaults. -#if __has_include("myconfig.h") +#if __has_include("mydconfig.h") #include "myconfig.h" #else - #warning "Using Default Settings: " - #warning "Copy myconfig.sample.h to myconfig.h and edit that to set your personal defaults" + #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[64]; const char password[64]; const bool dhcp;} - stationList[] = {{"ESP32-CAM-CONNECT","InsecurePassword", false}}; + stationList[] = {{"ESP32-CAM-CONNECT","InsecurePassword", true}}; #endif // Pin Mappings @@ -52,7 +52,7 @@ int sketchSize; int sketchSpace; String sketchMD5; -// Start with accesspoint mode disabled, we will enable it later in setup if +// Start with accesspoint mode disabled, wifi setup will activate it if // no known networks are found, and WIFI_AP_ENABLE has been defined bool accesspoint = false; @@ -88,8 +88,9 @@ extern void startCameraServer(int hPort, int sPort); #define WIFI_WATCHDOG 5000 #endif -// Number of stations in stationList[] +// Number of known networks in stationList[] 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; @@ -97,6 +98,12 @@ int stationCount = sizeof(stationList)/sizeof(stationList[0]); int firstStation = 0; #endif +// DNS server +const byte DNS_PORT = 53; +DNSServer dnsServer; +bool captivePortal = false; +char apName[64] = "Undefined"; + // The stream URL char streamURL[64] = {"Undefined"}; // Stream URL to pass to the app. @@ -176,79 +183,40 @@ void WifiSetup() { delay(100); flashLED(300); - if (accesspoint) { - #if defined(AP_CHAN) - WiFi.softAP(stationList[0].ssid, stationList[0].password, 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); - # else - WiFi.softAP(stationList[0].ssid, stationList[0].password); - Serial.println("Setting up AccessPoint"); - Serial.print("SSID : "); - Serial.println(stationList[0].ssid); - Serial.print("Password : "); - Serial.println(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 - - // find our IP details - ip = WiFi.softAPIP(); - net = WiFi.subnetMask(); // ? = 0,0,0,0 in AP mode ? - gw = WiFi.gatewayIP(); // ? = 0,0,0,0 in AP mode ? - } else { - int bestStation = -1; - long bestRSSI = -1024; - Serial.printf("Scanning local Wifi Stations\n"); - int stationsFound = WiFi.scanNetworks(); - if (stationsFound > 0) { - Serial.printf("%i networks found\n", stationsFound); - 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); - Serial.printf("%3i : %s (%i)", i + 1, thisSSID.c_str(), thisRSSI); - // Scan our list of known external stations, if any - if (stationCount > firstStation) { - for (int sta = firstStation; sta < stationCount; sta++) { - if (strcmp(stationList[sta].ssid, thisSSID.c_str()) == 0) { - Serial.print(" - Known!"); - // Chose the strongest RSSI seen - if (thisRSSI > bestRSSI) { - bestStation = sta; - bestRSSI = thisRSSI; - } + int bestStation = -1; + long bestRSSI = -1024; + Serial.printf("Scanning local Wifi Networks\n"); + int stationsFound = WiFi.scanNetworks(); + Serial.printf("%i networks found\n", stationsFound); + 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); + Serial.printf("%3i : %s (%i)", i + 1, thisSSID.c_str(), thisRSSI); + // Scan our list of known external stations, if any + if (stationCount > firstStation) { + for (int sta = firstStation; sta < stationCount; sta++) { + if (strcmp(stationList[sta].ssid, thisSSID.c_str()) == 0) { + Serial.print(" - Known!"); + // Chose the strongest RSSI seen + if (thisRSSI > bestRSSI) { + bestStation = sta; + bestRSSI = thisRSSI; } } } } + Serial.println(); } - if (WiFi.scanComplete() == WIFI_SCAN_FAILED) { - Serial.println("Scan Failed; no networks visible."); - return; - } - - if (bestStation == -1) { - Serial.println("No known networks found."); - // Failover to accesspoint mode if no known networks are visible and WIFI_AP_ENABLE is defined - #if defined(WIFI_AP_ENABLE) - accesspoint = true; - #endif - return; - } - + } + if (bestStation == -1) { + Serial.println("\nNo known networks found"); + #if defined(WIFI_AP_ENABLE) + // Failover to accesspoint mode if no known networks are visible + accesspoint = true; + #endif + } else { Serial.printf("Connecting to Wifi Network: %s\n", stationList[bestStation].ssid); if (stationList[bestStation].dhcp == false) { #if defined(ST_IP) @@ -274,27 +242,70 @@ void WifiSetup() { Serial.println("Static IP settings requested but not defined in config, falling back to dhcp"); #endif } - + // Initiate network connection request WiFi.begin(stationList[bestStation].ssid, stationList[bestStation].password); - + // Wait to connect, or timeout unsigned long start = millis(); while ((millis() - start <= WIFI_WATCHDOG) && (WiFi.status() != WL_CONNECTED)) { - delay(WIFI_WATCHDOG / 10); + delay(500); Serial.print('.'); } + } + + if (accesspoint) { + // The accesspoint has been enabled + #if defined(AP_CHAN) + WiFi.softAP(stationList[0].ssid, stationList[0].password, 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); + # else + WiFi.softAP(stationList[0].ssid, stationList[0].password); + Serial.println("Setting up AccessPoint"); + Serial.print("SSID : "); + Serial.println(stationList[0].ssid); + Serial.print("Password : "); + Serial.println(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 - // We have connected, inform user + // Note AP details + ip = WiFi.softAPIP(); + net = WiFi.subnetMask(); + gw = WiFi.gatewayIP(); + strcpy(apName, stationList[0].ssid); + + // Start the DNS captive portal if requested + if (stationList[0].dhcp == true) { + Serial.println("Starting Captive Portal"); + dnsServer.start(DNS_PORT, "*", ip); + captivePortal = true; + } + } else { + // If we have connected, inform user if (WiFi.status() == WL_CONNECTED) { Serial.println(" Client connection succeeded"); - // find our IP details + // Note IP details ip = WiFi.localIP(); net = WiFi.subnetMask(); gw = WiFi.gatewayIP(); } else { Serial.println(" Failed"); - WiFi.disconnect(); // Nothing to disconnect; but resets the WiFi scan etc. + WiFi.disconnect(); // (resets the WiFi scan) return; } } @@ -303,11 +314,7 @@ void WifiSetup() { if (!accesspoint) { Serial.printf("Netmask : %d.%d.%d.%d\n",net[0],net[1],net[2],net[3]); Serial.printf("Gateway : %d.%d.%d.%d\n",gw[0],gw[1],gw[2],gw[3]); - } else { - Serial.printf("?? Netmask : %d.%d.%d.%d\n",net[0],net[1],net[2],net[3]); - Serial.printf("?? Gateway : %d.%d.%d.%d\n",gw[0],gw[1],gw[2],gw[3]); } - // Burst flash the LED to show we are connected for (int i = 0; i < 5; i++) { flashLED(80); @@ -480,24 +487,22 @@ void setup() { // We need a working Wifi before we can start the http handlers Serial.println("Starting WiFi"); - Serial.println("Known external SSIDs"); + Serial.print("Known external SSIDs: "); if (firstStation < stationCount) { - for (int i=firstStation; i < stationCount; i++) Serial.println(stationList[i].ssid); + for (int i=firstStation; i < stationCount; i++) Serial.printf(" %s", stationList[i].ssid); } else { - Serial.println("None"); - #if defined(AP_MODE_ENABLE) - accesspoint = true; // we can safely assume a dedicate accesspoint at this point - #endif + Serial.print("None"); } + Serial.println(); byte mac[6]; WiFi.macAddress(mac); Serial.printf("MAC address: %02X:%02X:%02X:%02X:%02X:%02X\n", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); // Having got this far; start Wifi and loop until we succeed. - while (WiFi.status() != WL_CONNECTED) { + while ((WiFi.status() != WL_CONNECTED) && !accesspoint) { WifiSetup(); delay(WIFI_WATCHDOG); - } + } // Now we have a network we can start the two http handlers for the UI and Stream. startCameraServer(httpPort, streamPort); @@ -528,8 +533,12 @@ void loop() { * end of setup() will handle the camera and UI processing from now on. */ if (accesspoint) { - // Accespoint is permanently up, so just loop - delay(WIFI_WATCHDOG); + // Accespoint is permanently up, so just loop, servicing the captive portal as needed + unsigned long start = millis(); + while (millis() - start < WIFI_WATCHDOG ) { + delay(100); + if (captivePortal) dnsServer.processNextRequest(); + } } else { // client mode can fail; so reconnect as appropriate static bool warned = false; From 3866111461ffe299f7fd0d667e1c0b8caa63c9a5 Mon Sep 17 00:00:00 2001 From: Owen Date: Tue, 6 Oct 2020 09:10:53 +0200 Subject: [PATCH 09/19] Dump output tweaks --- app_httpd.cpp | 7 +++---- esp32-cam-webserver.ino | 17 ++++++++--------- 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/app_httpd.cpp b/app_httpd.cpp index 4ed6956..3fefb24 100644 --- a/app_httpd.cpp +++ b/app_httpd.cpp @@ -747,10 +747,9 @@ static esp_err_t dump_handler(httpd_req_t *req){ d+= sprintf(d,"Rssi: %i
\n", WiFi.RSSI()); Serial.printf("Rssi: %i\n", WiFi.RSSI()); } - byte bs[6]; - WiFi.macAddress(bs); - d+= sprintf(d,"BSSID: %02X:%02X:%02X:%02X:%02X:%02X
\n", bs[0], bs[1], bs[2], bs[3], bs[4], bs[5]); - Serial.printf("BSSID: %02X:%02X:%02X:%02X:%02X:%02X\n", bs[0], bs[1], bs[2], bs[3], bs[4], bs[5]); + String bssid = WiFi.BSSIDstr(); + d+= sprintf(d,"BSSID: %s
\n", bssid.c_str()); + Serial.printf("BSSID: %s\n", bssid.c_str()); d+= sprintf(d,"IP address: %d.%d.%d.%d
\n", ip[0], ip[1], ip[2], ip[3]); Serial.printf("IP address: %d.%d.%d.%d\n", ip[0], ip[1], ip[2], ip[3]); if (!accesspoint) { diff --git a/esp32-cam-webserver.ino b/esp32-cam-webserver.ino index ad09c82..8ad4b6e 100644 --- a/esp32-cam-webserver.ino +++ b/esp32-cam-webserver.ino @@ -1,4 +1,3 @@ - #include "esp_camera.h" #include #include @@ -30,7 +29,7 @@ */ // Primary config, or defaults. -#if __has_include("mydconfig.h") +#if __has_include("myconfig.h") #include "myconfig.h" #else #warning "Using Defaults: Copy myconfig.sample.h to myconfig.h and edit that to use your own settings" @@ -259,18 +258,18 @@ void WifiSetup() { #if defined(AP_CHAN) WiFi.softAP(stationList[0].ssid, stationList[0].password, AP_CHAN); Serial.println("Setting up Fixed Channel AccessPoint"); - Serial.print("SSID : "); + Serial.print(" SSID : "); Serial.println(stationList[0].ssid); - Serial.print("Password : "); + Serial.print(" Password : "); Serial.println(stationList[0].password); - Serial.print("Channel : "); + Serial.print(" Channel : "); Serial.println(AP_CHAN); # else WiFi.softAP(stationList[0].ssid, stationList[0].password); Serial.println("Setting up AccessPoint"); - Serial.print("SSID : "); + Serial.print(" SSID : "); Serial.println(stationList[0].ssid); - Serial.print("Password : "); + Serial.print(" Password : "); Serial.println(stationList[0].password); #endif #if defined(AP_ADDRESS) @@ -334,7 +333,7 @@ void setup() { if (stationCount == 0) { Serial.println("\nFatal Error; Halting"); - Serial.println("No wifi ssid details have been configured; we cannot connect to or start WiFi"); + Serial.println("No wifi ssid details have been configured; we cannot connect to WiFi or start our own AccessPoint"); while (true) delay(1000); } @@ -489,7 +488,7 @@ void setup() { Serial.println("Starting WiFi"); Serial.print("Known external SSIDs: "); if (firstStation < stationCount) { - for (int i=firstStation; i < stationCount; i++) Serial.printf(" %s", stationList[i].ssid); + for (int i=firstStation; i < stationCount; i++) Serial.printf(" '%s'", stationList[i].ssid); } else { Serial.print("None"); } From c920b3c2567362143840f1bcb37991fcb9319e09 Mon Sep 17 00:00:00 2001 From: Owen Date: Tue, 6 Oct 2020 09:25:44 +0200 Subject: [PATCH 10/19] bugs --- esp32-cam-webserver.ino | 2 +- myconfig.sample.h | 12 ------------ 2 files changed, 1 insertion(+), 13 deletions(-) diff --git a/esp32-cam-webserver.ino b/esp32-cam-webserver.ino index 8ad4b6e..4aefb3f 100644 --- a/esp32-cam-webserver.ino +++ b/esp32-cam-webserver.ino @@ -417,7 +417,7 @@ void setup() { // Config can override mirror and flip #if defined(H_MIRROR) - s->seror(s, H_MIRROR); + s->set_hmirror(s, H_MIRROR); #endif #if defined(V_FLIP) s->set_vflip(s, V_FLIP); diff --git a/myconfig.sample.h b/myconfig.sample.h index c631acc..a03a91f 100644 --- a/myconfig.sample.h +++ b/myconfig.sample.h @@ -88,18 +88,6 @@ struct station stationList[] = {{"ssid1", "pass1", true}, */ //#define WIFI_WATCHDOG 5000 -/* - * Http and Stream prefix; useful for embedding and casual snooping - * Prefix will be added to the beginning of the UIR path; - * eg http:/192.168.1.4/ becomes http:/192.168.1.4// etc. - * You must prefix it with '/'. - * - * Handy against casual 'evesdropping', but (important!) this is - * not an effective privacy measure until HTTPS is salso supported! - */ -#define HTTP_PREFIX "/the/http/interface" -#define STREAM_PREFIX "/the-stream-itself" - /* * Camera Hardware Settings * From b8228b34380edf7c0cf39eb5612e297a2fc3c347 Mon Sep 17 00:00:00 2001 From: Owen Date: Tue, 6 Oct 2020 13:22:07 +0200 Subject: [PATCH 11/19] Logic fixes and comments --- app_httpd.cpp | 8 ++--- esp32-cam-webserver.ino | 66 ++++++++++++++++++++--------------------- myconfig.sample.h | 39 ++++++++++++++---------- 3 files changed, 60 insertions(+), 53 deletions(-) diff --git a/app_httpd.cpp b/app_httpd.cpp index 3fefb24..30c91bb 100644 --- a/app_httpd.cpp +++ b/app_httpd.cpp @@ -608,7 +608,7 @@ static esp_err_t cmd_handler(httpd_req_t *req){ delay(150); Serial.print('.'); } - Serial.printf("\n..Thats all folks!\n\n"); + Serial.printf(" Thats all folks!\n\n"); ESP.restart(); } else { @@ -746,10 +746,10 @@ static esp_err_t dump_handler(httpd_req_t *req){ Serial.printf("Ssid: %s\n", ssidName.c_str()); d+= sprintf(d,"Rssi: %i
\n", WiFi.RSSI()); Serial.printf("Rssi: %i\n", WiFi.RSSI()); + String bssid = WiFi.BSSIDstr(); + d+= sprintf(d,"BSSID: %s
\n", bssid.c_str()); + Serial.printf("BSSID: %s\n", bssid.c_str()); } - String bssid = WiFi.BSSIDstr(); - d+= sprintf(d,"BSSID: %s
\n", bssid.c_str()); - Serial.printf("BSSID: %s\n", bssid.c_str()); d+= sprintf(d,"IP address: %d.%d.%d.%d
\n", ip[0], ip[1], ip[2], ip[3]); Serial.printf("IP address: %d.%d.%d.%d\n", ip[0], ip[1], ip[2], ip[3]); if (!accesspoint) { diff --git a/esp32-cam-webserver.ino b/esp32-cam-webserver.ino index 4aefb3f..b0a0582 100644 --- a/esp32-cam-webserver.ino +++ b/esp32-cam-webserver.ino @@ -241,7 +241,6 @@ void WifiSetup() { Serial.println("Static IP settings requested but not defined in config, falling back to dhcp"); #endif } - // Initiate network connection request WiFi.begin(stationList[bestStation].ssid, stationList[bestStation].password); @@ -251,12 +250,30 @@ void WifiSetup() { delay(500); Serial.print('.'); } + // If we have connected, inform user + 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\n",ip[0],ip[1],ip[2],ip[3]); + Serial.printf("Netmask : %d.%d.%d.%d\n",net[0],net[1],net[2],net[3]); + Serial.printf("Gateway : %d.%d.%d.%d\n",gw[0],gw[1],gw[2],gw[3]); + // Flash the LED to show we are connected + for (int i = 0; i < 5; i++) { + flashLED(50); + delay(150); + } + } else { + Serial.println("Client connection Failed"); + WiFi.disconnect(); // (resets the WiFi scan) + } } - - if (accesspoint) { + if (accesspoint && (WiFi.status() != WL_CONNECTED)) { // The accesspoint has been enabled #if defined(AP_CHAN) - WiFi.softAP(stationList[0].ssid, stationList[0].password, AP_CHAN); Serial.println("Setting up Fixed Channel AccessPoint"); Serial.print(" SSID : "); Serial.println(stationList[0].ssid); @@ -264,13 +281,14 @@ void WifiSetup() { Serial.println(stationList[0].password); Serial.print(" Channel : "); Serial.println(AP_CHAN); + WiFi.softAP(stationList[0].ssid, stationList[0].password, AP_CHAN); # else - WiFi.softAP(stationList[0].ssid, stationList[0].password); 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 @@ -281,44 +299,24 @@ void WifiSetup() { 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\n",ip[0],ip[1],ip[2],ip[3]); + // Flash the LED to show we are connected + for (int i = 0; i < 5; i++) { + flashLED(150); + delay(50); + } // Start the DNS captive portal if requested if (stationList[0].dhcp == true) { Serial.println("Starting Captive Portal"); dnsServer.start(DNS_PORT, "*", ip); captivePortal = true; } - } else { - // If we have connected, inform user - if (WiFi.status() == WL_CONNECTED) { - Serial.println(" Client connection succeeded"); - // Note IP details - ip = WiFi.localIP(); - net = WiFi.subnetMask(); - gw = WiFi.gatewayIP(); - } else { - Serial.println(" Failed"); - WiFi.disconnect(); // (resets the WiFi scan) - return; - } - } - - Serial.printf("IP address: %d.%d.%d.%d\n",ip[0],ip[1],ip[2],ip[3]); - if (!accesspoint) { - Serial.printf("Netmask : %d.%d.%d.%d\n",net[0],net[1],net[2],net[3]); - Serial.printf("Gateway : %d.%d.%d.%d\n",gw[0],gw[1],gw[2],gw[3]); - } - // Burst flash the LED to show we are connected - for (int i = 0; i < 5; i++) { - flashLED(80); - delay(120); - } + } } void setup() { @@ -497,10 +495,10 @@ void setup() { WiFi.macAddress(mac); Serial.printf("MAC address: %02X:%02X:%02X:%02X:%02X:%02X\n", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); - // Having got this far; start Wifi and loop until we succeed. + // Having got this far; start Wifi and loop until we are connected or have started an AccessPoint while ((WiFi.status() != WL_CONNECTED) && !accesspoint) { WifiSetup(); - delay(WIFI_WATCHDOG); + delay(1000); } // Now we have a network we can start the two http handlers for the UI and Stream. diff --git a/myconfig.sample.h b/myconfig.sample.h index a03a91f..a4e29d8 100644 --- a/myconfig.sample.h +++ b/myconfig.sample.h @@ -17,6 +17,7 @@ */ // WiFi Credentials +// A structure for each WiFi network entry struct station { const char ssid[64]; // ssid (max 64 chars) @@ -24,22 +25,24 @@ struct station const bool dhcp; // dhcp }; -struct station stationList[] = {{"my_ssid","my_password", false}}; - /* - * Extend the list above with additional SSID+Password pairs like this: - * The third (dhcp) column controls whether that station uses dhcp; or the optional static IP settings (below) - * Note the use of nested braces { and }, to group each entry, and commas to seperate them. + * Extend the list below with additional SSID+Password pairs like this: struct station stationList[] = {{"ssid1", "pass1", true}, {"ssid2", "pass2", true}, {"ssid3", "pass3", false}}; + + * The first entry will be used for the AccessPoint ssid and password when it is enabled + * The 'dhcp' setting controls wether the station uses static IP settings (if in doubt leave 'true') + * Note the use of nested braces '{' and '}' to group each entry, and commas ',' to seperate them. */ +struct station stationList[] = {{"my_ssid","my_password", false}}; + /* * Static network settings for client mode * - * Note: These settings will be applied to all client connections where .staticIP is true + * Note: The same settings will be applied to all client connections where the dhcp setting is 'false' * You must define all three: IP, Gateway and NetMask */ // warning - IP addresses must be seperated with commas (,) and not decimals (.) @@ -54,18 +57,24 @@ struct station stationList[] = {{"ssid1", "pass1", true}, * AccessPoint; * * Uncomment to enable AP mode; - * The AP ssid and password will be taken from the 1st entry in the stationList[] above. * - * This is a 'fallback' mode. The remaining stationList[] is first scanned; if a matching network - * is found we will loop attempting to connect to that. - * If no matching networks are found during the initial scan the AccessPoint mode will - * be permanently enabled and started; no further scan+connection attempts will be made. - * If you only list one entry in the stationList[] you will effectively create a dedicated - * AccessPoint setup. + */ +#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 and connected to if they are found. + * Making the AP a fallback mode that happens only when there are no 'real' networks available * - * ENHANCEMENT: use dhcp field to create a mdns captive portal? currently it is unused. + * Setting the dhcp field to true for the AP enables a captive portal and attempts to send + * all incoming pages to the webcam page, with varying degrees of success depending on the visitors + * browser and other settings. + * - The Captive Portal really needs a seperate landing page instead of using the 'main' page. + * Browsers and OS's restrict landing page functions, since they have been abused by marketing types + * and other low quality people. Video/Audio playback and Javascript are commonly disabled as a result. */ -// #define WIFI_AP_ENABLE // AccessPoint; optionally change the ip address (default = 192.168.4.1) // warning - IP addresses must be seperated with commas (,) and not decimals (.) From 5eae7d3c6d85cf872faebc9f1b6d1a5592b1189f Mon Sep 17 00:00:00 2001 From: Owen Date: Tue, 6 Oct 2020 17:03:05 +0200 Subject: [PATCH 12/19] Implement one URI for index pages, takes a parameter, ?view=full|simple|etc --- app_httpd.cpp | 74 +++++++++++++++++++++++++++++++++----- viewers.h => index_other.h | 0 myconfig.sample.h | 59 ++++++++++++++++-------------- 3 files changed, 98 insertions(+), 35 deletions(-) rename viewers.h => index_other.h (100%) diff --git a/app_httpd.cpp b/app_httpd.cpp index 30c91bb..274273c 100644 --- a/app_httpd.cpp +++ b/app_httpd.cpp @@ -20,7 +20,7 @@ #include "index_ov2640.h" #include "index_ov3660.h" -#include "viewers.h" +#include "index_other.h" #include "css.h" #include "src/favicons.h" #include "storage.h" @@ -821,15 +821,73 @@ static esp_err_t streamviewer_handler(httpd_req_t *req){ } static esp_err_t index_handler(httpd_req_t *req){ + char* buf; + size_t buf_len; + char view[32] = {0,}; + flashLED(75); - Serial.println("Index page requested"); - httpd_resp_set_type(req, "text/html"); - httpd_resp_set_hdr(req, "Content-Encoding", "identity"); - sensor_t * s = esp_camera_sensor_get(); - if (s->id.PID == OV3660_PID) { - return httpd_resp_send(req, (const char *)index_ov3660_html, index_ov3660_html_len); + // 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){ + 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 { + free(buf); + httpd_resp_send_404(req); + return ESP_FAIL; + } + } else { + free(buf); + httpd_resp_send_404(req); + return ESP_FAIL; + } + free(buf); + } else { + if (captivePortal) { + strcpy(view,"simple"); + } + // no target specified; default. + #if defined(DEFAULT_INDEX_FULL) + strcpy(view,"full"); + #else + strcpy(view,"simple"); + #endif + // If a captive portal page is created, we can use it here + //if (captivePortal) { + // strcpy(view,"portal"); + //} + } + + if (strncmp(view,"simple", sizeof(view)) == 0) { + Serial.println("Simple index page requested"); + httpd_resp_set_type(req, "text/html"); + httpd_resp_set_hdr(req, "Content-Encoding", "identity"); + return httpd_resp_send(req, (const char *)miniviewer_html, miniviewer_html_len); + } else if(strncmp(view,"full", sizeof(view)) == 0) { + Serial.println("Full index page requested"); + httpd_resp_set_type(req, "text/html"); + httpd_resp_set_hdr(req, "Content-Encoding", "identity"); + sensor_t * s = esp_camera_sensor_get(); + if (s->id.PID == 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) { + // // Do a captive portal landing page here. + // Serial.println("Portal page requested"); + // httpd_resp_send_404(req); + // return ESP_FAIL; + } else { + Serial.print("Unknown page requested: "); + Serial.println(view); + httpd_resp_send_404(req); + return ESP_FAIL; } - return httpd_resp_send(req, (const char *)index_ov2640_html, index_ov2640_html_len); } void startCameraServer(int hPort, int sPort){ diff --git a/viewers.h b/index_other.h similarity index 100% rename from viewers.h rename to index_other.h diff --git a/myconfig.sample.h b/myconfig.sample.h index a4e29d8..003fc08 100644 --- a/myconfig.sample.h +++ b/myconfig.sample.h @@ -45,7 +45,7 @@ struct station stationList[] = {{"my_ssid","my_password", false}}; * Note: The same settings will be applied to all client connections where the dhcp setting is 'false' * You must define all three: IP, Gateway and NetMask */ -// warning - IP addresses must be seperated with commas (,) and not decimals (.) +// warning - IP addresses must be seperated with commas (,) and not decimals (.) here // #define ST_IP 192,168,0,16 // #define ST_GATEWAY 192,168,0,2 // #define ST_NETMASK 255,255,255,0 @@ -63,24 +63,20 @@ struct station stationList[] = {{"my_ssid","my_password", false}}; /* AP Mode Notes: * - * Once enabled the AP ssid and password will be taken from the 1st entry in the stationList[] above. + * When WIFI_AP_ENABLE is set the AccessPoint 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 and connected to if they are found. - * Making the AP a fallback mode that happens only when there are no 'real' networks available + * If there are further entries in the list they will still be scanned at startup and connected to if they are found. + * The AP then becomes a fallback mode that happens only 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 incoming pages to the webcam page, with varying degrees of success depending on the visitors * browser and other settings. - * - The Captive Portal really needs a seperate landing page instead of using the 'main' page. - * Browsers and OS's restrict landing page functions, since they have been abused by marketing types - * and other low quality people. Video/Audio playback and Javascript are commonly disabled as a result. */ - -// AccessPoint; optionally change the ip address (default = 192.168.4.1) -// warning - IP addresses must be seperated with commas (,) and not decimals (.) +// Optionally change the AccessPoint ip address (default = 192.168.4.1) +// warning - IP addresses must be seperated with commas (,) and not decimals (.) here // #define AP_ADDRESS 192,168,4,1 -// AccessPoint; Uncomment this to force the channel number, default = 1 +// Uncomment this to force the AccessPoint channel number, default = 1 // #define AP_CHAN 1 /* @@ -98,21 +94,9 @@ struct station stationList[] = {{"my_ssid","my_password", false}}; //#define WIFI_WATCHDOG 5000 /* - * Camera Hardware Settings + * Camera Defaults * - * 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 - // Initial Reslolution, default SVGA // available values are: FRAMESIZE_[QQVGA|HQVGA|QVGA|CIF|VGA|SVGA|XGA|SXGA|UXGA|QXGA(ov3660)] // #define DEFAULT_RESOLUTION FRAMESIZE_SVGA @@ -129,7 +113,10 @@ struct station stationList[] = {{"my_ssid","my_password", false}}; /* * 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 illumination lamp features // #define LAMP_DISABLE @@ -137,11 +124,29 @@ struct station stationList[] = {{"my_ssid","my_password", false}}; // Define a initial lamp setting as a percentage, defaults to 0% // #define LAMP_DEFAULT 0 -// Assume we have SPIFFS/LittleFS partition, uncomment if not +// Assume we have SPIFFS/LittleFS partition, uncomment to disable this. +// Controls will still be shown in the UI but are inoperative. // #define NO_FS // Uncomment to enable Face Detection (+ Recognition if desired) by default -// Notes: You must set DEFAULT_RESOLUTION, above, to FRAMESIZE_CIF or lower -// Face recognition enrolements will be lost between reboots. +// Notes: You must set DEFAULT_RESOLUTION (above) to FRAMESIZE_CIF or lower before enabling this +// Face recognition enrolements are currently lost between reboots. // #define FACE_DETECTION // #define FACE_RECOGNITION + + +/* + * 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 From 67cec073adc7044e0e8469e4b14efa0aaddd5170 Mon Sep 17 00:00:00 2001 From: Owen Date: Tue, 6 Oct 2020 23:29:08 +0200 Subject: [PATCH 13/19] Change how we do the two main indexes (full and simple) Add an access portal page, add a logo to that and the dump page. fix a stoopid, possibly disruptive bug that I introduced when unpacking html in my 1st commit --- Docs/logo.svg | 33 ++--------- app_httpd.cpp | 63 ++++++++++++--------- css.h | 10 ++-- index_other.h | 48 +++++++++++----- index_ov2640.h | 12 ++-- index_ov3660.h | 2 +- myconfig.sample.h | 43 ++++++++------- src/logo.h | 137 ++++++++++++++++++++++++++++++++++++++++++++++ 8 files changed, 245 insertions(+), 103 deletions(-) create mode 100644 src/logo.h diff --git a/Docs/logo.svg b/Docs/logo.svg index b5c25fe..b9b2adc 100644 --- a/Docs/logo.svg +++ b/Docs/logo.svg @@ -7,17 +7,13 @@ xmlns="http://www.w3.org/2000/svg" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" - inkscape:export-ydpi="532.84314" - inkscape:export-xdpi="532.84314" - inkscape:export-filename="/home/owen/Arduino/esp32-cam-webserver/Docs/logo-big.png" - id="图层_1" - data-name="图层 1" + id="espeye_1" + data-name="Esp Eye 1" viewBox="0 0 86.471377 86.479481" version="1.1" sodipodi:docname="logo.svg" width="86.471375" - height="86.479485" - inkscape:version="1.0 (4035a4fb49, 2020-05-01)"> + height="86.479485"> @@ -29,34 +25,13 @@ - 导航栏-菜单-logo + id="title6">ESP32 CAM Webserver - logo \n"); @@ -713,6 +727,7 @@ static esp_err_t dump_handler(httpd_req_t *req){ d+= sprintf(d,"\n"); d+= sprintf(d,"\n"); d+= sprintf(d,"\n\n"); + d+= sprintf(d,"\n"); d+= sprintf(d,"

ESP32 Cam Webserver

\n"); // Module d+= sprintf(d,"Name: %s
\n", myName); @@ -804,14 +819,6 @@ static esp_err_t style_handler(httpd_req_t *req){ return httpd_resp_send(req, (const char *)style_css, style_css_len); } -static esp_err_t miniviewer_handler(httpd_req_t *req){ - flashLED(75); - Serial.println("Simple viewer requested"); - httpd_resp_set_type(req, "text/html"); - httpd_resp_set_hdr(req, "Content-Encoding", "identity"); - return httpd_resp_send(req, (const char *)miniviewer_html, miniviewer_html_len); -} - static esp_err_t streamviewer_handler(httpd_req_t *req){ flashLED(75); Serial.println("Stream Viewer requested"); @@ -858,18 +865,20 @@ static esp_err_t index_handler(httpd_req_t *req){ strcpy(view,"simple"); #endif // If a captive portal page is created, we can use it here - //if (captivePortal) { - // strcpy(view,"portal"); - //} + if (captivePortal) { + strcpy(view,"portal"); + } } if (strncmp(view,"simple", sizeof(view)) == 0) { Serial.println("Simple index page requested"); + Serial.println(index_simple_html_len); httpd_resp_set_type(req, "text/html"); httpd_resp_set_hdr(req, "Content-Encoding", "identity"); - return httpd_resp_send(req, (const char *)miniviewer_html, miniviewer_html_len); + return httpd_resp_send(req, (const char *)index_simple_html, index_simple_html_len); } else if(strncmp(view,"full", sizeof(view)) == 0) { Serial.println("Full index page requested"); + Serial.println(index_ov2640_html_len); httpd_resp_set_type(req, "text/html"); httpd_resp_set_hdr(req, "Content-Encoding", "identity"); sensor_t * s = esp_camera_sensor_get(); @@ -877,11 +886,13 @@ static esp_err_t index_handler(httpd_req_t *req){ 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) { - // // Do a captive portal landing page here. - // Serial.println("Portal page requested"); - // httpd_resp_send_404(req); - // return ESP_FAIL; + } else if(strncmp(view,"portal", sizeof(view)) == 0) { + //Prototype captive portal landing page. + Serial.println("Portal page requested"); + Serial.println(portal_html_len); + httpd_resp_set_type(req, "text/html"); + httpd_resp_set_hdr(req, "Content-Encoding", "identity"); + return httpd_resp_send(req, (const char *)portal_html, portal_html_len); } else { Serial.print("Unknown page requested: "); Serial.println(view); @@ -918,12 +929,6 @@ void startCameraServer(int hPort, int sPort){ .handler = capture_handler, .user_ctx = NULL }; - httpd_uri_t miniviewer_uri = { - .uri = "/view", - .method = HTTP_GET, - .handler = miniviewer_handler, - .user_ctx = NULL - }; httpd_uri_t style_uri = { .uri = "/style.css", .method = HTTP_GET, @@ -948,14 +953,18 @@ void startCameraServer(int hPort, int sPort){ .handler = favicon_ico_handler, .user_ctx = NULL }; - // DEBUG + httpd_uri_t logo_svg_uri = { + .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 }; - // DEBUG httpd_uri_t stream_uri = { .uri = "/", .method = HTTP_GET, @@ -1000,11 +1009,11 @@ void startCameraServer(int hPort, int sPort){ httpd_register_uri_handler(camera_httpd, &cmd_uri); httpd_register_uri_handler(camera_httpd, &status_uri); httpd_register_uri_handler(camera_httpd, &capture_uri); - httpd_register_uri_handler(camera_httpd, &miniviewer_uri); httpd_register_uri_handler(camera_httpd, &style_uri); httpd_register_uri_handler(camera_httpd, &favicon_16x16_uri); httpd_register_uri_handler(camera_httpd, &favicon_32x32_uri); httpd_register_uri_handler(camera_httpd, &favicon_ico_uri); + httpd_register_uri_handler(camera_httpd, &logo_svg_uri); httpd_register_uri_handler(camera_httpd, &dump_uri); } diff --git a/css.h b/css.h index 0d7dc6a..26a35f7 100644 --- a/css.h +++ b/css.h @@ -1,9 +1,8 @@ /* - * Master CSS file included in the camer_index_* and miniviewer HTML + * Master CSS file for the camera pages */ -const uint8_t style_css[] = R"=====( -/* +const uint8_t style_css[] = R"=====(/* * CSS for the esp32 cam webserver */ @@ -358,7 +357,6 @@ select { @keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } -} -)====="; +})====="; -size_t style_css_len = sizeof(style_css); +size_t style_css_len = sizeof(style_css)-1; diff --git a/index_other.h b/index_other.h index 644e723..f8c1fbd 100644 --- a/index_other.h +++ b/index_other.h @@ -1,14 +1,13 @@ /* - * Miniviewer and streamviewer + * simpleviewer and streamviewer */ - const uint8_t miniviewer_html[] = R"=====( - + const uint8_t index_simple_html[] = R"=====( - ESP32-CAM MiniViewer + ESP32-CAM Simplified View @@ -273,20 +272,18 @@ } swapButton.onclick = () => { - window.open('/','_self'); + window.open('/?view=full','_self'); } }) - -)====="; +)====="; -size_t miniviewer_html_len = sizeof(miniviewer_html); +size_t index_simple_html_len = sizeof(index_simple_html-1); /* Stream Viewer */ - const uint8_t streamviewer_html[] = R"=====( - + const uint8_t streamviewer_html[] = R"=====( @@ -432,7 +429,32 @@ size_t miniviewer_html_len = sizeof(miniviewer_html); } }) - -)====="; +)====="; -size_t streamviewer_html_len = sizeof(streamviewer_html); +size_t streamviewer_html_len = sizeof(streamviewer_html)-1; + +/* Prototype Captive Portal page */ + + const uint8_t portal_html[] = R"=====( + + + + + ESP32-CAM portal + + + + + + +

ESP32 cam access portal

+ +
+ Camera Details
+ ESP32 cam webserver on GitHub +)====="; + +size_t portal_html_len = sizeof(portal_html)-1; diff --git a/index_ov2640.h b/index_ov2640.h index 36affd5..c7f3e23 100644 --- a/index_ov2640.h +++ b/index_ov2640.h @@ -2,8 +2,7 @@ * primary HTML for the OV2640 camera module */ -const uint8_t index_ov2640_html[] = R"=====( - +const uint8_t index_ov2640_html[] = R"=====( @@ -265,7 +264,7 @@ const uint8_t index_ov2640_html[] = R"=====(
@@ -589,7 +588,7 @@ const uint8_t index_ov2640_html[] = R"=====( } swapButton.onclick = () => { - window.open('/view','_self'); + window.open('/?view=simple','_self'); } // saveFaceButton.onclick = () => { @@ -626,7 +625,6 @@ const uint8_t index_ov2640_html[] = R"=====( }) - -)====="; +)====="; -size_t index_ov2640_html_len = sizeof(index_ov2640_html); +size_t index_ov2640_html_len = sizeof(index_ov2640_html)-1; diff --git a/index_ov3660.h b/index_ov3660.h index ce8c9a9..efbe8c0 100644 --- a/index_ov3660.h +++ b/index_ov3660.h @@ -636,4 +636,4 @@ const uint8_t index_ov3660_html[] = R"=====( )====="; -size_t index_ov3660_html_len = sizeof(index_ov3660_html); +size_t index_ov3660_html_len = sizeof(index_ov3660_html)-1; diff --git a/myconfig.sample.h b/myconfig.sample.h index 003fc08..95fc877 100644 --- a/myconfig.sample.h +++ b/myconfig.sample.h @@ -17,26 +17,29 @@ */ // WiFi Credentials -// A structure for each WiFi network entry -struct station -{ - const char ssid[64]; // ssid (max 64 chars) - const char password[64]; // password (max 64 chars) - const bool dhcp; // dhcp -}; -/* - * Extend the list below with additional SSID+Password pairs like this: -struct station stationList[] = {{"ssid1", "pass1", true}, - {"ssid2", "pass2", true}, - {"ssid3", "pass3", false}}; - * The first entry will be used for the AccessPoint ssid and password when it is enabled +/* + * Extend the stationList[] below with additional SSID+Password pairs. + * The first block defines /what/ the structure holds + * The second block is where our list of ssid/passwords live + +struct station { + const char ssid[64]; // - ssid (max 64 chars) + const char password[64]; // - password (max 64 chars) + const bool dhcp; // - dhcp +} station stationList[] = {{"ssid1", "pass1", true}, + {"ssid2", "pass2", true}, + {"ssid3", "pass3", false}}; + + * The first entry in the stationList[] is special, if WIFI_AP_ENABLE has been uncommented (below) + * it will be used for the AccessPoint ssid and password. + * * The 'dhcp' setting controls wether the station uses static IP settings (if in doubt leave 'true') * Note the use of nested braces '{' and '}' to group each entry, and commas ',' to seperate them. */ -struct station stationList[] = {{"my_ssid","my_password", false}}; +struct station stationList[] = {{"my_ssid","my_password", true}}; /* @@ -59,17 +62,17 @@ struct station stationList[] = {{"my_ssid","my_password", false}}; * Uncomment to enable AP mode; * */ -#define WIFI_AP_ENABLE +// #define WIFI_AP_ENABLE /* AP Mode Notes: * - * When WIFI_AP_ENABLE is set the AccessPoint ssid and password will be taken from the 1st entry in the stationList[] above. + * Once enabled the AP ssid and password will be taken from the 1st entry in the stationList[] above. * - * If there are further entries in the list they will still be scanned at startup and connected to if they are found. - * The AP then becomes a fallback mode that happens only when there are no 'real' networks available. + * If there are more 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 incoming pages to the webcam page, with varying degrees of success depending on the visitors + * 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) diff --git a/src/logo.h b/src/logo.h new file mode 100644 index 0000000..754388e --- /dev/null +++ b/src/logo.h @@ -0,0 +1,137 @@ +/* + * Logo (svg format) + */ + +const uint8_t logo_svg[] = R"=====( + + + + + image/svg+xml + + + + + + + + ESP32 CAM Webserver - logo + + + + + + + + + + + + + + + + + + + + + + + + + + +)====="; + +size_t logo_svg_len = sizeof(logo_svg)-1; From b5e857db8f0a5e82b49c575d0eb94b117f6945e8 Mon Sep 17 00:00:00 2001 From: Owen Date: Wed, 7 Oct 2020 00:00:09 +0200 Subject: [PATCH 14/19] Fix default myconfig --- myconfig.sample.h | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/myconfig.sample.h b/myconfig.sample.h index 95fc877..61b778c 100644 --- a/myconfig.sample.h +++ b/myconfig.sample.h @@ -14,13 +14,7 @@ * WiFi Settings * * Note the the use of commas as seperators in IP addresses! - */ - -// WiFi Credentials - - -/* * Extend the stationList[] below with additional SSID+Password pairs. * The first block defines /what/ the structure holds * The second block is where our list of ssid/passwords live @@ -39,7 +33,11 @@ struct station { * The 'dhcp' setting controls wether the station uses static IP settings (if in doubt leave 'true') * Note the use of nested braces '{' and '}' to group each entry, and commas ',' to seperate them. */ -struct station stationList[] = {{"my_ssid","my_password", true}}; +struct station { + const char ssid[64]; // ssid (max 64 chars) + const char password[64]; // password (max 64 chars) + const bool dhcp; // dhcp +} stationList[] = {{"my_ssid","my_password", true}}; /* From 13a4645232fd2b64faa8e5b8d91a5c889fb91bcb Mon Sep 17 00:00:00 2001 From: Owen Date: Tue, 6 Oct 2020 17:03:05 +0200 Subject: [PATCH 15/19] Implement one URI for index pages, takes a parameter, ?view=full|simple|etc --- app_httpd.cpp | 74 +++++++++++++++++++++++++++++++++----- viewers.h => index_other.h | 0 myconfig.sample.h | 59 ++++++++++++++++-------------- 3 files changed, 98 insertions(+), 35 deletions(-) rename viewers.h => index_other.h (100%) diff --git a/app_httpd.cpp b/app_httpd.cpp index 30c91bb..274273c 100644 --- a/app_httpd.cpp +++ b/app_httpd.cpp @@ -20,7 +20,7 @@ #include "index_ov2640.h" #include "index_ov3660.h" -#include "viewers.h" +#include "index_other.h" #include "css.h" #include "src/favicons.h" #include "storage.h" @@ -821,15 +821,73 @@ static esp_err_t streamviewer_handler(httpd_req_t *req){ } static esp_err_t index_handler(httpd_req_t *req){ + char* buf; + size_t buf_len; + char view[32] = {0,}; + flashLED(75); - Serial.println("Index page requested"); - httpd_resp_set_type(req, "text/html"); - httpd_resp_set_hdr(req, "Content-Encoding", "identity"); - sensor_t * s = esp_camera_sensor_get(); - if (s->id.PID == OV3660_PID) { - return httpd_resp_send(req, (const char *)index_ov3660_html, index_ov3660_html_len); + // 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){ + 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 { + free(buf); + httpd_resp_send_404(req); + return ESP_FAIL; + } + } else { + free(buf); + httpd_resp_send_404(req); + return ESP_FAIL; + } + free(buf); + } else { + if (captivePortal) { + strcpy(view,"simple"); + } + // no target specified; default. + #if defined(DEFAULT_INDEX_FULL) + strcpy(view,"full"); + #else + strcpy(view,"simple"); + #endif + // If a captive portal page is created, we can use it here + //if (captivePortal) { + // strcpy(view,"portal"); + //} + } + + if (strncmp(view,"simple", sizeof(view)) == 0) { + Serial.println("Simple index page requested"); + httpd_resp_set_type(req, "text/html"); + httpd_resp_set_hdr(req, "Content-Encoding", "identity"); + return httpd_resp_send(req, (const char *)miniviewer_html, miniviewer_html_len); + } else if(strncmp(view,"full", sizeof(view)) == 0) { + Serial.println("Full index page requested"); + httpd_resp_set_type(req, "text/html"); + httpd_resp_set_hdr(req, "Content-Encoding", "identity"); + sensor_t * s = esp_camera_sensor_get(); + if (s->id.PID == 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) { + // // Do a captive portal landing page here. + // Serial.println("Portal page requested"); + // httpd_resp_send_404(req); + // return ESP_FAIL; + } else { + Serial.print("Unknown page requested: "); + Serial.println(view); + httpd_resp_send_404(req); + return ESP_FAIL; } - return httpd_resp_send(req, (const char *)index_ov2640_html, index_ov2640_html_len); } void startCameraServer(int hPort, int sPort){ diff --git a/viewers.h b/index_other.h similarity index 100% rename from viewers.h rename to index_other.h diff --git a/myconfig.sample.h b/myconfig.sample.h index a4e29d8..003fc08 100644 --- a/myconfig.sample.h +++ b/myconfig.sample.h @@ -45,7 +45,7 @@ struct station stationList[] = {{"my_ssid","my_password", false}}; * Note: The same settings will be applied to all client connections where the dhcp setting is 'false' * You must define all three: IP, Gateway and NetMask */ -// warning - IP addresses must be seperated with commas (,) and not decimals (.) +// warning - IP addresses must be seperated with commas (,) and not decimals (.) here // #define ST_IP 192,168,0,16 // #define ST_GATEWAY 192,168,0,2 // #define ST_NETMASK 255,255,255,0 @@ -63,24 +63,20 @@ struct station stationList[] = {{"my_ssid","my_password", false}}; /* AP Mode Notes: * - * Once enabled the AP ssid and password will be taken from the 1st entry in the stationList[] above. + * When WIFI_AP_ENABLE is set the AccessPoint 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 and connected to if they are found. - * Making the AP a fallback mode that happens only when there are no 'real' networks available + * If there are further entries in the list they will still be scanned at startup and connected to if they are found. + * The AP then becomes a fallback mode that happens only 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 incoming pages to the webcam page, with varying degrees of success depending on the visitors * browser and other settings. - * - The Captive Portal really needs a seperate landing page instead of using the 'main' page. - * Browsers and OS's restrict landing page functions, since they have been abused by marketing types - * and other low quality people. Video/Audio playback and Javascript are commonly disabled as a result. */ - -// AccessPoint; optionally change the ip address (default = 192.168.4.1) -// warning - IP addresses must be seperated with commas (,) and not decimals (.) +// Optionally change the AccessPoint ip address (default = 192.168.4.1) +// warning - IP addresses must be seperated with commas (,) and not decimals (.) here // #define AP_ADDRESS 192,168,4,1 -// AccessPoint; Uncomment this to force the channel number, default = 1 +// Uncomment this to force the AccessPoint channel number, default = 1 // #define AP_CHAN 1 /* @@ -98,21 +94,9 @@ struct station stationList[] = {{"my_ssid","my_password", false}}; //#define WIFI_WATCHDOG 5000 /* - * Camera Hardware Settings + * Camera Defaults * - * 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 - // Initial Reslolution, default SVGA // available values are: FRAMESIZE_[QQVGA|HQVGA|QVGA|CIF|VGA|SVGA|XGA|SXGA|UXGA|QXGA(ov3660)] // #define DEFAULT_RESOLUTION FRAMESIZE_SVGA @@ -129,7 +113,10 @@ struct station stationList[] = {{"my_ssid","my_password", false}}; /* * 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 illumination lamp features // #define LAMP_DISABLE @@ -137,11 +124,29 @@ struct station stationList[] = {{"my_ssid","my_password", false}}; // Define a initial lamp setting as a percentage, defaults to 0% // #define LAMP_DEFAULT 0 -// Assume we have SPIFFS/LittleFS partition, uncomment if not +// Assume we have SPIFFS/LittleFS partition, uncomment to disable this. +// Controls will still be shown in the UI but are inoperative. // #define NO_FS // Uncomment to enable Face Detection (+ Recognition if desired) by default -// Notes: You must set DEFAULT_RESOLUTION, above, to FRAMESIZE_CIF or lower -// Face recognition enrolements will be lost between reboots. +// Notes: You must set DEFAULT_RESOLUTION (above) to FRAMESIZE_CIF or lower before enabling this +// Face recognition enrolements are currently lost between reboots. // #define FACE_DETECTION // #define FACE_RECOGNITION + + +/* + * 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 From c105352f76f500c92120c0aa6ddc72c0f2cac21b Mon Sep 17 00:00:00 2001 From: Owen Date: Tue, 6 Oct 2020 23:29:08 +0200 Subject: [PATCH 16/19] Change how we do the two main indexes (full and simple) Add an access portal page, add a logo to that and the dump page. fix a stoopid, possibly disruptive bug that I introduced when unpacking html in my 1st commit --- Docs/logo.svg | 33 ++--------- app_httpd.cpp | 63 ++++++++++++--------- css.h | 10 ++-- index_other.h | 48 +++++++++++----- index_ov2640.h | 12 ++-- index_ov3660.h | 2 +- myconfig.sample.h | 43 ++++++++------- src/logo.h | 137 ++++++++++++++++++++++++++++++++++++++++++++++ 8 files changed, 245 insertions(+), 103 deletions(-) create mode 100644 src/logo.h diff --git a/Docs/logo.svg b/Docs/logo.svg index b5c25fe..b9b2adc 100644 --- a/Docs/logo.svg +++ b/Docs/logo.svg @@ -7,17 +7,13 @@ xmlns="http://www.w3.org/2000/svg" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" - inkscape:export-ydpi="532.84314" - inkscape:export-xdpi="532.84314" - inkscape:export-filename="/home/owen/Arduino/esp32-cam-webserver/Docs/logo-big.png" - id="图层_1" - data-name="图层 1" + id="espeye_1" + data-name="Esp Eye 1" viewBox="0 0 86.471377 86.479481" version="1.1" sodipodi:docname="logo.svg" width="86.471375" - height="86.479485" - inkscape:version="1.0 (4035a4fb49, 2020-05-01)"> + height="86.479485"> @@ -29,34 +25,13 @@ - 导航栏-菜单-logo + id="title6">ESP32 CAM Webserver - logo \n"); @@ -713,6 +727,7 @@ static esp_err_t dump_handler(httpd_req_t *req){ d+= sprintf(d,"\n"); d+= sprintf(d,"\n"); d+= sprintf(d,"\n\n"); + d+= sprintf(d,"\n"); d+= sprintf(d,"

ESP32 Cam Webserver

\n"); // Module d+= sprintf(d,"Name: %s
\n", myName); @@ -804,14 +819,6 @@ static esp_err_t style_handler(httpd_req_t *req){ return httpd_resp_send(req, (const char *)style_css, style_css_len); } -static esp_err_t miniviewer_handler(httpd_req_t *req){ - flashLED(75); - Serial.println("Simple viewer requested"); - httpd_resp_set_type(req, "text/html"); - httpd_resp_set_hdr(req, "Content-Encoding", "identity"); - return httpd_resp_send(req, (const char *)miniviewer_html, miniviewer_html_len); -} - static esp_err_t streamviewer_handler(httpd_req_t *req){ flashLED(75); Serial.println("Stream Viewer requested"); @@ -858,18 +865,20 @@ static esp_err_t index_handler(httpd_req_t *req){ strcpy(view,"simple"); #endif // If a captive portal page is created, we can use it here - //if (captivePortal) { - // strcpy(view,"portal"); - //} + if (captivePortal) { + strcpy(view,"portal"); + } } if (strncmp(view,"simple", sizeof(view)) == 0) { Serial.println("Simple index page requested"); + Serial.println(index_simple_html_len); httpd_resp_set_type(req, "text/html"); httpd_resp_set_hdr(req, "Content-Encoding", "identity"); - return httpd_resp_send(req, (const char *)miniviewer_html, miniviewer_html_len); + return httpd_resp_send(req, (const char *)index_simple_html, index_simple_html_len); } else if(strncmp(view,"full", sizeof(view)) == 0) { Serial.println("Full index page requested"); + Serial.println(index_ov2640_html_len); httpd_resp_set_type(req, "text/html"); httpd_resp_set_hdr(req, "Content-Encoding", "identity"); sensor_t * s = esp_camera_sensor_get(); @@ -877,11 +886,13 @@ static esp_err_t index_handler(httpd_req_t *req){ 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) { - // // Do a captive portal landing page here. - // Serial.println("Portal page requested"); - // httpd_resp_send_404(req); - // return ESP_FAIL; + } else if(strncmp(view,"portal", sizeof(view)) == 0) { + //Prototype captive portal landing page. + Serial.println("Portal page requested"); + Serial.println(portal_html_len); + httpd_resp_set_type(req, "text/html"); + httpd_resp_set_hdr(req, "Content-Encoding", "identity"); + return httpd_resp_send(req, (const char *)portal_html, portal_html_len); } else { Serial.print("Unknown page requested: "); Serial.println(view); @@ -918,12 +929,6 @@ void startCameraServer(int hPort, int sPort){ .handler = capture_handler, .user_ctx = NULL }; - httpd_uri_t miniviewer_uri = { - .uri = "/view", - .method = HTTP_GET, - .handler = miniviewer_handler, - .user_ctx = NULL - }; httpd_uri_t style_uri = { .uri = "/style.css", .method = HTTP_GET, @@ -948,14 +953,18 @@ void startCameraServer(int hPort, int sPort){ .handler = favicon_ico_handler, .user_ctx = NULL }; - // DEBUG + httpd_uri_t logo_svg_uri = { + .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 }; - // DEBUG httpd_uri_t stream_uri = { .uri = "/", .method = HTTP_GET, @@ -1000,11 +1009,11 @@ void startCameraServer(int hPort, int sPort){ httpd_register_uri_handler(camera_httpd, &cmd_uri); httpd_register_uri_handler(camera_httpd, &status_uri); httpd_register_uri_handler(camera_httpd, &capture_uri); - httpd_register_uri_handler(camera_httpd, &miniviewer_uri); httpd_register_uri_handler(camera_httpd, &style_uri); httpd_register_uri_handler(camera_httpd, &favicon_16x16_uri); httpd_register_uri_handler(camera_httpd, &favicon_32x32_uri); httpd_register_uri_handler(camera_httpd, &favicon_ico_uri); + httpd_register_uri_handler(camera_httpd, &logo_svg_uri); httpd_register_uri_handler(camera_httpd, &dump_uri); } diff --git a/css.h b/css.h index 0d7dc6a..26a35f7 100644 --- a/css.h +++ b/css.h @@ -1,9 +1,8 @@ /* - * Master CSS file included in the camer_index_* and miniviewer HTML + * Master CSS file for the camera pages */ -const uint8_t style_css[] = R"=====( -/* +const uint8_t style_css[] = R"=====(/* * CSS for the esp32 cam webserver */ @@ -358,7 +357,6 @@ select { @keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } -} -)====="; +})====="; -size_t style_css_len = sizeof(style_css); +size_t style_css_len = sizeof(style_css)-1; diff --git a/index_other.h b/index_other.h index 644e723..f8c1fbd 100644 --- a/index_other.h +++ b/index_other.h @@ -1,14 +1,13 @@ /* - * Miniviewer and streamviewer + * simpleviewer and streamviewer */ - const uint8_t miniviewer_html[] = R"=====( - + const uint8_t index_simple_html[] = R"=====( - ESP32-CAM MiniViewer + ESP32-CAM Simplified View @@ -273,20 +272,18 @@ } swapButton.onclick = () => { - window.open('/','_self'); + window.open('/?view=full','_self'); } }) - -)====="; +)====="; -size_t miniviewer_html_len = sizeof(miniviewer_html); +size_t index_simple_html_len = sizeof(index_simple_html-1); /* Stream Viewer */ - const uint8_t streamviewer_html[] = R"=====( - + const uint8_t streamviewer_html[] = R"=====( @@ -432,7 +429,32 @@ size_t miniviewer_html_len = sizeof(miniviewer_html); } }) - -)====="; +)====="; -size_t streamviewer_html_len = sizeof(streamviewer_html); +size_t streamviewer_html_len = sizeof(streamviewer_html)-1; + +/* Prototype Captive Portal page */ + + const uint8_t portal_html[] = R"=====( + + + + + ESP32-CAM portal + + + + + + +

ESP32 cam access portal

+ +
+ Camera Details
+ ESP32 cam webserver on GitHub +)====="; + +size_t portal_html_len = sizeof(portal_html)-1; diff --git a/index_ov2640.h b/index_ov2640.h index 36affd5..c7f3e23 100644 --- a/index_ov2640.h +++ b/index_ov2640.h @@ -2,8 +2,7 @@ * primary HTML for the OV2640 camera module */ -const uint8_t index_ov2640_html[] = R"=====( - +const uint8_t index_ov2640_html[] = R"=====( @@ -265,7 +264,7 @@ const uint8_t index_ov2640_html[] = R"=====(
@@ -589,7 +588,7 @@ const uint8_t index_ov2640_html[] = R"=====( } swapButton.onclick = () => { - window.open('/view','_self'); + window.open('/?view=simple','_self'); } // saveFaceButton.onclick = () => { @@ -626,7 +625,6 @@ const uint8_t index_ov2640_html[] = R"=====( }) - -)====="; +)====="; -size_t index_ov2640_html_len = sizeof(index_ov2640_html); +size_t index_ov2640_html_len = sizeof(index_ov2640_html)-1; diff --git a/index_ov3660.h b/index_ov3660.h index ce8c9a9..efbe8c0 100644 --- a/index_ov3660.h +++ b/index_ov3660.h @@ -636,4 +636,4 @@ const uint8_t index_ov3660_html[] = R"=====( )====="; -size_t index_ov3660_html_len = sizeof(index_ov3660_html); +size_t index_ov3660_html_len = sizeof(index_ov3660_html)-1; diff --git a/myconfig.sample.h b/myconfig.sample.h index 003fc08..95fc877 100644 --- a/myconfig.sample.h +++ b/myconfig.sample.h @@ -17,26 +17,29 @@ */ // WiFi Credentials -// A structure for each WiFi network entry -struct station -{ - const char ssid[64]; // ssid (max 64 chars) - const char password[64]; // password (max 64 chars) - const bool dhcp; // dhcp -}; -/* - * Extend the list below with additional SSID+Password pairs like this: -struct station stationList[] = {{"ssid1", "pass1", true}, - {"ssid2", "pass2", true}, - {"ssid3", "pass3", false}}; - * The first entry will be used for the AccessPoint ssid and password when it is enabled +/* + * Extend the stationList[] below with additional SSID+Password pairs. + * The first block defines /what/ the structure holds + * The second block is where our list of ssid/passwords live + +struct station { + const char ssid[64]; // - ssid (max 64 chars) + const char password[64]; // - password (max 64 chars) + const bool dhcp; // - dhcp +} station stationList[] = {{"ssid1", "pass1", true}, + {"ssid2", "pass2", true}, + {"ssid3", "pass3", false}}; + + * The first entry in the stationList[] is special, if WIFI_AP_ENABLE has been uncommented (below) + * it will be used for the AccessPoint ssid and password. + * * The 'dhcp' setting controls wether the station uses static IP settings (if in doubt leave 'true') * Note the use of nested braces '{' and '}' to group each entry, and commas ',' to seperate them. */ -struct station stationList[] = {{"my_ssid","my_password", false}}; +struct station stationList[] = {{"my_ssid","my_password", true}}; /* @@ -59,17 +62,17 @@ struct station stationList[] = {{"my_ssid","my_password", false}}; * Uncomment to enable AP mode; * */ -#define WIFI_AP_ENABLE +// #define WIFI_AP_ENABLE /* AP Mode Notes: * - * When WIFI_AP_ENABLE is set the AccessPoint ssid and password will be taken from the 1st entry in the stationList[] above. + * Once enabled the AP ssid and password will be taken from the 1st entry in the stationList[] above. * - * If there are further entries in the list they will still be scanned at startup and connected to if they are found. - * The AP then becomes a fallback mode that happens only when there are no 'real' networks available. + * If there are more 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 incoming pages to the webcam page, with varying degrees of success depending on the visitors + * 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) diff --git a/src/logo.h b/src/logo.h new file mode 100644 index 0000000..754388e --- /dev/null +++ b/src/logo.h @@ -0,0 +1,137 @@ +/* + * Logo (svg format) + */ + +const uint8_t logo_svg[] = R"=====( + + + + + image/svg+xml + + + + + + + + ESP32 CAM Webserver - logo + + + + + + + + + + + + + + + + + + + + + + + + + + +)====="; + +size_t logo_svg_len = sizeof(logo_svg)-1; From a187032e1988154a1f8af00173db4209291736c1 Mon Sep 17 00:00:00 2001 From: Owen Date: Wed, 7 Oct 2020 00:00:09 +0200 Subject: [PATCH 17/19] Fix default myconfig --- myconfig.sample.h | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/myconfig.sample.h b/myconfig.sample.h index 95fc877..61b778c 100644 --- a/myconfig.sample.h +++ b/myconfig.sample.h @@ -14,13 +14,7 @@ * WiFi Settings * * Note the the use of commas as seperators in IP addresses! - */ - -// WiFi Credentials - - -/* * Extend the stationList[] below with additional SSID+Password pairs. * The first block defines /what/ the structure holds * The second block is where our list of ssid/passwords live @@ -39,7 +33,11 @@ struct station { * The 'dhcp' setting controls wether the station uses static IP settings (if in doubt leave 'true') * Note the use of nested braces '{' and '}' to group each entry, and commas ',' to seperate them. */ -struct station stationList[] = {{"my_ssid","my_password", true}}; +struct station { + const char ssid[64]; // ssid (max 64 chars) + const char password[64]; // password (max 64 chars) + const bool dhcp; // dhcp +} stationList[] = {{"my_ssid","my_password", true}}; /* From 2b35a8463db8733b227a0856eee1411f8cad5fb5 Mon Sep 17 00:00:00 2001 From: Owen Date: Wed, 7 Oct 2020 00:42:02 +0200 Subject: [PATCH 18/19] redundant definition --- app_httpd.cpp | 7 ------- 1 file changed, 7 deletions(-) diff --git a/app_httpd.cpp b/app_httpd.cpp index 87e11dc..6a95ff6 100644 --- a/app_httpd.cpp +++ b/app_httpd.cpp @@ -69,13 +69,6 @@ extern String sketchMD5; #define FACE_COLOR_CYAN (FACE_COLOR_BLUE | FACE_COLOR_GREEN) #define FACE_COLOR_PURPLE (FACE_COLOR_BLUE | FACE_COLOR_RED) -struct station -{ - const char ssid[64]; // ssid (max 64 chars) - const char password[64]; // password (max 64 chars) - const bool dhcp; // dhcp -}; - typedef struct { size_t size; //number of values used for filtering size_t index; //current value index From 203abfbd54ac9192df20837b259b2775cb16f59f Mon Sep 17 00:00:00 2001 From: Owen Date: Wed, 7 Oct 2020 01:12:24 +0200 Subject: [PATCH 19/19] remove debug, fix simple page length --- app_httpd.cpp | 3 --- index_other.h | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/app_httpd.cpp b/app_httpd.cpp index 6a95ff6..3da1553 100644 --- a/app_httpd.cpp +++ b/app_httpd.cpp @@ -865,13 +865,11 @@ static esp_err_t index_handler(httpd_req_t *req){ if (strncmp(view,"simple", sizeof(view)) == 0) { Serial.println("Simple index page requested"); - Serial.println(index_simple_html_len); 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) { Serial.println("Full index page requested"); - Serial.println(index_ov2640_html_len); httpd_resp_set_type(req, "text/html"); httpd_resp_set_hdr(req, "Content-Encoding", "identity"); sensor_t * s = esp_camera_sensor_get(); @@ -882,7 +880,6 @@ static esp_err_t index_handler(httpd_req_t *req){ } else if(strncmp(view,"portal", sizeof(view)) == 0) { //Prototype captive portal landing page. Serial.println("Portal page requested"); - Serial.println(portal_html_len); httpd_resp_set_type(req, "text/html"); httpd_resp_set_hdr(req, "Content-Encoding", "identity"); return httpd_resp_send(req, (const char *)portal_html, portal_html_len); diff --git a/index_other.h b/index_other.h index f8c1fbd..5e307e8 100644 --- a/index_other.h +++ b/index_other.h @@ -279,7 +279,7 @@ )====="; -size_t index_simple_html_len = sizeof(index_simple_html-1); +size_t index_simple_html_len = sizeof(index_simple_html)-1; /* Stream Viewer */