From 7937d37e14796341b3927750ec8bbce6e052eaeb Mon Sep 17 00:00:00 2001 From: Me No Dev Date: Thu, 12 Mar 2020 13:27:48 +0200 Subject: [PATCH] Add support for OV5640 (#135) Also: - Add advanced settings - Add more resolutions and aspect ratios support. --- components/esp32-camera | 2 +- .../single_chip/camera_web_server/README.md | 3 +- .../camera_web_server/main/CMakeLists.txt | 3 +- .../camera_web_server/main/Kconfig.projbuild | 2 + .../camera_web_server/main/app_camera.c | 6 +- .../camera_web_server/main/app_httpd.c | 347 ++++- .../camera_web_server/main/component.mk | 1 + .../main/include/app_camera.h | 19 + .../main/www/index_ov2640.html | 352 ++++- .../main/www/index_ov2640.html.gz | Bin 4763 -> 6787 bytes .../main/www/index_ov3660.html | 542 ++++++- .../main/www/index_ov3660.html.gz | Bin 4847 -> 8887 bytes .../main/www/index_ov5640.html | 1359 +++++++++++++++++ .../main/www/index_ov5640.html.gz | Bin 0 -> 8765 bytes 14 files changed, 2549 insertions(+), 87 deletions(-) create mode 100644 examples/single_chip/camera_web_server/main/www/index_ov5640.html create mode 100644 examples/single_chip/camera_web_server/main/www/index_ov5640.html.gz diff --git a/components/esp32-camera b/components/esp32-camera index 193b090..4af111a 160000 --- a/components/esp32-camera +++ b/components/esp32-camera @@ -1 +1 @@ -Subproject commit 193b090df99dd73e18df4d8ab494ebd32d433648 +Subproject commit 4af111a5e274a2973fa10cd3c1ec69174a1bdb93 diff --git a/examples/single_chip/camera_web_server/README.md b/examples/single_chip/camera_web_server/README.md index 52316ff..7b7782b 100755 --- a/examples/single_chip/camera_web_server/README.md +++ b/examples/single_chip/camera_web_server/README.md @@ -5,7 +5,7 @@ To run this example, you need the following components: * An ESP32 Module: Either **ESP32-WROVER-KIT** or **ESP-EYE**, which we highly recommend for beginners, is used in this example. -* A Camera Module: Either **OV2640** or **OV3660** image sensor, which we highly recommend for beginners, is used in this example. +* A Camera Module: Either **OV2640** or **OV3660** or **OV5640** image sensor, which we highly recommend for beginners, is used in this example. # Quick Start @@ -17,5 +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`; 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/CMakeLists.txt b/examples/single_chip/camera_web_server/main/CMakeLists.txt index cb2f78c..86bc4e9 100644 --- a/examples/single_chip/camera_web_server/main/CMakeLists.txt +++ b/examples/single_chip/camera_web_server/main/CMakeLists.txt @@ -11,6 +11,7 @@ set(COMPONENT_REQUIRES set(COMPONENT_EMBED_FILES "www/index_ov2640.html.gz" - "www/index_ov3660.html.gz") + "www/index_ov3660.html.gz" + "www/index_ov5640.html.gz") register_component() diff --git a/examples/single_chip/camera_web_server/main/Kconfig.projbuild b/examples/single_chip/camera_web_server/main/Kconfig.projbuild index 79e41be..c591426 100644 --- a/examples/single_chip/camera_web_server/main/Kconfig.projbuild +++ b/examples/single_chip/camera_web_server/main/Kconfig.projbuild @@ -100,6 +100,8 @@ choice CAMERA_MODEL config CAMERA_MODEL_WROVER_KIT bool "WROVER-KIT With OV2640 Module" +config CAMERA_MODEL_ESP32_CAM_BOARD + bool "ESP32 Camera Development Board" config CAMERA_MODEL_ESP_EYE bool "ESP_EYE DevKit" config CAMERA_MODEL_M5STACK_PSRAM diff --git a/examples/single_chip/camera_web_server/main/app_camera.c b/examples/single_chip/camera_web_server/main/app_camera.c index 0c03df5..6d4fa89 100755 --- a/examples/single_chip/camera_web_server/main/app_camera.c +++ b/examples/single_chip/camera_web_server/main/app_camera.c @@ -30,7 +30,7 @@ static const char *TAG = "app_camera"; void app_camera_main () { -#if CONFIG_CAMERA_MODEL_ESP_EYE +#if CONFIG_CAMERA_MODEL_ESP_EYE || CONFIG_CAMERA_MODEL_ESP32_CAM_BOARD /* IO13, IO14 is designed for JTAG by default, * to use it as generalized input, * firstly declair it as pullup input */ @@ -97,7 +97,7 @@ void app_camera_main () config.xclk_freq_hz = 20000000; config.pixel_format = PIXFORMAT_JPEG; //init with high specs to pre-allocate larger buffers - config.frame_size = FRAMESIZE_UXGA; + config.frame_size = FRAMESIZE_QSXGA; config.jpeg_quality = 10; config.fb_count = 2; @@ -116,5 +116,5 @@ void app_camera_main () s->set_saturation(s, -2);//lower the saturation } //drop down frame size for higher initial frame rate - s->set_framesize(s, FRAMESIZE_QVGA); + s->set_framesize(s, FRAMESIZE_HD); } 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 e51304b..3099791 100644 --- a/examples/single_chip/camera_web_server/main/app_httpd.c +++ b/examples/single_chip/camera_web_server/main/app_httpd.c @@ -75,7 +75,7 @@ typedef struct #define PART_BOUNDARY "123456789000000000000987654321" static const char *_STREAM_CONTENT_TYPE = "multipart/x-mixed-replace;boundary=" PART_BOUNDARY; static const char *_STREAM_BOUNDARY = "\r\n--" PART_BOUNDARY "\r\n"; -static const char *_STREAM_PART = "Content-Type: image/jpeg\r\nContent-Length: %u\r\n\r\n"; +static const char *_STREAM_PART = "Content-Type: image/jpeg\r\nContent-Length: %u\r\nX-Timestamp: %d.%06d\r\n\r\n"; httpd_handle_t stream_httpd = NULL; httpd_handle_t camera_httpd = NULL; @@ -341,6 +341,10 @@ 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", "*"); + 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); + #if CONFIG_ESP_FACE_DETECT_ENABLED size_t out_len, out_width, out_height; uint8_t *out_buf; @@ -437,10 +441,11 @@ static esp_err_t capture_handler(httpd_req_t *req) static esp_err_t stream_handler(httpd_req_t *req) { camera_fb_t *fb = NULL; + struct timeval _timestamp; esp_err_t res = ESP_OK; size_t _jpg_buf_len = 0; uint8_t *_jpg_buf = NULL; - char *part_buf[64]; + char *part_buf[128]; #if CONFIG_ESP_FACE_DETECT_ENABLED dl_matrix3du_t *image_matrix = NULL; bool detected = false; @@ -465,6 +470,7 @@ 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"); #ifdef CONFIG_LED_ILLUMINATOR_ENABLED enable_led(true); @@ -486,6 +492,8 @@ static esp_err_t stream_handler(httpd_req_t *req) } else { + _timestamp.tv_sec = fb->timestamp.tv_sec; + _timestamp.tv_usec = fb->timestamp.tv_usec; #if CONFIG_ESP_FACE_DETECT_ENABLED fr_start = esp_timer_get_time(); fr_ready = fr_start; @@ -593,7 +601,7 @@ static esp_err_t stream_handler(httpd_req_t *req) } if (res == ESP_OK) { - size_t hlen = snprintf((char *)part_buf, 64, _STREAM_PART, _jpg_buf_len); + size_t hlen = snprintf((char *)part_buf, 128, _STREAM_PART, _jpg_buf_len, _timestamp.tv_sec, _timestamp.tv_usec); res = httpd_resp_send_chunk(req, (const char *)part_buf, hlen); } if (res == ESP_OK) @@ -654,60 +662,49 @@ static esp_err_t stream_handler(httpd_req_t *req) return res; } -static esp_err_t cmd_handler(httpd_req_t *req) +static esp_err_t parse_get(httpd_req_t *req, char **obuf) { - char *buf; - size_t buf_len; - char variable[32] = { - 0, - }; - char value[32] = { - 0, - }; + char *buf = NULL; + size_t buf_len = 0; buf_len = httpd_req_get_url_query_len(req) + 1; - if (buf_len > 1) - { + if (buf_len > 1) { buf = (char *)malloc(buf_len); - if (!buf) - { + if (!buf) { httpd_resp_send_500(req); return ESP_FAIL; } - if (httpd_req_get_url_query_str(req, buf, buf_len) == ESP_OK) - { - if (httpd_query_key_value(buf, "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; + if (httpd_req_get_url_query_str(req, buf, buf_len) == ESP_OK) { + *obuf = buf; + return ESP_OK; } free(buf); } - else - { + httpd_resp_send_404(req); + return ESP_FAIL; +} + +static esp_err_t cmd_handler(httpd_req_t *req) +{ + char *buf = NULL; + char variable[32]; + char value[32]; + + if (parse_get(req, &buf) != ESP_OK || + 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); return ESP_FAIL; } + 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 (!strcmp(variable, "framesize")) { if (s->pixformat == PIXFORMAT_JPEG) res = s->set_framesize(s, (framesize_t)val); } @@ -758,8 +755,7 @@ static esp_err_t cmd_handler(httpd_req_t *req) else if (!strcmp(variable, "ae_level")) res = s->set_ae_level(s, val); #ifdef CONFIG_LED_ILLUMINATOR_ENABLED - else if (!strcmp(variable, "led_intensity")) - { + else if (!strcmp(variable, "led_intensity")) { led_duty = val; if (isStreaming) enable_led(true); @@ -767,12 +763,10 @@ static esp_err_t cmd_handler(httpd_req_t *req) #endif #if CONFIG_ESP_FACE_DETECT_ENABLED - else if (!strcmp(variable, "face_detect")) - { + else if (!strcmp(variable, "face_detect")) { detection_enabled = val; #if CONFIG_ESP_FACE_RECOGNITION_ENABLED - if (!detection_enabled) - { + if (!detection_enabled) { recognition_enabled = 0; } #endif @@ -780,23 +774,19 @@ static esp_err_t cmd_handler(httpd_req_t *req) #if CONFIG_ESP_FACE_RECOGNITION_ENABLED else if (!strcmp(variable, "face_enroll")) is_enrolling = val; - else if (!strcmp(variable, "face_recognize")) - { + else if (!strcmp(variable, "face_recognize")) { recognition_enabled = val; - if (recognition_enabled) - { + if (recognition_enabled) { detection_enabled = val; } } #endif #endif - else - { + else { res = -1; } - if (res) - { + if (res) { return httpd_resp_send_500(req); } @@ -804,6 +794,10 @@ static esp_err_t cmd_handler(httpd_req_t *req) return httpd_resp_send(req, NULL, 0); } +static int print_reg(char * p, sensor_t * s, uint16_t reg, uint32_t mask){ + return sprintf(p, "\"0x%x\":%u,", reg, s->get_reg(s, reg, mask)); +} + static esp_err_t status_handler(httpd_req_t *req) { static char json_response[1024]; @@ -812,6 +806,35 @@ static esp_err_t status_handler(httpd_req_t *req) char *p = json_response; *p++ = '{'; + if(s->id.PID == OV5640_PID || s->id.PID == OV3660_PID){ + for(int reg = 0x3400; reg < 0x3406; reg+=2){ + p+=print_reg(p, s, reg, 0xFFF);//12 bit + } + p+=print_reg(p, s, 0x3406, 0xFF); + + p+=print_reg(p, s, 0x3500, 0xFFFF0);//16 bit + p+=print_reg(p, s, 0x3503, 0xFF); + p+=print_reg(p, s, 0x350a, 0x3FF);//10 bit + p+=print_reg(p, s, 0x350c, 0xFFFF);//16 bit + + for(int reg = 0x5480; reg <= 0x5490; reg++){ + p+=print_reg(p, s, reg, 0xFF); + } + + for(int reg = 0x5380; reg <= 0x538b; reg++){ + p+=print_reg(p, s, reg, 0xFF); + } + + for(int reg = 0x5580; reg < 0x558a; reg++){ + p+=print_reg(p, s, reg, 0xFF); + } + p+=print_reg(p, s, 0x558a, 0x1FF);//9 bit + } else { + p+=print_reg(p, s, 0xd3, 0xFF); + p+=print_reg(p, s, 0x111, 0xFF); + p+=print_reg(p, s, 0x132, 0xFF); + } + p += sprintf(p, "\"framesize\":%u,", s->status.framesize); p += sprintf(p, "\"quality\":%u,", s->status.quality); p += sprintf(p, "\"brightness\":%d,", s->status.brightness); @@ -855,6 +878,169 @@ static esp_err_t status_handler(httpd_req_t *req) return httpd_resp_send(req, json_response, strlen(json_response)); } +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) { + free(buf); + httpd_resp_send_404(req); + return ESP_FAIL; + } + free(buf); + + int xclk = atoi(_xclk); + ESP_LOGI(TAG, "Set XCLK: %d MHz", xclk); + + sensor_t *s = esp_camera_sensor_get(); + int res = s->set_xclk(s, LEDC_TIMER_0, xclk); + if (res) { + return httpd_resp_send_500(req); + } + + httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*"); + return httpd_resp_send(req, NULL, 0); +} + +static esp_err_t reg_handler(httpd_req_t *req) +{ + char *buf = NULL; + char _reg[32]; + char _mask[32]; + char _val[32]; + + if (parse_get(req, &buf) != ESP_OK || + 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); + httpd_resp_send_404(req); + return ESP_FAIL; + } + free(buf); + + int reg = atoi(_reg); + int mask = atoi(_mask); + int val = atoi(_val); + ESP_LOGI(TAG, "Set Register: reg: 0x%02x, mask: 0x%02x, value: 0x%02x", reg, mask, val); + + sensor_t *s = esp_camera_sensor_get(); + int res = s->set_reg(s, reg, mask, val); + if (res) { + return httpd_resp_send_500(req); + } + + httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*"); + return httpd_resp_send(req, NULL, 0); +} + +static esp_err_t greg_handler(httpd_req_t *req) +{ + char *buf = NULL; + char _reg[32]; + char _mask[32]; + + if (parse_get(req, &buf) != ESP_OK || + 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); + return ESP_FAIL; + } + free(buf); + + int reg = atoi(_reg); + int mask = atoi(_mask); + sensor_t *s = esp_camera_sensor_get(); + int res = s->get_reg(s, reg, mask); + if (res < 0) { + return httpd_resp_send_500(req); + } + ESP_LOGI(TAG, "Get Register: reg: 0x%02x, mask: 0x%02x, value: 0x%02x", reg, mask, res); + + char buffer[20]; + const char * val = itoa(res, buffer, 10); + httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*"); + return httpd_resp_send(req, val, strlen(val)); +} + +static int parse_get_var(char *buf, const char * key, int def) +{ + char _int[16]; + if(httpd_query_key_value(buf, key, _int, sizeof(_int)) != ESP_OK){ + return def; + } + return atoi(_int); +} + +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; + } + + int bypass = parse_get_var(buf, "bypass", 0); + int mul = parse_get_var(buf, "mul", 0); + int sys = parse_get_var(buf, "sys", 0); + int root = parse_get_var(buf, "root", 0); + int pre = parse_get_var(buf, "pre", 0); + int seld5 = parse_get_var(buf, "seld5", 0); + int pclken = parse_get_var(buf, "pclken", 0); + int pclk = parse_get_var(buf, "pclk", 0); + free(buf); + + ESP_LOGI(TAG, "Set Pll: bypass: %d, mul: %d, sys: %d, root: %d, pre: %d, seld5: %d, pclken: %d, pclk: %d", bypass, mul, sys, root, pre, seld5, pclken, pclk); + sensor_t *s = esp_camera_sensor_get(); + int res = s->set_pll(s, bypass, mul, sys, root, pre, seld5, pclken, pclk); + if (res) { + return httpd_resp_send_500(req); + } + + httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*"); + return httpd_resp_send(req, NULL, 0); +} + +static esp_err_t 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; + } + + int startX = parse_get_var(buf, "sx", 0); + int startY = parse_get_var(buf, "sy", 0); + int endX = parse_get_var(buf, "ex", 0); + int endY = parse_get_var(buf, "ey", 0); + int offsetX = parse_get_var(buf, "offx", 0); + int offsetY = parse_get_var(buf, "offy", 0); + int totalX = parse_get_var(buf, "tx", 0); + int totalY = parse_get_var(buf, "ty", 0); + int outputX = parse_get_var(buf, "ox", 0); + int outputY = parse_get_var(buf, "oy", 0); + bool scale = parse_get_var(buf, "scale", 0) == 1; + bool binning = parse_get_var(buf, "binning", 0) == 1; + free(buf); + + ESP_LOGI(TAG, "Set Window: Start: %d %d, End: %d %d, Offset: %d %d, Total: %d %d, Output: %d %d, Scale: %u, Binning: %u", startX, startY, endX, endY, offsetX, offsetY, totalX, totalY, outputX, outputY, scale, binning); + sensor_t *s = esp_camera_sensor_get(); + int res = s->set_res_raw(s, startX, startY, endX, endY, offsetX, offsetY, totalX, totalY, outputX, outputY, scale, binning); + if (res) { + return httpd_resp_send_500(req); + } + + httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*"); + return httpd_resp_send(req, NULL, 0); +} + static esp_err_t index_handler(httpd_req_t *req) { extern const unsigned char index_ov2640_html_gz_start[] asm("_binary_index_ov2640_html_gz_start"); @@ -865,22 +1051,22 @@ static esp_err_t index_handler(httpd_req_t *req) extern const unsigned char index_ov3660_html_gz_end[] asm("_binary_index_ov3660_html_gz_end"); size_t index_ov3660_html_gz_len = index_ov3660_html_gz_end - index_ov3660_html_gz_start; + extern const unsigned char index_ov5640_html_gz_start[] asm("_binary_index_ov5640_html_gz_start"); + extern const unsigned char index_ov5640_html_gz_end[] asm("_binary_index_ov5640_html_gz_end"); + size_t index_ov5640_html_gz_len = index_ov5640_html_gz_end - index_ov5640_html_gz_start; + httpd_resp_set_type(req, "text/html"); httpd_resp_set_hdr(req, "Content-Encoding", "gzip"); sensor_t *s = esp_camera_sensor_get(); - if (s != NULL) - { - if (s->id.PID == OV3660_PID) - { + if (s != NULL) { + if (s->id.PID == OV3660_PID) { return httpd_resp_send(req, (const char *)index_ov3660_html_gz_start, index_ov3660_html_gz_len); - } - else - { + } else if (s->id.PID == OV5640_PID) { + return httpd_resp_send(req, (const char *)index_ov5640_html_gz_start, index_ov5640_html_gz_len); + } else { return httpd_resp_send(req, (const char *)index_ov2640_html_gz_start, index_ov2640_html_gz_len); } - } - else - { + } else { ESP_LOGE(TAG, "Camera sensor not found"); return httpd_resp_send_500(req); } @@ -889,6 +1075,7 @@ static esp_err_t index_handler(httpd_req_t *req) void app_httpd_main() { httpd_config_t config = HTTPD_DEFAULT_CONFIG(); + config.max_uri_handlers = 10; httpd_uri_t index_uri = { .uri = "/", @@ -920,6 +1107,36 @@ void app_httpd_main() .handler = stream_handler, .user_ctx = NULL}; + httpd_uri_t xclk_uri = { + .uri = "/xclk", + .method = HTTP_GET, + .handler = xclk_handler, + .user_ctx = NULL}; + + httpd_uri_t reg_uri = { + .uri = "/reg", + .method = HTTP_GET, + .handler = reg_handler, + .user_ctx = NULL}; + + httpd_uri_t greg_uri = { + .uri = "/greg", + .method = HTTP_GET, + .handler = greg_handler, + .user_ctx = NULL}; + + httpd_uri_t pll_uri = { + .uri = "/pll", + .method = HTTP_GET, + .handler = pll_handler, + .user_ctx = NULL}; + + httpd_uri_t win_uri = { + .uri = "/resolution", + .method = HTTP_GET, + .handler = win_handler, + .user_ctx = NULL}; + ra_filter_init(&ra_filter, 20); #if CONFIG_ESP_FACE_DETECT_ENABLED @@ -956,6 +1173,12 @@ 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, &xclk_uri); + httpd_register_uri_handler(camera_httpd, ®_uri); + httpd_register_uri_handler(camera_httpd, &greg_uri); + httpd_register_uri_handler(camera_httpd, &pll_uri); + httpd_register_uri_handler(camera_httpd, &win_uri); } config.server_port += 1; diff --git a/examples/single_chip/camera_web_server/main/component.mk b/examples/single_chip/camera_web_server/main/component.mk index c2ecf38..0d16c28 100644 --- a/examples/single_chip/camera_web_server/main/component.mk +++ b/examples/single_chip/camera_web_server/main/component.mk @@ -8,3 +8,4 @@ # COMPONENT_EMBED_FILES := www/index_ov2640.html.gz COMPONENT_EMBED_FILES += www/index_ov3660.html.gz +COMPONENT_EMBED_FILES += www/index_ov5640.html.gz 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 8cccfd4..be229b4 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 @@ -43,6 +43,25 @@ #define HREF_GPIO_NUM 23 #define PCLK_GPIO_NUM 22 +#elif CONFIG_CAMERA_MODEL_ESP32_CAM_BOARD +#define PWDN_GPIO_NUM 32 +#define RESET_GPIO_NUM 33 +#define XCLK_GPIO_NUM 4 +#define SIOD_GPIO_NUM 18 +#define SIOC_GPIO_NUM 23 + +#define Y9_GPIO_NUM 36 +#define Y8_GPIO_NUM 19 +#define Y7_GPIO_NUM 21 +#define Y6_GPIO_NUM 39 +#define Y5_GPIO_NUM 35 +#define Y4_GPIO_NUM 14 +#define Y3_GPIO_NUM 13 +#define Y2_GPIO_NUM 34 +#define VSYNC_GPIO_NUM 5 +#define HREF_GPIO_NUM 27 +#define PCLK_GPIO_NUM 25 + #elif CONFIG_CAMERA_MODEL_ESP_EYE #define PWDN_GPIO_NUM -1 #define RESET_GPIO_NUM -1 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 3477ebb..cac38f9 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 @@ -345,6 +345,38 @@ .hidden { display: none } + + input[type=text] { + border: 1px solid #363636; + font-size: 14px; + height: 20px; + margin: 1px; + outline: 0; + border-radius: 5px + } + + .inline-button { + line-height: 20px; + margin: 2px; + padding: 1px 4px 2px 4px; + } + + label.toggle-section-label { + cursor: pointer; + display: block + } + + input.toggle-section-button { + outline: 0; + opacity: 0; + width: 0; + height: 0 + } + + input.toggle-section-button:checked+section.toggle-section { + display: none + } + @@ -356,24 +388,41 @@
@@ -572,6 +714,187 @@ document.addEventListener('DOMContentLoaded', function (event) { var baseHost = document.location.origin var streamUrl = baseHost + ':81' + function fetchUrl(url, cb){ + fetch(url) + .then(function (response) { + if (response.status !== 200) { + cb(response.status, response.statusText); + } else { + response.text().then(function(data){ + cb(200, data); + }).catch(function(err) { + cb(-1, err); + }); + } + }) + .catch(function(err) { + cb(-1, err); + }); + } + + function setReg(reg, offset, mask, value, cb){ + //console.log('Set Reg', '0x'+reg.toString(16), offset, '0x'+mask.toString(16), '0x'+value.toString(16), '('+value+')'); + value = (value & mask) << offset; + mask = mask << offset; + fetchUrl(`${baseHost}/reg?reg=${reg}&mask=${mask}&val=${value}`, cb); + } + + function getReg(reg, offset, mask, cb){ + mask = mask << offset; + fetchUrl(`${baseHost}/greg?reg=${reg}&mask=${mask}`, function(code, txt){ + let value = 0; + if(code == 200){ + value = parseInt(txt); + value = (value & mask) >> offset; + txt = ''+value; + } + cb(code, txt); + }); + } + + function setXclk(xclk, cb){ + fetchUrl(`${baseHost}/xclk?xclk=${xclk}`, cb); + } + + function setWindow(start_x, start_y, end_x, end_y, offset_x, offset_y, total_x, total_y, output_x, output_y, scaling, binning, cb){ + fetchUrl(`${baseHost}/resolution?sx=${start_x}&sy=${start_y}&ex=${end_x}&ey=${end_y}&offx=${offset_x}&offy=${offset_y}&tx=${total_x}&ty=${total_y}&ox=${output_x}&oy=${output_y}&scale=${scaling}&binning=${binning}`, cb); + } + + const setRegButton = document.getElementById('set-reg') + setRegButton.onclick = () => { + let reg = parseInt(document.getElementById('reg-addr').value); + let mask = parseInt(document.getElementById('reg-mask').value); + let value = parseInt(document.getElementById('reg-value').value); + + setReg(reg, 0, mask, value, function(code, txt){ + if(code != 200){ + alert('Error['+code+']: '+txt); + } + }); + } + + const getRegButton = document.getElementById('get-reg') + getRegButton.onclick = () => { + let reg = parseInt(document.getElementById('get-reg-addr').value); + let mask = parseInt(document.getElementById('get-reg-mask').value); + let value = document.getElementById('get-reg-value'); + + getReg(reg, 0, mask, function(code, txt){ + if(code != 200){ + value.innerHTML = 'Error['+code+']: '+txt; + } else { + value.innerHTML = '0x'+parseInt(txt).toString(16)+' ('+txt+')'; + } + }); + } + + const setXclkButton = document.getElementById('set-xclk') + setXclkButton.onclick = () => { + let xclk = parseInt(document.getElementById('xclk').value); + + setXclk(xclk, function(code, txt){ + if(code != 200){ + alert('Error['+code+']: '+txt); + } + }); + } + + const setResButton = document.getElementById('set-resolution') + setResButton.onclick = () => { + let start_x = parseInt(document.getElementById('start-x').value); + let offset_x = parseInt(document.getElementById('offset-x').value); + let offset_y = parseInt(document.getElementById('offset-y').value); + let total_x = parseInt(document.getElementById('total-x').value); + let total_y = parseInt(document.getElementById('total-y').value); + let output_x = parseInt(document.getElementById('output-x').value); + let output_y = parseInt(document.getElementById('output-y').value); + + setWindow(start_x, 0, 0, 0, offset_x, offset_y, total_x, total_y, output_x, output_y, false, false, function(code, txt){ + if(code != 200){ + alert('Error['+code+']: '+txt); + } + }); + } + + const setRegValue = (el) => { + let reg = el.attributes.reg?parseInt(el.attributes.reg.nodeValue):0; + let offset = el.attributes.offset?parseInt(el.attributes.offset.nodeValue):0; + let mask = el.attributes.mask?parseInt(el.attributes.mask.nodeValue):255; + let value = 0; + switch (el.type) { + case 'checkbox': + value = el.checked ? mask : 0; + break; + case 'range': + case 'text': + case 'select-one': + value = el.value; + break + default: + return; + } + + setReg(reg, offset, mask, value, function(code, txt){ + if(code != 200){ + alert('Error['+code+']: '+txt); + } + }); + } + + // Attach on change action for register elements + document + .querySelectorAll('.reg-action') + .forEach(el => { + if (el.type === 'text') { + el.onkeyup = function(e){ + if(e.keyCode == 13){ + setRegValue(el); + } + } + } else { + el.onchange = () => setRegValue(el) + } + }) + + + const updateRegValue = (el, value, updateRemote) => { + let initialValue; + let offset = el.attributes.offset?parseInt(el.attributes.offset.nodeValue):0; + let mask = (el.attributes.mask?parseInt(el.attributes.mask.nodeValue):255) << offset; + value = (value & mask) >> offset; + if (el.type === 'checkbox') { + initialValue = el.checked + value = !!value + el.checked = value + } else { + initialValue = el.value + el.value = value + } + } + + + const printReg = (el) => { + let reg = el.attributes.reg?parseInt(el.attributes.reg.nodeValue):0; + let offset = el.attributes.offset?parseInt(el.attributes.offset.nodeValue):0; + let mask = el.attributes.mask?parseInt(el.attributes.mask.nodeValue):255; + let value = 0; + switch (el.type) { + case 'checkbox': + value = el.checked ? mask : 0; + break; + case 'range': + case 'select-one': + value = el.value; + break + default: + return; + } + value = (value & mask) << offset; + return '0x'+reg.toString(16)+', 0x'+value.toString(16); + } + + + const hide = el => { el.classList.add('hidden') } @@ -669,6 +992,15 @@ document.addEventListener('DOMContentLoaded', function (event) { .forEach(el => { updateValue(el, state[el.id], false) }) + document + .querySelectorAll('.reg-action') + .forEach(el => { + let reg = el.attributes.reg?parseInt(el.attributes.reg.nodeValue):0; + if(reg == 0){ + return; + } + updateRegValue(el, state['0x'+reg.toString(16)], false) + }) }) const view = document.getElementById('stream') diff --git a/examples/single_chip/camera_web_server/main/www/index_ov2640.html.gz b/examples/single_chip/camera_web_server/main/www/index_ov2640.html.gz index 60cd5e6a09eaed7b69389dedfcc2f920affa0cc3..b0e1ddb0137af02160473b6e5403767ce29d32bf 100644 GIT binary patch literal 6787 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 prHjzUC||4 @@ -372,19 +389,40 @@
@@ -601,6 +910,212 @@ document.addEventListener('DOMContentLoaded', function (event) { var baseHost = document.location.origin var streamUrl = baseHost + ':81' + function fetchUrl(url, cb){ + fetch(url) + .then(function (response) { + if (response.status !== 200) { + cb(response.status, response.statusText); + } else { + response.text().then(function(data){ + cb(200, data); + }).catch(function(err) { + cb(-1, err); + }); + } + }) + .catch(function(err) { + cb(-1, err); + }); + } + + function setReg(reg, offset, mask, value, cb){ + console.log('Set Reg', '0x'+reg.toString(16), offset, '0x'+mask.toString(16), '0x'+value.toString(16), '('+value+')'); + value = (value & mask) << offset; + mask = mask << offset; + fetchUrl(`${baseHost}/reg?reg=${reg}&mask=${mask}&val=${value}`, cb); + } + + function getReg(reg, offset, mask, cb){ + mask = mask << offset; + fetchUrl(`${baseHost}/greg?reg=${reg}&mask=${mask}`, function(code, txt){ + let value = 0; + if(code == 200){ + value = parseInt(txt); + value = (value & mask) >> offset; + txt = ''+value; + } + cb(code, txt); + }); + } + + function setXclk(xclk, cb){ + fetchUrl(`${baseHost}/xclk?xclk=${xclk}`, cb); + } + + function setPll(bypass, mul, sys, root_, pre, seld5, pclken, pclk, cb){ + fetchUrl(`${baseHost}/pll?bypass=${bypass}&mul=${mul}&sys=${sys}&root=${root_}&pre=${pre}&seld5=${seld5}&pclken=${pclken}&pclk=${pclk}`, cb); + } + + function setWindow(start_x, start_y, end_x, end_y, offset_x, offset_y, total_x, total_y, output_x, output_y, scaling, binning, cb){ + fetchUrl(`${baseHost}/resolution?sx=${start_x}&sy=${start_y}&ex=${end_x}&ey=${end_y}&offx=${offset_x}&offy=${offset_y}&tx=${total_x}&ty=${total_y}&ox=${output_x}&oy=${output_y}&scale=${scaling}&binning=${binning}`, cb); + } + + const setRegButton = document.getElementById('set-reg') + setRegButton.onclick = () => { + let reg = parseInt(document.getElementById('reg-addr').value); + let mask = parseInt(document.getElementById('reg-mask').value); + let value = parseInt(document.getElementById('reg-value').value); + + setReg(reg, 0, mask, value, function(code, txt){ + if(code != 200){ + alert('Error['+code+']: '+txt); + } + }); + } + + const getRegButton = document.getElementById('get-reg') + getRegButton.onclick = () => { + let reg = parseInt(document.getElementById('get-reg-addr').value); + let mask = parseInt(document.getElementById('get-reg-mask').value); + let value = document.getElementById('get-reg-value'); + + getReg(reg, 0, mask, function(code, txt){ + if(code != 200){ + value.innerHTML = 'Error['+code+']: '+txt; + } else { + value.innerHTML = '0x'+parseInt(txt).toString(16)+' ('+txt+')'; + } + }); + } + + const setXclkButton = document.getElementById('set-xclk') + setXclkButton.onclick = () => { + let xclk = parseInt(document.getElementById('xclk').value); + + setXclk(xclk, function(code, txt){ + if(code != 200){ + alert('Error['+code+']: '+txt); + } + }); + } + + const setResButton = document.getElementById('set-resolution') + setResButton.onclick = () => { + let start_x = parseInt(document.getElementById('start-x').value); + let start_y = parseInt(document.getElementById('start-y').value); + let end_x = parseInt(document.getElementById('end-x').value); + let end_y = parseInt(document.getElementById('end-y').value); + let offset_x = parseInt(document.getElementById('offset-x').value); + let offset_y = parseInt(document.getElementById('offset-y').value); + let total_x = parseInt(document.getElementById('total-x').value); + let total_y = parseInt(document.getElementById('total-y').value); + let output_x = parseInt(document.getElementById('output-x').value); + let output_y = parseInt(document.getElementById('output-y').value); + let scaling = document.getElementById('scaling').checked?1:0; + let binning = document.getElementById('binning').checked?1:0; + + setWindow(start_x, start_y, end_x, end_y, offset_x, offset_y, total_x, total_y, output_x, output_y, scaling, binning, function(code, txt){ + if(code != 200){ + alert('Error['+code+']: '+txt); + } + }); + } + + const setPllButton = document.getElementById('set-pll') + setPllButton.onclick = () => { + var bypass = document.getElementById('bypass-pll').checked?1:0; + var mul = parseInt(document.getElementById('mul-pll').value); + var sys = parseInt(document.getElementById('sys-pll').value); + var root_ = parseInt(document.getElementById('root-pll').value); + var pre = parseInt(document.getElementById('pre-pll').value); + var seld5 = parseInt(document.getElementById('seld5-pll').value); + var pclken = document.getElementById('pclk-en').checked?1:0; + var pclk = parseInt(document.getElementById('pclk-pll').value); + + setPll(bypass, mul, sys, root_, pre, seld5, pclken, pclk, function(code, txt){ + if(code != 200){ + alert('Error['+code+']: '+txt); + } + }); + } + + const setRegValue = (el) => { + let reg = el.attributes.reg?parseInt(el.attributes.reg.nodeValue):0; + let offset = el.attributes.offset?parseInt(el.attributes.offset.nodeValue):0; + let mask = el.attributes.mask?parseInt(el.attributes.mask.nodeValue):255; + let value = 0; + switch (el.type) { + case 'checkbox': + value = el.checked ? mask : 0; + break; + case 'range': + case 'text': + case 'select-one': + value = el.value; + break + default: + return; + } + + setReg(reg, offset, mask, value, function(code, txt){ + if(code != 200){ + alert('Error['+code+']: '+txt); + } + }); + } + + // Attach on change action for register elements + document + .querySelectorAll('.reg-action') + .forEach(el => { + if (el.type === 'text') { + el.onkeyup = function(e){ + if(e.keyCode == 13){ + setRegValue(el); + } + } + } else { + el.onchange = () => setRegValue(el) + } + }) + + + const updateRegValue = (el, value, updateRemote) => { + let initialValue; + let offset = el.attributes.offset?parseInt(el.attributes.offset.nodeValue):0; + let mask = (el.attributes.mask?parseInt(el.attributes.mask.nodeValue):255) << offset; + value = (value & mask) >> offset; + if (el.type === 'checkbox') { + initialValue = el.checked + value = !!value + el.checked = value + } else { + initialValue = el.value + el.value = value + } + } + + + const printReg = (el) => { + let reg = el.attributes.reg?parseInt(el.attributes.reg.nodeValue):0; + let offset = el.attributes.offset?parseInt(el.attributes.offset.nodeValue):0; + let mask = el.attributes.mask?parseInt(el.attributes.mask.nodeValue):255; + let value = 0; + switch (el.type) { + case 'checkbox': + value = el.checked ? mask : 0; + break; + case 'range': + case 'select-one': + value = el.value; + break + default: + return; + } + value = (value & mask) << offset; + return '0x'+reg.toString(16)+', 0x'+value.toString(16); + } + const hide = el => { el.classList.add('hidden') } @@ -696,6 +1211,15 @@ document.addEventListener('DOMContentLoaded', function (event) { .forEach(el => { updateValue(el, state[el.id], false) }) + document + .querySelectorAll('.reg-action') + .forEach(el => { + let reg = el.attributes.reg?parseInt(el.attributes.reg.nodeValue):0; + if(reg == 0){ + return; + } + updateRegValue(el, state['0x'+reg.toString(16)], false) + }) }) const view = document.getElementById('stream') diff --git a/examples/single_chip/camera_web_server/main/www/index_ov3660.html.gz b/examples/single_chip/camera_web_server/main/www/index_ov3660.html.gz index db85d562eef4e4e72d597a29c8b2807505c6f86c..8274a211476a8aae52e797293247e0a53610d97f 100644 GIT binary patch literal 8887 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 F0RX(*5(WSO literal 4847 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 V;$Ka?IOhL(_rL5a6oHX<001QST$um> diff --git a/examples/single_chip/camera_web_server/main/www/index_ov5640.html b/examples/single_chip/camera_web_server/main/www/index_ov5640.html new file mode 100644 index 0000000..2d1cf8f --- /dev/null +++ b/examples/single_chip/camera_web_server/main/www/index_ov5640.html @@ -0,0 +1,1359 @@ + + + + + + ESP32 OV5640 + + + +
+ +
+ +
+ +
+
+
+ + + diff --git a/examples/single_chip/camera_web_server/main/www/index_ov5640.html.gz b/examples/single_chip/camera_web_server/main/www/index_ov5640.html.gz new file mode 100644 index 0000000000000000000000000000000000000000..3deba92ef04745cb481ff3d5dd80bbcd5a5e7aed GIT binary patch literal 8765 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 n6((_(sglY literal 0 HcmV?d00001