diff --git a/examples/camera_web_server/CMakeLists.txt b/examples/camera_web_server/CMakeLists.txt new file mode 100644 index 0000000..e1213ad --- /dev/null +++ b/examples/camera_web_server/CMakeLists.txt @@ -0,0 +1,8 @@ +# The following lines of boilerplate have to be in your project's +# CMakeLists in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.5) + +set(EXTRA_COMPONENT_DIRS ../../components) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(camera_web_server) diff --git a/examples/camera_web_server/Makefile b/examples/camera_web_server/Makefile new file mode 100755 index 0000000..3ed5850 --- /dev/null +++ b/examples/camera_web_server/Makefile @@ -0,0 +1,12 @@ +# +# This is a project Makefile. It is assumed the directory this Makefile resides in is a +# project subdirectory. +# + +PROJECT_NAME := camera_web_server + +SOLUTION_PATH ?= $(abspath $(shell pwd))/../../.. + +include $(SOLUTION_PATH)/components/component_conf.mk +include $(IDF_PATH)/make/project.mk + diff --git a/examples/camera_web_server/README.md b/examples/camera_web_server/README.md new file mode 100755 index 0000000..7b7782b --- /dev/null +++ b/examples/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; +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/camera_web_server/main/CMakeLists.txt b/examples/camera_web_server/main/CMakeLists.txt new file mode 100644 index 0000000..e48fa10 --- /dev/null +++ b/examples/camera_web_server/main/CMakeLists.txt @@ -0,0 +1,19 @@ +set(COMPONENT_SRCS "app_main.c" "app_wifi.c" "app_camera.c" "app_httpd.cpp" "app_mdns.c") +set(COMPONENT_ADD_INCLUDEDIRS "include") + +set(COMPONENT_REQUIRES + esp32-camera + esp-dl + nvs_flash + esp_http_server + fb_gfx + mdns + ) + +set(COMPONENT_EMBED_FILES + "www/index_ov2640.html.gz" + "www/index_ov3660.html.gz" + "www/index_ov5640.html.gz" + "www/monitor.html.gz") + +register_component() diff --git a/examples/camera_web_server/main/Kconfig.projbuild b/examples/camera_web_server/main/Kconfig.projbuild new file mode 100644 index 0000000..fecdbc4 --- /dev/null +++ b/examples/camera_web_server/main/Kconfig.projbuild @@ -0,0 +1,295 @@ +menu "Camera Web Server" + +menu "WiFi Settings" +config ESP_HOST_NAME + string "Camera Host Name" + default "" + help + Hostname that the camera will advertise over mDNS. + +config ESP_WIFI_SSID + string "WiFi STA SSID" + default "" + help + WiFi SSID (network name) to connect to or empty for Off. + +config ESP_WIFI_PASSWORD + string "WiFi STA Password" + default "" + help + WiFi Password if WEP/WPA/WPA2 or empty if Open. + +config ESP_WIFI_AP_SSID + string "WiFi AP SSID" + default "ESP32-Camera" + help + AP SSID (network name) to create or empty for Off. + +config ESP_WIFI_AP_PASSWORD + string "WiFi AP Password" + default "" + help + AP password for WPA2 or empty for Open. + +config MAX_STA_CONN + int "Maximal STA connections" + default 1 + help + Max number of the STA connects to AP. + +config ESP_WIFI_AP_CHANNEL + string "WiFi AP Channel" + default "" + help + AP channel for better connection performance. + +config SERVER_IP + string "WiFi AP IP Address" + default "192.168.4.1" + help + IP address that the ESP will assign to it's AP interface. You can use this IP to connect to the camera after flashing. + +config ESP_MAXIMUM_RETRY + int "Maximum retry" + default 5 + help + Set the Maximum retry to avoid station reconnecting to the AP unlimited when the AP is really inexistent. +endmenu + +menu "LED Illuminator" +config LED_ILLUMINATOR_ENABLED + bool "LED Illuminator Enabled" + default n + help + Enable an LED Flash or IR Illuminator + +config LED_LEDC_PIN + depends on LED_ILLUMINATOR_ENABLED + int "LED Illuminator GPIO Pin" + range 0 33 + default 4 + help + Set a pin to illuminate an onboard LED or IR Illuminator when streaming or taking snapshots. + +config LED_MAX_INTENSITY + depends on LED_ILLUMINATOR_ENABLED + int "LED Maximum Intensity (0-255)" + range 0 255 + default 255 + help + Limit the maximum intensity of the LED while streaming to prevent overheating (0-255). + +choice LED_LEDC_SPEED_MODE + depends on LED_ILLUMINATOR_ENABLED + bool "Select LEDC Timer Speed Mode" + default LED_LEDC_LOW_SPEED_MODE + help + Select a speed mode for the LEDC channel + +config LED_LEDC_LOW_SPEED_MODE + bool "LOW_SPEED_MODE" +config LED_LEDC_HIGH_SPEED_MODE + bool "HIGH_SPEED_MODE" +endchoice + +config LED_LEDC_TIMER + depends on LED_ILLUMINATOR_ENABLED + int "LEDC Timer" + range 0 3 + default 1 + help + Select the LEDC Timer (0-3) + +config LED_LEDC_CHANNEL + depends on LED_ILLUMINATOR_ENABLED + int "LEDC Channel" + range 0 7 + default 1 + help + Select the LEDC Channel (0-7) +endmenu + +menu "Camera Pins" +choice CAMERA_MODEL + bool "Select Camera Pinout" + default CAMERA_MODEL_WROVER_KIT + help + Select Camera Pinout. + +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 + bool "M5Stack Camera With PSRAM" +config CAMERA_MODEL_M5STACK_WIDE + bool "M5Stack Camera F (Wide)" +config CAMERA_MODEL_AI_THINKER + bool "ESP32-CAM by AI-Thinker" +config CAMERA_MODEL_CUSTOM + bool "Custom Camera Pinout" +endchoice + +config CAMERA_PIN_PWDN + depends on CAMERA_MODEL_CUSTOM + int "Power Down pin" + range -1 33 + default -1 + help + Select Power Down pin or -1 for unmanaged. + +config CAMERA_PIN_RESET + depends on CAMERA_MODEL_CUSTOM + int "Reset pin" + range -1 33 + default -1 + help + Select Camera Reset pin or -1 for software reset. + +config CAMERA_PIN_XCLK + depends on CAMERA_MODEL_CUSTOM + int "XCLK pin" + range 0 33 + default 21 + help + Select Camera XCLK pin. + +config CAMERA_PIN_SIOD + depends on CAMERA_MODEL_CUSTOM + int "SIOD pin" + range 0 33 + default 26 + help + Select Camera SIOD pin. + +config CAMERA_PIN_SIOC + depends on CAMERA_MODEL_CUSTOM + int "SIOC pin" + range 0 33 + default 27 + help + Select Camera SIOC pin. + +config CAMERA_PIN_VSYNC + depends on CAMERA_MODEL_CUSTOM + int "VSYNC pin" + range 0 39 + default 25 + help + Select Camera VSYNC pin. + +config CAMERA_PIN_HREF + depends on CAMERA_MODEL_CUSTOM + int "HREF pin" + range 0 39 + default 23 + help + Select Camera HREF pin. + +config CAMERA_PIN_PCLK + depends on CAMERA_MODEL_CUSTOM + int "PCLK pin" + range 0 39 + default 25 + help + Select Camera PCLK pin. + +config CAMERA_PIN_Y2 + depends on CAMERA_MODEL_CUSTOM + int "Y2 pin" + range 0 39 + default 4 + help + Select Camera Y2 pin. + +config CAMERA_PIN_Y3 + depends on CAMERA_MODEL_CUSTOM + int "Y3 pin" + range 0 39 + default 5 + help + Select Camera Y3 pin. + +config CAMERA_PIN_Y4 + depends on CAMERA_MODEL_CUSTOM + int "Y4 pin" + range 0 39 + default 18 + help + Select Camera Y4 pin. + +config CAMERA_PIN_Y5 + depends on CAMERA_MODEL_CUSTOM + int "Y5 pin" + range 0 39 + default 19 + help + Select Camera Y5 pin. + +config CAMERA_PIN_Y6 + depends on CAMERA_MODEL_CUSTOM + int "Y6 pin" + range 0 39 + default 36 + help + Select Camera Y6 pin. + +config CAMERA_PIN_Y7 + depends on CAMERA_MODEL_CUSTOM + int "Y7 pin" + range 0 39 + default 39 + help + Select Camera Y7 pin. + +config CAMERA_PIN_Y8 + depends on CAMERA_MODEL_CUSTOM + int "Y8 pin" + range 0 39 + default 34 + help + Select Camera Y8 pin. + +config CAMERA_PIN_Y9 + depends on CAMERA_MODEL_CUSTOM + int "Y9 pin" + range 0 39 + default 35 + help + Select Camera Y9 pin. + +endmenu + + + + menu "DL Configuration" + + config DL_DETECT_ENABLED + bool "Enable Detection" + default n + + choice DL_DETECT_TARGET + bool "Select Detection Model" + depends on DL_DETECT_ENABLED + default DL_DETECT_HUMAN_FACE + + config DL_DETECT_HUMAN_FACE + bool "Human Face Detection Model" + config DL_DETECT_CAT_FACE + bool "Cat Face Detection Model" + endchoice + + config DL_DETECT_HUMAN_FACE_WITH_KEYPOINT + bool "Enable Human Face Keypoint Detection" + default n + depends on DL_DETECT_HUMAN_FACE + + config DL_RECOGNIZE_HUMAN_FACE + bool "Enable Human Face Recognition" + depends on DL_DETECT_HUMAN_FACE_WITH_KEYPOINT + default n + endmenu + +endmenu diff --git a/examples/camera_web_server/main/app_camera.c b/examples/camera_web_server/main/app_camera.c new file mode 100755 index 0000000..54b1de0 --- /dev/null +++ b/examples/camera_web_server/main/app_camera.c @@ -0,0 +1,120 @@ +/* ESPRESSIF MIT License + * + * Copyright (c) 2018 + * + * Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, 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. + */ +#include "esp_log.h" +#include "driver/ledc.h" +#include "esp_camera.h" +#include "app_camera.h" +#include "sdkconfig.h" + + +static const char *TAG = "app_camera"; + +void app_camera_main () +{ +#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 */ + gpio_config_t conf; + conf.mode = GPIO_MODE_INPUT; + conf.pull_up_en = GPIO_PULLUP_ENABLE; + conf.pull_down_en = GPIO_PULLDOWN_DISABLE; + conf.intr_type = GPIO_INTR_DISABLE; + conf.pin_bit_mask = 1LL << 13; + gpio_config(&conf); + conf.pin_bit_mask = 1LL << 14; + gpio_config(&conf); +#endif + +#ifdef CONFIG_LED_ILLUMINATOR_ENABLED + gpio_set_direction(CONFIG_LED_LEDC_PIN,GPIO_MODE_OUTPUT); + ledc_timer_config_t ledc_timer = { + .duty_resolution = LEDC_TIMER_8_BIT, // resolution of PWM duty + .freq_hz = 1000, // frequency of PWM signal + .speed_mode = LEDC_LOW_SPEED_MODE, // timer mode + .timer_num = CONFIG_LED_LEDC_TIMER // timer index + }; + ledc_channel_config_t ledc_channel = { + .channel = CONFIG_LED_LEDC_CHANNEL, + .duty = 0, + .gpio_num = CONFIG_LED_LEDC_PIN, + .speed_mode = LEDC_LOW_SPEED_MODE, + .hpoint = 0, + .timer_sel = CONFIG_LED_LEDC_TIMER + }; + #ifdef CONFIG_LED_LEDC_HIGH_SPEED_MODE + ledc_timer.speed_mode = ledc_channel.speed_mode = LEDC_HIGH_SPEED_MODE; + #endif + switch (ledc_timer_config(&ledc_timer)) { + case ESP_ERR_INVALID_ARG: ESP_LOGE(TAG, "ledc_timer_config() parameter error"); break; + case ESP_FAIL: ESP_LOGE(TAG, "ledc_timer_config() Can not find a proper pre-divider number base on the given frequency and the current duty_resolution"); break; + case ESP_OK: if (ledc_channel_config(&ledc_channel) == ESP_ERR_INVALID_ARG) { + ESP_LOGE(TAG, "ledc_channel_config() parameter error"); + } + break; + default: break; + } +#endif + + camera_config_t config; + config.ledc_channel = LEDC_CHANNEL_0; + config.ledc_timer = LEDC_TIMER_0; + config.pin_d0 = Y2_GPIO_NUM; + config.pin_d1 = Y3_GPIO_NUM; + config.pin_d2 = Y4_GPIO_NUM; + config.pin_d3 = Y5_GPIO_NUM; + config.pin_d4 = Y6_GPIO_NUM; + config.pin_d5 = Y7_GPIO_NUM; + config.pin_d6 = Y8_GPIO_NUM; + config.pin_d7 = Y9_GPIO_NUM; + config.pin_xclk = XCLK_GPIO_NUM; + config.pin_pclk = PCLK_GPIO_NUM; + config.pin_vsync = VSYNC_GPIO_NUM; + config.pin_href = HREF_GPIO_NUM; + config.pin_sscb_sda = SIOD_GPIO_NUM; + config.pin_sscb_scl = SIOC_GPIO_NUM; + config.pin_pwdn = PWDN_GPIO_NUM; + config.pin_reset = RESET_GPIO_NUM; + config.xclk_freq_hz = 20000000; + config.pixel_format = PIXFORMAT_JPEG; + //init with high specs to pre-allocate larger buffers + config.frame_size = FRAMESIZE_QSXGA; + config.jpeg_quality = 10; + config.fb_count = 1; + + // camera init + esp_err_t err = esp_camera_init(&config); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Camera init failed with error 0x%x", err); + return; + } + + sensor_t * s = esp_camera_sensor_get(); + s->set_vflip(s, 1);//flip it back + //initial sensors are flipped vertically and colors are a bit saturated + if (s->id.PID == OV3660_PID) { + s->set_brightness(s, 1);//up the blightness just a bit + s->set_saturation(s, -2);//lower the saturation + } + //drop down frame size for higher initial frame rate + s->set_framesize(s, FRAMESIZE_HD); +} diff --git a/examples/camera_web_server/main/app_httpd.cpp b/examples/camera_web_server/main/app_httpd.cpp new file mode 100644 index 0000000..b051dd6 --- /dev/null +++ b/examples/camera_web_server/main/app_httpd.cpp @@ -0,0 +1,977 @@ +// 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 "sdkconfig.h" +#include "app_mdns.h" +#include "app_camera.h" +#include "dl_tool.hpp" + +#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_DL_DETECT_ENABLED +#include +#include "dl_image.hpp" +#include "dl_detect_define.hpp" + +#define RGB888_RED 0x0000FF +#define RGB888_GREEN 0x00FF00 +#define RGB888_BLUE 0xFF0000 + +static int8_t detection_enabled = 0; +#endif + +#if CONFIG_DL_DETECT_HUMAN_FACE +#include "human_face_detect_msr01.hpp" +#endif + +#if CONFIG_DL_DETECT_HUMAN_FACE_WITH_KEYPOINT +#include "human_face_detect_mnp01.hpp" +#endif + +#if CONFIG_DL_DETECT_CAT_FACE +#include "cat_face_detect_mn03.hpp" +#endif + +#if CONFIG_DL_RECOGNIZE_HUMAN_FACE +// TODO: recognize human face +static int8_t recognition_enabled = 0; +static int8_t is_enrolling = 0; +#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; + +#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 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) +{ + dl::tool::Latency latency_total; + latency_total.start(); + + camera_fb_t *fb = NULL; + esp_err_t res = ESP_OK; + +#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); + + 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); + latency_total.end(); + + ESP_LOGI(TAG, "JPG: %uB %ums", (uint32_t)(fb_len), latency_total.get_period() / 1000); + return res; +} + +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]; + dl::tool::Latency latency_total(24); + +#if CONFIG_DL_DETECT_ENABLED + uint8_t *image_rgb888 = NULL; + dl::tool::Latency latency_decode; + dl::tool::Latency latency_encode; + dl::tool::Latency latency_detect; +#endif + +#if CONFIG_DL_DETECT_HUMAN_FACE + HumanFaceDetectMSR01 detector(0.3F, 0.3F, 10, 0.3F); +#endif + +#if CONFIG_DL_DETECT_HUMAN_FACE_WITH_KEYPOINT + HumanFaceDetectMNP01 detector2(0.4F, 0.3F, 10); +#endif + +#if CONFIG_DL_DETECT_CAT_FACE + CatFaceDetectMN03 detector(0.4F, 0.3F, 10, 0.3F); +#endif + +#if CONFIG_DL_RECOGNIZE_HUMAN_FACE + dl::tool::Latency latency_recognize; +// TODO: recognize human face +#endif + + 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) + { + latency_total.start(); + 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_DL_DETECT_ENABLED + 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_DL_DETECT_ENABLED + } + else + { + image_rgb888 = (uint8_t *)dl::tool::malloc_aligned(fb->height * fb->width * 3, sizeof(uint8_t)); + + if (!image_rgb888) + { + ESP_LOGE(TAG, "dl_matrix3du_alloc failed"); + res = ESP_FAIL; + } + else + { + latency_decode.start(); + if (!fmt2rgb888(fb->buf, fb->len, fb->format, image_rgb888)) + { + ESP_LOGE(TAG, "fmt2rgb888 failed"); + res = ESP_FAIL; + } + else + { + latency_decode.end(); + latency_detect.start(); +#if CONFIG_DL_DETECT_HUMAN_FACE_WITH_KEYPOINT + std::list &candidates = detector.infer((uint8_t *)image_rgb888, {(int)fb->height, (int)fb->width, 3}); + std::list &results = detector2.infer((uint8_t *)image_rgb888, {(int)fb->height, (int)fb->width, 3}, candidates); +#else + std::list &results = detector.infer((uint8_t *)image_rgb888, {(int)fb->height, (int)fb->width, 3}); +#endif + latency_detect.end(); + + if (results.size() > 0 || fb->format != PIXFORMAT_JPEG) + { + int i = 0; + for (std::list::iterator prediction = results.begin(); prediction != results.end(); prediction++, i++) + { + dl::image::draw_hollow_rectangle(image_rgb888, fb->height, fb->width, + DL_MAX(prediction->box[0], 0), + DL_MAX(prediction->box[1], 0), + DL_MAX(prediction->box[2], 0), + DL_MAX(prediction->box[3], 0), + RGB888_GREEN); +#if CONFIG_DL_DETECT_HUMAN_FACE_WITH_KEYPOINT + dl::image::draw_point(image_rgb888, fb->height, fb->width, DL_MAX(prediction->keypoint[0], 0), DL_MAX(prediction->keypoint[1], 0), 4, RGB888_RED); // left eye + dl::image::draw_point(image_rgb888, fb->height, fb->width, DL_MAX(prediction->keypoint[2], 0), DL_MAX(prediction->keypoint[3], 0), 4, RGB888_RED); // mouth left corner + dl::image::draw_point(image_rgb888, fb->height, fb->width, DL_MAX(prediction->keypoint[4], 0), DL_MAX(prediction->keypoint[5], 0), 4, RGB888_GREEN); // nose + dl::image::draw_point(image_rgb888, fb->height, fb->width, DL_MAX(prediction->keypoint[6], 0), DL_MAX(prediction->keypoint[7], 0), 4, RGB888_BLUE); // right eye + dl::image::draw_point(image_rgb888, fb->height, fb->width, DL_MAX(prediction->keypoint[8], 0), DL_MAX(prediction->keypoint[9], 0), 4, RGB888_BLUE); // mouth right corner +#endif + } + +#if CONFIG_DL_RECOGNIZE_HUMAN_FACE + latency_recognize.start(); + // TODO: recognize human face + latency_recognize.end(); +#endif + + latency_encode.start(); + if (!fmt2jpg(image_rgb888, 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; + latency_encode.end(); + } + else + { + _jpg_buf = fb->buf; + _jpg_buf_len = fb->len; + } + } + dl::tool::free_aligned(image_rgb888); + } + } +#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; + } + latency_total.end(); + + uint32_t frame_latency = latency_total.get_period() / 1000; + uint32_t average_frame_latency = latency_total.get_average_period() / 1000; + +#if CONFIG_DL_RECOGNIZE_HUMAN_FACE + if (recognition_enabled) + { + ESP_LOGI(TAG, "Frame: %4ums (%.1ffps), Average: %4ums (%.1ffps) | decode: %4ums, detect: %4ums, recognize: %5ums, encode: %4ums", + frame_latency, 1000.0 / frame_latency, average_frame_latency, 1000.0 / average_frame_latency, + latency_decode.get_period() / 1000, + latency_detect.get_period() / 1000, + latency_recognize.get_period() / 1000, + latency_encode.get_period() / 1000); + } + else +#endif +#if CONFIG_DL_DETECT_ENABLED + if (detection_enabled) + { + ESP_LOGI(TAG, "Frame: %4ums (%.1ffps), Average: %4ums (%.1ffps) | decode: %4ums, detect: %4ums, encode: %4ums", + frame_latency, 1000.0 / frame_latency, average_frame_latency, 1000.0 / average_frame_latency, + latency_decode.get_period() / 1000, + latency_detect.get_period() / 1000, + latency_encode.get_period() / 1000); + } + else +#endif + { + ESP_LOGI(TAG, "Frame: %4ums (%.1ffps), Average: %4ums (%.1ffps)", + frame_latency, 1000.0 / frame_latency, average_frame_latency, 1000.0 / average_frame_latency); + } + } + +#ifdef CONFIG_LED_ILLUMINATOR_ENABLED + isStreaming = false; + enable_led(false); +#endif + + 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 || + 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_DL_DETECT_ENABLED + else if (!strcmp(variable, "face_detect")) + { + detection_enabled = val; +#if CONFIG_DL_RECOGNIZE_HUMAN_FACE + if (!detection_enabled) + { + recognition_enabled = 0; + } +#endif + } +#if CONFIG_DL_RECOGNIZE_HUMAN_FACE + else if (!strcmp(variable, "face_enroll")) + is_enrolling = val; + else if (!strcmp(variable, "face_recognize")) + { + recognition_enabled = val; + if (recognition_enabled) + { + detection_enabled = val; + } + } +#endif +#endif + else + { + res = -1; + } + + 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 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 + { + 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_DL_DETECT_ENABLED + p += sprintf(p, ",\"face_detect\":%u", detection_enabled); +#if CONFIG_DL_RECOGNIZE_HUMAN_FACE + 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 || + 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"); + 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 = 12; + + 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 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}; + + 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, &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/camera_web_server/main/app_main.c b/examples/camera_web_server/main/app_main.c new file mode 100755 index 0000000..ed67ee4 --- /dev/null +++ b/examples/camera_web_server/main/app_main.c @@ -0,0 +1,34 @@ +/* ESPRESSIF MIT License + * + * Copyright (c) 2018 + * + * Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, 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. + */ + +#include "app_camera.h" +#include "app_wifi.h" +#include "app_httpd.h" +#include "app_mdns.h" + +void app_main() +{ + app_wifi_main(); + app_camera_main(); + app_httpd_main(); + app_mdns_main(); +} diff --git a/examples/camera_web_server/main/app_mdns.c b/examples/camera_web_server/main/app_mdns.c new file mode 100644 index 0000000..17449aa --- /dev/null +++ b/examples/camera_web_server/main/app_mdns.c @@ -0,0 +1,250 @@ +/* + * ESPRESSIF MIT License + * + * Copyright (c) 2020 + * + * 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. + * + */ +#include +#include "sdkconfig.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/semphr.h" +#include "esp_log.h" +#include "esp_wifi.h" +#include "esp_camera.h" +#include "mdns.h" +#include "app_camera.h" + +static const char *TAG = "camera mdns"; + +static const char * service_name = "_esp-cam"; +static const char * proto = "_tcp"; +static mdns_result_t * found_cams = NULL; +static SemaphoreHandle_t query_lock = NULL; +static char iname[64]; +static char hname[64]; +static char framesize[4]; +static char pixformat[4]; +static const char * model = NULL; + +static void mdns_query_for_cams() +{ + mdns_result_t * new_cams = NULL; + esp_err_t err = mdns_query_ptr(service_name, proto, 5000, 4, &new_cams); + if(err){ + ESP_LOGE(TAG, "MDNS Query Failed: %s", esp_err_to_name(err)); + return; + } + xSemaphoreTake(query_lock, portMAX_DELAY); + if (found_cams != NULL) { + mdns_query_results_free(found_cams); + } + found_cams = new_cams; + xSemaphoreGive(query_lock); +} + +static void mdns_task(void * arg) +{ + for (;;) { + mdns_query_for_cams(); + //delay 55 seconds + vTaskDelay((55 * 1000) / portTICK_PERIOD_MS); + } + vTaskDelete(NULL); +} + +/* +* Public Functions +*/ + +const char * app_mdns_query(size_t * out_len) +{ + //build JSON + static char json_response[2048]; + char *p = json_response; + *p++ = '['; + + //add own data first + tcpip_adapter_ip_info_t ip; + if (strlen(CONFIG_ESP_WIFI_SSID)) { + tcpip_adapter_get_ip_info(TCPIP_ADAPTER_IF_STA, &ip); + } else { + tcpip_adapter_get_ip_info(TCPIP_ADAPTER_IF_AP, &ip); + } + *p++ = '{'; + p += sprintf(p, "\"instance\":\"%s\",", iname); + p += sprintf(p, "\"host\":\"%s.local\",", hname); + p += sprintf(p, "\"port\":80,"); + p += sprintf(p, "\"txt\":{"); + p += sprintf(p, "\"pixformat\":\"%s\",", pixformat); + p += sprintf(p, "\"framesize\":\"%s\",", framesize); + p += sprintf(p, "\"stream_port\":\"81\","); + p += sprintf(p, "\"board\":\"%s\",", CAM_BOARD); + p += sprintf(p, "\"model\":\"%s\"", model); + *p++ = '}'; + *p++ = ','; + p += sprintf(p, "\"ip\":\"" IPSTR "\",", IP2STR(&(ip.ip))); + p += sprintf(p, "\"id\":\"" IPSTR ":80\",", IP2STR(&(ip.ip))); + p += sprintf(p, "\"service\":\"%s\",", service_name); + p += sprintf(p, "\"proto\":\"%s\"", proto); + *p++ = '}'; + + xSemaphoreTake(query_lock, portMAX_DELAY); + if (found_cams) { + *p++ = ','; + } + mdns_result_t * r = found_cams; + mdns_ip_addr_t * a = NULL; + int t; + while(r){ + *p++ = '{'; + if(r->instance_name){ + p += sprintf(p, "\"instance\":\"%s\",", r->instance_name); + } + if(r->hostname){ + p += sprintf(p, "\"host\":\"%s.local\",", r->hostname); + p += sprintf(p, "\"port\":%u,", r->port); + } + if(r->txt_count){ + p += sprintf(p, "\"txt\":{"); + for(t=0; ttxt_count; t++){ + if (t > 0) { + *p++ = ','; + } + p += sprintf(p, "\"%s\":\"%s\"", r->txt[t].key, r->txt[t].value?r->txt[t].value:"NULL"); + } + *p++ = '}'; + *p++ = ','; + } + a = r->addr; + while(a){ + if(a->addr.type != IPADDR_TYPE_V6){ + p += sprintf(p, "\"ip\":\"" IPSTR "\",", IP2STR(&(a->addr.u_addr.ip4))); + p += sprintf(p, "\"id\":\"" IPSTR ":%u\",", IP2STR(&(a->addr.u_addr.ip4)), r->port); + break; + } + a = a->next; + } + p += sprintf(p, "\"service\":\"%s\",", service_name); + p += sprintf(p, "\"proto\":\"%s\"", proto); + *p++ = '}'; + r = r->next; + if (r) { + *p++ = ','; + } + } + xSemaphoreGive(query_lock); + *p++ = ']'; + *out_len = (uint32_t)p - (uint32_t)json_response; + *p++ = '\0'; + ESP_LOGI(TAG, "JSON: %uB", *out_len); + return (const char *)json_response; +} + +void app_mdns_update_framesize(int size) +{ + snprintf(framesize, 4, "%d", size); + if(mdns_service_txt_item_set(service_name, proto, "framesize", (char*)framesize)){ + ESP_LOGE(TAG, "mdns_service_txt_item_set() framesize Failed"); + } +} + +void app_mdns_main() +{ + uint8_t mac[6]; + + query_lock = xSemaphoreCreateBinary(); + if (query_lock == NULL) { + ESP_LOGE(TAG, "xSemaphoreCreateMutex() Failed"); + return; + } + xSemaphoreGive(query_lock); + + sensor_t * s = esp_camera_sensor_get(); + if(s == NULL){ + return; + } + switch(s->id.PID){ + case OV2640_PID: model = "OV2640"; break; + case OV3660_PID: model = "OV3660"; break; + case OV5640_PID: model = "OV5640"; break; + case OV7725_PID: model = "OV7725"; break; + default: model = "UNKNOWN"; break; + } + + if (strlen(CONFIG_ESP_HOST_NAME) > 0) { + snprintf(iname, 64, "%s", CONFIG_ESP_HOST_NAME); + } else { + if (esp_read_mac(mac, ESP_MAC_WIFI_STA) != ESP_OK) { + ESP_LOGE(TAG, "esp_read_mac() Failed"); + return; + } + snprintf(iname, 64, "%s-%s-%02X%02X%02X", CAM_BOARD, model, mac[3], mac[4], mac[5]); + } + + snprintf(framesize, 4, "%d", s->status.framesize); + snprintf(pixformat, 4, "%d", s->pixformat); + + char * src = iname, * dst = hname, c; + while (*src) { + c = *src++; + if (c >= 'A' && c <= 'Z') { + c -= 'A' - 'a'; + } + *dst++ = c; + } + *dst++ = '\0'; + + if(mdns_init() != ESP_OK){ + ESP_LOGE(TAG, "mdns_init() Failed"); + return; + } + + if(mdns_hostname_set(hname) != ESP_OK){ + ESP_LOGE(TAG, "mdns_hostname_set(\"%s\") Failed", hname); + return; + } + + if(mdns_instance_name_set(iname) != ESP_OK){ + ESP_LOGE(TAG, "mdns_instance_name_set(\"%s\") Failed", iname); + return; + } + + if(mdns_service_add(NULL, "_http", "_tcp", 80, NULL, 0) != ESP_OK){ + ESP_LOGE(TAG, "mdns_service_add() HTTP Failed"); + return; + } + + + mdns_txt_item_t camera_txt_data[] = { + {(char*)"board" ,(char*)CAM_BOARD}, + {(char*)"model" ,(char*)model}, + {(char*)"stream_port" ,(char*)"81"}, + {(char*)"framesize" ,(char*)framesize}, + {(char*)"pixformat" ,(char*)pixformat} + }; + + if(mdns_service_add(NULL, service_name, proto, 80, camera_txt_data, 5)) { + ESP_LOGE(TAG, "mdns_service_add() ESP-CAM Failed"); + return; + } + + xTaskCreate(mdns_task, "mdns-cam", 2048, NULL, 2, NULL); +} \ No newline at end of file diff --git a/examples/camera_web_server/main/app_wifi.c b/examples/camera_web_server/main/app_wifi.c new file mode 100644 index 0000000..df65f85 --- /dev/null +++ b/examples/camera_web_server/main/app_wifi.c @@ -0,0 +1,183 @@ +/* ESPRESSIF MIT License + * + * Copyright (c) 2018 + * + * Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, 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. + */ +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/event_groups.h" +#include "esp_system.h" +#include "esp_wifi.h" +#include "esp_event_loop.h" +#include "esp_log.h" +#include "nvs_flash.h" +#include "sdkconfig.h" + +#include "lwip/err.h" +#include "lwip/sys.h" + +#include "mdns.h" + +/* The examples use WiFi configuration that you can set via 'make menuconfig'. + + If you'd rather not, just change the below entries to strings with + the config you want - ie #define EXAMPLE_WIFI_SSID "mywifissid" +*/ +#define EXAMPLE_ESP_WIFI_SSID CONFIG_ESP_WIFI_SSID +#define EXAMPLE_ESP_WIFI_PASS CONFIG_ESP_WIFI_PASSWORD +#define EXAMPLE_ESP_MAXIMUM_RETRY CONFIG_ESP_MAXIMUM_RETRY +#define EXAMPLE_ESP_WIFI_AP_SSID CONFIG_ESP_WIFI_AP_SSID +#define EXAMPLE_ESP_WIFI_AP_PASS CONFIG_ESP_WIFI_AP_PASSWORD +#define EXAMPLE_MAX_STA_CONN CONFIG_MAX_STA_CONN +#define EXAMPLE_IP_ADDR CONFIG_SERVER_IP +#define EXAMPLE_ESP_WIFI_AP_CHANNEL CONFIG_ESP_WIFI_AP_CHANNEL + +static const char *TAG = "camera wifi"; + +static int s_retry_num = 0; + +static esp_err_t event_handler(void *ctx, system_event_t *event) +{ + switch(event->event_id) { + case SYSTEM_EVENT_AP_STACONNECTED: + ESP_LOGI(TAG, "station:" MACSTR " join, AID=%d", + MAC2STR(event->event_info.sta_connected.mac), + event->event_info.sta_connected.aid); + break; + case SYSTEM_EVENT_AP_STADISCONNECTED: + ESP_LOGI(TAG, "station:" MACSTR "leave, AID=%d", + MAC2STR(event->event_info.sta_disconnected.mac), + event->event_info.sta_disconnected.aid); + break; + case SYSTEM_EVENT_STA_START: + esp_wifi_connect(); + break; + case SYSTEM_EVENT_STA_GOT_IP: + ESP_LOGI(TAG, "got ip:%s", + ip4addr_ntoa(&event->event_info.got_ip.ip_info.ip)); + s_retry_num = 0; + break; + case SYSTEM_EVENT_STA_DISCONNECTED: + { + if (s_retry_num < EXAMPLE_ESP_MAXIMUM_RETRY) { + esp_wifi_connect(); + s_retry_num++; + ESP_LOGI(TAG,"retry to connect to the AP"); + } + ESP_LOGI(TAG,"connect to the AP fail"); + break; + } + default: + break; + } + mdns_handle_system_event(ctx, event); + return ESP_OK; +} + +void wifi_init_softap() +{ + if (strcmp(EXAMPLE_IP_ADDR, "192.168.4.1")) + { + int a, b, c, d; + sscanf(EXAMPLE_IP_ADDR, "%d.%d.%d.%d", &a, &b, &c, &d); + tcpip_adapter_ip_info_t ip_info; + IP4_ADDR(&ip_info.ip, a, b, c, d); + IP4_ADDR(&ip_info.gw, a, b, c, d); + IP4_ADDR(&ip_info.netmask, 255, 255, 255, 0); + ESP_ERROR_CHECK(tcpip_adapter_dhcps_stop(WIFI_IF_AP)); + ESP_ERROR_CHECK(tcpip_adapter_set_ip_info(WIFI_IF_AP, &ip_info)); + ESP_ERROR_CHECK(tcpip_adapter_dhcps_start(WIFI_IF_AP)); + } + wifi_config_t wifi_config; + memset(&wifi_config, 0, sizeof(wifi_config_t)); + snprintf((char*)wifi_config.ap.ssid, 32, "%s", EXAMPLE_ESP_WIFI_AP_SSID); + wifi_config.ap.ssid_len = strlen((char*)wifi_config.ap.ssid); + snprintf((char*)wifi_config.ap.password, 64, "%s", EXAMPLE_ESP_WIFI_AP_PASS); + wifi_config.ap.max_connection = EXAMPLE_MAX_STA_CONN; + wifi_config.ap.authmode = WIFI_AUTH_WPA_WPA2_PSK; + if (strlen(EXAMPLE_ESP_WIFI_AP_PASS) == 0) { + wifi_config.ap.authmode = WIFI_AUTH_OPEN; + } + if (strlen(EXAMPLE_ESP_WIFI_AP_CHANNEL)) { + int channel; + sscanf(EXAMPLE_ESP_WIFI_AP_CHANNEL, "%d", &channel); + wifi_config.ap.channel = channel; + } + + ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_AP, &wifi_config)); + + ESP_LOGI(TAG, "wifi_init_softap finished.SSID:%s password:%s", + EXAMPLE_ESP_WIFI_AP_SSID, EXAMPLE_ESP_WIFI_AP_PASS); +} + +void wifi_init_sta() +{ + wifi_config_t wifi_config; + memset(&wifi_config, 0, sizeof(wifi_config_t)); + snprintf((char*)wifi_config.sta.ssid, 32, "%s", EXAMPLE_ESP_WIFI_SSID); + snprintf((char*)wifi_config.sta.password, 64, "%s", EXAMPLE_ESP_WIFI_PASS); + + ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config) ); + + ESP_LOGI(TAG, "wifi_init_sta finished."); + ESP_LOGI(TAG, "connect to ap SSID:%s password:%s", + EXAMPLE_ESP_WIFI_SSID, EXAMPLE_ESP_WIFI_PASS); +} + +void app_wifi_main() +{ + wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); + wifi_mode_t mode = WIFI_MODE_NULL; + + if (strlen(EXAMPLE_ESP_WIFI_AP_SSID) && strlen(EXAMPLE_ESP_WIFI_SSID)) { + mode = WIFI_MODE_APSTA; + } else if (strlen(EXAMPLE_ESP_WIFI_AP_SSID)) { + mode = WIFI_MODE_AP; + } else if (strlen(EXAMPLE_ESP_WIFI_SSID)) { + mode = WIFI_MODE_STA; + } + + esp_err_t ret = nvs_flash_init(); + if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) { + ESP_ERROR_CHECK(nvs_flash_erase()); + ret = nvs_flash_init(); + } + ESP_ERROR_CHECK(ret); + + if (mode == WIFI_MODE_NULL) { + ESP_LOGW(TAG,"Neither AP or STA have been configured. WiFi will be off."); + return; + } + + tcpip_adapter_init(); + ESP_ERROR_CHECK(esp_event_loop_init(event_handler, NULL)); + ESP_ERROR_CHECK(esp_wifi_init(&cfg)); + ESP_ERROR_CHECK(esp_wifi_set_mode(mode)); + + if (mode & WIFI_MODE_AP) { + wifi_init_softap(); + } + + if (mode & WIFI_MODE_STA) { + wifi_init_sta(); + } + ESP_ERROR_CHECK(esp_wifi_start()); + ESP_ERROR_CHECK(esp_wifi_set_ps(WIFI_PS_NONE)); +} diff --git a/examples/camera_web_server/main/component.mk b/examples/camera_web_server/main/component.mk new file mode 100644 index 0000000..5557b3e --- /dev/null +++ b/examples/camera_web_server/main/component.mk @@ -0,0 +1,12 @@ +# +# Main Makefile. This is basically the same as a component makefile. +# +# This Makefile should, at the very least, just include $(SDK_PATH)/make/component.mk. By default, +# this will take the sources in the src/ directory, compile them and link them into +# lib(subdirectory_name).a in the build directory. This behaviour is entirely configurable, +# please read the SDK documents if you need to do this. +# +COMPONENT_EMBED_FILES := www/index_ov2640.html.gz +COMPONENT_EMBED_FILES += www/index_ov3660.html.gz +COMPONENT_EMBED_FILES += www/index_ov5640.html.gz +COMPONENT_EMBED_FILES += www/monitor.html.gz diff --git a/examples/camera_web_server/main/include/app_camera.h b/examples/camera_web_server/main/include/app_camera.h new file mode 100755 index 0000000..333fd3e --- /dev/null +++ b/examples/camera_web_server/main/include/app_camera.h @@ -0,0 +1,179 @@ +/* + * 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 +#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 +#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 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_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 diff --git a/examples/camera_web_server/main/include/app_httpd.h b/examples/camera_web_server/main/include/app_httpd.h new file mode 100644 index 0000000..6f31655 --- /dev/null +++ b/examples/camera_web_server/main/include/app_httpd.h @@ -0,0 +1,27 @@ +// 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. +#ifndef _CAMERA_HTTPD_H_ +#define _CAMERA_HTTPD_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +void app_httpd_main(); + +#ifdef __cplusplus +} +#endif + +#endif /* _CAMERA_HTTPD_H_ */ diff --git a/examples/camera_web_server/main/include/app_mdns.h b/examples/camera_web_server/main/include/app_mdns.h new file mode 100644 index 0000000..90769d1 --- /dev/null +++ b/examples/camera_web_server/main/include/app_mdns.h @@ -0,0 +1,31 @@ +// Copyright 2015-2020 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. +#ifndef _CAMERA_MDNS_H_ +#define _CAMERA_MDNS_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +void app_mdns_main(); +void app_mdns_update_framesize(int size); +const char * app_mdns_query(size_t * out_len); + +#ifdef __cplusplus +} +#endif + +#endif /* _CAMERA_MDNS_H_ */ diff --git a/examples/camera_web_server/main/include/app_wifi.h b/examples/camera_web_server/main/include/app_wifi.h new file mode 100644 index 0000000..dc164e9 --- /dev/null +++ b/examples/camera_web_server/main/include/app_wifi.h @@ -0,0 +1,37 @@ +/* + * 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_WIFI_H_ +#define _APP_WIFI_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +void app_wifi_main(); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/examples/camera_web_server/main/www/compress_pages.sh b/examples/camera_web_server/main/www/compress_pages.sh new file mode 100755 index 0000000..f7c13fb --- /dev/null +++ b/examples/camera_web_server/main/www/compress_pages.sh @@ -0,0 +1,7 @@ +#!/bin/bash +for file in `ls *.html`; do + echo "Compressing: $file" + cp "$file" "copy_$file" && \ + gzip -f "$file" && \ + mv "copy_$file" "$file" +done diff --git a/examples/camera_web_server/main/www/index_ov2640.html b/examples/camera_web_server/main/www/index_ov2640.html new file mode 100644 index 0000000..cac38f9 --- /dev/null +++ b/examples/camera_web_server/main/www/index_ov2640.html @@ -0,0 +1,1152 @@ + + + + + + ESP32 OV2460 + + + +
+ +
+ +
+ +
+
+
+ + + diff --git a/examples/camera_web_server/main/www/index_ov2640.html.gz b/examples/camera_web_server/main/www/index_ov2640.html.gz new file mode 100644 index 0000000..b0e1ddb Binary files /dev/null and b/examples/camera_web_server/main/www/index_ov2640.html.gz differ diff --git a/examples/camera_web_server/main/www/index_ov3660.html b/examples/camera_web_server/main/www/index_ov3660.html new file mode 100644 index 0000000..e7b71cf --- /dev/null +++ b/examples/camera_web_server/main/www/index_ov3660.html @@ -0,0 +1,1368 @@ + + + + + + ESP32 OV3660 + + + +
+ +
+ +
+ +
+
+
+ + + diff --git a/examples/camera_web_server/main/www/index_ov3660.html.gz b/examples/camera_web_server/main/www/index_ov3660.html.gz new file mode 100644 index 0000000..8274a21 Binary files /dev/null and b/examples/camera_web_server/main/www/index_ov3660.html.gz differ diff --git a/examples/camera_web_server/main/www/index_ov5640.html b/examples/camera_web_server/main/www/index_ov5640.html new file mode 100644 index 0000000..a6c01f9 --- /dev/null +++ b/examples/camera_web_server/main/www/index_ov5640.html @@ -0,0 +1,1391 @@ + + + + + + ESP32 OV5640 + + + +
+ +
+ +
+ +
+
+
+ + + diff --git a/examples/camera_web_server/main/www/index_ov5640.html.gz b/examples/camera_web_server/main/www/index_ov5640.html.gz new file mode 100644 index 0000000..b5f4f48 Binary files /dev/null and b/examples/camera_web_server/main/www/index_ov5640.html.gz differ diff --git a/examples/camera_web_server/main/www/monitor.html b/examples/camera_web_server/main/www/monitor.html new file mode 100755 index 0000000..1b98342 --- /dev/null +++ b/examples/camera_web_server/main/www/monitor.html @@ -0,0 +1,1012 @@ + + + + + + + ESP-EYE Monitor + + +
+
+
+
+ + + + + + + + + +
+
+
+
+ + + + + + + + +
+
+
+ +
+
+
+

Console

+
+
+
+
+
+ + diff --git a/examples/camera_web_server/main/www/monitor.html.gz b/examples/camera_web_server/main/www/monitor.html.gz new file mode 100755 index 0000000..ef4a8fc Binary files /dev/null and b/examples/camera_web_server/main/www/monitor.html.gz differ diff --git a/examples/camera_web_server/partitions.csv b/examples/camera_web_server/partitions.csv new file mode 100755 index 0000000..dd2fbea --- /dev/null +++ b/examples/camera_web_server/partitions.csv @@ -0,0 +1,4 @@ +# Espressif ESP32 Partition Table +# Name, Type, SubType, Offset, Size +factory, app, factory, 0x010000, 3840K +nvs, data, nvs, 0x3D0000, 16K