diff --git a/.travis.yml b/.travis.yml
index db24e40..693813a 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -28,7 +28,7 @@ before_script:
script:
- cd $TRAVIS_BUILD_DIR
- export PATH="$HOME/arduino_ide:$PATH"
- - arduino --board esp32:esp32:esp32:PartitionScheme=huge_app,FlashFreq=80 --pref compiler.warning_level=all --save-prefs
+ - arduino --board esp32:esp32:esp32:PartitionScheme=default,FlashFreq=80 --pref compiler.warning_level=all --save-prefs
- arduino --verbose --verify esp32-cam-webserver.ino
- cp --preserve --verbose myconfig.sample.h myconfig.h
- arduino --verbose --verify esp32-cam-webserver.ino
diff --git a/Docs/ota-board-selection-small.png b/Docs/ota-board-selection-small.png
new file mode 100644
index 0000000..f90fb6b
Binary files /dev/null and b/Docs/ota-board-selection-small.png differ
diff --git a/Docs/ota-board-selection-vanilla.png b/Docs/ota-board-selection-vanilla.png
new file mode 100644
index 0000000..0dc81d6
Binary files /dev/null and b/Docs/ota-board-selection-vanilla.png differ
diff --git a/Docs/ota-board-selection.png b/Docs/ota-board-selection.png
new file mode 100644
index 0000000..f56dbdc
Binary files /dev/null and b/Docs/ota-board-selection.png differ
diff --git a/README.md b/README.md
index 987087e..933ee92 100644
--- a/README.md
+++ b/README.md
@@ -10,8 +10,14 @@ But expanded with:
* Save and restore settings
* Control of on-board lamps, rotate the view in the browser
* Dedicated standalone stream viewer
+* Over The Air firmware updates
* Lots of minor fixes and tweaks, documentation etc.
+And 'reduced' by removing the Face Recognition features
+* **If you want to try the Face Recognition features** please use the [`3.x` maintenance branch](https://github.com/easytarget/esp32-cam-webserver/tree/3.x), which still recieves bugfixes, but is not reciving any further development.
+* They were a demo, only worked in low resolution modes, did not preserve the face database between power cycles, and were of little use in real-world applications.
+* There are other (specialised) sketches for the ESP-CAM that do use face recognitioni more effectively, if this is your thing :-)
+
The original example is a bit incomprehensible and hard to modify as supplied. It is very focused on showing off the face recognition capabilities, and forgets the 'webcam' part.
* There are many other variants of a webcam server for these modules online, but most are created for a specific scenario and not good for general, casual, webcam use.
@@ -41,8 +47,6 @@ The ESP itself is susceptable to the usual list of WiFi problems, not helped by
A basic limitation of the sketch is that it can can only support one stream at a time. If you try to connect to a cam that is already streaming (or attempting to stream) you will get no response and, eventually, a timeout. The stream itself is a [MJPEG stream](https://en.wikipedia.org/wiki/Motion_JPEG), which relies on the client (the web browser) to hold the connection open and request each new frame in turn via javascript. This can cause errors when browsers run into Javascript or caching problem, fail to request new frames or refuse to close the connection.
-Another known issue is that if you are streaming with face detection turned on any new tatic mage capture request will cause the stream to exit.
-
The existing [issues list](https://github.com/easytarget/esp32-cam-webserver/issues?q=is%3Aissue) on Github is a good place to start if you have a specific issue not covered above
## Setup:
@@ -78,13 +82,17 @@ To make a permanent config with your home wifi settings, different defaults or a
### Programming
-Assuming you are using the latest Espressif Arduino core the `AI-THINKER` board (or whatever board you select for programming) will appear in the ESP32 Arduino section of the boards list.
-![IDE board config](Docs/board-selection-small.png)
+Assuming you are using the latest Espressif Arduino core the `ESP32 Dev Module` board will appear in the ESP32 Arduino section of the boards list. Select this (do not use the `AI-THINKER` entry listed in the boiards menu, it is not OTA compatible, and will caus the module to crash and reboot rather than updating if you use it.
+![IDE board config](Docs/ota-board-selection.png)
+
+Make sure you select the `Default 4MB with Spiffs` partition scheme and turn `PSRAM` on.
-Compile and upload the code from the IDE, when the `Connecting...` appears in the console reboot the ESP32 module while keeping **GPIO0** grounded. You can release GPO0 once the sketch is uploading, most boards have a 'boot' button to trigger a reboot.
+The first time you program (or if OTA is failing) you need to compile and upload the code from the IDE, and when the `Connecting...` appears in the console reboot the ESP32 module while keeping **GPIO0** grounded. You can release GPO0 once the sketch is uploading, most boards have a 'boot' button to trigger a reboot.
Once the upload completes (be patient, it can be a bit slow) open the serial monitor in the IDE and reboot the board again without GPIO0 grounded. In the serial monitor you should see the board start, connect to the wifi and then report the IP address it has been assigned.
+Once you have the initial upload done and the board is connected to the wifi network you should see it appearing in the `network ports` list of the IDE, and you can upload wirelessly.
+
If you have a status LED configured it will give a double flash when it begins attempting to conenct to WiFi, and five short flashes once it has succeeded. It will also flash briefly when you access the camera to change settings.
Go to the URL given in the serial output, the web UI should appear with the settings panel open. Click away!
@@ -95,17 +103,17 @@ Go to the URL given in the serial output, the web UI should appear with the sett
The WiFi details can be stored in an (optional) header file to allow easier code development, and a camera name for the UI title can be configured. The lamp and status LED's are optional, and the lamp uses a exponential scale for brightness so that the control has some finess.
+All of the face recognition code has been removed as of V4.0; this reduces the code size enough to allow OTA programming while improving compile and programming times.
+
The compressed and binary encoded HTML used in the example has been unpacked to raw text, this makes it much easier to access and modify the Javascript and UI elements. Given the relatively small size of the index page there is very little benefit from compressing it.
The streamviewer, lamp control, and all the other new features have been added. I have tried to retain the basic structure of the original example,extending where necesscary.
-I have left all the Face Recognition code untouched, it works, and with good lighting and camera position it can work quite well. But you can only use it in low-resolution modes, and it is not something I will be using.
-
The web UI has had changes to add the lamp control (only when enabled) and make the streamm window rotate and resize appropriately. I also made the 'Start Stream' and 'Snapshot' controls more prominent, and added feedback of the camera name + firmware.
I would also like to shoutout to @jmfloyd; who suggested rotating the image in the browser since the esp32 itself cannot do this.
-![The stream viewer](Docs/streamview.png) *Standalone StreamViewer; No decoration or controls, resizable, doubleclick image for fullscreen*
+![The stream viewer](Docs/streamview.png) *Standalone StreamViewer; No decoration or controls, the image is resizable, and you can doubleclick it for fullscreen*
![The info page](Docs/infodump.png) *Boring Details*
@@ -125,10 +133,15 @@ Contributions are welcome; please see the [Contribution guidelines](CONTRIBUTING
Time allowing; my Current plan is:
-V4 Remove face recognition entirely;
-* Dont try to make it optional, this is a code and maintenance nightmare. V3 can be maintained on a branch for those who need it.
+V4
+* Remove face recognition entirely;
+ * **Done**, see the `NoFace` branch :sunglasses:
+ * Not optional, this is a code and maintenance nightmare. V3 can be maintained on a branch for those who need it.
* Investigate using SD card to capture images
-* implement OTA and a better network stack for remembering multiple AP's, auto-config etc.
+* Implement OTA and a better network stack for remembering multiple AP's, auto-config etc.
+ * **Basic OTA is Done**, see the `NoFace` branch.
+ * Advanced (web upload) OTA might be nice to have is possible
+ * For the Network setup I want to implement https://github.com/Hieromon/AutoConnect
* UI Skinning/Theming
* OSD
* Temperature/humidity/pressure sensor suport (bme20,dht11)
diff --git a/app_httpd.cpp b/app_httpd.cpp
index a8ab2d4..5facbcc 100644
--- a/app_httpd.cpp
+++ b/app_httpd.cpp
@@ -17,7 +17,6 @@
#include
#include
#include
-#include
#include
#include
@@ -54,41 +53,12 @@ extern unsigned long imagesServed;
extern int myRotation;
extern int lampVal;
extern bool autoLamp;
-extern int8_t detection_enabled;
-extern int8_t recognition_enabled;
extern bool filesystem;
extern String critERR;
extern bool debugData;
extern int sketchSize;
extern int sketchSpace;
extern String sketchMD5;
-extern char knownFaceText[];
-extern char unknownFaceText[];
-
-
-#include "fb_gfx.h"
-#include "fd_forward.h"
-#include "fr_forward.h"
-
-#define ENROLL_CONFIRM_TIMES 5
-#define FACE_ID_SAVE_NUMBER 7
-
-#define FACE_COLOR_WHITE 0x00FFFFFF
-#define FACE_COLOR_BLACK 0x00000000
-#define FACE_COLOR_RED 0x000000FF
-#define FACE_COLOR_GREEN 0x0000FF00
-#define FACE_COLOR_BLUE 0x00FF0000
-#define FACE_COLOR_YELLOW (FACE_COLOR_RED | FACE_COLOR_GREEN)
-#define FACE_COLOR_CYAN (FACE_COLOR_BLUE | FACE_COLOR_GREEN)
-#define FACE_COLOR_PURPLE (FACE_COLOR_BLUE | FACE_COLOR_RED)
-
-typedef struct {
- size_t size; //number of values used for filtering
- size_t index; //current value index
- size_t count; //value count
- int sum;
- int * values; //array to be filled with values
-} ra_filter_t;
typedef struct {
httpd_req_t *req;
@@ -100,156 +70,9 @@ static const char* _STREAM_CONTENT_TYPE = "multipart/x-mixed-replace;boundary="
static const char* _STREAM_BOUNDARY = "\r\n--" PART_BOUNDARY "\r\n";
static const char* _STREAM_PART = "Content-Type: image/jpeg\r\nContent-Length: %u\r\n\r\n";
-static ra_filter_t ra_filter;
httpd_handle_t stream_httpd = NULL;
httpd_handle_t camera_httpd = NULL;
-static mtmn_config_t mtmn_config = {0};
-static int8_t is_enrolling = 0;
-static face_id_list id_list = {0};
-int id_list_alloc = 0;
-
-static ra_filter_t * ra_filter_init(ra_filter_t * filter, size_t sample_size){
- memset(filter, 0, sizeof(ra_filter_t));
-
- filter->values = (int *)malloc(sample_size * sizeof(int));
- if(!filter->values){
- return NULL;
- }
- memset(filter->values, 0, sample_size * sizeof(int));
-
- filter->size = sample_size;
- return filter;
-}
-
-static int ra_filter_run(ra_filter_t * filter, int value) {
- if(!filter->values){
- return value;
- }
- filter->sum -= filter->values[filter->index];
- filter->values[filter->index] = value;
- filter->sum += filter->values[filter->index];
- filter->index++;
- filter->index = filter->index % filter->size;
- if (filter->count < filter->size) {
- filter->count++;
- }
- return filter->sum / filter->count;
-}
-
-static void rgb_print(dl_matrix3du_t *image_matrix, uint32_t color, const char * str){
- fb_data_t fb;
- fb.width = image_matrix->w;
- fb.height = image_matrix->h;
- fb.data = image_matrix->item;
- fb.bytes_per_pixel = 3;
- fb.format = FB_BGR888;
- fb_gfx_print(&fb, (fb.width - (strlen(str) * 14)) / 2, 10, color, str);
-}
-
-static int rgb_printf(dl_matrix3du_t *image_matrix, uint32_t color, const char *format, ...){
- char loc_buf[64];
- char * temp = loc_buf;
- int len;
- va_list arg;
- va_list copy;
- va_start(arg, format);
- va_copy(copy, arg);
- len = vsnprintf(loc_buf, sizeof(loc_buf), format, arg);
- va_end(copy);
- if(len >= sizeof(loc_buf)){
- temp = (char*)malloc(len+1);
- if(temp == NULL) {
- return 0;
- }
- }
- vsnprintf(temp, len+1, format, arg);
- va_end(arg);
- rgb_print(image_matrix, color, temp);
- if(len > 64){
- free(temp);
- }
- return len;
-}
-
-static void draw_face_boxes(dl_matrix3du_t *image_matrix, box_array_t *boxes, int face_id){
- int x, y, w, h, i;
- uint32_t color = FACE_COLOR_YELLOW;
- if(face_id < 0){
- color = FACE_COLOR_RED;
- } else if(face_id > 0){
- color = FACE_COLOR_GREEN;
- }
- fb_data_t fb;
- fb.width = image_matrix->w;
- fb.height = image_matrix->h;
- fb.data = image_matrix->item;
- fb.bytes_per_pixel = 3;
- fb.format = FB_BGR888;
- for (i = 0; i < boxes->len; i++){
- // rectangle box
- x = (int)boxes->box[i].box_p[0];
- y = (int)boxes->box[i].box_p[1];
- w = (int)boxes->box[i].box_p[2] - x + 1;
- h = (int)boxes->box[i].box_p[3] - y + 1;
- fb_gfx_drawFastHLine(&fb, x, y, w, color);
- 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
- // landmark
- int x0, y0, j;
- for (j = 0; j < 10; j+=2) {
- x0 = (int)boxes->landmark[i].landmark_p[j];
- y0 = (int)boxes->landmark[i].landmark_p[j+1];
- fb_gfx_fillRect(&fb, x0, y0, 3, 3, color);
- }
- #endif
- }
-}
-
-static int run_face_recognition(dl_matrix3du_t *image_matrix, box_array_t *net_boxes){
- dl_matrix3du_t *aligned_face = NULL;
- int matched_id = 0;
-
- aligned_face = dl_matrix3du_alloc(1, FACE_WIDTH, FACE_HEIGHT, 3);
- if(!aligned_face){
- Serial.println("FACE: could not allocate face recognition buffer");
- return matched_id;
- }
- if (align_face(net_boxes, image_matrix, aligned_face) == ESP_OK){
- if (is_enrolling == 1){
- int8_t this_face = id_list.tail + 1;
- int8_t left_sample_face = enroll_face(&id_list, aligned_face);
-
- if(left_sample_face == (ENROLL_CONFIRM_TIMES - 1)){
- Serial.printf("FACE: enrolling new face ID: %d\r\n", this_face);
- }
- Serial.printf("FACE: enroll ID: %d sample %d\r\n", this_face, ENROLL_CONFIRM_TIMES - left_sample_face);
- rgb_printf(image_matrix, FACE_COLOR_CYAN, "ID[%u] Sample[%u]", this_face, ENROLL_CONFIRM_TIMES - left_sample_face);
- if (left_sample_face == 0){
- is_enrolling = 0;
- Serial.printf("FACE: enrolled face ID: %d\r\n", this_face);
- }
- } else {
- matched_id = recognize_face(&id_list, aligned_face) + 1;
- if (matched_id > 0) {
- Serial.printf("FACE: match ID: %u: ", matched_id);
- rgb_printf(image_matrix, FACE_COLOR_GREEN, "%s%u", knownFaceText, matched_id);
- } else {
- matched_id = -1;
- Serial.println("FACE: no match found:");
- rgb_printf(image_matrix, FACE_COLOR_RED, "%s", unknownFaceText);
- }
- }
- } else {
- Serial.println("FACE: not aligned:");
- rgb_print(image_matrix, FACE_COLOR_YELLOW, "???");
- }
-
- dl_matrix3du_free(aligned_face);
- return matched_id;
-}
void serialDump() {
Serial.println("\r\nPreferences file: ");
@@ -303,7 +126,6 @@ void serialDump() {
if (filesystem) {
Serial.printf("Spiffs: %i, used: %i\r\n", SPIFFS.totalBytes(), SPIFFS.usedBytes());
}
- Serial.printf("Enrolled faces: %i (max %i)\r\n", id_list.count, id_list.size);
Serial.println();
return;
}
@@ -342,84 +164,21 @@ static esp_err_t capture_handler(httpd_req_t *req){
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;
- bool detected = false;
- int face_id = 0;
- if(!detection_enabled || fb->width > 400 || streamCount != 0){
- size_t fb_len = 0;
- if(fb->format == PIXFORMAT_JPEG){
- fb_len = fb->len;
- res = httpd_resp_send(req, (const char *)fb->buf, fb->len);
- } else {
- jpg_chunking_t jchunk = {req, 0};
- 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);
- int64_t fr_end = esp_timer_get_time();
- if (debugData) {
- Serial.printf("JPG: %uB %ums\r\n", (uint32_t)(fb_len), (uint32_t)((fr_end - fr_start)/1000));
- }
- imagesServed++;
- if (autoLamp && (lampVal != -1)) setLamp(0);
- return res;
- }
-
- dl_matrix3du_t *image_matrix = dl_matrix3du_alloc(1, fb->width, fb->height, 3);
- if (!image_matrix) {
- esp_camera_fb_return(fb);
- Serial.println("CAPTURE: dl_matrix3du_alloc failed");
- httpd_resp_send_500(req);
- if (autoLamp && (lampVal != -1)) setLamp(0);
- return ESP_FAIL;
+ size_t fb_len = 0;
+ if(fb->format == PIXFORMAT_JPEG){
+ fb_len = fb->len;
+ res = httpd_resp_send(req, (const char *)fb->buf, fb->len);
+ } else {
+ jpg_chunking_t jchunk = {req, 0};
+ res = frame2jpg_cb(fb, 80, jpg_encode_stream, &jchunk)?ESP_OK:ESP_FAIL;
+ httpd_resp_send_chunk(req, NULL, 0);
+ fb_len = jchunk.len;
}
-
- 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("CAPTURE: frame convert to rgb888 failed");
- httpd_resp_send_500(req);
- if (autoLamp && (lampVal != -1)) setLamp(0);
- return ESP_FAIL;
- }
-
- box_array_t *net_boxes = face_detect(image_matrix, &mtmn_config);
-
- if (net_boxes){
- detected = true;
- if(recognition_enabled){
- face_id = run_face_recognition(image_matrix, net_boxes);
- }
- draw_face_boxes(image_matrix, net_boxes, face_id);
- dl_lib_free(net_boxes->score);
- dl_lib_free(net_boxes->box);
- dl_lib_free(net_boxes->landmark);
- dl_lib_free(net_boxes);
- }
-
- 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("CAPTURE: JPEG compression failed");
- if (autoLamp && (lampVal != -1)) setLamp(0);
- return ESP_FAIL;
- }
-
int64_t fr_end = esp_timer_get_time();
if (debugData) {
- Serial.printf("JPG: %uB %ums %s%d\r\n", (uint32_t)(jchunk.len), (uint32_t)((fr_end - fr_start)/1000), detected?"DETECTED ":"", face_id);
+ Serial.printf("JPG: %uB %ums\r\n", (uint32_t)(fb_len), (uint32_t)((fr_end - fr_start)/1000));
}
-
imagesServed++;
if (autoLamp && (lampVal != -1)) setLamp(0);
return res;
@@ -431,14 +190,6 @@ static esp_err_t stream_handler(httpd_req_t *req){
size_t _jpg_buf_len = 0;
uint8_t * _jpg_buf = NULL;
char * part_buf[64];
- dl_matrix3du_t *image_matrix = NULL;
- int face_id = 0;
- bool detected = false;
- int64_t fr_start = 0;
- int64_t fr_face = 0;
- int64_t fr_recognize = 0;
- int64_t fr_encode = 0;
- int64_t fr_ready = 0;
Serial.println("Stream requested");
if (autoLamp && (lampVal != -1)) setLamp(lampVal);
@@ -463,78 +214,22 @@ static esp_err_t stream_handler(httpd_req_t *req){
httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*");
while(true){
- detected = false;
- face_id = 0;
fb = esp_camera_fb_get();
if (!fb) {
Serial.println("STREAM: failed to acquire frame");
res = ESP_FAIL;
} else {
- fr_start = esp_timer_get_time();
- fr_ready = fr_start;
- fr_face = fr_start;
- fr_encode = fr_start;
- fr_recognize = fr_start;
-
- if(!detection_enabled || fb->width > 400){
- if(fb->format != PIXFORMAT_JPEG){
- bool jpeg_converted = frame2jpg(fb, 80, &_jpg_buf, &_jpg_buf_len);
- esp_camera_fb_return(fb);
- fb = NULL;
- if(!jpeg_converted){
- Serial.println("STREAM: JPEG compression failed");
- res = ESP_FAIL;
- }
- } else {
- _jpg_buf_len = fb->len;
- _jpg_buf = fb->buf;
- }
- } else {
-
- image_matrix = dl_matrix3du_alloc(1, fb->width, fb->height, 3);
-
- if (!image_matrix) {
- Serial.println("STREAM: dl_matrix3du_alloc failed");
+ if(fb->format != PIXFORMAT_JPEG){
+ bool jpeg_converted = frame2jpg(fb, 80, &_jpg_buf, &_jpg_buf_len);
+ esp_camera_fb_return(fb);
+ fb = NULL;
+ if(!jpeg_converted){
+ Serial.println("STREAM: JPEG compression failed");
res = ESP_FAIL;
- } else {
- if(!fmt2rgb888(fb->buf, fb->len, fb->format, image_matrix->item)){
- Serial.println("STREAM: frame convert to rgb888 failed");
- res = ESP_FAIL;
- } else {
- fr_ready = esp_timer_get_time();
- box_array_t *net_boxes = NULL;
- if(detection_enabled){
- net_boxes = face_detect(image_matrix, &mtmn_config);
- }
- fr_face = esp_timer_get_time();
- fr_recognize = fr_face;
- if (net_boxes || fb->format != PIXFORMAT_JPEG){
- if(net_boxes){
- detected = true;
- if(recognition_enabled){
- face_id = run_face_recognition(image_matrix, net_boxes);
- }
- fr_recognize = esp_timer_get_time();
- draw_face_boxes(image_matrix, net_boxes, face_id);
- dl_lib_free(net_boxes->score);
- dl_lib_free(net_boxes->box);
- dl_lib_free(net_boxes->landmark);
- dl_lib_free(net_boxes);
- }
- if(!fmt2jpg(image_matrix->item, fb->width*fb->height*3, fb->width, fb->height, PIXFORMAT_RGB888, 90, &_jpg_buf, &_jpg_buf_len)){
- Serial.println("STREAM: fmt2jpg failed");
- res = ESP_FAIL;
- }
- esp_camera_fb_return(fb);
- fb = NULL;
- } else {
- _jpg_buf = fb->buf;
- _jpg_buf_len = fb->len;
- }
- fr_encode = esp_timer_get_time();
- }
- dl_matrix3du_free(image_matrix);
}
+ } else {
+ _jpg_buf_len = fb->len;
+ _jpg_buf = fb->buf;
}
}
if(res == ESP_OK){
@@ -560,31 +255,13 @@ static esp_err_t stream_handler(httpd_req_t *req){
// We end the stream here only if a Hard failure has been encountered or the connection has been interrupted.
break;
}
-
- int64_t fr_end = esp_timer_get_time();
- int64_t ready_time = (fr_ready - fr_start)/1000;
- int64_t face_time = (fr_face - fr_ready)/1000;
- int64_t recognize_time = (fr_recognize - fr_face)/1000;
- int64_t encode_time = (fr_encode - fr_recognize)/1000;
- int64_t process_time = (fr_encode - fr_start)/1000;
- int64_t frame_time = fr_end - last_frame;
- last_frame = fr_end;
+ int64_t frame_time = esp_timer_get_time() - last_frame;
+ last_frame = esp_timer_get_time();;
frame_time /= 1000;
- uint32_t avg_frame_time = ra_filter_run(&ra_filter, frame_time);
if (debugData) {
- if (detection_enabled) {
- Serial.printf("MJPG: %uB %ums (%.1ffps), AVG: %ums (%.1ffps), %u+%u+%u+%u=%u %s%d\r\n",
- (uint32_t)(_jpg_buf_len),
- (uint32_t)frame_time, 1000.0 / (uint32_t)frame_time,
- avg_frame_time, 1000.0 / avg_frame_time,
- (uint32_t)ready_time, (uint32_t)face_time, (uint32_t)recognize_time, (uint32_t)encode_time, (uint32_t)process_time,
- (detected)?"DETECTED ":"", face_id);
- } else {
- Serial.printf("MJPG: %uB %ums (%.1ffps), AVG: %ums (%.1ffps)\r\n",
- (uint32_t)(_jpg_buf_len),
- (uint32_t)frame_time, 1000.0 / (uint32_t)frame_time,
- avg_frame_time, 1000.0 / avg_frame_time);
- }
+ Serial.printf("MJPG: %uB %ums (%.1ffps)\r\n",
+ (uint32_t)(_jpg_buf_len),
+ (uint32_t)frame_time, 1000.0 / (uint32_t)frame_time);
}
}
@@ -659,19 +336,6 @@ static esp_err_t cmd_handler(httpd_req_t *req){
else if(!strcmp(variable, "wb_mode")) res = s->set_wb_mode(s, val);
else if(!strcmp(variable, "ae_level")) res = s->set_ae_level(s, val);
else if(!strcmp(variable, "rotate")) myRotation = val;
- else if(!strcmp(variable, "face_detect")) {
- detection_enabled = val;
- if(!detection_enabled) {
- recognition_enabled = 0;
- }
- }
- else if(!strcmp(variable, "face_enroll")) is_enrolling = val;
- else if(!strcmp(variable, "face_recognize")) {
- recognition_enabled = val;
- if(recognition_enabled){
- detection_enabled = val;
- }
- }
else if(!strcmp(variable, "autolamp") && (lampVal != -1)) {
autoLamp = val;
if (autoLamp) {
@@ -690,12 +354,6 @@ static esp_err_t cmd_handler(httpd_req_t *req){
setLamp(lampVal);
}
}
- else if(!strcmp(variable, "save_face")) {
- if (filesystem) saveFaceDB(SPIFFS);
- }
- else if(!strcmp(variable, "clear_face")) {
- if (filesystem) removeFaceDB(SPIFFS);
- }
else if(!strcmp(variable, "save_prefs")) {
if (filesystem) savePrefs(SPIFFS);
}
@@ -758,9 +416,6 @@ static esp_err_t status_handler(httpd_req_t *req){
p+=sprintf(p, "\"hmirror\":%u,", s->status.hmirror);
p+=sprintf(p, "\"dcw\":%u,", s->status.dcw);
p+=sprintf(p, "\"colorbar\":%u,", s->status.colorbar);
- p+=sprintf(p, "\"face_detect\":%u,", detection_enabled);
- p+=sprintf(p, "\"face_enroll\":%u,", is_enrolling);
- p+=sprintf(p, "\"face_recognize\":%u,", recognition_enabled);
p+=sprintf(p, "\"cam_name\":\"%s\",", myName);
p+=sprintf(p, "\"code_ver\":\"%s\",", myVer);
p+=sprintf(p, "\"rotate\":\"%d\",", myRotation);
@@ -880,7 +535,6 @@ static esp_err_t dump_handler(httpd_req_t *req){
if (filesystem) {
d+= sprintf(d,"Spiffs: %i, used: %i \n", SPIFFS.totalBytes(), SPIFFS.usedBytes());
}
- d+= sprintf(d,"Enrolled faces: %i (max %i) \n", id_list.count, id_list.size);
// Footer
d+= sprintf(d,"
\n");
@@ -1093,29 +747,6 @@ void startCameraServer(int hPort, int sPort){
.user_ctx = NULL
};
- // Filter list; used during face detection
- ra_filter_init(&ra_filter, 20);
-
- // Mtmn config values (face detection and recognition parameters)
- mtmn_config.type = FAST;
- mtmn_config.min_face = 80;
- mtmn_config.pyramid = 0.707;
- mtmn_config.pyramid_times = 4;
- mtmn_config.p_threshold.score = 0.6;
- mtmn_config.p_threshold.nms = 0.7;
- mtmn_config.p_threshold.candidate_number = 20;
- mtmn_config.r_threshold.score = 0.7;
- mtmn_config.r_threshold.nms = 0.7;
- mtmn_config.r_threshold.candidate_number = 10;
- mtmn_config.o_threshold.score = 0.7;
- mtmn_config.o_threshold.nms = 0.7;
- mtmn_config.o_threshold.candidate_number = 1;
-
- // Face ID list (settings + pointer to the data allocation)
- face_id_init(&id_list, FACE_ID_SAVE_NUMBER, ENROLL_CONFIRM_TIMES);
- // The size of the allocated data block; calculated in dl_lib_calloc()
-
-
// Request Handlers; config.max_uri_handlers (above) must be >= the number of handlers
config.server_port = hPort;
config.ctrl_port = hPort;
diff --git a/esp32-cam-webserver.ino b/esp32-cam-webserver.ino
index 2f9ed1e..ec1805a 100644
--- a/esp32-cam-webserver.ino
+++ b/esp32-cam-webserver.ino
@@ -3,6 +3,7 @@
#include
#include
#include
+#include
#include "src/parsebytes.h"
@@ -51,7 +52,7 @@
#include "camera_pins.h"
// Internal filesystem (SPIFFS)
-// used for non-volatile camera settings and face DB store
+// used for non-volatile camera settings
#include "storage.h"
// Sketch Info
@@ -163,28 +164,12 @@ const int pwmMax = pow(2,pwmresolution)-1;
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
+#if defined(NO_OTA)
+ bool otaEnabled = false;
#else
- int8_t detection_enabled = 0;
- int8_t recognition_enabled = 0;
+ bool otaEnabled = true;
#endif
-#if defined (GOOD_FACE_TEXT)
- char knownFaceText[] = GOOD_FACE_TEXT;
-#else
- char knownFaceText[] ="Hello Subject ";
-#endif
-#if defined (BAD_FACE_TEXT)
- char unknownFaceText[] = BAD_FACE_TEXT;
-#else
- char unknownFaceText[] = "Intruder Alert!";
-#endif
// Critical error string; if set during init (camera hardware failure) it
// will be returned for all http requests
@@ -591,9 +576,8 @@ void setup() {
if (filesystem) {
filesystemStart();
loadPrefs(SPIFFS);
- loadFaceDB(SPIFFS);
} else {
- Serial.println("No Internal Filesystem, cannot save preferences or face DB");
+ Serial.println("No Internal Filesystem, cannot save preferences");
}
}
@@ -615,7 +599,48 @@ void setup() {
while ((WiFi.status() != WL_CONNECTED) && !accesspoint) {
WifiSetup();
delay(1000);
- }
+ }
+
+ if (otaEnabled) {
+ // Start OTA once connected
+ Serial.println("Setting up OTA");
+ // Port defaults to 3232
+ // ArduinoOTA.setPort(3232);
+ // Hostname defaults to esp3232-[MAC]
+ ArduinoOTA.setHostname(myName);
+ // No authentication by default
+ #if defined (OTA_PASSWORD)
+ ArduinoOTA.setPassword(OTA_PASSWORD);
+ Serial.printf("OTA Password: %s\n\r", OTA_PASSWORD);
+ #endif
+ ArduinoOTA
+ .onStart([]() {
+ String type;
+ if (ArduinoOTA.getCommand() == U_FLASH)
+ type = "sketch";
+ else // U_SPIFFS
+ type = "filesystem";
+ // NOTE: if updating SPIFFS this would be the place to unmount SPIFFS using SPIFFS.end()
+ Serial.println("Start updating " + type);
+ })
+ .onEnd([]() {
+ Serial.println("\nEnd");
+ })
+ .onProgress([](unsigned int progress, unsigned int total) {
+ Serial.printf("Progress: %u%%\r", (progress / (total / 100)));
+ })
+ .onError([](ota_error_t error) {
+ Serial.printf("Error[%u]: ", error);
+ if (error == OTA_AUTH_ERROR) Serial.println("Auth Failed");
+ else if (error == OTA_BEGIN_ERROR) Serial.println("Begin Failed");
+ else if (error == OTA_CONNECT_ERROR) Serial.println("Connect Failed");
+ else if (error == OTA_RECEIVE_ERROR) Serial.println("Receive Failed");
+ else if (error == OTA_END_ERROR) Serial.println("End Failed");
+ });
+ ArduinoOTA.begin();
+ } else {
+ Serial.println("OTA is disabled");
+ }
// Now we have a network we can start the two http handlers for the UI and Stream.
startCameraServer(httpPort, streamPort);
@@ -647,6 +672,8 @@ void setup() {
} else {
Serial.printf("\r\nCamera unavailable due to initialisation errors.\r\n\r\n");
}
+ Serial.print("\r\nThis is the 4.0 alpha\r\n - Face detection has been removed!\r\n");
+
// Used when dumping status; these are slow functions, so just do them once during startup
sketchSize = ESP.getSketchSize();
@@ -669,6 +696,7 @@ void loop() {
unsigned long start = millis();
while (millis() - start < WIFI_WATCHDOG ) {
delay(100);
+ if (otaEnabled) ArduinoOTA.handle();
handleSerial();
if (captivePortal) dnsServer.processNextRequest();
}
@@ -686,6 +714,7 @@ void loop() {
unsigned long start = millis();
while (millis() - start < WIFI_WATCHDOG ) {
delay(100);
+ if (otaEnabled) ArduinoOTA.handle();
handleSerial();
}
} else {
diff --git a/index_other.h b/index_other.h
index 89cb1cc..71b6971 100644
--- a/index_other.h
+++ b/index_other.h
@@ -57,8 +57,6 @@ const uint8_t index_simple_html[] = R"=====(
-
-