All functioning...except OTA...

This commit is contained in:
Sam 2020-10-31 18:24:05 -05:00
parent 79c9c5b623
commit 5c5a04d55d
12 changed files with 707 additions and 995 deletions

View File

@ -1,45 +1,41 @@
#include "esp_http_server.h"
#include "esp_timer.h"
#include "esp_camera.h"
#include "img_converters.h"
#include "Arduino.h"
#include <WiFi.h> #include <WiFi.h>
#include <WiFiClient.h>
#include <esp_bt.h>
#include <esp_wifi.h>
#include <esp_sleep.h>
#include <driver/rtc_io.h>
#include "myconfig.h"
#include "esp_camera.h"
#include "camera_pins.h"
#include <AsyncTCP.h>
#include <ESPAsyncWebServer.h>
#include <Update.h>
#include "index_ov2640.h" #include "src/html/firmware.h"
#include "index_ov3660.h" #include "src/html/index_ov2640.h"
#include "index_other.h" #include "src/html/index_ov3660.h"
#include "css.h" #include "src/html/index_simple.h"
#include "src/html/css.h"
#include "src/favicons.h" #include "src/favicons.h"
#include "src/logo.h" #include "src/logo.h"
#include "storage.h" #include "storage.h"
#if !defined(HTTP_PORT)
#define HTTP_PORT 80
#endif
int httpPort = HTTP_PORT;
// Functions from the main .ino // Functions from the main .ino
extern void flashLED(int flashtime); extern void flashLED(int flashtime);
extern void setLamp(int newVal); extern void setLamp(int newVal);
extern void resetWiFiConfig();
// External variables declared in the main .ino // External variables declared in the main .ino
extern char myName[]; extern char camera_name[];
extern char myVer[]; extern char myVer[];
extern char baseVersion[]; extern char baseVersion[];
extern IPAddress ip; extern IPAddress espIP;
extern IPAddress net; extern IPAddress espSubnet;
extern IPAddress gw; extern IPAddress espGateway;
extern bool accesspoint;
extern char apName[];
extern bool captivePortal;
extern char httpURL[];
extern char streamURL[]; extern char streamURL[];
extern char default_index[]; extern char default_index[];
extern int myRotation; extern int myRotation;
extern int lampVal; extern int lampVal;
extern int8_t detection_enabled;
extern int8_t recognition_enabled;
extern bool filesystem; extern bool filesystem;
extern bool debugData; extern bool debugData;
extern int sketchSize; extern int sketchSize;
@ -50,35 +46,37 @@ extern String sketchMD5;
#include "fd_forward.h" #include "fd_forward.h"
#include "fr_forward.h" #include "fr_forward.h"
#define ENROLL_CONFIRM_TIMES 5 #if !defined(HTTP_PORT)
typedef struct #define HTTP_PORT 80
{ #endif
httpd_req_t *req; int httpPort = HTTP_PORT;
size_t len; char httpURL[64] = {"Undefined"};
} jpg_chunking_t;
AsyncWebServer appServer(httpPort);
bool updating;
void appServerCB(void *pvParameters);
size_t jpg_encode_stream(void *arg, size_t index, const void *data, size_t len);
void capture_handler(AsyncWebServerRequest *request);
void cmd_handler(AsyncWebServerRequest *request);
void status_handler(AsyncWebServerRequest *request);
void favicon_16x16_handler(AsyncWebServerRequest *request);
void favicon_32x32_handler(AsyncWebServerRequest *request);
void favicon_ico_handler(AsyncWebServerRequest *request);
void logo_svg_handler(AsyncWebServerRequest *request);
void dump_handler(AsyncWebServerRequest *request);
void style_handler(AsyncWebServerRequest *request);
void handleIndex(AsyncWebServerRequest *request);
void handleFirmware(AsyncWebServerRequest *request);
void handleCameraClient();
void startCameraServer();
#define PART_BOUNDARY "123456789000000000000987654321"; #define PART_BOUNDARY "123456789000000000000987654321";
httpd_handle_t camera_httpd = NULL;
static size_t jpg_encode_stream(void *arg, size_t index, const void *data, size_t len) void capture_handler(AsyncWebServerRequest *request)
{
jpg_chunking_t *j = (jpg_chunking_t *)arg;
if (!index)
{
j->len = 0;
}
if (httpd_resp_send_chunk(j->req, (const char *)data, len) != ESP_OK)
{
return 0;
}
j->len += len;
return len;
}
static esp_err_t capture_handler(httpd_req_t *req)
{ {
camera_fb_t *fb = NULL; camera_fb_t *fb = NULL;
esp_err_t res = ESP_OK;
Serial.println("Capture Requested"); Serial.println("Capture Requested");
@ -90,208 +88,118 @@ static esp_err_t capture_handler(httpd_req_t *req)
if (!fb) if (!fb)
{ {
Serial.println("Camera capture failed"); Serial.println("Camera capture failed");
httpd_resp_send_500(req); request->send(500, "text/plain", "Camera Capture Failed");
return ESP_FAIL; return;
} }
httpd_resp_set_type(req, "image/jpeg");
httpd_resp_set_hdr(req, "Content-Disposition", "inline; filename=capture.jpg");
httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*");
size_t out_len, out_width, out_height;
uint8_t *out_buf;
bool s;
if (!detection_enabled || fb->width > 400)
{
size_t fb_len = 0; size_t fb_len = 0;
if (fb->format == PIXFORMAT_JPEG) if (fb->format == PIXFORMAT_JPEG)
{ {
fb_len = fb->len; fb_len = fb->len;
res = httpd_resp_send(req, (const char *)fb->buf, fb->len); request->send(200, "image/jpeg", (const char *)fb->buf);
} }
else else
{ {
jpg_chunking_t jchunk = {req, 0}; request->send(500, "text/plain", "Camera Capture Failed");
res = frame2jpg_cb(fb, 80, jpg_encode_stream, &jchunk) ? ESP_OK : ESP_FAIL;
httpd_resp_send_chunk(req, NULL, 0);
fb_len = jchunk.len;
} }
esp_camera_fb_return(fb); esp_camera_fb_return(fb);
int64_t fr_end = esp_timer_get_time(); int64_t fr_end = esp_timer_get_time();
Serial.printf("JPG: %uB %ums\n", (uint32_t)(fb_len), (uint32_t)((fr_end - fr_start) / 1000)); Serial.printf("JPG: %uB %ums\r\n", (uint32_t)(fb_len), (uint32_t)((fr_end - fr_start) / 1000));
return res;
} }
dl_matrix3du_t *image_matrix = dl_matrix3du_alloc(1, fb->width, fb->height, 3); void cmd_handler(AsyncWebServerRequest *request)
if (!image_matrix)
{ {
esp_camera_fb_return(fb); Serial.println("Command received!");
Serial.println("dl_matrix3du_alloc failed");
httpd_resp_send_500(req);
return ESP_FAIL;
}
out_buf = image_matrix->item;
out_len = fb->width * fb->height * 3;
out_width = fb->width;
out_height = fb->height;
s = fmt2rgb888(fb->buf, fb->len, fb->format, out_buf);
esp_camera_fb_return(fb);
if (!s)
{
dl_matrix3du_free(image_matrix);
Serial.println("to rgb888 failed");
httpd_resp_send_500(req);
return ESP_FAIL;
}
jpg_chunking_t jchunk = {req, 0};
s = fmt2jpg_cb(out_buf, out_len, out_width, out_height, PIXFORMAT_RGB888, 90, jpg_encode_stream, &jchunk);
dl_matrix3du_free(image_matrix);
if (!s)
{
Serial.println("JPEG compression failed");
return ESP_FAIL;
}
int64_t fr_end = esp_timer_get_time();
if (debugData)
{
Serial.printf("FACE: %uB %ums\n", (uint32_t)(jchunk.len), (uint32_t)((fr_end - fr_start) / 1000));
}
return res;
}
static esp_err_t cmd_handler(httpd_req_t *req)
{
char *buf;
size_t buf_len;
char variable[32] = {
0,
};
char value[32] = {
0,
};
flashLED(75); flashLED(75);
buf_len = httpd_req_get_url_query_len(req) + 1; if (!request->hasArg("var") || !request->hasArg("val"))
if (buf_len > 1)
{ {
buf = (char *)malloc(buf_len); Serial.println("No var or val");
if (!buf) request->send(404, "text/plain", "Invalid parameters");
{ return;
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, "var", variable, sizeof(variable)) == ESP_OK &&
httpd_query_key_value(buf, "val", value, sizeof(value)) == 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
{
httpd_resp_send_404(req);
return ESP_FAIL;
} }
int val = atoi(value); String variable = request->arg("var").c_str();
String value = request->arg("val").c_str();
Serial.print("Var ");
Serial.println(variable);
int val = atoi(value.c_str());
sensor_t *s = esp_camera_sensor_get(); sensor_t *s = esp_camera_sensor_get();
int res = 0; int res = 0;
if (!strcmp(variable, "framesize")) if (variable.compareTo("framesize") == 0)
{ {
if (s->pixformat == PIXFORMAT_JPEG) if (s->pixformat == PIXFORMAT_JPEG)
res = s->set_framesize(s, (framesize_t)val); res = s->set_framesize(s, (framesize_t)val);
} }
else if (!strcmp(variable, "quality")) else if (variable.compareTo("quality") == 0)
res = s->set_quality(s, val); res = s->set_quality(s, val);
else if (!strcmp(variable, "contrast")) else if (variable.compareTo("contrast") == 0)
res = s->set_contrast(s, val); res = s->set_contrast(s, val);
else if (!strcmp(variable, "brightness")) else if (variable.compareTo("brightness") == 0)
res = s->set_brightness(s, val); res = s->set_brightness(s, val);
else if (!strcmp(variable, "saturation")) else if (variable.compareTo("saturation") == 0)
res = s->set_saturation(s, val); res = s->set_saturation(s, val);
else if (!strcmp(variable, "gainceiling")) else if (variable.compareTo("gainceiling") == 0)
res = s->set_gainceiling(s, (gainceiling_t)val); res = s->set_gainceiling(s, (gainceiling_t)val);
else if (!strcmp(variable, "colorbar")) else if (variable.compareTo("colorbar") == 0)
res = s->set_colorbar(s, val); res = s->set_colorbar(s, val);
else if (!strcmp(variable, "awb")) else if (variable.compareTo("awb") == 0)
res = s->set_whitebal(s, val); res = s->set_whitebal(s, val);
else if (!strcmp(variable, "agc")) else if (variable.compareTo("agc") == 0)
res = s->set_gain_ctrl(s, val); res = s->set_gain_ctrl(s, val);
else if (!strcmp(variable, "aec")) else if (variable.compareTo("aec") == 0)
res = s->set_exposure_ctrl(s, val); res = s->set_exposure_ctrl(s, val);
else if (!strcmp(variable, "hmirror")) else if (variable.compareTo("hmirror") == 0)
res = s->set_hmirror(s, val); res = s->set_hmirror(s, val);
else if (!strcmp(variable, "vflip")) else if (variable.compareTo("vFlip") == 0)
res = s->set_vflip(s, val); res = s->set_vflip(s, val);
else if (!strcmp(variable, "awb_gain")) else if (variable.compareTo("awb_gain") == 0)
res = s->set_awb_gain(s, val); res = s->set_awb_gain(s, val);
else if (!strcmp(variable, "agc_gain")) else if (variable.compareTo("agc_gain") == 0)
res = s->set_agc_gain(s, val); res = s->set_agc_gain(s, val);
else if (!strcmp(variable, "aec_value")) else if (variable.compareTo("aec_value") == 0)
res = s->set_aec_value(s, val); res = s->set_aec_value(s, val);
else if (!strcmp(variable, "aec2")) else if (variable.compareTo("aec2") == 0)
res = s->set_aec2(s, val); res = s->set_aec2(s, val);
else if (!strcmp(variable, "dcw")) else if (variable.compareTo("dcw") == 0)
res = s->set_dcw(s, val); res = s->set_dcw(s, val);
else if (!strcmp(variable, "bpc")) else if (variable.compareTo("bpc") == 0)
res = s->set_bpc(s, val); res = s->set_bpc(s, val);
else if (!strcmp(variable, "wpc")) else if (variable.compareTo("wpc") == 0)
res = s->set_wpc(s, val); res = s->set_wpc(s, val);
else if (!strcmp(variable, "raw_gma")) else if (variable.compareTo("raw_gma") == 0)
res = s->set_raw_gma(s, val); res = s->set_raw_gma(s, val);
else if (!strcmp(variable, "lenc")) else if (variable.compareTo("lenc") == 0)
res = s->set_lenc(s, val); res = s->set_lenc(s, val);
else if (!strcmp(variable, "special_effect")) else if (variable.compareTo("special_effect") == 0)
res = s->set_special_effect(s, val); res = s->set_special_effect(s, val);
else if (!strcmp(variable, "wb_mode")) else if (variable.compareTo("wb_mode") == 0)
res = s->set_wb_mode(s, val); res = s->set_wb_mode(s, val);
else if (!strcmp(variable, "ae_level")) else if (variable.compareTo("ae_level") == 0)
res = s->set_ae_level(s, val); res = s->set_ae_level(s, val);
else if (!strcmp(variable, "rotate")) else if (variable.compareTo("rotate") == 0)
myRotation = val; myRotation = val;
else if (!strcmp(variable, "face_detect")) else if (variable.compareTo("lamp") == 0)
{
detection_enabled = val;
if (!detection_enabled)
{
recognition_enabled = 0;
}
}
else if (!strcmp(variable, "lamp") && (lampVal != -1))
{ {
lampVal = constrain(val, 0, 100); lampVal = constrain(val, 0, 100);
setLamp(lampVal); setLamp(lampVal);
} }
else if (!strcmp(variable, "save_prefs")) else if (variable.compareTo("save_prefs") == 0)
{ {
if (filesystem) if (filesystem)
savePrefs(SPIFFS); savePrefs(SPIFFS);
} }
else if (!strcmp(variable, "clear_prefs")) else if (variable.compareTo("clear_prefs") == 0)
{ {
if (filesystem) if (filesystem)
removePrefs(SPIFFS); removePrefs(SPIFFS);
} }
else if (!strcmp(variable, "reboot")) else if (variable.compareTo("reboot") == 0)
{ {
request->send(200, "text/plain", "Rebooting...");
Serial.print("REBOOT requested"); Serial.print("REBOOT requested");
for (int i = 0; i < 20; i++) for (int i = 0; i < 20; i++)
{ {
@ -299,24 +207,34 @@ static esp_err_t cmd_handler(httpd_req_t *req)
delay(150); delay(150);
Serial.print('.'); Serial.print('.');
} }
Serial.printf(" Thats all folks!\n\n"); Serial.printf(" Thats all folks!\r\n\r\n");
ESP.restart(); ESP.restart();
} }
else if (variable.compareTo("clear_wifi") == 0)
{
request->send(200, "text/plain", "Reseting WiFi...");
Serial.println("Wifi reset requested");
resetWiFiConfig();
}
else else
{ {
res = -1; res = -1;
} }
if (res) if (res)
{ {
return httpd_resp_send_500(req); Serial.print("Unable to determine command: ");
Serial.println(variable);
request->send(404, "text/plain", "Invalid parameters");
return;
} }
httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*"); request->send(200);
return httpd_resp_send(req, NULL, 0); return;
} }
static esp_err_t status_handler(httpd_req_t *req) void status_handler(AsyncWebServerRequest *request)
{ {
static char json_response[1024]; char json_response[1024];
sensor_t *s = esp_camera_sensor_get(); sensor_t *s = esp_camera_sensor_get();
char *p = json_response; char *p = json_response;
*p++ = '{'; *p++ = '{';
@ -346,325 +264,275 @@ static esp_err_t status_handler(httpd_req_t *req)
p += sprintf(p, "\"hmirror\":%u,", s->status.hmirror); p += sprintf(p, "\"hmirror\":%u,", s->status.hmirror);
p += sprintf(p, "\"dcw\":%u,", s->status.dcw); p += sprintf(p, "\"dcw\":%u,", s->status.dcw);
p += sprintf(p, "\"colorbar\":%u,", s->status.colorbar); p += sprintf(p, "\"colorbar\":%u,", s->status.colorbar);
p += sprintf(p, "\"cam_name\":\"%s\",", myName); p += sprintf(p, "\"cam_name\":\"%s\",", camera_name);
p += sprintf(p, "\"code_ver\":\"%s\",", myVer); p += sprintf(p, "\"code_ver\":\"%s\",", myVer);
p += sprintf(p, "\"rotate\":\"%d\",", myRotation); p += sprintf(p, "\"rotate\":\"%d\",", myRotation);
p += sprintf(p, "\"stream_url\":\"%s\"", streamURL); p += sprintf(p, "\"stream_url\":\"%s\"", streamURL);
*p++ = '}'; *p++ = '}';
*p++ = 0; *p++ = 0;
httpd_resp_set_type(req, "application/json");
httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*"); request->send(200, "application/json", json_response);
return httpd_resp_send(req, json_response, strlen(json_response));
} }
static esp_err_t favicon_16x16_handler(httpd_req_t *req) void favicon_16x16_handler(AsyncWebServerRequest *request)
{ {
httpd_resp_set_type(req, "image/png"); request->send(200, "image/png", (const char *)favicon_16x16_png);
httpd_resp_set_hdr(req, "Content-Encoding", "identity");
return httpd_resp_send(req, (const char *)favicon_16x16_png, favicon_16x16_png_len);
} }
static esp_err_t favicon_32x32_handler(httpd_req_t *req) void favicon_32x32_handler(AsyncWebServerRequest *request)
{ {
httpd_resp_set_type(req, "image/png"); request->send(200, "image/png", (const char *)favicon_32x32_png);
httpd_resp_set_hdr(req, "Content-Encoding", "identity");
return httpd_resp_send(req, (const char *)favicon_32x32_png, favicon_32x32_png_len);
} }
static esp_err_t favicon_ico_handler(httpd_req_t *req) void favicon_ico_handler(AsyncWebServerRequest *request)
{ {
httpd_resp_set_type(req, "image/x-icon"); request->send(200, "image/png", (const char *)favicon_ico);
httpd_resp_set_hdr(req, "Content-Encoding", "identity");
return httpd_resp_send(req, (const char *)favicon_ico, favicon_ico_len);
} }
static esp_err_t logo_svg_handler(httpd_req_t *req) void logo_svg_handler(AsyncWebServerRequest *request)
{ {
httpd_resp_set_type(req, "image/svg+xml"); request->send(200, "image/svg+xml", (const char *)logo_svg);
httpd_resp_set_hdr(req, "Content-Encoding", "identity");
return httpd_resp_send(req, (const char *)logo_svg, logo_svg_len);
} }
static esp_err_t dump_handler(httpd_req_t *req) void dump_handler(AsyncWebServerRequest *request)
{ {
flashLED(75); flashLED(75);
Serial.println("\nDump Requested"); Serial.println("\r\nDump Requested");
Serial.print("Preferences file: "); Serial.print("Preferences file: ");
dumpPrefs(SPIFFS); dumpPrefs(SPIFFS);
static char dumpOut[1200] = ""; char dumpOut[1200] = "";
char *d = dumpOut; char *d = dumpOut;
// Header // Header
d += sprintf(d, "<html><head><meta charset=\"utf-8\">\n"); d += sprintf(d, "<html><head><meta charset=\"utf-8\">\r\n");
d += sprintf(d, "<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n"); d += sprintf(d, "<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\r\n");
d += sprintf(d, "<title>%s - Status</title>\n", myName); d += sprintf(d, "<title>%s - Status</title>\r\n", camera_name);
d += sprintf(d, "<link rel=\"icon\" type=\"image/png\" sizes=\"32x32\" href=\"/favicon-32x32.png\">\n"); d += sprintf(d, "<link rel=\"icon\" type=\"image/png\" sizes=\"32x32\" href=\"/favicon-32x32.png\">\r\n");
d += sprintf(d, "<link rel=\"icon\" type=\"image/png\" sizes=\"16x16\" href=\"/favicon-16x16.png\">\n"); d += sprintf(d, "<link rel=\"icon\" type=\"image/png\" sizes=\"16x16\" href=\"/favicon-16x16.png\">\r\n");
d += sprintf(d, "<link rel=\"stylesheet\" type=\"text/css\" href=\"/style.css\">\n"); d += sprintf(d, "<link rel=\"stylesheet\" type=\"text/css\" href=\"/style.css\">\r\n");
d += sprintf(d, "</head>\n<body>\n"); d += sprintf(d, "</head>\r\n<body>\r\n");
d += sprintf(d, "<img src=\"/logo.svg\" style=\"position: relative; float: right;\">\n"); d += sprintf(d, "<img src=\"/logo.svg\" style=\"position: relative; float: right;\">\r\n");
d += sprintf(d, "<h1>ESP32 Cam Webserver</h1>\n"); d += sprintf(d, "<h1>ESP32 Cam Webserver</h1>\r\n");
// Module // Module
d += sprintf(d, "Name: %s<br>\n", myName); d += sprintf(d, "Name: %s<br>\r\n", camera_name);
Serial.printf("Name: %s\n", myName); Serial.printf("Name: %s\r\n", camera_name);
d += sprintf(d, "Firmware: %s (base: %s)<br>\n", myVer, baseVersion); d += sprintf(d, "Firmware: %s (base: %s)<br>\r\n", myVer, baseVersion);
Serial.printf("Firmware: %s (base: %s)\n", myVer, baseVersion); Serial.printf("Firmware: %s (base: %s)\r\n", myVer, baseVersion);
float sketchPct = 100 * sketchSize / sketchSpace; float sketchPct = 100 * sketchSize / sketchSpace;
d += sprintf(d, "Sketch Size: %i (total: %i, %.1f%% used)<br>\n", sketchSize, sketchSpace, sketchPct); d += sprintf(d, "Sketch Size: %i (total: %i, %.1f%% used)<br>\r\n", sketchSize, sketchSpace, sketchPct);
Serial.printf("Sketch Size: %i (total: %i, %.1f%% used)\n", sketchSize, sketchSpace, sketchPct); Serial.printf("Sketch Size: %i (total: %i, %.1f%% used)\r\n", sketchSize, sketchSpace, sketchPct);
d += sprintf(d, "MD5: %s<br>\n", sketchMD5.c_str()); d += sprintf(d, "MD5: %s<br>\r\n", sketchMD5.c_str());
Serial.printf("MD5: %s\n", sketchMD5.c_str()); Serial.printf("MD5: %s\r\n", sketchMD5.c_str());
d += sprintf(d, "ESP sdk: %s<br>\n", ESP.getSdkVersion()); d += sprintf(d, "ESP sdk: %s<br>\r\n", ESP.getSdkVersion());
Serial.printf("ESP sdk: %s\n", ESP.getSdkVersion()); Serial.printf("ESP sdk: %s\r\n", ESP.getSdkVersion());
// Network // Network
d += sprintf(d, "<h2>WiFi</h2>\n"); d += sprintf(d, "<h2>WiFi</h2>\r\n");
if (accesspoint) d += sprintf(d, "Mode: Client<br>\r\n");
{ Serial.printf("Mode: Client\r\n");
if (captivePortal)
{
d += sprintf(d, "Mode: AccessPoint with captive portal<br>\n");
Serial.printf("Mode: AccessPoint with captive portal\n");
}
else
{
d += sprintf(d, "Mode: AccessPoint<br>\n");
Serial.printf("Mode: AccessPoint\n");
}
d += sprintf(d, "SSID: %s<br>\n", apName);
Serial.printf("SSID: %s\n", apName);
}
else
{
d += sprintf(d, "Mode: Client<br>\n");
Serial.printf("Mode: Client\n");
String ssidName = WiFi.SSID(); String ssidName = WiFi.SSID();
d += sprintf(d, "SSID: %s<br>\n", ssidName.c_str()); d += sprintf(d, "SSID: %s<br>\r\n", ssidName.c_str());
Serial.printf("Ssid: %s\n", ssidName.c_str()); Serial.printf("Ssid: %s\r\n", ssidName.c_str());
d += sprintf(d, "Rssi: %i<br>\n", WiFi.RSSI()); d += sprintf(d, "Rssi: %i<br>\r\n", WiFi.RSSI());
Serial.printf("Rssi: %i\n", WiFi.RSSI()); Serial.printf("Rssi: %i\r\n", WiFi.RSSI());
String bssid = WiFi.BSSIDstr(); String bssid = WiFi.BSSIDstr();
d += sprintf(d, "BSSID: %s<br>\n", bssid.c_str()); d += sprintf(d, "BSSID: %s<br>\r\n", bssid.c_str());
Serial.printf("BSSID: %s\n", bssid.c_str()); Serial.printf("BSSID: %s\r\n", bssid.c_str());
}
d += sprintf(d, "IP address: %d.%d.%d.%d<br>\n", ip[0], ip[1], ip[2], ip[3]); d += sprintf(d, "IP address: %d.%d.%d.%d<br>\r\n", espIP[0], espIP[1], espIP[2], espIP[3]);
Serial.printf("IP address: %d.%d.%d.%d\n", ip[0], ip[1], ip[2], ip[3]); Serial.printf("IP address: %d.%d.%d.%d\r\n", espIP[0], espIP[1], espIP[2], espIP[3]);
if (!accesspoint) d += sprintf(d, "Http port: %i<br>\r\n", httpPort);
{ Serial.printf("Http port: %i\r\n", httpPort);
d += sprintf(d, "Netmask: %d.%d.%d.%d<br>\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<br>\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<br>\n", httpPort);
Serial.printf("Http port: %i\n", httpPort);
byte mac[6]; byte mac[6];
WiFi.macAddress(mac); WiFi.macAddress(mac);
d += sprintf(d, "MAC: %02X:%02X:%02X:%02X:%02X:%02X<br>\n", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); d += sprintf(d, "MAC: %02X:%02X:%02X:%02X:%02X:%02X<br>\r\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]); Serial.printf("MAC: %02X:%02X:%02X:%02X:%02X:%02X\r\n", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
// System // System
d += sprintf(d, "<h2>System</h2>\n"); d += sprintf(d, "<h2>System</h2>\r\n");
int64_t sec = esp_timer_get_time() / 1000000; int64_t sec = esp_timer_get_time() / 1000000;
int64_t upDays = int64_t(floor(sec / 86400)); int64_t upDays = int64_t(floor(sec / 86400));
int upHours = int64_t(floor(sec / 3600)) % 24; int upHours = int64_t(floor(sec / 3600)) % 24;
int upMin = int64_t(floor(sec / 60)) % 60; int upMin = int64_t(floor(sec / 60)) % 60;
int upSec = sec % 60; int upSec = sec % 60;
d += sprintf(d, "Up: %" PRId64 ":%02i:%02i:%02i (d:h:m:s)<br>\n", upDays, upHours, upMin, upSec); d += sprintf(d, "Up: %" PRId64 ":%02i:%02i:%02i (d:h:m:s)<br>\r\n", upDays, upHours, upMin, upSec);
Serial.printf("Up: %" PRId64 ":%02i:%02i:%02i (d:h:m:s)\n", upDays, upHours, upMin, upSec); Serial.printf("Up: %" PRId64 ":%02i:%02i:%02i (d:h:m:s)\r\n", upDays, upHours, upMin, upSec);
d += sprintf(d, "Freq: %i MHz<br>\n", ESP.getCpuFreqMHz()); d += sprintf(d, "Freq: %i MHz<br>\r\n", ESP.getCpuFreqMHz());
Serial.printf("Freq: %i MHz\n", ESP.getCpuFreqMHz()); Serial.printf("Freq: %i MHz\r\n", ESP.getCpuFreqMHz());
d += sprintf(d, "Heap: %i, free: %i, min free: %i, max block: %i<br>\n", ESP.getHeapSize(), ESP.getFreeHeap(), ESP.getMinFreeHeap(), ESP.getMaxAllocHeap()); d += sprintf(d, "Heap: %i, free: %i, min free: %i, max block: %i<br>\r\n", ESP.getHeapSize(), ESP.getFreeHeap(), ESP.getMinFreeHeap(), ESP.getMaxAllocHeap());
Serial.printf("Heap: %i, free: %i, min free: %i, max block: %i\n", ESP.getHeapSize(), ESP.getFreeHeap(), ESP.getMinFreeHeap(), ESP.getMaxAllocHeap()); Serial.printf("Heap: %i, free: %i, min free: %i, max block: %i\r\n", ESP.getHeapSize(), ESP.getFreeHeap(), ESP.getMinFreeHeap(), ESP.getMaxAllocHeap());
d += sprintf(d, "Psram: %i, free: %i, min free: %i, max block: %i<br>\n", ESP.getPsramSize(), ESP.getFreePsram(), ESP.getMinFreePsram(), ESP.getMaxAllocPsram()); d += sprintf(d, "Psram: %i, free: %i, min free: %i, max block: %i<br>\r\n", ESP.getPsramSize(), ESP.getFreePsram(), ESP.getMinFreePsram(), ESP.getMaxAllocPsram());
Serial.printf("Psram: %i, free: %i, min free: %i, max block: %i\n", ESP.getPsramSize(), ESP.getFreePsram(), ESP.getMinFreePsram(), ESP.getMaxAllocPsram()); Serial.printf("Psram: %i, free: %i, min free: %i, max block: %i\r\n", ESP.getPsramSize(), ESP.getFreePsram(), ESP.getMinFreePsram(), ESP.getMaxAllocPsram());
if (filesystem) if (filesystem)
{ {
d += sprintf(d, "Spiffs: %i, used: %i<br>\n", SPIFFS.totalBytes(), SPIFFS.usedBytes()); d += sprintf(d, "Spiffs: %i, used: %i<br>\r\n", SPIFFS.totalBytes(), SPIFFS.usedBytes());
Serial.printf("Spiffs: %i, used: %i\n", SPIFFS.totalBytes(), SPIFFS.usedBytes()); Serial.printf("Spiffs: %i, used: %i\r\n", SPIFFS.totalBytes(), SPIFFS.usedBytes());
} }
// Footer // Footer
d += sprintf(d, "<br><div class=\"input-group\">\n"); d += sprintf(d, "<br><div class=\"input-group\">\r\n");
d += sprintf(d, "<button title=\"Refresh this page\" onclick=\"location.replace(document.URL)\">Refresh</button>\n"); d += sprintf(d, "<button title=\"Refresh this page\" onclick=\"location.replace(document.URL)\">Refresh</button>\r\n");
d += sprintf(d, "<button title=\"Close this page\" onclick=\"javascript:window.close()\">Close</button>\n"); d += sprintf(d, "<button title=\"Close this page\" onclick=\"javascript:window.close()\">Close</button>\r\n");
d += sprintf(d, "</div>\n</body>\n</html>\n"); d += sprintf(d, "</div>\r\n</body>\r\n</html>\r\n");
*d++ = 0; *d++ = 0;
httpd_resp_set_type(req, "text/html");
httpd_resp_set_hdr(req, "Content-Encoding", "identity"); request->send(200, "image/html", dumpOut);
return httpd_resp_send(req, dumpOut, strlen(dumpOut));
} }
static esp_err_t style_handler(httpd_req_t *req) void style_handler(AsyncWebServerRequest *request)
{ {
httpd_resp_set_type(req, "text/css"); request->send(200, "text/css", (const char *)style_css);
httpd_resp_set_hdr(req, "Content-Encoding", "identity");
return httpd_resp_send(req, (const char *)style_css, style_css_len);
} }
static esp_err_t index_handler(httpd_req_t *req) void handleIndex(AsyncWebServerRequest *request)
{
flashLED(150);
// See if we have a specific target (full/simple/portal) and serve as appropriate
String view = default_index;
if (request->hasArg("view"))
{
view = request->arg("view").c_str();
}
if (strncmp(view.c_str(), "simple", sizeof(view)) == 0)
{
Serial.println("Simple index page requested");
request->send_P(200, "text/html", (const char *)index_simple_html);
return;
}
else if (strncmp(view.c_str(), "full", sizeof(view)) == 0)
{
sensor_t *s = esp_camera_sensor_get();
if (s->id.PID == OV3660_PID)
{
Serial.println("Full OV3660 index page requested");
request->send_P(200, "text/html", (const char *)index_ov3660_html);
return;
}
Serial.println("Full OV2640 index page requested");
request->send_P(200, "text/html",(const char *)index_ov2640_html);
return;
}
Serial.print("Unknown page requested: ");
Serial.println(view);
request->send(404, "text/plain", "Unknown page requested");
}
void handleFirmware(AsyncWebServerRequest *request)
{ {
char *buf;
size_t buf_len;
char view[32] = {
0,
};
flashLED(75); flashLED(75);
// See if we have a specific target (full/simple/portal) and serve as appropriate // 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) Serial.println("Firmware page requested");
{ request->send(200, "text/html", firmware_html.c_str());
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)
{ String getHTMLHead()
if (httpd_query_key_value(buf, "view", view, sizeof(view)) == ESP_OK)
{ {
String header = F("<!DOCTYPE html><html lang=\"en\"><head>");
header += F("<link href=\"/local.css\" rel=\"stylesheet\">");
header += F("</head>");
header += F("<body>");
return header;
} }
else
String getHTMLFoot()
{ {
free(buf); return F("</body></html>");
httpd_resp_send_404(req);
return ESP_FAIL;
} }
} void handleUpdate(AsyncWebServerRequest *request)
else
{ {
free(buf); String response_message;
httpd_resp_send_404(req); response_message.reserve(1000);
return ESP_FAIL; response_message = getHTMLHead();
} response_message += "<script> function notify_update() {document.getElementById(\"update\").innerHTML = \"<h2>Updating...</h2>\"\; } </script>";
free(buf); response_message += "Firmware = *.esp32.bin<br>SPIFFS = *.spiffs.bin<br> \
} <form method='POST' action='/doUpdate' enctype='multipart/form-data' target='_self' onsubmit='notify_update()'> \
else <input type='file' name='update'><br> \
<input type='submit' value='Do update'></form> \
<div id=\"update\"></div>";
response_message += getHTMLFoot();
request->send(200, "text/html", response_message);
};
void handleDoUpdate(AsyncWebServerRequest *request, const String &filename, size_t index, uint8_t *data, size_t len, bool final)
{ {
// no target specified; default. updating = true;
strcpy(view, default_index); delay(500);
// If captive portal is active send that instead
if (captivePortal) if (!index)
{ {
strcpy(view, "portal"); // check file names for type
int cmd = (filename.indexOf(F(".spiffs.bin")) > -1) ? U_SPIFFS : U_FLASH;
if (cmd == U_FLASH && !(filename.indexOf(F("esp32.bin")) > -1))
return; // wrong image for ESP32
if (!Update.begin(UPDATE_SIZE_UNKNOWN, cmd))
{
Update.printError(Serial);
} }
} }
if (strncmp(view, "simple", sizeof(view)) == 0) if (Update.write(data, len) != len)
{ {
Serial.println("Simple index page requested"); Update.printError(Serial);
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)
if (final)
{ {
Serial.println("Full index page requested"); if (!Update.end(true))
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); Update.printError(Serial);
}
return httpd_resp_send(req, (const char *)index_ov2640_html, index_ov2640_html_len);
}
else if (strncmp(view, "portal", sizeof(view)) == 0)
{
//Prototype captive portal landing page.
Serial.println("Portal page requested");
std::string s(portal_html);
size_t index;
while ((index = s.find("<APPURL>")) != std::string::npos)
s.replace(index, strlen("<APPURL>"), httpURL);
while ((index = s.find("<STREAMURL>")) != std::string::npos)
s.replace(index, strlen("<STREAMURL>"), streamURL);
while ((index = s.find("<CAMNAME>")) != std::string::npos)
s.replace(index, strlen("<CAMNAME>"), myName);
httpd_resp_set_type(req, "text/html");
httpd_resp_set_hdr(req, "Content-Encoding", "identity");
return httpd_resp_send(req, (const char *)s.c_str(), s.length());
} }
else else
{ {
Serial.print("Unknown page requested: "); String response_message;
Serial.println(view); response_message.reserve(1000);
httpd_resp_send_404(req); response_message = getHTMLHead();
return ESP_FAIL; response_message += "<h2>Please wait while the device reboots</h2> <meta http-equiv=\"refresh\" content=\"20;url=/\" />";
response_message += getHTMLFoot();
AsyncWebServerResponse *response = request->beginResponse(200, "text/html", response_message);
response->addHeader("Refresh", "20");
response->addHeader("Location", "/");
request->send(response);
delay(100);
ESP.restart();
}
} }
} }
void startCameraServer() void startCameraServer()
{ {
httpd_config_t config = HTTPD_DEFAULT_CONFIG(); Serial.printf("Starting web server on port: '%d'\r\n", httpPort);
config.max_uri_handlers = 12; // we use more than the default 8 (on port 80)
httpd_uri_t index_uri = { appServer.on("/", HTTP_GET, [](AsyncWebServerRequest *request) {
.uri = "/", handleIndex(request);
.method = HTTP_GET, });
.handler = index_handler, appServer.on("/firmware", HTTP_GET, handleFirmware);
.user_ctx = NULL}; appServer.on("/status", HTTP_GET, status_handler);
httpd_uri_t status_uri = { appServer.on("/control", HTTP_GET, cmd_handler);
.uri = "/status", appServer.on("/capture", HTTP_GET, capture_handler);
.method = HTTP_GET, appServer.on("/style.css", HTTP_GET, style_handler);
.handler = status_handler, appServer.on("/favicon-16x16.png", HTTP_GET, favicon_16x16_handler);
.user_ctx = NULL}; appServer.on("/favicon-32x32.png", HTTP_GET, favicon_32x32_handler);
httpd_uri_t cmd_uri = { appServer.on("/favicon.ico", HTTP_GET, favicon_ico_handler);
.uri = "/control", appServer.on("/logo.svg", HTTP_GET, logo_svg_handler);
.method = HTTP_GET, appServer.on("/dump", HTTP_GET, dump_handler);
.handler = cmd_handler, /*handling uploading firmware file */
.user_ctx = NULL}; appServer.on("/update", HTTP_GET, [](AsyncWebServerRequest *request) {
httpd_uri_t capture_uri = { handleUpdate(request);
.uri = "/capture", });
.method = HTTP_GET, appServer.on(
.handler = capture_handler, "/doUpdate", HTTP_POST, [](AsyncWebServerRequest *request) {}, [](AsyncWebServerRequest *request, const String &filename, size_t index, uint8_t *data, size_t len, bool final) { handleDoUpdate(request, filename, index, data, len, final); });
.user_ctx = NULL};
httpd_uri_t style_uri = {
.uri = "/style.css",
.method = HTTP_GET,
.handler = style_handler,
.user_ctx = NULL};
httpd_uri_t favicon_16x16_uri = {
.uri = "/favicon-16x16.png",
.method = HTTP_GET,
.handler = favicon_16x16_handler,
.user_ctx = NULL};
httpd_uri_t favicon_32x32_uri = {
.uri = "/favicon-32x32.png",
.method = HTTP_GET,
.handler = favicon_32x32_handler,
.user_ctx = NULL};
httpd_uri_t favicon_ico_uri = {
.uri = "/favicon.ico",
.method = HTTP_GET,
.handler = favicon_ico_handler,
.user_ctx = NULL};
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};
config.server_port = httpPort; DefaultHeaders::Instance().addHeader("Access-Control-Allow-Origin", "*");
config.ctrl_port = httpPort; appServer.begin();
Serial.printf("Starting web server on port: '%d'\n", config.server_port); Serial.println("Web server started!");
if (httpd_start(&camera_httpd, &config) == ESP_OK)
{ sprintf(httpURL, "http://%d.%d.%d.%d:%d/", espIP[0], espIP[1], espIP[2], espIP[3], httpPort);
// Note; config.max_uri_handlers (above) must be >= the number of handlers
httpd_register_uri_handler(camera_httpd, &index_uri);
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, &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);
}
} }

View File

@ -1,78 +1,42 @@
#include <Arduino.h> #include <Arduino.h>
#include "esp_camera.h"
#include <WiFi.h> #include <WiFi.h>
#include <DNSServer.h> #include "esp_camera.h"
#include "src/version.h"
#include "myconfig.h"
#include "camera_pins.h"
#include "storage.h"
#include <WiFiClient.h>
#include <WebServer.h>
#define APP_CPU 1 // IP address, Netmask and Gateway, populated when connected
#define PRO_CPU 0 extern IPAddress espIP;
extern IPAddress espSubnet;
extern IPAddress espGateway;
// The app and stream URLs
extern char httpURL[64];
extern char streamURL[64];
extern char camera_name[];
extern void startWiFi();
extern void startCameraServer();
extern void startStreamServer();
extern void startMQTTClient();
extern void handleMQTT();
// Primary config, or defaults. // Primary config, or defaults.
#if __has_include("myconfig.h") #if __has_include("myconfig.h")
#include "myconfig.h" #include "myconfig.h"
#else #else
#warning "Using Defaults: Copy myconfig.sample.h to myconfig.h and edit that to use your own settings" #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 #define CAMERA_MODEL_AI_THINKER
struct station
{
const char ssid[64];
const char password[64];
const bool dhcp;
} stationList[] = {{"ESP32-CAM-CONNECT", "InsecurePassword", true}};
#endif #endif
// Upstream version string
#include "src/version.h"
// Pin Mappings
#include "camera_pins.h"
// Internal filesystem (SPIFFS)
// used for non-volatile camera settings and face DB store
#include "storage.h"
// Sketch Info // Sketch Info
int sketchSize; int sketchSize;
int sketchSpace; int sketchSpace;
String sketchMD5; String sketchMD5;
// 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;
// IP address, Netmask and Gateway, populated when connected
IPAddress ip;
IPAddress net;
IPAddress gw;
// Declare external function from app_httpd.cpp
extern void startCameraServer();
// Declare external function from esp32-streamserver.cpp
extern void startStreamServer();
// Declare external function from esp32-streamserver.cpp
void startMQTTClient(const char *, int, const char *, const char *, const void *);
// A Name for the Camera. (set in myconfig.h)
#if defined(CAM_NAME)
char myName[] = CAM_NAME;
#else
char myName[] = "ESP32 camera server";
#endif
#if !defined(WIFI_WATCHDOG)
#define WIFI_WATCHDOG 5000
#endif
// 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;
#else
int firstStation = 0;
#endif
// Select bvetween full and simple index as the default. // Select bvetween full and simple index as the default.
#if defined(DEFAULT_INDEX_FULL) #if defined(DEFAULT_INDEX_FULL)
char default_index[] = "full"; char default_index[] = "full";
@ -80,16 +44,6 @@ char default_index[] = "full";
char default_index[] = "simple"; char default_index[] = "simple";
#endif #endif
// DNS server
const byte DNS_PORT = 53;
DNSServer dnsServer;
bool captivePortal = false;
char apName[64] = "Undefined";
// The app and stream URLs
char httpURL[64] = {"Undefined"};
char streamURL[64] = {"Undefined"};
// This will be displayed to identify the firmware // This will be displayed to identify the firmware
char myVer[] PROGMEM = __DATE__ " @ " __TIME__; char myVer[] PROGMEM = __DATE__ " @ " __TIME__;
@ -124,18 +78,6 @@ bool filesystem = false;
bool filesystem = true; bool filesystem = true;
#endif #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
#else
int8_t detection_enabled = 0;
int8_t recognition_enabled = 0;
#endif
// Debug Data for stream and capture // Debug Data for stream and capture
#if defined(DEBUG_DEFAULT_ON) #if defined(DEBUG_DEFAULT_ON)
bool debugData = true; bool debugData = true;
@ -174,200 +116,6 @@ void setLamp(int newVal)
Serial.println(brightness); Serial.println(brightness);
} }
} }
void WifiSetup()
{
// Feedback that we are now attempting to connect
flashLED(300);
delay(100);
flashLED(300);
Serial.println("Starting WiFi");
Serial.print("Known external SSIDs: ");
if (stationCount > firstStation)
{
for (int i = firstStation; i < stationCount; i++)
Serial.printf(" '%s'", stationList[i].ssid);
}
else
{
Serial.print("None");
}
Serial.println();
byte mac[6];
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]);
int bestStation = -1;
long bestRSSI = -1024;
if (stationCount > firstStation)
{
// We have a list to scan
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
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();
}
}
}
else
{
// No list to scan, therefore we are an accesspoint
accesspoint = true;
}
if (bestStation == -1)
{
if (!accesspoint)
{
#if defined(WIFI_AP_ENABLE)
Serial.println("No known networks found, entering AccessPoint fallback mode");
accesspoint = true;
#else
Serial.println("No known networks found");
#endif
}
else
{
Serial.println("AccessPoint mode selected in config");
}
}
else
{
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(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 && (WiFi.status() != WL_CONNECTED))
{
// The accesspoint has been enabled, and we have not connected to any existing networks
#if defined(AP_CHAN)
Serial.println("Setting up Fixed Channel AccessPoint");
Serial.print(" SSID : ");
Serial.println(stationList[0].ssid);
Serial.print(" Password : ");
Serial.println(stationList[0].password);
Serial.print(" Channel : ");
Serial.println(AP_CHAN);
WiFi.softAP(stationList[0].ssid, stationList[0].password, AP_CHAN);
#else
Serial.println("Setting up AccessPoint");
Serial.print(" SSID : ");
Serial.println(stationList[0].ssid);
Serial.print(" Password : ");
Serial.println(stationList[0].password);
WiFi.softAP(stationList[0].ssid, stationList[0].password);
#endif
#if defined(AP_ADDRESS)
// User has specified the AP details; apply them after a short delay
// (https://github.com/espressif/arduino-esp32/issues/985#issuecomment-359157428)
delay(100);
IPAddress local_IP(AP_ADDRESS);
IPAddress gateway(AP_ADDRESS);
IPAddress subnet(255, 255, 255, 0);
WiFi.softAPConfig(local_IP, gateway, subnet);
#endif
// Note AP details
ip = WiFi.softAPIP();
net = WiFi.subnetMask();
gw = WiFi.gatewayIP();
strcpy(apName, stationList[0].ssid);
Serial.printf("IP address: %d.%d.%d.%d\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;
}
}
}
void setup() void setup()
{ {
Serial.begin(115200); Serial.begin(115200);
@ -375,18 +123,17 @@ void setup()
Serial.println(); Serial.println();
Serial.println("===="); Serial.println("====");
Serial.print("esp32-cam-webserver: "); Serial.print("esp32-cam-webserver: ");
Serial.println(myName); Serial.println(camera_name);
Serial.print("Code Built: "); Serial.print("Code Built: ");
Serial.println(myVer); Serial.println(myVer);
Serial.print("Base Release: "); Serial.print("Base Release: ");
Serial.println(baseVersion); Serial.println(baseVersion);
if (stationCount == 0) startWiFi();
while (!WiFi.isConnected())
{ {
Serial.println("\nFatal Error; Halting"); delay(50);
Serial.println("No wifi ssid details have been configured; we cannot connect to WiFi or start our own AccessPoint");
while (true)
delay(1000);
} }
#if defined(LED_PIN) // If we have a notification LED, set it to output #if defined(LED_PIN) // If we have a notification LED, set it to output
@ -445,7 +192,7 @@ void setup()
{ {
delay(100); // need a delay here or the next serial o/p gets missed delay(100); // need a delay here or the next serial o/p gets missed
Serial.println("Halted: Camera sensor failed to initialise"); Serial.println("Halted: Camera sensor failed to initialise");
Serial.println("Will reboot to try again in 10s\n"); Serial.println("Will reboot to try again in 10s\r\n");
delay(10000); delay(10000);
ESP.restart(); ESP.restart();
} }
@ -480,23 +227,23 @@ void setup()
// M5 Stack Wide has special needs // M5 Stack Wide has special needs
#if defined(CAMERA_MODEL_M5STACK_WIDE) #if defined(CAMERA_MODEL_M5STACK_WIDE)
s->set_vflip(s, 1); espCamSensor->set_vflip(espCamSensor, 1);
s->set_hmirror(s, 1); espCamSensor->set_hmirror(espCamSensor, 1);
#endif #endif
// Config can override mirror and flip // Config can override mirror and flip
#if defined(H_MIRROR) #if defined(H_MIRROR)
s->set_hmirror(s, H_MIRROR); espCamSensor->set_hmirror(espCamSensor, H_MIRROR);
#endif #endif
#if defined(V_FLIP) #if defined(V_FLIP)
s->set_vflip(s, V_FLIP); sespCamSensor->set_vflip(espCamSensor, V_FLIP);
#endif #endif
// set initial frame rate // set initial frame rate
#if defined(DEFAULT_RESOLUTION) #if defined(DEFAULT_RESOLUTION)
espCamSensor->set_framesize(espCamSensor, DEFAULT_RESOLUTION); espCamSensor->set_framesize(espCamSensor, DEFAULT_RESOLUTION);
#else #else
s->set_framesize(s, FRAMESIZE_SVGA); espCamSensor->set_framesize(espCamSensor, FRAMESIZE_SVGA);
#endif #endif
/* /*
@ -507,34 +254,6 @@ void setup()
* https://github.com/espressif/esp32-camera/blob/master/driver/include/sensor.h#L149 * https://github.com/espressif/esp32-camera/blob/master/driver/include/sensor.h#L149
*/ */
//s->set_framesize(s, FRAMESIZE_SVGA); // FRAMESIZE_[QQVGA|HQVGA|QVGA|CIF|VGA|SVGA|XGA|SXGA|UXGA|QXGA(ov3660)]);
//s->set_quality(s, val); // 10 to 63
//s->set_brightness(s, 0); // -2 to 2
//s->set_contrast(s, 0); // -2 to 2
//s->set_saturation(s, 0); // -2 to 2
//s->set_special_effect(s, 0); // 0 to 6 (0 - No Effect, 1 - Negative, 2 - Grayscale, 3 - Red Tint, 4 - Green Tint, 5 - Blue Tint, 6 - Sepia)
//s->set_whitebal(s, 1); // aka 'awb' in the UI; 0 = disable , 1 = enable
//s->set_awb_gain(s, 1); // 0 = disable , 1 = enable
//s->set_wb_mode(s, 0); // 0 to 4 - if awb_gain enabled (0 - Auto, 1 - Sunny, 2 - Cloudy, 3 - Office, 4 - Home)
//s->set_exposure_ctrl(s, 1); // 0 = disable , 1 = enable
//s->set_aec2(s, 0); // 0 = disable , 1 = enable
//s->set_ae_level(s, 0); // -2 to 2
//s->set_aec_value(s, 300); // 0 to 1200
//s->set_gain_ctrl(s, 1); // 0 = disable , 1 = enable
//s->set_agc_gain(s, 0); // 0 to 30
//s->set_gainceiling(s, (gainceiling_t)0); // 0 to 6
//s->set_bpc(s, 0); // 0 = disable , 1 = enable
//s->set_wpc(s, 1); // 0 = disable , 1 = enable
//s->set_raw_gma(s, 1); // 0 = disable , 1 = enable
//s->set_lenc(s, 1); // 0 = disable , 1 = enable
//s->set_hmirror(s, 0); // 0 = disable , 1 = enable
//s->set_vflip(s, 0); // 0 = disable , 1 = enable
//s->set_dcw(s, 1); // 0 = disable , 1 = enable
//s->set_colorbar(s, 0); // 0 = disable , 1 = enable
// We now have camera with default init
// check for saved preferences and apply them
if (filesystem) if (filesystem)
{ {
filesystemStart(); filesystemStart();
@ -543,13 +262,9 @@ void setup()
} }
else else
{ {
Serial.println("No Internal Filesystem, cannot save preferences or face DB"); Serial.println("No Internal Filesystem, cannot save preferences");
} }
/*
* Camera setup complete; initialise the rest of the hardware.
*/
// Initialise and set the lamp // Initialise and set the lamp
if (lampVal != -1) if (lampVal != -1)
{ {
@ -562,34 +277,17 @@ void setup()
Serial.println("No lamp, or lamp disabled in config"); Serial.println("No lamp, or lamp disabled in config");
} }
// Having got this far; start Wifi and loop until we are connected or have started an AccessPoint
while ((WiFi.status() != WL_CONNECTED) && !accesspoint)
{
WifiSetup();
delay(1000);
}
// Now we have a network we can start the two http handlers for the UI and Stream. // Now we have a network we can start the two http handlers for the UI and Stream.
startCameraServer(); startCameraServer();
startStreamServer(); startStreamServer();
// Construct the app and stream URLs Serial.printf("\r\nCamera Ready!\r\nUse '%s' to connect\r\n", httpURL);
sprintf(httpURL, "http://%d.%d.%d.%d:%d/", ip[0], ip[1], ip[2], ip[3], HTTP_PORT);
Serial.printf("\nCamera Ready!\nUse '%s' to connect\n", httpURL);
if (debugData)
Serial.println("Camera debug data is enabled (send any char to disable)");
else
Serial.println("Camera debug data is disabled (send any char to enable)");
/* /*
* Main camera streams running, now enable MQTT * Main camera streams running, now enable MQTT
* */ * */
#if defined(MQTT_HOST) startMQTTClient();
startMQTTClient(MQTT_HOST, MQTT_PORT, MQTT_TOPIC, myName, MQTT_CERT);
#endif
// Used when dumping status; these are slow functions, so just do them once during startup // Used when dumping status; these are slow functions, so just do them once during startup
sketchSize = ESP.getSketchSize(); sketchSize = ESP.getSketchSize();
@ -604,19 +302,6 @@ void loop()
* The stream and URI handler processes initiated by the startCameraServer() call at the * 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. * end of setup() will handle the camera and UI processing from now on.
*/ */
if (accesspoint)
{
// 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 // client mode can fail; so reconnect as appropriate
static bool warned = false; static bool warned = false;
if (WiFi.status() == WL_CONNECTED) if (WiFi.status() == WL_CONNECTED)
@ -628,28 +313,7 @@ void loop()
Serial.println("WiFi reconnected"); Serial.println("WiFi reconnected");
warned = false; warned = false;
} }
// loop here for WIFI_WATCHDOG, turning debugData true/false depending on serial input.. handleMQTT();
unsigned long start = millis();
while (millis() - start < WIFI_WATCHDOG)
{
delay(100);
if (Serial.available())
{
// Toggle debug output on serial input
if (debugData)
{
debugData = false;
Serial.println("Camera debug data is disabled (send any char to enable)");
}
else
{
debugData = true;
Serial.println("Camera debug data is enabled (send any char to disable)");
}
}
while (Serial.available())
Serial.read(); // chomp the buffer
}
} }
else else
{ {
@ -661,7 +325,6 @@ void loop()
Serial.println("WiFi disconnected, retrying"); Serial.println("WiFi disconnected, retrying");
warned = true; warned = true;
} }
WifiSetup(); startWiFi();
}
} }
} }

View File

@ -1,119 +1,105 @@
#include "Arduino.h" #include "Arduino.h"
#include <WiFi.h>
#include <WiFiClientSecure.h> #include <WiFiClientSecure.h>
#include "PubSubClient.h" #include "PubSubClient.h"
#include <Preferences.h>
#include <ESPmDNS.h> #include <ESPmDNS.h>
#include <cstring> #include <cstring>
#include "myconfig.h"
#include <string.h>
extern UBaseType_t activeClients; extern UBaseType_t activeClients;
extern int mqtt_port;
extern bool updating;
char KEY_CAM_NAME[] = "cam_name";
char KEY_MMQT_HOST[] = "mmqt_host";
char KEY_MMQT_PORT[] = "mmqt_port";
char KEY_MMQT_USER[] = "mmqt_user";
char KEY_MMQT_PASS[] = "mmqt_pass";
char KEY_MMQT_TOPIC[] = "mmqt_topic";
char KEY_MMQT_CERT[] = "mmqt_cert";
char camera_name[64];
char client_id[64];
char mqtt_topic[64];
const unsigned long publishPeriod = 60000;
unsigned long lastMQTTPublish = 0;
const static char *MQTT_JSON = "{ \
\"ClientId\":\"%s\", \
\"CameraName\":\"%s\", \
\"Millis\":\"%lu\", \
\"ActiveClients\":%d \
}";
WiFiClient wifiClient; WiFiClient wifiClient;
PubSubClient mqttClient(wifiClient); PubSubClient mqttClient(wifiClient);
uint16_t mqtt_port = 1883;
char *mqtt_host = new char[64];
char *mqtt_pub_topic = new char[256];
char *mqtt_client_id = new char[64];
const char *MQTT_JSON = "{\"ClientId\":\"%s\",\"ActiveClients\":%d}";
void startMQTTClient(char *host, char *topic, char *cert);
void mqttconnect(); void mqttconnect();
void mqttCB(void *pvParameters); void startMQTTClient(char *host, char *topic, char *cert);
TaskHandle_t tMQTT; // handles client connection to the MQTT Server
void mqttCB(void *pvParameters)
{
TickType_t xLastWakeTime = xTaskGetTickCount();
TickType_t xFrequency = pdMS_TO_TICKS(60000); //Every minute
while (true)
{
/* if client was disconnected then try to reconnect again */
if (!mqttClient.connected())
{
mqttconnect();
}
Serial.printf("ActiveClients %d\n", activeClients);
char msg[64] = "";
snprintf(msg, 64, MQTT_JSON, mqtt_client_id, activeClients);
Serial.printf("Publishing message %s\n", msg);
/* publish the message */
mqttClient.publish(mqtt_pub_topic, msg);
// Let other tasks run after serving every client
taskYIELD();
vTaskDelayUntil(&xLastWakeTime, xFrequency);
}
}
void mqttconnect() void mqttconnect()
{ {
u32_t defaultIp = 0;
IPAddress noConnIP = IPAddress(defaultIp);
IPAddress serverIp = IPAddress(defaultIp);
unsigned long period = 5000;
unsigned long lastRun = 0;
/* Loop until reconnected */ Preferences preferences;
while (!mqttClient.connected()) preferences.begin("ESP32-CAM", true);
{
if (millis() - lastRun < period) String tmp_camera_name = preferences.getString(KEY_CAM_NAME, "Camera Name");
String tmp_mqtt_topic = preferences.getString(KEY_MMQT_TOPIC, "default/topic");
int mqtt_port = preferences.getInt(KEY_MMQT_PORT, 1883);
String mqtt_host = preferences.getString(KEY_MMQT_HOST, "");
String mqtt_user = preferences.getString(KEY_MMQT_USER, "");
String mqtt_pass = preferences.getString(KEY_MMQT_PASS, "");
String mqtt_cert = preferences.getString(KEY_MMQT_CERT, "");
preferences.end();
if (mqtt_host == "" || mqtt_pass == "" || mqtt_user == "")
{ {
yield(); Serial.printf("Couldn't establish MQTT Connection: %s, %s, %s", mqtt_host.c_str(), mqtt_user.c_str(), mqtt_pass.c_str());
continue; return;
} }
lastRun = millis(); sprintf(camera_name, "%s", tmp_camera_name.c_str());
sprintf(mqtt_topic, "%s", tmp_mqtt_topic.c_str());
/* Configure the MQTT server with IPaddress and port. */ /* Configure the MQTT server with IPaddress and port. */
while (noConnIP == serverIp) Serial.printf("Querying '%s:%d'...\r\n", mqtt_host.c_str(), mqtt_port);
{
Serial.printf("Querying '%s:%d'...\n", mqtt_host, mqtt_port);
serverIp = MDNS.queryHost(mqtt_host); IPAddress serverIp = MDNS.queryHost(mqtt_host);
Serial.printf("IP address of server: %s\n", serverIp.toString().c_str());
if (noConnIP == serverIp)
{
WiFi.hostByName(mqtt_host, serverIp);
Serial.printf("IP address of server: %s\n", serverIp.toString().c_str());
if ((uint32_t)serverIp == 0) if ((uint32_t)serverIp == 0)
{ {
serverIp = noConnIP; WiFi.hostByName(mqtt_host.c_str(), serverIp);
}
} }
if (noConnIP != serverIp) if ((uint32_t)serverIp == 0)
{ {
break; return;
}
yield();
} }
mqttClient.setServer(serverIp, mqtt_port); Serial.printf("IP address of MQTT server: %s\r\n", serverIp.toString().c_str());
/* connect now */ tmp_camera_name.toUpperCase();
char clientId[100] = {};
char random[32] = {'-'}; char random[32] = {'-'};
char char1[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
srand((unsigned int)(time(NULL))); for (int index = 1; index < 31; index++)
int index = 0;
char char1[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
for (index = 1; index < 31; index++)
{ {
random[index] = char1[rand() % (sizeof char1 - 1)]; random[index] = char1[rand() % (sizeof char1 - 1)];
} }
Serial.println(random); sprintf(client_id, "%s%s", tmp_camera_name.c_str(), random);
sprintf(clientId, "%s", mqtt_client_id);
strcat(clientId, random);
Serial.printf("MQTT connecting as '%s'...\n", clientId); /* connect now */
if (mqttClient.connect(clientId, "testUser", "1234")) //, "security/healthcheck", 20, true, "WillMessage", true)) Serial.printf("MQTT connecting as '%s' (%s:%s)...\r\n", client_id, mqtt_host.c_str(), mqtt_pass.c_str());
try
{ {
Serial.println("connected");
mqttClient.setServer(serverIp, mqtt_port);
if (mqttClient.connect(client_id, mqtt_user.c_str(), mqtt_pass.c_str()))
{
Serial.printf("MQTT Connected to '%s'\r\n", mqtt_host.c_str());
} }
else else
{ {
@ -123,27 +109,52 @@ void mqttconnect()
Serial.printf("try again in %d seconds/n", 5); Serial.printf("try again in %d seconds/n", 5);
} }
} }
catch (const std::exception &e)
{ // reference to the base of a polymorphic object
Serial.println(e.what()); // information from length_error printed
}
} }
void startMQTTClient(const char *host, int port, const char *topic, const char *client_id, const void *cert) void handleMQTT()
{ {
mqtt_port = port;
strcpy(mqtt_pub_topic, topic);
strcpy(mqtt_host, host);
strcpy(mqtt_client_id, client_id);
Serial.printf("Starting MQTT Client '%s'\n", client_id); if (updating)
{
Serial.println("Updating, killing MQTT Server");
mqttClient.disconnect();
}
mqttClient.loop();
unsigned long now = millis();
if (lastMQTTPublish == 0 || ((now - lastMQTTPublish) >= publishPeriod))
{
/* if client was disconnected then try to reconnect again */
if (!mqttClient.connected())
{
mqttconnect();
}
lastMQTTPublish = now;
char msg[128] = "";
snprintf(msg, 128, MQTT_JSON, client_id, camera_name, lastMQTTPublish, activeClients);
Serial.printf("Publishing message %s\r\n", msg);
/* publish the message */
mqttClient.publish(mqtt_topic, msg);
}
}
void startMQTTClient()
{
Serial.printf("Starting MQTT Client '%s'\r\n", camera_name);
/* Set SSL/TLS certificate */ /* Set SSL/TLS certificate */
// wifiClient.setCACert(ca_cert); // wifiClient.setCACert(ca_cert);
/* Start main task. */ if (!mqttClient.connected())
xTaskCreatePinnedToCore( {
mqttCB, mqttconnect();
"mqtt", }
4096,
NULL,
2,
&tMQTT,
1);
} }

View File

@ -19,4 +19,9 @@ monitor_speed = 115200
build_flags = build_flags =
-DBOARD_HAS_PSRAM -DBOARD_HAS_PSRAM
-mfix-esp32-psram-cache-issue -mfix-esp32-psram-cache-issue
lib_deps = knolleary/PubSubClient@^2.8 lib_deps =
knolleary/PubSubClient@^2.8
https://github.com/tzapu/WiFiManager.git#development
ottowinter/ESPAsyncWebServer-esphome@^1.2.7
me-no-dev/ESPAsyncTCP@^1.2.2
Micro-RTSP@>=0.1.6

53
src/html/firmware.h Normal file
View File

@ -0,0 +1,53 @@
#include<Arduino.h>
String firmware_style_css =
"<style>#file-input,input{width:100%;height:44px;border-radius:4px;margin:10px auto;font-size:15px}"
"input{background:#f1f1f1;border:0;padding:0 15px}body{background:#3498db;font-family:sans-serif;font-size:14px;color:#777}"
"#file-input{padding:0;border:1px solid #ddd;line-height:44px;text-align:left;display:block;cursor:pointer}"
"#bar,#prgbar{background-color:#f1f1f1;border-radius:10px}#bar{background-color:#3498db;width:0%;height:10px}"
"form{background:#fff;max-width:258px;margin:75px auto;padding:30px;border-radius:5px;text-align:center}"
".btn{background:#3498db;color:#fff;cursor:pointer}</style>";
String firmware_html =
"<script src='https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js'></script>"
"<form method='POST' action='#' enctype='multipart/form-data' id='upload_form'>"
"<input type='file' name='update' id='file' onchange='sub(this)' style=display:none>"
"<label id='file-input' for='file'> Choose file...</label>"
"<input type='submit' class=btn value='Update'>"
"<br><br>"
"<div id='prg'></div>"
"<br><div id='prgbar'><div id='bar'></div></div><br></form>"
"<script>"
"function sub(obj){"
"var fileName = obj.value.split('\\\\');"
"document.getElementById('file-input').innerHTML = ' '+ fileName[fileName.length-1];"
"};"
"$('form').submit(function(e){"
"e.preventDefault();"
"var form = $('#upload_form')[0];"
"var data = new FormData(form);"
"$.ajax({"
"url: '/doUpdate',"
"type: 'POST',"
"data: data,"
"contentType: false,"
"processData:false,"
"xhr: function() {"
"var xhr = new window.XMLHttpRequest();"
"xhr.upload.addEventListener('progress', function(evt) {"
"if (evt.lengthComputable) {"
"var per = evt.loaded / evt.total;"
"$('#prg').html('progress: ' + Math.round(per*100) + '%');"
"$('#bar').css('width',Math.round(per*100) + '%');"
"}"
"}, false);"
"return xhr;"
"},"
"success:function(d, s) {"
"console.log('success!') "
"},"
"error: function (a, b, c) {"
"}"
"});"
"});"
"</script>" + firmware_style_css;

View File

@ -252,6 +252,11 @@ const uint8_t index_ov2640_html[] = R"=====(<!doctype html>
<button id="save_prefs" title="Save Preferences on camera module">Save</button> <button id="save_prefs" title="Save Preferences on camera module">Save</button>
<button id="clear_prefs" title="Erase saved Preferences on camera module">Erase</button> <button id="clear_prefs" title="Erase saved Preferences on camera module">Erase</button>
</div> </div>
<div class="input-group" id="esp32-group">
<label for="clear_wifi" style="line-height: 2em;">Config</label>
<button id="clear_wifi" title="Clear the WiFi config">Clear WiFi Config</button>
<button id="firmware" title="Update module firmware">Update Firmware</button>
</div>
<div class="input-group" id="cam_name-group"> <div class="input-group" id="cam_name-group">
<label for="cam_name"> <label for="cam_name">
<a href="/dump" title="System Info" target="_blank">Name</a></label> <a href="/dump" title="System Info" target="_blank">Name</a></label>
@ -303,11 +308,11 @@ const uint8_t index_ov2640_html[] = R"=====(<!doctype html>
const recognize = document.getElementById('face_recognize') const recognize = document.getElementById('face_recognize')
const framesize = document.getElementById('framesize') const framesize = document.getElementById('framesize')
const swapButton = document.getElementById('swap-viewer') const swapButton = document.getElementById('swap-viewer')
// const saveFaceButton = document.getElementById('save_face')
// const clearFaceButton = document.getElementById('clear_face')
const savePrefsButton = document.getElementById('save_prefs') const savePrefsButton = document.getElementById('save_prefs')
const clearPrefsButton = document.getElementById('clear_prefs') const clearPrefsButton = document.getElementById('clear_prefs')
const rebootButton = document.getElementById('reboot') const rebootButton = document.getElementById('reboot')
const clearWifiButton = document.getElementById('clear_wifi')
const firmwareButton = document.getElementById('firmware')
const hide = el => { const hide = el => {
el.classList.add('hidden') el.classList.add('hidden')
@ -593,18 +598,6 @@ const uint8_t index_ov2640_html[] = R"=====(<!doctype html>
window.open('/?view=simple','_self'); window.open('/?view=simple','_self');
} }
// saveFaceButton.onclick = () => {
// if (confirm("Saving the current face database?")) {
// updateConfig(saveFaceButton);
// }
// }
// clearFaceButton.onclick = () => {
// if (confirm("Removing the face database?")) {
// updateConfig(clearFaceButton);
// }
// }
savePrefsButton.onclick = () => { savePrefsButton.onclick = () => {
if (confirm("Save the current preferences?")) { if (confirm("Save the current preferences?")) {
updateConfig(savePrefsButton); updateConfig(savePrefsButton);
@ -625,6 +618,17 @@ const uint8_t index_ov2640_html[] = R"=====(<!doctype html>
} }
} }
clearWifiButton.onclick = () => {
if (confirm("Clear the Wifi Config?")) {
updateConfig(clearWifiButton);
location.reload();
}
}
firmwareButton.onclick = () => {
window.open('/firmware','_self');
}
}) })
</script> </script>
</html>)====="; </html>)=====";

View File

@ -6,15 +6,13 @@
extern void flashLED(int flashtime); extern void flashLED(int flashtime);
extern int myRotation; // Rotation extern int myRotation; // Rotation
extern int lampVal; // The current Lamp value extern int lampVal; // The current Lamp value
extern int8_t detection_enabled; // Face detection enable
extern int8_t recognition_enabled; // Face recognition enable
/* /*
* Useful utility when debugging... * Useful utility when debugging...
*/ */
void listDir(fs::FS &fs, const char * dirname, uint8_t levels){ void listDir(fs::FS &fs, const char * dirname, uint8_t levels){
Serial.printf("Listing SPIFFS directory: %s\n", dirname); Serial.printf("Listing SPIFFS directory: %s\r\n", dirname);
File root = fs.open(dirname); File root = fs.open(dirname);
if(!root){ if(!root){
@ -52,7 +50,7 @@ void dumpPrefs(fs::FS &fs){
Serial.println(""); Serial.println("");
file.close(); file.close();
} else { } else {
Serial.printf("%s not found, nothing to dump.\n", PREFERENCES_FILE); Serial.printf("%s not found, nothing to dump.\r\n", PREFERENCES_FILE);
} }
} }
@ -60,7 +58,7 @@ void loadPrefs(fs::FS &fs){
if (fs.exists(PREFERENCES_FILE)) { if (fs.exists(PREFERENCES_FILE)) {
// read file into a string // read file into a string
String prefs; String prefs;
Serial.printf("Loading preferences from file %s\n", PREFERENCES_FILE); Serial.printf("Loading preferences from file %s\r\n", PREFERENCES_FILE);
File file = fs.open(PREFERENCES_FILE, FILE_READ); File file = fs.open(PREFERENCES_FILE, FILE_READ);
if (!file) { if (!file) {
Serial.println("Failed to open preferences file"); Serial.println("Failed to open preferences file");
@ -100,22 +98,20 @@ void loadPrefs(fs::FS &fs){
s->set_hmirror(s, jsonExtract(prefs, "hmirror").toInt()); s->set_hmirror(s, jsonExtract(prefs, "hmirror").toInt());
s->set_dcw(s, jsonExtract(prefs, "dcw").toInt()); s->set_dcw(s, jsonExtract(prefs, "dcw").toInt());
s->set_colorbar(s, jsonExtract(prefs, "colorbar").toInt()); s->set_colorbar(s, jsonExtract(prefs, "colorbar").toInt());
detection_enabled = jsonExtract(prefs, "face_detect").toInt();
recognition_enabled = jsonExtract(prefs, "face_recognize").toInt();
myRotation = jsonExtract(prefs, "rotate").toInt(); myRotation = jsonExtract(prefs, "rotate").toInt();
// close the file // close the file
file.close(); file.close();
dumpPrefs(SPIFFS); dumpPrefs(SPIFFS);
} else { } else {
Serial.printf("Preference file %s not found; using system defaults.\n", PREFERENCES_FILE); Serial.printf("Preference file %s not found; using system defaults.\r\n", PREFERENCES_FILE);
} }
} }
void savePrefs(fs::FS &fs){ void savePrefs(fs::FS &fs){
if (fs.exists(PREFERENCES_FILE)) { if (fs.exists(PREFERENCES_FILE)) {
Serial.printf("Updating %s\n", PREFERENCES_FILE); Serial.printf("Updating %s\r\n", PREFERENCES_FILE);
} else { } else {
Serial.printf("Creating %s\n", PREFERENCES_FILE); Serial.printf("Creating %s\r\n", PREFERENCES_FILE);
} }
File file = fs.open(PREFERENCES_FILE, FILE_WRITE); File file = fs.open(PREFERENCES_FILE, FILE_WRITE);
static char json_response[1024]; static char json_response[1024];
@ -147,8 +143,6 @@ void savePrefs(fs::FS &fs){
p+=sprintf(p, "\"hmirror\":%u,", s->status.hmirror); p+=sprintf(p, "\"hmirror\":%u,", s->status.hmirror);
p+=sprintf(p, "\"dcw\":%u,", s->status.dcw); p+=sprintf(p, "\"dcw\":%u,", s->status.dcw);
p+=sprintf(p, "\"colorbar\":%u,", s->status.colorbar); p+=sprintf(p, "\"colorbar\":%u,", s->status.colorbar);
p+=sprintf(p, "\"face_detect\":%u,", detection_enabled);
p+=sprintf(p, "\"face_recognize\":%u,", recognition_enabled);
p+=sprintf(p, "\"rotate\":\"%d\"", myRotation); p+=sprintf(p, "\"rotate\":\"%d\"", myRotation);
*p++ = '}'; *p++ = '}';
*p++ = 0; *p++ = 0;
@ -159,7 +153,7 @@ void savePrefs(fs::FS &fs){
void removePrefs(fs::FS &fs) { void removePrefs(fs::FS &fs) {
if (fs.exists(PREFERENCES_FILE)) { if (fs.exists(PREFERENCES_FILE)) {
Serial.printf("Removing %s\r\n", PREFERENCES_FILE); Serial.printf("Removing %s\r\r\n", PREFERENCES_FILE);
if (!fs.remove(PREFERENCES_FILE)) { if (!fs.remove(PREFERENCES_FILE)) {
Serial.println("Error removing preferences"); Serial.println("Error removing preferences");
} }

View File

@ -11,16 +11,12 @@
#include <esp_wifi.h> #include <esp_wifi.h>
#include <esp_sleep.h> #include <esp_sleep.h>
#include <driver/rtc_io.h> #include <driver/rtc_io.h>
#include "myconfig.h"
// Select camera model
//#define CAMERA_MODEL_WROVER_KIT
//#define CAMERA_MODEL_ESP_EYE
//#define CAMERA_MODEL_M5STACK_PSRAM
//#define CAMERA_MODEL_M5STACK_WIDE
#define CAMERA_MODEL_AI_THINKER // default
#include "camera_pins.h" #include "camera_pins.h"
extern IPAddress espIP;
extern bool updating;
/* /*
Next one is an include with wifi credentials. Next one is an include with wifi credentials.
This is what you need to do: This is what you need to do:
@ -39,6 +35,7 @@
#endif #endif
int streamPort = STREAM_PORT; int streamPort = STREAM_PORT;
WebServer server(streamPort); WebServer server(streamPort);
char streamURL[64] = {"Undefined"};
// ===== rtos task handles ========================= // ===== rtos task handles =========================
// Streaming is implemented with 3 tasks: // Streaming is implemented with 3 tasks:
@ -54,7 +51,7 @@ QueueHandle_t streamingClients;
UBaseType_t activeClients; UBaseType_t activeClients;
// We will try to achieve 25 FPS frame rate // We will try to achieve 25 FPS frame rate
const int FPS = 14; const int FPS = 10;
// We will handle web client requests every 50 ms (20 Hz) // We will handle web client requests every 50 ms (20 Hz)
const int WSINTERVAL = 100; const int WSINTERVAL = 100;
@ -67,8 +64,7 @@ void handleJPGSstream(void);
void streamCB(void *pvParameters); void streamCB(void *pvParameters);
void handleJPG(void); void handleJPG(void);
void handleNotFound(); void handleNotFound();
void startStreamServer(int sPort); void startStreamServer();
// ==== Memory allocator that takes advantage of PSRAM if present ======================= // ==== Memory allocator that takes advantage of PSRAM if present =======================
char *allocateMemory(char *aPtr, size_t aSize) char *allocateMemory(char *aPtr, size_t aSize)
@ -133,7 +129,7 @@ void mjpegCB(void *pvParameters)
NULL, // parameters NULL, // parameters
2, // priority 2, // priority
&tCam, // RTOS task handle &tCam, // RTOS task handle
APP_CPU); // core tskNO_AFFINITY); // core
// Creating task to push the stream to all connected clients // Creating task to push the stream to all connected clients
xTaskCreatePinnedToCore( xTaskCreatePinnedToCore(
@ -143,7 +139,7 @@ void mjpegCB(void *pvParameters)
NULL, //(void*) handler, NULL, //(void*) handler,
2, 2,
&tStream, &tStream,
APP_CPU); tskNO_AFFINITY);
// Registering webserver handling routines // Registering webserver handling routines
server.on("/mjpeg/1", HTTP_GET, handleJPGSstream); server.on("/mjpeg/1", HTTP_GET, handleJPGSstream);
@ -427,7 +423,7 @@ void handleNotFound()
void startStreamServer() void startStreamServer()
{ {
Serial.printf("Starting web server on port: '%d'\n", streamPort); Serial.printf("Starting stream server on port: '%d'\n", streamPort);
// Start mainstreaming RTOS task // Start mainstreaming RTOS task
xTaskCreatePinnedToCore( xTaskCreatePinnedToCore(
@ -437,5 +433,7 @@ void startStreamServer()
NULL, NULL,
2, 2,
&tMjpeg, &tMjpeg,
APP_CPU); tskNO_AFFINITY);
sprintf(streamURL, "http://%d.%d.%d.%d:%d/mjpeg/1/", espIP[0], espIP[1], espIP[2], espIP[3], streamPort);
} }

116
wifi_manager.cpp Normal file
View File

@ -0,0 +1,116 @@
#include <WiFiManager.h>
#include <Preferences.h>
#include <ESPmDNS.h>
extern void flashLED(int flashtime);
WiFiManager wifiManager;
IPAddress espIP;
IPAddress espSubnet;
IPAddress espGateway;
extern char KEY_CAM_NAME[];
extern char KEY_MMQT_HOST[];
extern char KEY_MMQT_PORT[];
extern char KEY_MMQT_USER[];
extern char KEY_MMQT_PASS[];
extern char KEY_MMQT_TOPIC[];
extern char KEY_MMQT_CERT[];
void resetWiFiConfig()
{
wifiManager.resetSettings();
WiFi.disconnect(true);
delay(2000);
ESP.restart();
}
void configModeCallback(WiFiManager *myWiFiManager)
{
Serial.printf("Entered config mode. SSID '%s' AP: '%s'\r\n", myWiFiManager->getConfigPortalSSID().c_str(), WiFi.softAPIP().toString().c_str());
}
//callback notifying us of the need to save config
void saveConfigCallback()
{
Serial.println("Saving config");
}
void startWiFi()
{
Serial.println("Starting WiFi");
// Feedback that we are now attempting to connect
flashLED(300);
delay(100);
flashLED(300);
wifiManager.setAPCallback(configModeCallback);
wifiManager.setSaveConfigCallback(saveConfigCallback);
wifiManager.setConfigPortalTimeout(180);
wifiManager.setConnectTimeout(30);
wifiManager.setTimeout(30);
Preferences preferences;
preferences.begin("ESP32-CAM", false);
String camera_name = preferences.getString(KEY_CAM_NAME, "ESP32-CAM");
String mqtt_port = preferences.getString(KEY_MMQT_PORT, "1883");
String mqtt_host = preferences.getString(KEY_MMQT_HOST, "");
String mqtt_user = preferences.getString(KEY_MMQT_USER, "");
String mqtt_pass = preferences.getString(KEY_MMQT_PASS, "");
String mqtt_topic = preferences.getString(KEY_MMQT_TOPIC, "");
String mqtt_cert = preferences.getString(KEY_MMQT_CERT, "");
WiFiManagerParameter camName(KEY_CAM_NAME, "Camera Name", camera_name.c_str(), 64);
WiFiManagerParameter mqttHost(KEY_MMQT_HOST, "MQTT Host", mqtt_host.c_str(), 64);
WiFiManagerParameter mqttPort(KEY_MMQT_PORT, "MQTT Port", mqtt_port.c_str(), 6);
WiFiManagerParameter mqttUser(KEY_MMQT_USER, "MQTT User", mqtt_user.c_str(), 64);
WiFiManagerParameter mqttPass(KEY_MMQT_PASS, "MQTT Password", mqtt_pass.c_str(), 256);
WiFiManagerParameter mqttTopic(KEY_MMQT_TOPIC, "MQTT Publish Topic", mqtt_topic.c_str(), 256);
WiFiManagerParameter mqttCert(KEY_MMQT_CERT, "MQTT Certificate", mqtt_cert.c_str(), 4096);
wifiManager.addParameter(&camName);
wifiManager.addParameter(&mqttHost);
wifiManager.addParameter(&mqttPort);
wifiManager.addParameter(&mqttUser);
wifiManager.addParameter(&mqttPass);
wifiManager.addParameter(&mqttTopic);
wifiManager.addParameter(&mqttCert);
wifiManager.setHostname(camera_name.c_str());
if (!wifiManager.autoConnect(camera_name.c_str()))
{
ESP.restart();
}
espIP = WiFi.localIP();
espSubnet = WiFi.subnetMask();
espGateway = WiFi.gatewayIP();
preferences.putString(KEY_CAM_NAME, camName.getValue());
preferences.putString(KEY_MMQT_PORT, mqttPort.getValue());
preferences.putString(KEY_MMQT_HOST, mqttHost.getValue());
preferences.putString(KEY_MMQT_USER, mqttUser.getValue());
preferences.putString(KEY_MMQT_PASS, mqttPass.getValue());
preferences.putString(KEY_MMQT_TOPIC, mqttTopic.getValue());
preferences.putString(KEY_MMQT_CERT, mqttCert.getValue());
preferences.end();
Serial.printf("Host: '%s'\r\nUser: '%s'\r\nPass: '%s'\r\nTopic: '%s'\r\nCert: '%s'\r\n",
preferences.getString("mqttHost", "").c_str(),
preferences.getString("mqtt_user", "").c_str(),
preferences.getString("mqttPass", "").c_str(),
preferences.getString("mqttTopic", "").c_str(),
preferences.getString("mqttCert", "").c_str());
if (!MDNS.begin(camera_name.c_str()))
{
Serial.println("Error setting up MDNS responder!");
}
Serial.println("mDNS responder started");
delay(500);
Serial.println("Wifi Started");
}