diff --git a/README.md b/README.md index 6c1c41b4..82419a8e 100644 --- a/README.md +++ b/README.md @@ -93,9 +93,9 @@ The folder of [examples](examples) contains sample applications demonstrating th Take one Face Detection as an example. -1. Get into one example folder `esp-who/examples/single_chip/detection_with_command_line`. +1. Get into one example folder `esp-who/examples/single_chip/face_detection_with_command_line`. ``` -cd esp-who/examples/single_chip/detection_with_command_line +cd esp-who/examples/single_chip/face_detection_with_command_line ``` 2. Compile and flash the project. diff --git a/components/esp32-camera b/components/esp32-camera index af931850..a5ccbecf 160000 --- a/components/esp32-camera +++ b/components/esp32-camera @@ -1 +1 @@ -Subproject commit af931850f4a32566f51403758c3a49032327225c +Subproject commit a5ccbecf08f98eb84c7443d8aebe4a529ba737a7 diff --git a/examples/single_chip/camera_web_server/README.md b/examples/single_chip/camera_web_server/README.md index 7b7782b2..32587ff4 100755 --- a/examples/single_chip/camera_web_server/README.md +++ b/examples/single_chip/camera_web_server/README.md @@ -17,6 +17,6 @@ After you've completed the hardware settings, please follow the steps below: 4. **Open Your Browser** and point it to `http://[ip-of-esp32]/`; 5. **To Get Image** press `Get Still` or `Start Stream`; 6. **Use The Options** to enable/disable Face Detection, Face Recognition and more; -t. **View The Stream** in a player like VLC: Open Network `http://[ip-of-esp32]:81/stream`; +7. **View The Stream** in a player like VLC: Open Network `http://[ip-of-esp32]:81/stream`; For more details of the http handler, please refer to [esp32-camera](https://github.com/espressif/esp32-camera). diff --git a/examples/single_chip/camera_web_server/main/app_httpd.c b/examples/single_chip/camera_web_server/main/app_httpd.c index 801e97ef..f0bb9fea 100644 --- a/examples/single_chip/camera_web_server/main/app_httpd.c +++ b/examples/single_chip/camera_web_server/main/app_httpd.c @@ -62,6 +62,11 @@ bool isStreaming = false; #endif #endif +#define FPS_DEFAULT -1 // -1 = Unlimited FPS +#define FPS_TO_PERIOD(FPS) (FPS<1?0:(1000000/FPS)) +unsigned int fps_max = FPS_DEFAULT; +uint64_t fps_period_us = FPS_TO_PERIOD(FPS_DEFAULT); + typedef struct { httpd_req_t *req; @@ -289,6 +294,146 @@ void enable_led(bool en) } #endif +static esp_err_t set_variable(const char *variable, int val, sensor_t *s, bool ignore_unknown) +{ + int res = 0; + + ESP_LOGI(TAG, "%s = %d", variable, val); + + if (!strcmp(variable, "framesize")) { + if (s->pixformat == PIXFORMAT_JPEG) { + res = s->set_framesize(s, (framesize_t)val); + if (res == 0) { + app_mdns_update_framesize(val); + } + } + } + else if (!strcmp(variable, "fps_max")) { + fps_max = val; + fps_period_us = FPS_TO_PERIOD(fps_max); + } + else if (!strcmp(variable, "quality")) + res = s->set_quality(s, val); + else if (!strcmp(variable, "contrast")) + res = s->set_contrast(s, val); + else if (!strcmp(variable, "brightness")) + res = s->set_brightness(s, val); + else if (!strcmp(variable, "saturation")) + res = s->set_saturation(s, val); + else if (!strcmp(variable, "gainceiling")) + res = s->set_gainceiling(s, (gainceiling_t)val); + else if (!strcmp(variable, "colorbar")) + res = s->set_colorbar(s, val); + else if (!strcmp(variable, "awb")) + res = s->set_whitebal(s, val); + else if (!strcmp(variable, "agc")) + res = s->set_gain_ctrl(s, val); + else if (!strcmp(variable, "aec")) + res = s->set_exposure_ctrl(s, val); + else if (!strcmp(variable, "hmirror")) + res = s->set_hmirror(s, val); + else if (!strcmp(variable, "vflip")) + res = s->set_vflip(s, val); + else if (!strcmp(variable, "awb_gain")) + res = s->set_awb_gain(s, val); + else if (!strcmp(variable, "agc_gain")) + res = s->set_agc_gain(s, val); + else if (!strcmp(variable, "aec_value")) + res = s->set_aec_value(s, val); + else if (!strcmp(variable, "aec2")) + res = s->set_aec2(s, val); + else if (!strcmp(variable, "dcw")) + res = s->set_dcw(s, val); + else if (!strcmp(variable, "bpc")) + res = s->set_bpc(s, val); + else if (!strcmp(variable, "wpc")) + res = s->set_wpc(s, val); + else if (!strcmp(variable, "raw_gma")) + res = s->set_raw_gma(s, val); + else if (!strcmp(variable, "lenc")) + res = s->set_lenc(s, val); + else if (!strcmp(variable, "special_effect")) + res = s->set_special_effect(s, val); + else if (!strcmp(variable, "wb_mode")) + res = s->set_wb_mode(s, val); + else if (!strcmp(variable, "ae_level")) + res = s->set_ae_level(s, val); +#ifdef CONFIG_LED_ILLUMINATOR_ENABLED + else if (!strcmp(variable, "led_intensity")) { + led_duty = val; + if (isStreaming) + enable_led(true); + } +#endif + +#if CONFIG_ESP_FACE_DETECT_ENABLED + else if (!strcmp(variable, "face_detect")) { + detection_enabled = val; +#if CONFIG_ESP_FACE_RECOGNITION_ENABLED + if (!detection_enabled) { + recognition_enabled = 0; + } +#endif + } +#if CONFIG_ESP_FACE_RECOGNITION_ENABLED + else if (!strcmp(variable, "face_enroll")) + is_enrolling = val; + else if (!strcmp(variable, "face_recognize")) { + recognition_enabled = val; + if (recognition_enabled) { + detection_enabled = val; + } + } +#endif +#endif + else { + ESP_LOGI(TAG, "Unknown variable: %s = %d", variable, val); + if (!ignore_unknown) { + res = -1; + } + } + + return res; +} + +static esp_err_t bmp_handler(httpd_req_t *req) +{ + camera_fb_t *fb = NULL; + esp_err_t res = ESP_OK; + uint64_t fr_start = esp_timer_get_time(); + fb = esp_camera_fb_get(); + if (!fb) + { + ESP_LOGE(TAG, "Camera capture failed"); + httpd_resp_send_500(req); + return ESP_FAIL; + } + + httpd_resp_set_type(req, "image/x-windows-bmp"); + httpd_resp_set_hdr(req, "Content-Disposition", "inline; filename=capture.bmp"); + httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*"); + + char ts[32]; + snprintf(ts, 32, "%ld.%06ld", fb->timestamp.tv_sec, fb->timestamp.tv_usec); + httpd_resp_set_hdr(req, "X-Timestamp", (const char *)ts); + + + uint8_t * buf = NULL; + size_t buf_len = 0; + bool converted = frame2bmp(fb, &buf, &buf_len); + esp_camera_fb_return(fb); + if(!converted){ + ESP_LOGE(TAG, "BMP Conversion failed"); + httpd_resp_send_500(req); + return ESP_FAIL; + } + res = httpd_resp_send(req, (const char *)buf, buf_len); + free(buf); + uint64_t fr_end = esp_timer_get_time(); + ESP_LOGI(TAG, "BMP: %llums, %uB", (uint64_t)((fr_end - fr_start) / 1000), buf_len); + return res; +} + static size_t jpg_encode_stream(void *arg, size_t index, const void *data, size_t len) { jpg_chunking_t *j = (jpg_chunking_t *)arg; @@ -304,12 +449,59 @@ static size_t jpg_encode_stream(void *arg, size_t index, const void *data, size_ return len; } +static esp_err_t process_query_string(httpd_req_t *req, sensor_t *s) +{ + esp_err_t ret = ESP_OK; + + // Extract query string from the request + size_t qs_len = httpd_req_get_url_query_len(req); + if (qs_len == 0) + return ESP_OK; // No variables to set + char *qs = malloc(qs_len + 1); + if (!qs) { + ESP_LOGE(TAG, "QS malloc(%d) error", qs_len + 1); + httpd_resp_send_500(req); + return ESP_FAIL; + } + ret = httpd_req_get_url_query_str(req, qs, qs_len + 1); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "QS httpd_req_get_url_query_str() error"); + free(qs); + return ret; + } + + // Parse and set individual variables + char *p, *q = NULL; + char *p_token = strtok_r(qs, "&", &p); + while (p_token != NULL) { + char *variable = strtok_r(p_token, "=", &q); + char *val_str = strtok_r(NULL, "=", &q); + + if (variable != NULL && val_str != NULL) { + int val = atoi(val_str); + ret = set_variable(variable, val, s, true); + if (ret != ESP_OK) + break; + } + + p_token = strtok_r(NULL, "&", &p); + } + free(qs); + return ret; +} + static esp_err_t capture_handler(httpd_req_t *req) { camera_fb_t *fb = NULL; esp_err_t res = ESP_OK; int64_t fr_start = esp_timer_get_time(); + // Parse variables from URL query string + sensor_t *sensor = esp_camera_sensor_get(); + res = process_query_string(req, sensor); + if (res != ESP_OK) + return res; + #ifdef CONFIG_LED_ILLUMINATOR_ENABLED enable_led(true); vTaskDelay(150 / portTICK_PERIOD_MS); // The LED needs to be turned on ~150ms before the call to esp_camera_fb_get() @@ -440,6 +632,7 @@ static esp_err_t stream_handler(httpd_req_t *req) int64_t fr_encode = 0; #endif + int64_t next_frame_time = 0; static int64_t last_frame = 0; if (!last_frame) { @@ -455,6 +648,12 @@ static esp_err_t stream_handler(httpd_req_t *req) httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*"); httpd_resp_set_hdr(req, "X-Framerate", "60"); + // Parse variables from URL query string + sensor_t *sensor = esp_camera_sensor_get(); + res = process_query_string(req, sensor); + if (res != ESP_OK) + return res; + #ifdef CONFIG_LED_ILLUMINATOR_ENABLED enable_led(true); isStreaming = true; @@ -462,6 +661,13 @@ static esp_err_t stream_handler(httpd_req_t *req) while (true) { + // Wait to limit FPS if required + // NB fps_delay_ms can be negative if processing takes too long -> in that case don't wait + int64_t fps_delay_ms = (next_frame_time - esp_timer_get_time()) / 1000; + if (fps_max > 0 && fps_delay_ms > 0) + vTaskDelay(fps_delay_ms / portTICK_PERIOD_MS); + next_frame_time = esp_timer_get_time() + fps_period_us; + #if CONFIG_ESP_FACE_DETECT_ENABLED detected = false; face_id = 0; @@ -663,9 +869,12 @@ static esp_err_t cmd_handler(httpd_req_t *req) char *buf = NULL; char variable[32]; char value[32]; + esp_err_t res; - if (parse_get(req, &buf) != ESP_OK || - httpd_query_key_value(buf, "var", variable, sizeof(variable)) != ESP_OK || + if (parse_get(req, &buf) != ESP_OK) { + return ESP_FAIL; + } + if (httpd_query_key_value(buf, "var", variable, sizeof(variable)) != ESP_OK || httpd_query_key_value(buf, "val", value, sizeof(value)) != ESP_OK) { free(buf); httpd_resp_send_404(req); @@ -674,97 +883,11 @@ static esp_err_t cmd_handler(httpd_req_t *req) free(buf); int val = atoi(value); - ESP_LOGI(TAG, "%s = %d", variable, val); sensor_t *s = esp_camera_sensor_get(); - int res = 0; - - if (!strcmp(variable, "framesize")) { - if (s->pixformat == PIXFORMAT_JPEG) { - res = s->set_framesize(s, (framesize_t)val); - if (res == 0) { - app_mdns_update_framesize(val); - } - } - } - else if (!strcmp(variable, "quality")) - res = s->set_quality(s, val); - else if (!strcmp(variable, "contrast")) - res = s->set_contrast(s, val); - else if (!strcmp(variable, "brightness")) - res = s->set_brightness(s, val); - else if (!strcmp(variable, "saturation")) - res = s->set_saturation(s, val); - else if (!strcmp(variable, "gainceiling")) - res = s->set_gainceiling(s, (gainceiling_t)val); - else if (!strcmp(variable, "colorbar")) - res = s->set_colorbar(s, val); - else if (!strcmp(variable, "awb")) - res = s->set_whitebal(s, val); - else if (!strcmp(variable, "agc")) - res = s->set_gain_ctrl(s, val); - else if (!strcmp(variable, "aec")) - res = s->set_exposure_ctrl(s, val); - else if (!strcmp(variable, "hmirror")) - res = s->set_hmirror(s, val); - else if (!strcmp(variable, "vflip")) - res = s->set_vflip(s, val); - else if (!strcmp(variable, "awb_gain")) - res = s->set_awb_gain(s, val); - else if (!strcmp(variable, "agc_gain")) - res = s->set_agc_gain(s, val); - else if (!strcmp(variable, "aec_value")) - res = s->set_aec_value(s, val); - else if (!strcmp(variable, "aec2")) - res = s->set_aec2(s, val); - else if (!strcmp(variable, "dcw")) - res = s->set_dcw(s, val); - else if (!strcmp(variable, "bpc")) - res = s->set_bpc(s, val); - else if (!strcmp(variable, "wpc")) - res = s->set_wpc(s, val); - else if (!strcmp(variable, "raw_gma")) - res = s->set_raw_gma(s, val); - else if (!strcmp(variable, "lenc")) - res = s->set_lenc(s, val); - else if (!strcmp(variable, "special_effect")) - res = s->set_special_effect(s, val); - else if (!strcmp(variable, "wb_mode")) - res = s->set_wb_mode(s, val); - else if (!strcmp(variable, "ae_level")) - res = s->set_ae_level(s, val); -#ifdef CONFIG_LED_ILLUMINATOR_ENABLED - else if (!strcmp(variable, "led_intensity")) { - led_duty = val; - if (isStreaming) - enable_led(true); - } -#endif -#if CONFIG_ESP_FACE_DETECT_ENABLED - else if (!strcmp(variable, "face_detect")) { - detection_enabled = val; -#if CONFIG_ESP_FACE_RECOGNITION_ENABLED - if (!detection_enabled) { - recognition_enabled = 0; - } -#endif - } -#if CONFIG_ESP_FACE_RECOGNITION_ENABLED - else if (!strcmp(variable, "face_enroll")) - is_enrolling = val; - else if (!strcmp(variable, "face_recognize")) { - recognition_enabled = val; - if (recognition_enabled) { - detection_enabled = val; - } - } -#endif -#endif - else { - res = -1; - } + res = set_variable(variable, val, s, false); - if (res) { + if (res < 0) { return httpd_resp_send_500(req); } @@ -807,7 +930,7 @@ static esp_err_t status_handler(httpd_req_t *req) p+=print_reg(p, s, reg, 0xFF); } p+=print_reg(p, s, 0x558a, 0x1FF);//9 bit - } else { + } else if(s->id.PID == OV2640_PID){ p+=print_reg(p, s, 0xd3, 0xFF); p+=print_reg(p, s, 0x111, 0xFF); p+=print_reg(p, s, 0x132, 0xFF); @@ -817,6 +940,7 @@ static esp_err_t status_handler(httpd_req_t *req) p += sprintf(p, "\"xclk\":%u,", s->xclk_freq_hz / 1000000); p += sprintf(p, "\"pixformat\":%u,", s->pixformat); p += sprintf(p, "\"framesize\":%u,", s->status.framesize); + p += sprintf(p, "\"fps_max\":%u,", fps_max); p += sprintf(p, "\"quality\":%u,", s->status.quality); p += sprintf(p, "\"brightness\":%d,", s->status.brightness); p += sprintf(p, "\"contrast\":%d,", s->status.contrast); @@ -838,6 +962,7 @@ static esp_err_t status_handler(httpd_req_t *req) p += sprintf(p, "\"raw_gma\":%u,", s->status.raw_gma); p += sprintf(p, "\"lenc\":%u,", s->status.lenc); p += sprintf(p, "\"hmirror\":%u,", s->status.hmirror); + p += sprintf(p, "\"vflip\":%u,", s->status.vflip); p += sprintf(p, "\"dcw\":%u,", s->status.dcw); p += sprintf(p, "\"colorbar\":%u", s->status.colorbar); #ifdef CONFIG_LED_ILLUMINATOR_ENABLED @@ -873,8 +998,10 @@ static esp_err_t xclk_handler(httpd_req_t *req) char *buf = NULL; char _xclk[32]; - if (parse_get(req, &buf) != ESP_OK || - httpd_query_key_value(buf, "xclk", _xclk, sizeof(_xclk)) != ESP_OK) { + if (parse_get(req, &buf) != ESP_OK) { + return ESP_FAIL; + } + if (httpd_query_key_value(buf, "xclk", _xclk, sizeof(_xclk)) != ESP_OK) { free(buf); httpd_resp_send_404(req); return ESP_FAIL; @@ -901,8 +1028,10 @@ static esp_err_t reg_handler(httpd_req_t *req) char _mask[32]; char _val[32]; - if (parse_get(req, &buf) != ESP_OK || - httpd_query_key_value(buf, "reg", _reg, sizeof(_reg)) != ESP_OK || + if (parse_get(req, &buf) != ESP_OK) { + return ESP_FAIL; + } + if (httpd_query_key_value(buf, "reg", _reg, sizeof(_reg)) != ESP_OK || httpd_query_key_value(buf, "mask", _mask, sizeof(_mask)) != ESP_OK || httpd_query_key_value(buf, "val", _val, sizeof(_val)) != ESP_OK) { free(buf); @@ -932,8 +1061,10 @@ static esp_err_t greg_handler(httpd_req_t *req) char _reg[32]; char _mask[32]; - if (parse_get(req, &buf) != ESP_OK || - httpd_query_key_value(buf, "reg", _reg, sizeof(_reg)) != ESP_OK || + if (parse_get(req, &buf) != ESP_OK) { + return ESP_FAIL; + } + if (httpd_query_key_value(buf, "reg", _reg, sizeof(_reg)) != ESP_OK || httpd_query_key_value(buf, "mask", _mask, sizeof(_mask)) != ESP_OK) { free(buf); httpd_resp_send_404(req); @@ -970,8 +1101,6 @@ static esp_err_t pll_handler(httpd_req_t *req) char *buf = NULL; if (parse_get(req, &buf) != ESP_OK) { - free(buf); - httpd_resp_send_404(req); return ESP_FAIL; } @@ -1001,8 +1130,6 @@ static esp_err_t win_handler(httpd_req_t *req) char *buf = NULL; if (parse_get(req, &buf) != ESP_OK) { - free(buf); - httpd_resp_send_404(req); return ESP_FAIL; } @@ -1075,7 +1202,7 @@ static esp_err_t monitor_handler(httpd_req_t *req) void app_httpd_main() { httpd_config_t config = HTTPD_DEFAULT_CONFIG(); - config.max_uri_handlers = 12; + config.max_uri_handlers = 16; httpd_uri_t index_uri = { .uri = "/", @@ -1107,6 +1234,12 @@ void app_httpd_main() .handler = stream_handler, .user_ctx = NULL}; + httpd_uri_t bmp_uri = { + .uri = "/bmp", + .method = HTTP_GET, + .handler = bmp_handler, + .user_ctx = NULL}; + httpd_uri_t xclk_uri = { .uri = "/xclk", .method = HTTP_GET, @@ -1179,6 +1312,7 @@ void app_httpd_main() 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, &bmp_uri); httpd_register_uri_handler(camera_httpd, &xclk_uri); httpd_register_uri_handler(camera_httpd, ®_uri); diff --git a/examples/single_chip/camera_web_server/main/include/app_camera.h b/examples/single_chip/camera_web_server/main/include/app_camera.h index 333fd3e7..6a080be7 100755 --- a/examples/single_chip/camera_web_server/main/include/app_camera.h +++ b/examples/single_chip/camera_web_server/main/include/app_camera.h @@ -45,6 +45,9 @@ #define PCLK_GPIO_NUM 22 #elif CONFIG_CAMERA_MODEL_ESP32_CAM_BOARD +// The 18 pin header on the board has Y5 and Y3 swapped +#define USE_BOARD_HEADER 0 + #define CAM_BOARD "ESP-DEVCAM" #define PWDN_GPIO_NUM 32 #define RESET_GPIO_NUM 33 @@ -56,9 +59,17 @@ #define Y8_GPIO_NUM 19 #define Y7_GPIO_NUM 21 #define Y6_GPIO_NUM 39 +#if USE_BOARD_HEADER +#define Y5_GPIO_NUM 13 +#else #define Y5_GPIO_NUM 35 +#endif #define Y4_GPIO_NUM 14 +#if USE_BOARD_HEADER +#define Y3_GPIO_NUM 35 +#else #define Y3_GPIO_NUM 13 +#endif #define Y2_GPIO_NUM 34 #define VSYNC_GPIO_NUM 5 #define HREF_GPIO_NUM 27 diff --git a/examples/single_chip/camera_web_server/main/www/index_ov2640.html b/examples/single_chip/camera_web_server/main/www/index_ov2640.html index cac38f95..91ceb950 100644 --- a/examples/single_chip/camera_web_server/main/www/index_ov2640.html +++ b/examples/single_chip/camera_web_server/main/www/index_ov2640.html @@ -419,6 +419,19 @@ +