diff --git a/README.md b/README.md
index 6bbcded4..82419a8e 100644
--- a/README.md
+++ b/README.md
@@ -1,119 +1,121 @@
-# ESP-WHO [[中文]](./README_CN.md)
+# ESP-WHO
-ESP-WHO is an image processing development platform based on Espressif chips. It contains development examples that may be applied in practical applications.
+ESP-WHO is a face detection and recognition platform that is currently based on Espressif Systems' [ESP32](https://espressif.com/en/products/hardware/esp32/overview) chip.
-## Overview
+# Overview
+ESP-WHO supports development of face detection and recognition applications based around Espressif Systems' [ESP32](https://espressif.com/en/products/hardware/esp32/overview) chip in the most convenient way. With ESP-WHO, you can easily build up face detection- and recognition-featured applications, for instance:
+* A coffee machine that brews coffee according to your taste preference;
+* Home applicance that will shut off the electricity automatically when unsupervised children are operating them;
+* And other more applications that suit your needs.
-ESP-WHO provides examples such as Human Face Detection, Human Face Recognition, Cat Face Detection, Gesture Recognition, etc. You can develop a variety of practical applications based on these examples. ESP-WHO runs on ESP-IDF. [ESP-DL](https://github.com/espressif/esp-dl) provides rich deep learning related interfaces for ESP-WHO, which can be implemented with various peripherals to realize many interesting applications.
+In general, the ESP-WHO features will be supported as shown below:
-
-
-
+
+In ESP-WHO, Detection, Recognition and Image Utility are at the core of the platform.
-## What You Need
+* **Image Utility** offers fundamental image processing APIs.
-### Hardware
+* **Detection** takes images as input and give the position of face if there is a face. It is implemented with MTMN model, which refers to [MTCNN](http://cn.arxiv.org/abs/1604.02878) and [MobileNets](https://arxiv.org/abs/1801.04381).
-We recommend novice developers to use the development boards designed by Espressif. The examples provided by ESP-WHO are developed based on the following Espressif development board, and the corresponding relationships between the development boards and SoC are shown in the table below.
-
-| SoC | [ESP32](https://www.espressif.com/en/products/socs/esp32) | [ESP32-S2](https://www.espressif.com/en/products/socs/esp32-s2) | [ESP32-S3](https://www.espressif.com/en/products/socs/esp32-s3) |
-| :------- | :----------------------------------------------------------: | :----------------------------------------------------------: | :----------------------------------------------------------: |
-| Development Board | [ESP-EYE](https://www.espressif.com/en/products/devkits/esp-eye/overview) | [ESP32-S2-Kaluga-1](https://docs.espressif.com/projects/esp-idf/en/latest/esp32s2/hw-reference/esp32s2/user-guide-esp32-s2-kaluga-1-kit.html) | [ESP-S3-EYE](https://www.espressif.com/en/products/devkits/esp-s3-eye/overview) |
+* **Recognition** is to identify the particular person, and it needs the results of detection. It is implemented with MobileFace model.
-> Using a development board not mentioned in the table above, configure pins assigned to peripherals manually, such as camera, LCD, and buttons.
+* **Optimization** is mainly to increase the precision of the inference, and to accelerate the whole process. But also it might change the structure of the network, update the coefficients, refactor the code, etc.
-### Software
+Both input and output are flexible.
-#### Get ESP-IDF
+* Image sources could be **input** via camera. However, we don't provide many drivers right now, those for other camera modules will be released in the future.
-ESP-WHO runs on ESP-IDF. For details on getting ESP-IDF, please refer to [ESP-IDF Programming Guide](https://idf.espressif.com/).
+* Results could be **output** and displayed through Command line, LCD or even website via Wi-Fi http service.
-> Please use the latest version of ESP-IDF on the master branch.
-#### Get ESP-WHO
+# Quick Start with ESP-WHO
-Run the following commands in your terminal to download ESP-WHO:
+## Hardware Preparation
-```bash
-git clone --recursive https://github.com/espressif/esp-who.git
-```
+To run ESP-WHO, you need to have a development board which integrates a **ESP32 module** that has sufficient GPIO pins and more than **4 MB** external SPI RAM. Either [ESP-WROVER-KIT](https://www.espressif.com/en/products/hardware/esp-wrover-kit/overview) or [ESP-EYE](https://www.espressif.com/en/products/hardware/esp-eye/overview) can be a good choice as the test board.
-> Remember to use ``git submodule update --recursive --init`` to pull and update submodules of ESP-WHO.
-
-## Run Examples
-
-All examples of ESP-WHO are stored in [examples](./examples) folder. Structure of this folder is shown below:
-
-```bash
-├── examples
-│ ├── cat_face_detection // Cat Face Detection examples
-│ │ ├── lcd // Output displayed on LCD screen
-│ │ └── terminal // Output displayed on terminal
-│ ├── code_recognition // Barcode and QR Code Recognition examples
-│ ├── human_face_detection // Human Face Detection examples
-│ │ ├── lcd
-│ │ └── terminal
-│ ├── human_face_recognition // Human Face Recognition examples
-│ │ ├── lcd
-│ │ ├── terminal
-│ │ └── README.md // Detailed description of examples
-│ └── motion_detection // Motion Detection examples
-│ ├── lcd
-│ ├── terminal
-│ ├── web
-│ └── README.rst
-```
+On how to configure ESP32 module for your applications, please refer to the README.md of each example.
+
+
+## Software Preparation
+
+### Image
+
+The recommended resolution of input image is **QVGA (320x240)**.
+
+As for choosing camera as an image offer, make sure that the ESP32 module you choose offers specific pins that your camera needs.
+
+By now, we have provided some drivers of cameras, which are highly recommended to get started with:
+
+**OV2640**
-For the development boards mentioned in [Hardware](#Hardware), all examples are available out of the box. To run the examples, you only need to perform [Step 1: Set the target chip] (#Step-1 Set the target chip) and [Step 4: Launch and monitor] (#Step-4 Launch and monitor).
+**OV3660**
-### Step 1: Set the target chip
+**OV5640**
-Open the terminal and go to any folder that stores examples (e.g. examples/human_face_detection/lcd). Run the following command to set the target chip:
+### ESP-WHO
+Make sure you clone the project recursive:
-```bash
-idf.py set-target [SoC]
```
+git clone --recursive https://github.com/espressif/esp-who.git
+```
+
+If you clone project without `--recursive` flag, please go to the `esp-who` directory and run command `git submodule update --init` before doing anything.
+
+### ESP-IDF
+See setup guides for detailed instructions to set up the ESP-IDF:
+
+* [Getting Started Guide for ESP-IDF v4.0](https://docs.espressif.com/projects/esp-idf/en/v4.0/get-started/index.html)
-Replace [SoC] with your target chip, e.g. esp32, esp32s2, esp32s3.
+ We take [ESP-IDF v4.0](https://github.com/espressif/esp-idf/tree/release/v4.0) as the default version.
-### (Optional) Step 2: Configure the camera
+ > Now this repository supports two versions of IDF: v3.3.1 and v4.0.0.
-If not using the Espressif development boards mentioned in [Hardware](#Hardware), configure the camera pins manually. Enter `idf.py menuconfig` in the terminal and click (Top) -> Component config -> ESP-WHO Configuration to enter the ESP-WHO configuration interface, as shown below:
-
+# Components
-Click Camera Configuration to select the pin configuration of the camera according to the development board you use, as shown in the following figure:
+Components is the main framework of the SDK, with some drivers and algorithm inside.
-
+## Camera
-If the board you are using is not shown in the figure above, please select ``Custom Camera Pinout`` and configure the corresponding pins correctly, as shown in the following figure:
+The [camera](https://github.com/espressif/esp32-camera/tree/master) component contains drivers for camera devices of ESP32.
-
-### (Optional) Step 3: Configure the Wi-Fi
+## esp-face
-If the output of example is displayed on web server, click Wi-Fi Configuration to configure Wi-Fi password and other parameters, as shown in the following figure:
+The [esp-face](https://github.com/espressif/esp-face/tree/master) component contains the APIs of ESP-WHO neural networks, including face detection and recognition framework.
-
-### Step 4: Launch and monitor
+# Examples
+The folder of [examples](examples) contains sample applications demonstrating the API features of ESP-WHO.
-Flash the program and launch IDF Monitor:
+Take one Face Detection as an example.
-```bash
-idf.py flash monitor
+1. Get into one example folder `esp-who/examples/single_chip/face_detection_with_command_line`.
+```
+cd esp-who/examples/single_chip/face_detection_with_command_line
+```
+
+2. Compile and flash the project.
+```
+idf.py build
+idf.py flash -p [PORT]
```
+3. Advanced users can modify some options by using the command `idf.py menuconfig`.
+The default configuration of all examples is based on [ESP-EYE](https://www.espressif.com/en/products/hardware/esp-eye/overview).
-## Default Binaries of Development Boards
+Check README.md of each example for more details.
-The default binaries for each development board are stored in the folder [default_bin](./default_bin). You can use Flash Download Tool (https://www.espressif.com/en/support/download/other-tools) to flash binaries.
+# Default bin
+The default bin is [HERE](docs/zh_CN/get-started/default_bin/). You can use [Flash Download Tools](https://www.espressif.com/en/support/download/other-tools) to write the default bin to the ESP-EYE.
-## Feedback
+# Resources
+* [Check the Issues section on github](https://github.com/espressif/esp-who/issues) if you find a bug or have a feature request, please check existing Issues before opening a new one.
-Please submit an [issue](https://github.com/espressif/esp-who/issues) if you find any problems using our products, and we will reply as soon as possible.
+* If you're interested in contributing to ESP-WHO, please check the [Contributions Guide](https://esp-idf.readthedocs.io/en/latest/contribute/index.html).
diff --git a/components/esp32-camera b/components/esp32-camera
index e02038c3..61400bc7 160000
--- a/components/esp32-camera
+++ b/components/esp32-camera
@@ -1 +1 @@
-Subproject commit e02038c3f0c6d30c6cd2f038fe2d6255e5666ec2
+Subproject commit 61400bc73b18e9ffcfd5b3806fee26d135c45fe7
diff --git a/components/modules/Kconfig b/components/modules/Kconfig
index 3690c36f..c036983c 100644
--- a/components/modules/Kconfig
+++ b/components/modules/Kconfig
@@ -82,6 +82,8 @@ menu "ESP-WHO Configuration"
bool "M5Stack Camera F (Wide)"
config CAMERA_MODULE_AI_THINKER
bool "ESP32-CAM by AI-Thinker"
+ config CAMERA_MODULE_S3_KS_DIY
+ bool "ESP32-S3-CAM by S3_KS-DIY"
config CAMERA_MODULE_CUSTOM
bool "Custom Camera Pinout"
endchoice
diff --git a/components/modules/camera/who_camera.h b/components/modules/camera/who_camera.h
index 2b72ceb9..e2c41f8b 100644
--- a/components/modules/camera/who_camera.h
+++ b/components/modules/camera/who_camera.h
@@ -171,6 +171,26 @@
#define CAMERA_PIN_HREF 23
#define CAMERA_PIN_PCLK 22
+#elif CONFIG_CAMERA_MODULE_S3_KS_DIY
+#define CAMERA_MODULE_NAME "S3_KS_DIY"
+#define CAMERA_PIN_PWDN -4
+#define CAMERA_PIN_RESET -1
+#define CAMERA_PIN_XCLK -1
+#define CAMERA_PIN_SIOD 17
+#define CAMERA_PIN_SIOC 18
+
+#define CAMERA_PIN_D7 39
+#define CAMERA_PIN_D6 41
+#define CAMERA_PIN_D5 42
+#define CAMERA_PIN_D4 5
+#define CAMERA_PIN_D3 40
+#define CAMERA_PIN_D2 14
+#define CAMERA_PIN_D1 47
+#define CAMERA_PIN_D0 45
+#define CAMERA_PIN_VSYNC 21
+#define CAMERA_PIN_HREF 38
+#define CAMERA_PIN_PCLK 48
+
#elif CONFIG_CAMERA_MODULE_CUSTOM
#define CAMERA_MODULE_NAME "CUSTOM"
#define CAMERA_PIN_PWDN CONFIG_CAMERA_PIN_PWDN
diff --git a/examples/code_recognition/main/include/app_peripherals.h b/examples/code_recognition/main/include/app_peripherals.h
index d69f6d04..9d4b0f40 100644
--- a/examples/code_recognition/main/include/app_peripherals.h
+++ b/examples/code_recognition/main/include/app_peripherals.h
@@ -174,6 +174,25 @@
#define CAMERA_PIN_HREF 23
#define CAMERA_PIN_PCLK 22
+#elif CONFIG_CAMERA_MODULE_S3_KS_DIY
+#define CAMERA_MODULE_NAME "S3_KS_DIY"
+#define CAMERA_PIN_PWDN -1
+#define CAMERA_PIN_RESET -1
+#define CAMERA_PIN_XCLK -1
+#define CAMERA_PIN_SIOD 17
+#define CAMERA_PIN_SIOC 18
+
+#define CAMERA_PIN_D7 39
+#define CAMERA_PIN_D6 41
+#define CAMERA_PIN_D5 42
+#define CAMERA_PIN_D4 5
+#define CAMERA_PIN_D3 40
+#define CAMERA_PIN_D2 14
+#define CAMERA_PIN_D1 47
+#define CAMERA_PIN_D0 45
+#define CAMERA_PIN_VSYNC 21
+#define CAMERA_PIN_HREF 38
+#define CAMERA_PIN_PCLK 48
#elif CONFIG_CAMERA_MODULE_CUSTOM
#define CAMERA_MODULE_NAME "CUSTOM"
#define CAMERA_PIN_PWDN CONFIG_CAMERA_PIN_PWDN
@@ -197,8 +216,8 @@
#define XCLK_FREQ_HZ 20000000
#define CAMERA_PIXFORMAT PIXFORMAT_RGB565
-#define CAMERA_FRAME_SIZE FRAMESIZE_240X240
-#define CAMERA_FB_COUNT 2
+#define CAMERA_FRAME_SIZE FRAMESIZE_QVGA
+#define CAMERA_FB_COUNT 1
@@ -232,6 +251,23 @@
#define LCD_WIDTH 240
#define LCD_HEIGHT 320
#define LCD_ROTATE SCR_SWAP_XY|SCR_MIRROR_X
+
+#elif CAMERA_MODULE_S3_KS_DIY
+#define LCD_CONTROLLER SCREEN_CONTROLLER_ST7789
+
+#define LCD_MOSI 11
+#define LCD_MISO 13
+#define LCD_SCLK 12
+#define LCD_CS 10
+#define LCD_DC 9
+#define LCD_RST -1
+#define LCD_BCKL -1
+
+// LCD display width and height
+#define LCD_WIDTH 240
+#define LCD_HEIGHT 320
+#define LCD_ROTATE SCR_SWAP_XY|SCR_MIRROR_X
+
#endif
diff --git a/examples/single_chip/camera_web_server/README.md b/examples/single_chip/camera_web_server/README.md
new file mode 100755
index 00000000..32587ff4
--- /dev/null
+++ b/examples/single_chip/camera_web_server/README.md
@@ -0,0 +1,22 @@
+# Camera with Web Server
+
+# Preparation
+
+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** or **OV5640** image sensor, which we highly recommend for beginners, is used in this example.
+
+# Quick Start
+
+After you've completed the hardware settings, please follow the steps below:
+
+1. **Connect** the camera to ESP32 module. For connection pins, please see [here](../../../docs/en/Camera_connections.md)
+2. **Configure** the example through `idf.py menuconfig`;
+3. **Build And Flash** the application to ESP32;
+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;
+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
new file mode 100644
index 00000000..eca8754c
--- /dev/null
+++ b/examples/single_chip/camera_web_server/main/app_httpd.c
@@ -0,0 +1,1250 @@
+// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+#include "app_httpd.h"
+#include "esp_http_server.h"
+#include "esp_timer.h"
+#include "esp_camera.h"
+#include "img_converters.h"
+#include "fb_gfx.h"
+#include "driver/ledc.h"
+//#include "camera_index.h"
+#include "sdkconfig.h"
+#include "app_mdns.h"
+#include "app_camera.h"
+
+#if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG)
+#include "esp32-hal-log.h"
+#define TAG ""
+#else
+#include "esp_log.h"
+static const char *TAG = "camera_httpd";
+#endif
+
+#if CONFIG_ESP_FACE_DETECT_ENABLED
+
+#include "fd_forward.h"
+
+#if CONFIG_ESP_FACE_RECOGNITION_ENABLED
+#include "fr_forward.h"
+
+#define ENROLL_CONFIRM_TIMES 5
+#define FACE_ID_SAVE_NUMBER 7
+#endif
+
+#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)
+#endif
+
+#ifdef CONFIG_LED_ILLUMINATOR_ENABLED
+int led_duty = 0;
+bool isStreaming = false;
+#ifdef CONFIG_LED_LEDC_LOW_SPEED_MODE
+#define CONFIG_LED_LEDC_SPEED_MODE LEDC_LOW_SPEED_MODE
+#else
+#define CONFIG_LED_LEDC_SPEED_MODE LEDC_HIGH_SPEED_MODE
+#endif
+#endif
+
+typedef struct
+{
+ httpd_req_t *req;
+ size_t len;
+} jpg_chunking_t;
+
+#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\nX-Timestamp: %d.%06d\r\n\r\n";
+
+httpd_handle_t stream_httpd = NULL;
+httpd_handle_t camera_httpd = NULL;
+
+#if CONFIG_ESP_FACE_DETECT_ENABLED
+
+static int8_t detection_enabled = 0;
+
+static mtmn_config_t mtmn_config = {0};
+
+#if CONFIG_ESP_FACE_RECOGNITION_ENABLED
+static int8_t recognition_enabled = 0;
+static int8_t is_enrolling = 0;
+static face_id_list id_list = {0};
+#endif
+
+#endif
+
+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;
+
+static ra_filter_t ra_filter;
+
+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;
+}
+
+#if CONFIG_ESP_FACE_DETECT_ENABLED
+#if CONFIG_ESP_FACE_RECOGNITION_ENABLED
+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;
+}
+#endif
+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
+ }
+}
+
+#if CONFIG_ESP_FACE_RECOGNITION_ENABLED
+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)
+ {
+ ESP_LOGE(TAG, "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 left_sample_face = enroll_face(&id_list, aligned_face);
+
+ if (left_sample_face == (ENROLL_CONFIRM_TIMES - 1))
+ {
+ ESP_LOGD(TAG, "Enrolling Face ID: %d", id_list.tail);
+ }
+ ESP_LOGD(TAG, "Enrolling Face ID: %d sample %d", id_list.tail, ENROLL_CONFIRM_TIMES - left_sample_face);
+ rgb_printf(image_matrix, FACE_COLOR_CYAN, "ID[%u] Sample[%u]", id_list.tail, ENROLL_CONFIRM_TIMES - left_sample_face);
+ if (left_sample_face == 0)
+ {
+ is_enrolling = 0;
+ ESP_LOGD(TAG, "Enrolled Face ID: %d", id_list.tail);
+ }
+ }
+ else
+ {
+ matched_id = recognize_face(&id_list, aligned_face);
+ if (matched_id >= 0)
+ {
+ ESP_LOGW(TAG, "Match Face ID: %u", matched_id);
+ rgb_printf(image_matrix, FACE_COLOR_GREEN, "Hello Subject %u", matched_id);
+ }
+ else
+ {
+ ESP_LOGW(TAG, "No Match Found");
+ rgb_print(image_matrix, FACE_COLOR_RED, "Intruder Alert!");
+ matched_id = -1;
+ }
+ }
+ }
+ else
+ {
+ ESP_LOGW(TAG, "Face Not Aligned");
+ //rgb_print(image_matrix, FACE_COLOR_YELLOW, "Human Detected");
+ }
+
+ dl_matrix3du_free(aligned_face);
+ return matched_id;
+}
+#endif
+#endif
+
+#ifdef CONFIG_LED_ILLUMINATOR_ENABLED
+void enable_led(bool en)
+{ // Turn LED On or Off
+ int duty = en ? led_duty : 0;
+ if (en && isStreaming && (led_duty > CONFIG_LED_MAX_INTENSITY))
+ {
+ duty = CONFIG_LED_MAX_INTENSITY;
+ }
+ ledc_set_duty(CONFIG_LED_LEDC_SPEED_MODE, CONFIG_LED_LEDC_CHANNEL, duty);
+ ledc_update_duty(CONFIG_LED_LEDC_SPEED_MODE, CONFIG_LED_LEDC_CHANNEL);
+ ESP_LOGI(TAG, "Set LED intensity to %d", duty);
+}
+#endif
+
+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;
+ 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;
+ esp_err_t res = ESP_OK;
+ int64_t fr_start = esp_timer_get_time();
+
+#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()
+ fb = esp_camera_fb_get(); // or it won't be visible in the frame. A better way to do this is needed.
+ enable_led(false);
+#else
+ fb = esp_camera_fb_get();
+#endif
+
+ if (!fb)
+ {
+ ESP_LOGE(TAG, "Camera capture failed");
+ httpd_resp_send_500(req);
+ return ESP_FAIL;
+ }
+
+ 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", "*");
+
+ 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;
+ bool s;
+ bool detected = false;
+ int face_id = 0;
+ if (!detection_enabled || fb->width > 400)
+ {
+#endif
+ 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();
+ ESP_LOGI(TAG, "JPG: %uB %ums", (uint32_t)(fb_len), (uint32_t)((fr_end - fr_start) / 1000));
+ return res;
+#if CONFIG_ESP_FACE_DETECT_ENABLED
+ }
+
+ dl_matrix3du_t *image_matrix = dl_matrix3du_alloc(1, fb->width, fb->height, 3);
+ if (!image_matrix)
+ {
+ esp_camera_fb_return(fb);
+ ESP_LOGE(TAG, "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);
+ ESP_LOGE(TAG, "to rgb888 failed");
+ httpd_resp_send_500(req);
+ return ESP_FAIL;
+ }
+
+ box_array_t *net_boxes = face_detect(image_matrix, &mtmn_config);
+
+ if (net_boxes)
+ {
+ detected = true;
+#if CONFIG_ESP_FACE_RECOGNITION_ENABLED
+ if (recognition_enabled)
+ {
+ face_id = run_face_recognition(image_matrix, net_boxes);
+ }
+#endif
+ draw_face_boxes(image_matrix, net_boxes, face_id);
+ dl_lib_free(net_boxes->score);
+ dl_lib_free(net_boxes->box);
+ if (net_boxes->landmark != NULL)
+ 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)
+ {
+ ESP_LOGE(TAG, "JPEG compression failed");
+ return ESP_FAIL;
+ }
+
+ int64_t fr_end = esp_timer_get_time();
+ ESP_LOGI(TAG, "FACE: %uB %ums %s%d", (uint32_t)(jchunk.len), (uint32_t)((fr_end - fr_start) / 1000), detected ? "DETECTED " : "", face_id);
+ return res;
+#endif
+}
+
+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[128];
+#if CONFIG_ESP_FACE_DETECT_ENABLED
+ dl_matrix3du_t *image_matrix = NULL;
+ bool detected = false;
+ int face_id = 0;
+ int64_t fr_start = 0;
+ int64_t fr_ready = 0;
+ int64_t fr_face = 0;
+ int64_t fr_recognize = 0;
+ int64_t fr_encode = 0;
+#endif
+
+ static int64_t last_frame = 0;
+ if (!last_frame)
+ {
+ last_frame = esp_timer_get_time();
+ }
+
+ res = httpd_resp_set_type(req, _STREAM_CONTENT_TYPE);
+ if (res != ESP_OK)
+ {
+ return res;
+ }
+
+ 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);
+ isStreaming = true;
+#endif
+
+ while (true)
+ {
+#if CONFIG_ESP_FACE_DETECT_ENABLED
+ detected = false;
+ face_id = 0;
+#endif
+
+ fb = esp_camera_fb_get();
+ if (!fb)
+ {
+ ESP_LOGE(TAG, "Camera capture failed");
+ res = ESP_FAIL;
+ }
+ 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;
+ fr_face = fr_start;
+ fr_encode = fr_start;
+ fr_recognize = fr_start;
+ if (!detection_enabled || fb->width > 400)
+ {
+#endif
+ 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)
+ {
+ ESP_LOGE(TAG, "JPEG compression failed");
+ res = ESP_FAIL;
+ }
+ }
+ else
+ {
+ _jpg_buf_len = fb->len;
+ _jpg_buf = fb->buf;
+ }
+#if CONFIG_ESP_FACE_DETECT_ENABLED
+ }
+ else
+ {
+
+ image_matrix = dl_matrix3du_alloc(1, fb->width, fb->height, 3);
+
+ if (!image_matrix)
+ {
+ ESP_LOGE(TAG, "dl_matrix3du_alloc failed");
+ res = ESP_FAIL;
+ }
+ else
+ {
+ if (!fmt2rgb888(fb->buf, fb->len, fb->format, image_matrix->item))
+ {
+ ESP_LOGE(TAG, "fmt2rgb888 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 CONFIG_ESP_FACE_RECOGNITION_ENABLED
+ if (recognition_enabled)
+ {
+ face_id = run_face_recognition(image_matrix, net_boxes);
+ }
+ fr_recognize = esp_timer_get_time();
+#endif
+ draw_face_boxes(image_matrix, net_boxes, face_id);
+ dl_lib_free(net_boxes->score);
+ dl_lib_free(net_boxes->box);
+ if (net_boxes->landmark != NULL)
+ 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))
+ {
+ ESP_LOGE(TAG, "fmt2jpg failed");
+ }
+ 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);
+ }
+ }
+#endif
+ }
+ if (res == ESP_OK)
+ {
+ res = httpd_resp_send_chunk(req, _STREAM_BOUNDARY, strlen(_STREAM_BOUNDARY));
+ }
+ if (res == ESP_OK)
+ {
+ 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)
+ {
+ res = httpd_resp_send_chunk(req, (const char *)_jpg_buf, _jpg_buf_len);
+ }
+ if (fb)
+ {
+ esp_camera_fb_return(fb);
+ fb = NULL;
+ _jpg_buf = NULL;
+ }
+ else if (_jpg_buf)
+ {
+ free(_jpg_buf);
+ _jpg_buf = NULL;
+ }
+ if (res != ESP_OK)
+ {
+ break;
+ }
+ int64_t fr_end = esp_timer_get_time();
+
+#if CONFIG_ESP_FACE_DETECT_ENABLED
+ 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;
+#endif
+
+ int64_t frame_time = fr_end - last_frame;
+ last_frame = fr_end;
+ frame_time /= 1000;
+ uint32_t avg_frame_time = ra_filter_run(&ra_filter, frame_time);
+ ESP_LOGI(TAG, "MJPG: %uB %ums (%.1ffps), AVG: %ums (%.1ffps)"
+#if CONFIG_ESP_FACE_DETECT_ENABLED
+ ", %u+%u+%u+%u=%u %s%d"
+#endif
+ ,
+ (uint32_t)(_jpg_buf_len),
+ (uint32_t)frame_time, 1000.0 / (uint32_t)frame_time,
+ avg_frame_time, 1000.0 / avg_frame_time
+#if CONFIG_ESP_FACE_DETECT_ENABLED
+ ,
+ (uint32_t)ready_time, (uint32_t)face_time, (uint32_t)recognize_time, (uint32_t)encode_time, (uint32_t)process_time,
+ (detected) ? "DETECTED " : "", face_id
+#endif
+ );
+ }
+
+#ifdef CONFIG_LED_ILLUMINATOR_ENABLED
+ isStreaming = false;
+ enable_led(false);
+#endif
+
+ last_frame = 0;
+ return res;
+}
+
+static esp_err_t parse_get(httpd_req_t *req, char **obuf)
+{
+ char *buf = NULL;
+ size_t buf_len = 0;
+
+ buf_len = httpd_req_get_url_query_len(req) + 1;
+ if (buf_len > 1) {
+ buf = (char *)malloc(buf_len);
+ if (!buf) {
+ httpd_resp_send_500(req);
+ return ESP_FAIL;
+ }
+ if (httpd_req_get_url_query_str(req, buf, buf_len) == ESP_OK) {
+ *obuf = buf;
+ return ESP_OK;
+ }
+ free(buf);
+ }
+ 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) {
+ 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);
+ 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 (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 {
+ ESP_LOGI(TAG, "Unknown command: %s", variable);
+ res = -1;
+ }
+
+ if (res < 0) {
+ return httpd_resp_send_500(req);
+ }
+
+ httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*");
+ 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];
+
+ sensor_t *s = esp_camera_sensor_get();
+ 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 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);
+ }
+
+ p += sprintf(p, "\"board\":\"%s\",", CAM_BOARD);
+ 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, "\"quality\":%u,", s->status.quality);
+ p += sprintf(p, "\"brightness\":%d,", s->status.brightness);
+ p += sprintf(p, "\"contrast\":%d,", s->status.contrast);
+ p += sprintf(p, "\"saturation\":%d,", s->status.saturation);
+ p += sprintf(p, "\"sharpness\":%d,", s->status.sharpness);
+ p += sprintf(p, "\"special_effect\":%u,", s->status.special_effect);
+ p += sprintf(p, "\"wb_mode\":%u,", s->status.wb_mode);
+ p += sprintf(p, "\"awb\":%u,", s->status.awb);
+ p += sprintf(p, "\"awb_gain\":%u,", s->status.awb_gain);
+ p += sprintf(p, "\"aec\":%u,", s->status.aec);
+ p += sprintf(p, "\"aec2\":%u,", s->status.aec2);
+ p += sprintf(p, "\"ae_level\":%d,", s->status.ae_level);
+ p += sprintf(p, "\"aec_value\":%u,", s->status.aec_value);
+ p += sprintf(p, "\"agc\":%u,", s->status.agc);
+ p += sprintf(p, "\"agc_gain\":%u,", s->status.agc_gain);
+ p += sprintf(p, "\"gainceiling\":%u,", s->status.gainceiling);
+ p += sprintf(p, "\"bpc\":%u,", s->status.bpc);
+ p += sprintf(p, "\"wpc\":%u,", s->status.wpc);
+ 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, "\"dcw\":%u,", s->status.dcw);
+ p += sprintf(p, "\"colorbar\":%u", s->status.colorbar);
+#ifdef CONFIG_LED_ILLUMINATOR_ENABLED
+ p += sprintf(p, ",\"led_intensity\":%u", led_duty);
+#else
+ p += sprintf(p, ",\"led_intensity\":%d", -1);
+#endif
+#if CONFIG_ESP_FACE_DETECT_ENABLED
+ p += sprintf(p, ",\"face_detect\":%u", detection_enabled);
+#if CONFIG_ESP_FACE_RECOGNITION_ENABLED
+ p += sprintf(p, ",\"face_enroll\":%u,", is_enrolling);
+ p += sprintf(p, "\"face_recognize\":%u", recognition_enabled);
+#endif
+#endif
+ *p++ = '}';
+ *p++ = 0;
+ httpd_resp_set_type(req, "application/json");
+ httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*");
+ return httpd_resp_send(req, json_response, strlen(json_response));
+}
+
+static esp_err_t mdns_handler(httpd_req_t *req)
+{
+ size_t json_len = 0;
+ const char * json_response = app_mdns_query(&json_len);
+ httpd_resp_set_type(req, "application/json");
+ httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*");
+ return httpd_resp_send(req, json_response, json_len);
+}
+
+static esp_err_t xclk_handler(httpd_req_t *req)
+{
+ char *buf = NULL;
+ char _xclk[32];
+
+ 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;
+ }
+ 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) {
+ 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);
+ 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) {
+ 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);
+ 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) {
+ 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) {
+ 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");
+ extern const unsigned char index_ov2640_html_gz_end[] asm("_binary_index_ov2640_html_gz_end");
+ size_t index_ov2640_html_gz_len = index_ov2640_html_gz_end - index_ov2640_html_gz_start;
+
+ extern const unsigned char index_ov3660_html_gz_start[] asm("_binary_index_ov3660_html_gz_start");
+ 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) {
+ return httpd_resp_send(req, (const char *)index_ov3660_html_gz_start, index_ov3660_html_gz_len);
+ } 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 {
+ ESP_LOGE(TAG, "Camera sensor not found");
+ return httpd_resp_send_500(req);
+ }
+}
+
+static esp_err_t monitor_handler(httpd_req_t *req)
+{
+ extern const unsigned char monitor_html_gz_start[] asm("_binary_monitor_html_gz_start");
+ extern const unsigned char monitor_html_gz_end[] asm("_binary_monitor_html_gz_end");
+ size_t monitor_html_gz_len = monitor_html_gz_end - monitor_html_gz_start;
+ httpd_resp_set_type(req, "text/html");
+ httpd_resp_set_hdr(req, "Content-Encoding", "gzip");
+ return httpd_resp_send(req, (const char *)monitor_html_gz_start, monitor_html_gz_len);
+}
+
+void app_httpd_main()
+{
+ httpd_config_t config = HTTPD_DEFAULT_CONFIG();
+ config.max_uri_handlers = 16;
+
+ httpd_uri_t index_uri = {
+ .uri = "/",
+ .method = HTTP_GET,
+ .handler = index_handler,
+ .user_ctx = NULL};
+
+ httpd_uri_t status_uri = {
+ .uri = "/status",
+ .method = HTTP_GET,
+ .handler = status_handler,
+ .user_ctx = NULL};
+
+ httpd_uri_t cmd_uri = {
+ .uri = "/control",
+ .method = HTTP_GET,
+ .handler = cmd_handler,
+ .user_ctx = NULL};
+
+ httpd_uri_t capture_uri = {
+ .uri = "/capture",
+ .method = HTTP_GET,
+ .handler = capture_handler,
+ .user_ctx = NULL};
+
+ httpd_uri_t stream_uri = {
+ .uri = "/stream",
+ .method = HTTP_GET,
+ .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,
+ .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};
+
+ httpd_uri_t mdns_uri = {
+ .uri = "/mdns",
+ .method = HTTP_GET,
+ .handler = mdns_handler,
+ .user_ctx = NULL};
+
+ httpd_uri_t monitor_uri = {
+ .uri = "/monitor",
+ .method = HTTP_GET,
+ .handler = monitor_handler,
+ .user_ctx = NULL};
+
+ ra_filter_init(&ra_filter, 20);
+
+#if CONFIG_ESP_FACE_DETECT_ENABLED
+
+ 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;
+
+#if CONFIG_ESP_FACE_RECOGNITION_ENABLED
+ face_id_init(&id_list, FACE_ID_SAVE_NUMBER, ENROLL_CONFIRM_TIMES);
+#endif
+
+#endif
+ ESP_LOGI(TAG, "Starting web server on port: '%d'", config.server_port);
+ if (httpd_start(&camera_httpd, &config) == ESP_OK)
+ {
+ 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, &bmp_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);
+
+ httpd_register_uri_handler(camera_httpd, &mdns_uri);
+ httpd_register_uri_handler(camera_httpd, &monitor_uri);
+ }
+
+ config.server_port += 1;
+ config.ctrl_port += 1;
+ ESP_LOGI(TAG, "Starting stream server on port: '%d'", config.server_port);
+ if (httpd_start(&stream_httpd, &config) == ESP_OK)
+ {
+ httpd_register_uri_handler(stream_httpd, &stream_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
new file mode 100755
index 00000000..661df8e0
--- /dev/null
+++ b/examples/single_chip/camera_web_server/main/include/app_camera.h
@@ -0,0 +1,209 @@
+/*
+ * ESPRESSIF MIT License
+ *
+ * Copyright (c) 2017
+ *
+ * Permission is hereby granted for use on ESPRESSIF SYSTEMS products only, in which case,
+ * it is free of charge, to any person obtaining a copy of this software and associated
+ * documentation files (the "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the Software is furnished
+ * to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or
+ * substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+ * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+#ifndef _APP_CAMERA_H_
+#define _APP_CAMERA_H_
+
+#if CONFIG_CAMERA_MODEL_WROVER_KIT
+#define CAM_BOARD "WROVER-KIT"
+#define PWDN_GPIO_NUM -1
+#define RESET_GPIO_NUM -1
+#define XCLK_GPIO_NUM 21
+#define SIOD_GPIO_NUM 26
+#define SIOC_GPIO_NUM 27
+
+#define Y9_GPIO_NUM 35
+#define Y8_GPIO_NUM 34
+#define Y7_GPIO_NUM 39
+#define Y6_GPIO_NUM 36
+#define Y5_GPIO_NUM 19
+#define Y4_GPIO_NUM 18
+#define Y3_GPIO_NUM 5
+#define Y2_GPIO_NUM 4
+#define VSYNC_GPIO_NUM 25
+#define HREF_GPIO_NUM 23
+#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
+#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
+#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
+#define PCLK_GPIO_NUM 25
+
+#elif CONFIG_CAMERA_MODEL_ESP_EYE
+#define CAM_BOARD "ESP-EYE"
+#define PWDN_GPIO_NUM -1
+#define RESET_GPIO_NUM -1
+#define XCLK_GPIO_NUM 4
+#define SIOD_GPIO_NUM 18
+#define SIOC_GPIO_NUM 23
+
+#define Y9_GPIO_NUM 36
+#define Y8_GPIO_NUM 37
+#define Y7_GPIO_NUM 38
+#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_M5STACK_PSRAM
+#define CAM_BOARD "M5CAM"
+#define PWDN_GPIO_NUM -1
+#define RESET_GPIO_NUM 15
+#define XCLK_GPIO_NUM 27
+#define SIOD_GPIO_NUM 25
+#define SIOC_GPIO_NUM 23
+
+#define Y9_GPIO_NUM 19
+#define Y8_GPIO_NUM 36
+#define Y7_GPIO_NUM 18
+#define Y6_GPIO_NUM 39
+#define Y5_GPIO_NUM 5
+#define Y4_GPIO_NUM 34
+#define Y3_GPIO_NUM 35
+#define Y2_GPIO_NUM 32
+#define VSYNC_GPIO_NUM 22
+#define HREF_GPIO_NUM 26
+#define PCLK_GPIO_NUM 21
+
+#elif CONFIG_CAMERA_MODEL_M5STACK_WIDE
+#define CAM_BOARD "M5CAMW"
+#define PWDN_GPIO_NUM -1
+#define RESET_GPIO_NUM 15
+#define XCLK_GPIO_NUM 27
+#define SIOD_GPIO_NUM 22
+#define SIOC_GPIO_NUM 23
+
+#define Y9_GPIO_NUM 19
+#define Y8_GPIO_NUM 36
+#define Y7_GPIO_NUM 18
+#define Y6_GPIO_NUM 39
+#define Y5_GPIO_NUM 5
+#define Y4_GPIO_NUM 34
+#define Y3_GPIO_NUM 35
+#define Y2_GPIO_NUM 32
+#define VSYNC_GPIO_NUM 25
+#define HREF_GPIO_NUM 26
+#define PCLK_GPIO_NUM 21
+
+#elif CONFIG_CAMERA_MODEL_AI_THINKER
+#define CAM_BOARD "AI-THINKER"
+#define PWDN_GPIO_NUM 32
+#define RESET_GPIO_NUM -1
+#define XCLK_GPIO_NUM 0
+#define SIOD_GPIO_NUM 26
+#define SIOC_GPIO_NUM 27
+
+#define Y9_GPIO_NUM 35
+#define Y8_GPIO_NUM 34
+#define Y7_GPIO_NUM 39
+#define Y6_GPIO_NUM 36
+#define Y5_GPIO_NUM 21
+#define Y4_GPIO_NUM 19
+#define Y3_GPIO_NUM 18
+#define Y2_GPIO_NUM 5
+#define VSYNC_GPIO_NUM 25
+#define HREF_GPIO_NUM 23
+#define PCLK_GPIO_NUM 22
+
+#elif CONFIG_CAMERA_MODULE_S3_KS_DIY
+#define CAMERA_MODULE_NAME "S3_KS_DIY"
+#define CAMERA_PIN_PWDN -4
+#define CAMERA_PIN_RESET -1
+#define CAMERA_PIN_XCLK -1
+#define CAMERA_PIN_SIOD 17
+#define CAMERA_PIN_SIOC 18
+
+#define CAMERA_PIN_D7 39
+#define CAMERA_PIN_D6 41
+#define CAMERA_PIN_D5 42
+#define CAMERA_PIN_D4 5
+#define CAMERA_PIN_D3 40
+#define CAMERA_PIN_D2 14
+#define CAMERA_PIN_D1 47
+#define CAMERA_PIN_D0 45
+#define CAMERA_PIN_VSYNC 21
+#define CAMERA_PIN_HREF 38
+#define CAMERA_PIN_PCLK 48
+
+#elif CONFIG_CAMERA_MODEL_CUSTOM
+#define CAM_BOARD "CUSTOM"
+#define PWDN_GPIO_NUM CONFIG_CAMERA_PIN_PWDN
+#define RESET_GPIO_NUM CONFIG_CAMERA_PIN_RESET
+#define XCLK_GPIO_NUM CONFIG_CAMERA_PIN_XCLK
+#define SIOD_GPIO_NUM CONFIG_CAMERA_PIN_SIOD
+#define SIOC_GPIO_NUM CONFIG_CAMERA_PIN_SIOC
+
+#define Y9_GPIO_NUM CONFIG_CAMERA_PIN_Y9
+#define Y8_GPIO_NUM CONFIG_CAMERA_PIN_Y8
+#define Y7_GPIO_NUM CONFIG_CAMERA_PIN_Y7
+#define Y6_GPIO_NUM CONFIG_CAMERA_PIN_Y6
+#define Y5_GPIO_NUM CONFIG_CAMERA_PIN_Y5
+#define Y4_GPIO_NUM CONFIG_CAMERA_PIN_Y4
+#define Y3_GPIO_NUM CONFIG_CAMERA_PIN_Y3
+#define Y2_GPIO_NUM CONFIG_CAMERA_PIN_Y2
+#define VSYNC_GPIO_NUM CONFIG_CAMERA_PIN_VSYNC
+#define HREF_GPIO_NUM CONFIG_CAMERA_PIN_HREF
+#define PCLK_GPIO_NUM CONFIG_CAMERA_PIN_PCLK
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void app_camera_main();
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif