Merge branch 'feature/seeed_xiao_esp32s3' into develop

This commit is contained in:
Rene Zeldenthuis
2023-12-18 23:47:57 +01:00
26 changed files with 212 additions and 283 deletions

View File

@@ -6,11 +6,18 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- name: Set up python
uses: actions/setup-python@v3
with: with:
python-version: '3.x' submodules: 'true'
architecture: 'x64' - uses: actions/cache@v3
with:
path: |
~/.cache/pip
~/.platformio/.cache
key: ${{ runner.os }}-pio
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.9'
- name: Install PlatformIO - name: Install PlatformIO
run: python -m pip install platformio run: python -m pip install platformio
- name: Build firmware - name: Build firmware
@@ -18,5 +25,5 @@ jobs:
- name: Archive - name: Archive
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v3
with: with:
name: firmware.bin name: firmwares.zip
path: .pio/build/*/firmware.bin path: .pio/build/*/firmware.bin

View File

@@ -14,17 +14,17 @@ Flashing this software on a ESP32CAM module will make it a **RTSP streaming came
Supported protocols Supported protocols
- :white_check_mark: RTSP - RTSP
The RTSP protocol is an industry standard and allows many CCTV systems and applications (like for example [VLC](https://www.videolan.org/vlc/)) to connect directly to the ESP32CAM camera stream. The RTSP protocol is an industry standard and allows many CCTV systems and applications (like for example [VLC](https://www.videolan.org/vlc/)) to connect directly to the ESP32CAM camera stream.
It is also possible to stream directly to a server using [ffmpeg](https://ffmpeg.org). It is also possible to stream directly to a server using [ffmpeg](https://ffmpeg.org).
This makes the module a camera server allowing recording and the stream can be stored on a disk and replayed later. This makes the module a camera server allowing recording and the stream can be stored on a disk and replayed later.
The URL is rtsp://<ip address>:554/mjpeg/1 The URL is rtsp://<ip address>:554/mjpeg/1
- :white_check_mark: HTTP Motion JPEG - HTTP Motion JPEG
The HTTP JPEG streamer makes it possible to watch the camera stream directly in your browser. The HTTP JPEG streamer makes it possible to watch the camera stream directly in your browser.
The URL is http://<ip address>/stream The URL is http://<ip address>/stream
- :white_check_mark: HTTP image - HTTP image
The HTTP Image returns an HTTP JPEG image of the camera. The HTTP Image returns an HTTP JPEG image of the camera.
The URL is http://<ip address>/snapshot The URL is http://<ip address>/snapshot
@@ -36,15 +36,12 @@ This software supports the following ESP32-CAM (and alike) modules:
- WROVER-KIT - WROVER-KIT
- M5STACK - M5STACK
![ESP32CAM module](assets/ESP32-CAM.jpg)
The software provides a **configuration web server**, that can be used to: The software provides a **configuration web server**, that can be used to:
- Provide information about the state of the device, wifi connection and camera, - Provide information about the state of the device, wifi connection and camera,
- Set the WiFi parameters, - Set the WiFi parameters,
- Set the timeout for connecting to the access point, - Set the timeout for connecting to the access point,
- Set an access password, - Set an access password,
- Select the board type,
- Select the image size, - Select the image size,
- Select the frame rate, - Select the frame rate,
- Select the JPEG quality - Select the JPEG quality
@@ -83,25 +80,30 @@ It advertises HTTP (port 80) and RTSP (port 554)
- USB to Serial (TTL level) converter, piggyback board ESP32-CAM-MB or other way to connect to the device, - USB to Serial (TTL level) converter, piggyback board ESP32-CAM-MB or other way to connect to the device,
- [**PlatformIO**](https://platformio.org/) software (free download) - [**PlatformIO**](https://platformio.org/) software (free download)
## Boards
There are a lot of boards available that are all called ESP32-CAM.
However, there are differences in CPU (type/speed/cores), how the camera is connected, presence of PSRAM or not...
To select the right board use the table below and use the configuration that is listed below for your board:
| Board | Configuration | Image | CPU | RAM | Camera | Site |
|--- |--- |--- |--- |--- |--- |--- |
| Espressif ESP32-Wropver CAM | | <img src="assets/boards/esp32-wrover-cam.jpg" height="100" /> | ESP32 | 520KB SRAM 4MB PSRAM | OV2640 | |
| AI-Thinker ESP32-CAM | ai_thinker_esp32cam | <img src="assets/boards/ai-thinker-esp32-cam-ipex.jpg" height="100" /> <img src="assets/boards/ai-thinker-esp32-cam.jpg" height="100" /> | ESP32-S / 160Mhz | 520KB SRAM 4MB PSRAM | OV2640 | https://docs.ai-thinker.com/en/esp32-cam |
| Espressif ESP-EYE | | <img src="assets/boards/espressif-esp-eye.jpg" height="100" /> | ESP32 | 520KB SRAM 4MB PSRAM | OV2640 | |
| Espressif ESP-S3-EYE| | <img src="assets/boards/espressif-esps3-eye.jpg" height="100" /> | ESP32-S3 | 520KB SRAM 4MB PSRAM | OV2640 | https://www.espressif.com/en/products/devkits/esp-eye/overview |
| LilyGo camera module| | <img src="assets/boards/lilygo-camera-module.jpg" height="100" /> | ESP32 Wrover | 520KB SRAM 4MB PSRAM | OV2640 / OV5640
| LilyGo Simcam| | <img src="assets/boards/lilygo-simcam.jpg" height="100" /> | | | OV2640 | |
| LilyGo TTGO-T Camera| | <img src="assets/boards/lilygo-ttgo-t-camera.jpg" height="100" /> || | OV2640 | |
| M5 Stack Camera| | <img src="assets/boards/m5stack-esp32-camera.jpg" height="100" /> ||| OV2640 | |
| Seeed studio Xiao ESPS3 Sense| seeed_xiao_esp32s3_sense | <img src="assets/boards/seeed-studio-xiao-esp32s3-sense.jpg" height="100" /> | ESP32-S3 | 520KB SRAM 4MB PSRAM | OV2640 | |
## Installing and running PlatformIO ## Installing and running PlatformIO
PlatformIO is available for all major operating systems: Windows, Linux and MacOS. It is also provided as a plugin to [Visual Studio Code](https://visualstudio.microsoft.com). PlatformIO is available for all major operating systems: Windows, Linux and MacOS. It is also provided as a plugin to [Visual Studio Code](https://visualstudio.microsoft.com).
More information can be found at: [https://docs.platformio.org/en/latest/installation.html](https://docs.platformio.org/en/latest/installation.html) below the basics. More information can be found at: [https://docs.platformio.org/en/latest/installation.html](https://docs.platformio.org/en/latest/installation.html) below the basics.
### Debian based systems command-line install Install [**Visual Studio Code**](https://code.visualstudio.com) and install the PlatformIO plugin.
Install platformIO
```sh
sudo apt-get install python-pip
sudo pip install platformio
pio upgrade
```
### Windows, Linux and MacOS
Install [**Visual Studio code**](https://code.visualstudio.com) and install the PlatformIO plugin.
For command line usage Python and PlatformIO-Core is sufficient.
## Putting the ESP32-CAM in download mode ## Putting the ESP32-CAM in download mode
@@ -290,7 +292,7 @@ Not all the boards are equipped with PSRAM:
| ESP32CAM (USB-C) | No | | ESP32CAM (USB-C) | No |
| AI THINKER | Yes | | AI THINKER | Yes |
| TTGO T-CAM | No | | TTGO T-CAM | No |
| M5 STACK| | No | | M5 STACK | No |
| WROVER KIT | Yes | | WROVER KIT | Yes |
Depending on the image resolution, framerate and quality, the PSRAM must be enabled and/or the number of frame buffers increased to keep up with the data generated by the sensor. Depending on the image resolution, framerate and quality, the PSRAM must be enabled and/or the number of frame buffers increased to keep up with the data generated by the sensor.
@@ -323,6 +325,10 @@ esp32cam-rtsp depends on PlatformIO, Bootstrap 5 and Micro-RTSP by Kevin Hester.
## Change history ## Change history
- Oktober 2023
- Added support for Seeed Xiao esp32s3
- New build system
- Updated documentation
- March 2023 - March 2023
- Added options to set PSRAM / Frame buffers - Added options to set PSRAM / Frame buffers
- Added JPEG Motion streaming - Added JPEG Motion streaming

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 152 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 151 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 84 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 141 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 180 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 156 KiB

View File

@@ -2,4 +2,3 @@
. python3 -m pip install minify-html . python3 -m pip install minify-html
. python3 ./minify.py ./html/index.html ./html/index.min.html . python3 ./minify.py ./html/index.html ./html/index.min.html
. python3 ./minify.py ./html/restart.html ./html/restart.min.html

View File

@@ -4,4 +4,3 @@ python3 -m pip install --upgrade pip setuptools wheel
python3 -m pip install minify-html python3 -m pip install minify-html
python3 ./minify.py ./html/index.html ./html/index.min.html python3 ./minify.py ./html/index.html ./html/index.min.html
python3 ./minify.py ./html/restart.html ./html/restart.min.html

View File

@@ -104,17 +104,11 @@
</h3> </h3>
</div> </div>
{{#ConfigChanged}}
<div class="alert alert-danger">
<h3 class="text-center">
The configuration has been changed.<br>
It is recommended to restart the device.<br><br>
<button type="button" class="btn btn-danger" onclick="location.href='restart'">Restart</button>
</h3>
</div>
{{/ConfigChanged}}
<h2 class="text-center">ESP32</h2> <h2 class="text-center">ESP32</h2>
<div class="flex-table"> <div class="flex-table">
<div class="row">Board type:</div>
<div>{{BoardType}}</div>
<div class="row">SDK Version:</div> <div class="row">SDK Version:</div>
<div>{{SDKVersion}}</div> <div>{{SDKVersion}}</div>
<div class="row">CPU model:</div> <div class="row">CPU model:</div>
@@ -135,8 +129,6 @@
<div class="flex-table"> <div class="flex-table">
<div class="row">Uptime:</div> <div class="row">Uptime:</div>
<div>{{Uptime}}</div> <div>{{Uptime}}</div>
<div class="row">Chip temperature:</div>
<div>{{Temperature}} &deg;C</div>
<div class="row">RTSP sessions:</div> <div class="row">RTSP sessions:</div>
<div>{{NumRTSPSessions}}</div> <div>{{NumRTSPSessions}}</div>
<div class="row">Free heap:</div> <div class="row">Free heap:</div>
@@ -145,14 +137,6 @@
<div>{{MaxAllocHeap}}</div> <div>{{MaxAllocHeap}}</div>
</div> </div>
<h2 class="text-center">Peripheral</h2>
<div class="flex-table">
<div class="row">Board type:</div>
<div>{{BoardType}}</div>
<div class="row">LED intensity:</div>
<div>{{LedIntensity}} [0-100]</div>
</div>
<h2 class="text-center">Network</h2> <h2 class="text-center">Network</h2>
<div class="flex-table"> <div class="flex-table">
<div class="row">Host name:</div> <div class="row">Host name:</div>
@@ -191,10 +175,6 @@
<div>{{FrameSize}}</div> <div>{{FrameSize}}</div>
<div class="row">JPEG quality:</div> <div class="row">JPEG quality:</div>
<div>{{JpegQuality}} [1-100]</div> <div>{{JpegQuality}} [1-100]</div>
<div class="row">Enable PSRAM:</div>
<div>{{#EnablePSRAM}}Enabled{{/EnablePSRAM}}{{^EnablePSRAM}}Disabled{{/EnablePSRAM}}</div>
<div class="row">Number of frame buffers:</div>
<div>{{FrameBuffers}}</div>
<div class="row">Brightness:</div> <div class="row">Brightness:</div>
<div>{{Brightness}} [-2,2]</div> <div>{{Brightness}} [-2,2]</div>
<div class="row">Contrast:</div> <div class="row">Contrast:</div>
@@ -262,13 +242,9 @@
<div class="row">RTSP camera stream:</div> <div class="row">RTSP camera stream:</div>
<div><a href="rtsp://{{IpV4}}:{{RtspPort}}/mjpeg/1">rtsp://{{IpV4}}:{{RtspPort}}/mjpeg/1</a></div> <div><a href="rtsp://{{IpV4}}:{{RtspPort}}/mjpeg/1">rtsp://{{IpV4}}:{{RtspPort}}/mjpeg/1</a></div>
<div class="row">JPEG Motion stream:</div> <div class="row">JPEG Motion stream:</div>
<div><a href="http://{{IpV4}}/stream" target="_blank">http://{{IpV4}}/stream</a></div> <div><a href="http://{{IpV4}}/stream" target="_blank" rel="noopener">http://{{IpV4}}/stream</a></div>
<div class="row">Snapshot of the camera:</div> <div class="row">Snapshot of the camera:</div>
<div><a href="http://{{IpV4}}/snapshot " target="_blank">http://{{IpV4}}/snapshot</a> </div> <div><a href="http://{{IpV4}}/snapshot " target="_blank" rel="noopener">http://{{IpV4}}/snapshot</a> </div>
<div class="row">Intensity of the flash led (0-255):</div>
<div><a href="http://{{IpV4}}/flash?v=0">http://{{IpV4}}/flash?v=0</a> (Authentication required)</div>
<div class="row">Restart the camera:</div>
<div><a href="http://{{IpV4}}/restart">http://{{IpV4}}/restart</a> (Authentication required)</div>
</div> </div>
</body> </body>

File diff suppressed because one or more lines are too long

View File

@@ -1,45 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta http-equiv="refresh" content="10;url=/index.html">
<style>
html,
body {
color: #222;
font-size: 16px;
font-family: Arial, Verdana, Helvetica, sans-serif;
min-height: 100%;
}
.text-center {
text-align: center;
}
.alert-info {
color: #31708f;
background-color: #d9edf7;
border-color: #bce8f1;
}
</style>
<title>{{AppTitle}} v{{AppVersion}}</title>
</head>
<body>
<h1 class="text-center">{{ThingName}}</h1>
<hr>
<div class="alert-info">
<h3 class="text-center">Restarting</h3>
<h4 class="text-center">
The device is restarting...<br><br>
If this page takes longer than a minute, consider performing a power cycle.
</h4>
</div>
</body>
</html>

View File

@@ -1 +0,0 @@
<!doctypehtml><html lang=en><meta charset=utf-8><meta content=width=device-width,initial-scale=1,shrink-to-fit=no name=viewport><meta content=10;url=/index.html http-equiv=refresh><style>body,html{color:#222;font:16px Arial,Verdana,Helvetica,sans-serif;min-height:100%}.text-center{text-align:center}.alert-info{color:#31708f;background:#d9edf7;border:#bce8f1}</style><title>{{AppTitle}} v{{AppVersion}}</title><body><h1 class=text-center>{{ThingName}}</h1><hr><div class=alert-info><h3 class=text-center>Restarting</h3><h4 class=text-center>The device is restarting...<br><br> If this page takes longer than a minute, consider performing a power cycle.</h4></div>

View File

@@ -1,15 +1,8 @@
#pragma once #pragma once
#include <string.h>
#include <esp_camera.h> #include <esp_camera.h>
typedef struct constexpr camera_config_t esp32cam_camera_settings = {
{
const char name[11];
const camera_config_t config;
} camera_config_entry_t;
constexpr camera_config_t esp32cam_settings = {
.pin_pwdn = -1, .pin_pwdn = -1,
.pin_reset = 15, .pin_reset = 15,
.pin_xclk = 27, .pin_xclk = 27,
@@ -32,9 +25,38 @@ constexpr camera_config_t esp32cam_settings = {
.pixel_format = PIXFORMAT_JPEG, .pixel_format = PIXFORMAT_JPEG,
.frame_size = FRAMESIZE_SVGA, .frame_size = FRAMESIZE_SVGA,
.jpeg_quality = 12, .jpeg_quality = 12,
.fb_count = 2}; .fb_count = 1,
.fb_location = CAMERA_FB_IN_DRAM,
.grab_mode = CAMERA_GRAB_LATEST};
constexpr camera_config_t esp32cam_aithinker_settings = { constexpr camera_config_t esp_eye_camera_settings = {
.pin_pwdn = -1,
.pin_reset = -1,
.pin_xclk = 4,
.pin_sscb_sda = 18,
.pin_sscb_scl = 23,
.pin_d7 = 36,
.pin_d6 = 37,
.pin_d5 = 38,
.pin_d4 = 39,
.pin_d3 = 35,
.pin_d2 = 14,
.pin_d1 = 13,
.pin_d0 = 34,
.pin_vsync = 5,
.pin_href = 27,
.pin_pclk = 25,
.xclk_freq_hz = 20000000,
.ledc_timer = LEDC_TIMER_0,
.ledc_channel = LEDC_CHANNEL_0,
.pixel_format = PIXFORMAT_JPEG,
.frame_size = FRAMESIZE_SVGA,
.jpeg_quality = 12,
.fb_count = 1,
.fb_location = CAMERA_FB_IN_DRAM,
.grab_mode = CAMERA_GRAB_LATEST};
constexpr camera_config_t aithinker_camera_settings = {
.pin_pwdn = 32, .pin_pwdn = 32,
.pin_reset = -1, .pin_reset = -1,
.pin_xclk = 0, .pin_xclk = 0,
@@ -57,9 +79,11 @@ constexpr camera_config_t esp32cam_aithinker_settings = {
.pixel_format = PIXFORMAT_JPEG, .pixel_format = PIXFORMAT_JPEG,
.frame_size = FRAMESIZE_SVGA, .frame_size = FRAMESIZE_SVGA,
.jpeg_quality = 12, .jpeg_quality = 12,
.fb_count = 2}; .fb_count = 2,
.fb_location = CAMERA_FB_IN_PSRAM,
.grab_mode = CAMERA_GRAB_LATEST};
constexpr camera_config_t esp32cam_ttgo_t_settings = { constexpr camera_config_t ttgo_t_camera_settings = {
.pin_pwdn = 26, .pin_pwdn = 26,
.pin_reset = -1, .pin_reset = -1,
.pin_xclk = 32, .pin_xclk = 32,
@@ -82,9 +106,11 @@ constexpr camera_config_t esp32cam_ttgo_t_settings = {
.pixel_format = PIXFORMAT_JPEG, .pixel_format = PIXFORMAT_JPEG,
.frame_size = FRAMESIZE_SVGA, .frame_size = FRAMESIZE_SVGA,
.jpeg_quality = 12, .jpeg_quality = 12,
.fb_count = 2}; .fb_count = 1,
.fb_location = CAMERA_FB_IN_DRAM,
.grab_mode = CAMERA_GRAB_LATEST};
constexpr camera_config_t esp32cam_m5stack_settings = { constexpr camera_config_t m5stack_camera_settings = {
.pin_pwdn = -1, .pin_pwdn = -1,
.pin_reset = 15, .pin_reset = 15,
.pin_xclk = 27, .pin_xclk = 27,
@@ -107,9 +133,11 @@ constexpr camera_config_t esp32cam_m5stack_settings = {
.pixel_format = PIXFORMAT_JPEG, .pixel_format = PIXFORMAT_JPEG,
.frame_size = FRAMESIZE_SVGA, .frame_size = FRAMESIZE_SVGA,
.jpeg_quality = 12, .jpeg_quality = 12,
.fb_count = 2}; .fb_count = 1,
.fb_location = CAMERA_FB_IN_DRAM,
.grab_mode = CAMERA_GRAB_LATEST};
constexpr camera_config_t esp32cam_wrover_kit_settings = { constexpr camera_config_t wrover_kit_camera_settings = {
.pin_pwdn = -1, .pin_pwdn = -1,
.pin_reset = -1, .pin_reset = -1,
.pin_xclk = 21, .pin_xclk = 21,
@@ -132,21 +160,33 @@ constexpr camera_config_t esp32cam_wrover_kit_settings = {
.pixel_format = PIXFORMAT_JPEG, .pixel_format = PIXFORMAT_JPEG,
.frame_size = FRAMESIZE_SVGA, .frame_size = FRAMESIZE_SVGA,
.jpeg_quality = 12, .jpeg_quality = 12,
.fb_count = 2}; .fb_count = 2,
.fb_location = CAMERA_FB_IN_PSRAM,
.grab_mode = CAMERA_GRAB_LATEST};
constexpr const camera_config_entry_t camera_configs[] = { constexpr camera_config_t xiao_esp32s3_camera_settings = {
{"ESP32CAM", esp32cam_settings}, .pin_pwdn = -1,
{"AI THINKER", esp32cam_aithinker_settings}, .pin_reset = -1,
{"TTGO T-CAM", esp32cam_ttgo_t_settings}, .pin_xclk = 10,
{"M5 STACK", esp32cam_m5stack_settings}, .pin_sscb_sda = 40,
{"WROVER KIT", esp32cam_wrover_kit_settings}}; .pin_sscb_scl = 39,
.pin_d7 = 48,
const camera_config_t lookup_camera_config(const char *name) .pin_d6 = 11,
{ .pin_d5 = 12,
// Lookup table for the frame name to framesize_t .pin_d4 = 14,
for (const auto &entry : camera_configs) .pin_d3 = 16,
if (strncmp(entry.name, name, sizeof(entry.name)) == 0) .pin_d2 = 18,
return entry.config; .pin_d1 = 17,
.pin_d0 = 15,
return camera_config_t{}; .pin_vsync = 38,
} .pin_href = 47,
.pin_pclk = 13,
.xclk_freq_hz = 20000000,
.ledc_timer = LEDC_TIMER_0,
.ledc_channel = LEDC_CHANNEL_0,
.pixel_format = PIXFORMAT_JPEG, // for streaming
.frame_size = FRAMESIZE_UXGA,
.jpeg_quality = 12,
.fb_count = 2,
.fb_location = CAMERA_FB_IN_PSRAM,
.grab_mode = CAMERA_GRAB_LATEST};

View File

@@ -5,23 +5,45 @@
#define WIFI_SSID "ESP32CAM-RTSP" #define WIFI_SSID "ESP32CAM-RTSP"
#define WIFI_PASSWORD nullptr #define WIFI_PASSWORD nullptr
#define CONFIG_VERSION "1.5" #define CONFIG_VERSION "1.6"
#define OTA_PASSWORD "ESP32CAM-RTSP" #define OTA_PASSWORD "ESP32CAM-RTSP"
#define RTSP_PORT 554 #define RTSP_PORT 554
#define DEFAULT_CAMERA_CONFIG "AI THINKER" #if defined(BOARD_ESP32CAM)
#define DEFAULT_ENABLE_PSRAM psramFound() constexpr const char *board_name = "ESP32CAM";
#define DEFAULT_BUFFERS (psramFound() ? 2 : 1) constexpr camera_config_t default_camera_config = esp32cam_camera_settings;
#elif defined(BOARD_AITHINKER_ESP32CAM)
constexpr const char *board_name = "AI-Thinker ESP32CAM";
constexpr camera_config_t default_camera_config = aithinker_camera_settings;
#elif defined(BOARD_ESP_EYE)
constexpr const char *board_name = "ESP-EYE";
constexpr camera_config_t default_camera_config = esp_eye_camera_settings;
#elif defined(BOARD_TTGO_T_CAMERA)
constexpr const char *board_name = "TTGO-T-CAMERA";
constexpr camera_config_t default_camera_config = ttgo_t_camera_settings;
#elif defined(BOARD_M5STACK_ESP32CAM)
constexpr const char *board_name = "M5STACK-CAMERA";
constexpr camera_config_t default_camera_config = m5stack_camera_settings;
#elif defined(BOARD_ESP32_WROVER_CAM)
constexpr const char *board_name = "WROVER-KIT";
constexpr camera_config_t default_camera_config = wrover_kit_camera_settings;
#elif defined(BOARD_SEEED_XIAO_ESP32S3_SENSE)
constexpr const char *board_name = "Seed Xiao ESP32S3 Sense";
constexpr camera_config_t default_camera_config = xiao_esp32s3_camera_settings;
#else
#error No board defined
#endif
#define DEFAULT_FRAME_DURATION 200 #define DEFAULT_FRAME_DURATION 200
#define DEFAULT_FRAME_SIZE "VGA (640x480)" #define DEFAULT_FRAME_SIZE "VGA (640x480)"
#define DEFAULT_JPEG_QUALITY (psramFound() ? 12 : 14) #define DEFAULT_JPEG_QUALITY (psramFound() ? 12 : 14)
#define DEFAULT_BRIGHTNESS 0 #define DEFAULT_BRIGHTNESS 0
#define DEFAULT_CONTRAST 0 #define DEFAULT_CONTRAST 0
#define DEFAULT_SATURATION 0 #define DEFAULT_SATURATION 0
#define DEFAULT_EFFECT "Normal" #define DEFAULT_EFFECT "Normal"
#define DEFAULT_WHITE_BALANCE true #define DEFAULT_WHITE_BALANCE true
#define DEFAULT_WHITE_BALANCE_GAIN true #define DEFAULT_WHITE_BALANCE_GAIN true
#define DEFAULT_WHITE_BALANCE_MODE "Auto" #define DEFAULT_WHITE_BALANCE_MODE "Auto"

View File

@@ -29,7 +29,7 @@ private:
std::shared_ptr<CRtspSession> session; std::shared_ptr<CRtspSession> session;
}; };
OV2640 cam_; OV2640 cam_;
std::list<std::unique_ptr<rtsp_client>> clients_; std::list<std::unique_ptr<rtsp_client>> clients_;
uintptr_t task_; uintptr_t task_;
Timer<> timer_; Timer<> timer_;

View File

@@ -8,15 +8,14 @@
; Please visit documentation for the other options and examples ; Please visit documentation for the other options and examples
; https://docs.platformio.org/page/projectconf.html ; https://docs.platformio.org/page/projectconf.html
[env:esp32cam] ###############################################################################
[env]
platform = espressif32 platform = espressif32
board = esp32cam
framework = arduino framework = arduino
#upload_protocol = espota #upload_protocol = espota
#upload_port = 192.168.50.222 #upload_port = 192.168.178.223
#upload_flags = #upload_flags = --auth='ESP32CAM-RTSP'
# --auth='ESP32CAM-RTSP'
# Partition scheme for OTA # Partition scheme for OTA
board_build.partitions = min_spiffs.csv board_build.partitions = min_spiffs.csv
@@ -27,19 +26,54 @@ monitor_dtr = 0
monitor_filters = log2file, time, default, esp32_exception_decoder monitor_filters = log2file, time, default, esp32_exception_decoder
build_flags = build_flags =
-O2 -O2
-D CORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_VERBOSE -DCORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_VERBOSE
-D LED_FLASH=4 -DIOTWEBCONF_PASSWORD_LEN=64
-D LED_BUILTIN=33
-D BOARD_HAS_PSRAM
-mfix-esp32-psram-cache-issue
-D IOTWEBCONF_PASSWORD_LEN=64
board_build.embed_txtfiles = board_build.embed_txtfiles =
html/index.min.html html/index.min.html
html/restart.min.html
lib_deps = lib_deps =
prampec/IotWebConf @ ^3.2.1 prampec/IotWebConf@^3.2.1
geeksville/Micro-RTSP @ ^0.1.6 geeksville/Micro-RTSP@^0.1.6
rzeldent/micro-moustache@^1.0.1 rzeldent/micro-moustache@^1.0.1
[env:esp32cam]
board = esp32cam
build_flags =
-DBOARD_ESP32CAM
-DLED_BUILTIN=33
[env:ai_thinker_esp32cam]
board = esp32cam
build_flags =
-DBOARD_AITHINKER_ESP32CAM
-DLED_BUILTIN=33
[env:esp_eye]
board = esp32cam
build_flags =
-DBOARD_ESP_EYE
-DLED_BUILTIN=33
[env:ttgo_t_camera]
board = ttgo-t-beam
build_flags =
-DBOARD_TTGO_T_CAMERA
[env:m5stack_esp32cam]
board = m5stack-core-esp32
build_flags =
-DBOARD_M5STACK_ESP32CAM
-DLED_BUILTIN=33
[env:esp32_wrover_cam]
board = esp-wrover-kit
build_flags =
-DBOARD_ESP32_WROVER_CAM
-DLED_BUILTIN=2
[env:seeed_xiao_esp32s3]
board = seeed_xiao_esp32s3
build_flags =
-D BOARD_SEEED_XIAO_ESP32S3_SENSE

View File

@@ -7,7 +7,7 @@
#include <OV2640.h> #include <OV2640.h>
#include <ESPmDNS.h> #include <ESPmDNS.h>
#include <rtsp_server.h> #include <rtsp_server.h>
#include <lookup_camera_config.h> #include <camera_config.h>
#include <lookup_camera_effect.h> #include <lookup_camera_effect.h>
#include <lookup_camera_frame_size.h> #include <lookup_camera_frame_size.h>
#include <lookup_camera_gainceiling.h> #include <lookup_camera_gainceiling.h>
@@ -17,21 +17,13 @@
#include <moustache.h> #include <moustache.h>
#include <settings.h> #include <settings.h>
extern "C" uint8_t temprature_sens_read();
// HTML files // HTML files
extern const char index_html_min_start[] asm("_binary_html_index_min_html_start"); extern const char index_html_min_start[] asm("_binary_html_index_min_html_start");
extern const char restart_html_min_start[] asm("_binary_html_restart_min_html_start");
auto param_group_board = iotwebconf::ParameterGroup("board", "Board settings");
auto param_board = iotwebconf::Builder<iotwebconf::SelectTParameter<sizeof(camera_configs[0])>>("bt").label("Board").optionValues((const char *)&camera_configs).optionNames((const char *)&camera_configs).optionCount(sizeof(camera_configs) / sizeof(camera_configs[0])).nameLength(sizeof(camera_configs[0])).defaultValue(DEFAULT_CAMERA_CONFIG).build();
auto param_group_camera = iotwebconf::ParameterGroup("camera", "Camera settings"); auto param_group_camera = iotwebconf::ParameterGroup("camera", "Camera settings");
auto param_frame_duration = iotwebconf::Builder<iotwebconf::UIntTParameter<unsigned long>>("fd").label("Frame duration (ms)").defaultValue(DEFAULT_FRAME_DURATION).min(10).build(); auto param_frame_duration = iotwebconf::Builder<iotwebconf::UIntTParameter<unsigned long>>("fd").label("Frame duration (ms)").defaultValue(DEFAULT_FRAME_DURATION).min(10).build();
auto param_frame_size = iotwebconf::Builder<iotwebconf::SelectTParameter<sizeof(frame_sizes[0])>>("fs").label("Frame size").optionValues((const char *)&frame_sizes).optionNames((const char *)&frame_sizes).optionCount(sizeof(frame_sizes) / sizeof(frame_sizes[0])).nameLength(sizeof(frame_sizes[0])).defaultValue(DEFAULT_FRAME_SIZE).build(); auto param_frame_size = iotwebconf::Builder<iotwebconf::SelectTParameter<sizeof(frame_sizes[0])>>("fs").label("Frame size").optionValues((const char *)&frame_sizes).optionNames((const char *)&frame_sizes).optionCount(sizeof(frame_sizes) / sizeof(frame_sizes[0])).nameLength(sizeof(frame_sizes[0])).defaultValue(DEFAULT_FRAME_SIZE).build();
auto param_jpg_quality = iotwebconf::Builder<iotwebconf::UIntTParameter<byte>>("q").label("JPG quality").defaultValue(DEFAULT_JPEG_QUALITY).min(1).max(100).build(); auto param_jpg_quality = iotwebconf::Builder<iotwebconf::UIntTParameter<byte>>("q").label("JPG quality").defaultValue(DEFAULT_JPEG_QUALITY).min(1).max(100).build();
auto param_enable_psram = iotwebconf::Builder<iotwebconf::CheckboxTParameter>("eps").label("Enable PSRAM if available").defaultValue(DEFAULT_ENABLE_PSRAM).build();
auto param_frame_buffers = iotwebconf::Builder<iotwebconf::IntTParameter<int>>("fb").label("Buffers").defaultValue(DEFAULT_BUFFERS).min(1).max(4).build();
auto param_brightness = iotwebconf::Builder<iotwebconf::IntTParameter<int>>("b").label("Brightness").defaultValue(DEFAULT_BRIGHTNESS).min(-2).max(2).build(); auto param_brightness = iotwebconf::Builder<iotwebconf::IntTParameter<int>>("b").label("Brightness").defaultValue(DEFAULT_BRIGHTNESS).min(-2).max(2).build();
auto param_contrast = iotwebconf::Builder<iotwebconf::IntTParameter<int>>("c").label("Contrast").defaultValue(DEFAULT_CONTRAST).min(-2).max(2).build(); auto param_contrast = iotwebconf::Builder<iotwebconf::IntTParameter<int>>("c").label("Contrast").defaultValue(DEFAULT_CONTRAST).min(-2).max(2).build();
auto param_saturation = iotwebconf::Builder<iotwebconf::IntTParameter<int>>("s").label("Saturation").defaultValue(DEFAULT_SATURATION).min(-2).max(2).build(); auto param_saturation = iotwebconf::Builder<iotwebconf::IntTParameter<int>>("s").label("Saturation").defaultValue(DEFAULT_SATURATION).min(-2).max(2).build();
@@ -55,9 +47,6 @@ auto param_vflip = iotwebconf::Builder<iotwebconf::CheckboxTParameter>("vm").lab
auto param_dcw = iotwebconf::Builder<iotwebconf::CheckboxTParameter>("dcw").label("Downsize enable").defaultValue(DEFAULT_DCW).build(); auto param_dcw = iotwebconf::Builder<iotwebconf::CheckboxTParameter>("dcw").label("Downsize enable").defaultValue(DEFAULT_DCW).build();
auto param_colorbar = iotwebconf::Builder<iotwebconf::CheckboxTParameter>("cb").label("Colorbar").defaultValue(DEFAULT_COLORBAR).build(); auto param_colorbar = iotwebconf::Builder<iotwebconf::CheckboxTParameter>("cb").label("Colorbar").defaultValue(DEFAULT_COLORBAR).build();
auto param_group_peripheral = iotwebconf::ParameterGroup("io", "peripheral settings");
auto param_led_intensity = iotwebconf::Builder<iotwebconf::UIntTParameter<byte>>("li").label("LED intensity").defaultValue(DEFAULT_LED_INTENSITY).min(0).max(100).build();
// Camera // Camera
OV2640 cam; OV2640 cam;
// DNS Server // DNS Server
@@ -70,8 +59,6 @@ WebServer web_server(80);
auto thingName = String(WIFI_SSID) + "-" + String(ESP.getEfuseMac(), 16); auto thingName = String(WIFI_SSID) + "-" + String(ESP.getEfuseMac(), 16);
IotWebConf iotWebConf(thingName.c_str(), &dnsServer, &web_server, WIFI_PASSWORD, CONFIG_VERSION); IotWebConf iotWebConf(thingName.c_str(), &dnsServer, &web_server, WIFI_PASSWORD, CONFIG_VERSION);
// Keep track of config changes. This will allow a reset of the device
bool config_changed = false;
// Camera initialization result // Camera initialization result
esp_err_t camera_init_result; esp_err_t camera_init_result;
@@ -103,11 +90,10 @@ void handle_root()
auto ipv6 = WiFi.getMode() == WIFI_MODE_AP ? WiFi.softAPIPv6() : WiFi.localIPv6(); auto ipv6 = WiFi.getMode() == WIFI_MODE_AP ? WiFi.softAPIPv6() : WiFi.localIPv6();
moustache_variable_t substitutions[] = { moustache_variable_t substitutions[] = {
// Config Changed?
{"ConfigChanged", String(config_changed)},
// Version / CPU // Version / CPU
{"AppTitle", APP_TITLE}, {"AppTitle", APP_TITLE},
{"AppVersion", APP_VERSION}, {"AppVersion", APP_VERSION},
{"BoardType", board_name},
{"ThingName", iotWebConf.getThingName()}, {"ThingName", iotWebConf.getThingName()},
{"SDKVersion", ESP.getSdkVersion()}, {"SDKVersion", ESP.getSdkVersion()},
{"ChipModel", ESP.getChipModel()}, {"ChipModel", ESP.getChipModel()},
@@ -119,7 +105,6 @@ void handle_root()
{"PsRamSize", format_memory(ESP.getPsramSize(), 0)}, {"PsRamSize", format_memory(ESP.getPsramSize(), 0)},
// Diagnostics // Diagnostics
{"Uptime", String(format_duration(millis() / 1000))}, {"Uptime", String(format_duration(millis() / 1000))},
{"Temperature", String((temprature_sens_read() - 32) / 1.8)},
{"FreeHeap", format_memory(ESP.getFreeHeap())}, {"FreeHeap", format_memory(ESP.getFreeHeap())},
{"MaxAllocHeap", format_memory(ESP.getMaxAllocHeap())}, {"MaxAllocHeap", format_memory(ESP.getMaxAllocHeap())},
{"NumRTSPSessions", camera_server != nullptr ? String(camera_server->num_connected()) : "RTSP server disabled"}, {"NumRTSPSessions", camera_server != nullptr ? String(camera_server->num_connected()) : "RTSP server disabled"},
@@ -134,13 +119,10 @@ void handle_root()
{"NetworkState.ApMode", String(iotWebConf.getState() == iotwebconf::NetworkState::ApMode)}, {"NetworkState.ApMode", String(iotWebConf.getState() == iotwebconf::NetworkState::ApMode)},
{"NetworkState.OnLine", String(iotWebConf.getState() == iotwebconf::NetworkState::OnLine)}, {"NetworkState.OnLine", String(iotWebConf.getState() == iotwebconf::NetworkState::OnLine)},
// Camera // Camera
{"BoardType", String(param_board.value())},
{"FrameSize", String(param_frame_size.value())}, {"FrameSize", String(param_frame_size.value())},
{"FrameDuration", String(param_frame_duration.value())}, {"FrameDuration", String(param_frame_duration.value())},
{"FrameFrequency", String(1000.0 / param_frame_duration.value(), 1)}, {"FrameFrequency", String(1000.0 / param_frame_duration.value(), 1)},
{"JpegQuality", String(param_jpg_quality.value())}, {"JpegQuality", String(param_jpg_quality.value())},
{"EnablePSRAM", String(param_enable_psram.value())},
{"FrameBuffers", String(param_frame_buffers.value())},
{"CameraInitialized", String(camera_init_result == ESP_OK)}, {"CameraInitialized", String(camera_init_result == ESP_OK)},
{"CameraInitResult", String(camera_init_result)}, {"CameraInitResult", String(camera_init_result)},
{"CameraInitResultText", esp_err_to_name(camera_init_result)}, {"CameraInitResultText", esp_err_to_name(camera_init_result)},
@@ -167,8 +149,6 @@ void handle_root()
{"VFlip", String(param_vflip.value())}, {"VFlip", String(param_vflip.value())},
{"Dcw", String(param_dcw.value())}, {"Dcw", String(param_dcw.value())},
{"ColorBar", String(param_colorbar.value())}, {"ColorBar", String(param_colorbar.value())},
// LED
{"LedIntensity", String(param_led_intensity.value())},
// RTSP // RTSP
{"RtspPort", String(RTSP_PORT)}}; {"RtspPort", String(RTSP_PORT)}};
@@ -177,28 +157,6 @@ void handle_root()
web_server.send(200, "text/html", html); web_server.send(200, "text/html", html);
} }
void handle_restart()
{
log_v("Handle restart");
if (!web_server.authenticate("admin", iotWebConf.getApPasswordParameter()->valueBuffer))
{
web_server.requestAuthentication(BASIC_AUTH, APP_TITLE, "401 Unauthorized<br><br>The password is incorrect.");
return;
}
moustache_variable_t substitutions[] = {
{"AppTitle", APP_TITLE},
{"AppVersion", APP_VERSION},
{"ThingName", iotWebConf.getThingName()}};
auto html = moustache_render(restart_html_min_start, substitutions);
web_server.send(200, "text/html", html);
log_v("Restarting... Press refresh to connect again");
sleep(100);
ESP.restart();
}
void handle_snapshot() void handle_snapshot()
{ {
log_v("handle_snapshot"); log_v("handle_snapshot");
@@ -209,7 +167,7 @@ void handle_snapshot()
} }
// Remove old images stored in the frame buffer // Remove old images stored in the frame buffer
auto frame_buffers = param_frame_buffers.value(); auto frame_buffers = default_camera_config.fb_count;
while (frame_buffers--) while (frame_buffers--)
cam.run(); cam.run();
@@ -257,64 +215,19 @@ void handle_stream()
client.stop(); client.stop();
log_v("stopped streaming"); log_v("stopped streaming");
} }
void handle_flash()
{
log_v("handle_flash");
if (!web_server.authenticate("admin", iotWebConf.getApPasswordParameter()->valueBuffer))
{
web_server.requestAuthentication(BASIC_AUTH, APP_TITLE, "401 Unauthorized<br><br>The password is incorrect.");
return;
}
// If no value present, use value from config
if (web_server.hasArg("v"))
{
auto v = (uint8_t)min(web_server.arg("v").toInt(), 255L);
// If conversion fails, v = 0
analogWrite(LED_FLASH, v);
}
else
{
analogWrite(LED_FLASH, param_led_intensity.value());
}
web_server.sendHeader("Cache-Control", "no-cache, no-store, must-revalidate");
web_server.send(200);
}
esp_err_t initialize_camera() esp_err_t initialize_camera()
{ {
log_v("initialize_camera"); log_v("initialize_camera");
log_i("Camera config: %s", param_board.value());
auto camera_config_template = lookup_camera_config(param_board.value());
// Copy the settings // Copy the settings
camera_config_t camera_config; camera_config_t camera_config;
memset(&camera_config, 0, sizeof(camera_config_t)); memset(&camera_config, 0, sizeof(camera_config_t));
memcpy(&camera_config, &camera_config_template, sizeof(camera_config_t)); memcpy(&camera_config, &default_camera_config, sizeof(camera_config_t));
log_i("Frame size: %s", param_frame_size.value()); log_i("Frame size: %s", param_frame_size.value());
auto frame_size = lookup_frame_size(param_frame_size.value()); auto frame_size = lookup_frame_size(param_frame_size.value());
log_i("JPEG quality: %d", param_jpg_quality.value()); log_i("JPEG quality: %d", param_jpg_quality.value());
log_i("Frame duration: %d ms", param_frame_duration.value()); log_i("Frame duration: %d ms", param_frame_duration.value());
camera_config.frame_size = frame_size; camera_config.frame_size = frame_size;
camera_config.jpeg_quality = param_jpg_quality.value(); camera_config.jpeg_quality = param_jpg_quality.value();
camera_config.grab_mode = CAMERA_GRAB_LATEST;
log_i("Enable PSRAM: %d", param_enable_psram.value());
log_i("Frame buffers: %d", param_frame_buffers.value());
camera_config.fb_count = param_frame_buffers.value();
if (param_enable_psram.value() && psramFound())
{
camera_config.fb_location = CAMERA_FB_IN_PSRAM;
log_i("PSRAM enabled!");
}
else
{
camera_config.fb_location = CAMERA_FB_IN_DRAM;
log_i("PSRAM disabled");
}
return cam.init(camera_config); return cam.init(camera_config);
} }
@@ -375,26 +288,17 @@ void on_connected()
void on_config_saved() void on_config_saved()
{ {
log_v("on_config_saved"); log_v("on_config_saved");
// Set flash led intensity
analogWrite(LED_FLASH, param_led_intensity.value());
// Update camera setting
update_camera_settings(); update_camera_settings();
config_changed = true;
} }
void setup() void setup()
{ {
// Disable brownout // Disable brownout
WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0); WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0);
// LED_BUILTIN (GPIO33) has inverted logic false => LED on // LED_BUILTIN (GPIO33) has inverted logic false => LED on
pinMode(LED_BUILTIN, OUTPUT); pinMode(LED_BUILTIN, OUTPUT);
digitalWrite(LED_BUILTIN, false); digitalWrite(LED_BUILTIN, false);
pinMode(LED_FLASH, OUTPUT);
// Turn flash led off
analogWrite(LED_FLASH, 0);
#ifdef CORE_DEBUG_LEVEL #ifdef CORE_DEBUG_LEVEL
Serial.begin(115200); Serial.begin(115200);
Serial.setDebugOutput(true); Serial.setDebugOutput(true);
@@ -403,22 +307,19 @@ void setup()
log_i("CPU Freq: %d Mhz, %d core(s)", getCpuFrequencyMhz(), ESP.getChipCores()); log_i("CPU Freq: %d Mhz, %d core(s)", getCpuFrequencyMhz(), ESP.getChipCores());
log_i("Free heap: %d bytes", ESP.getFreeHeap()); log_i("Free heap: %d bytes", ESP.getFreeHeap());
log_i("SDK version: %s", ESP.getSdkVersion()); log_i("SDK version: %s", ESP.getSdkVersion());
log_i("Board: %s", board_name);
log_i("Starting " APP_TITLE "..."); log_i("Starting " APP_TITLE "...");
if (psramFound())
{
psramInit();
log_v("PSRAM found and initialized");
}
param_group_board.addItem(&param_board); if (default_camera_config.fb_location == CAMERA_FB_IN_PSRAM)
iotWebConf.addParameterGroup(&param_group_board); {
if (!psramInit())
log_e("Failed to initialize PSRAM");
}
param_group_camera.addItem(&param_frame_duration); param_group_camera.addItem(&param_frame_duration);
param_group_camera.addItem(&param_frame_size); param_group_camera.addItem(&param_frame_size);
param_group_camera.addItem(&param_jpg_quality); param_group_camera.addItem(&param_jpg_quality);
param_group_camera.addItem(&param_enable_psram);
param_group_camera.addItem(&param_frame_buffers);
param_group_camera.addItem(&param_brightness); param_group_camera.addItem(&param_brightness);
param_group_camera.addItem(&param_contrast); param_group_camera.addItem(&param_contrast);
param_group_camera.addItem(&param_saturation); param_group_camera.addItem(&param_saturation);
@@ -443,9 +344,6 @@ void setup()
param_group_camera.addItem(&param_colorbar); param_group_camera.addItem(&param_colorbar);
iotWebConf.addParameterGroup(&param_group_camera); iotWebConf.addParameterGroup(&param_group_camera);
param_group_peripheral.addItem(&param_led_intensity);
iotWebConf.addParameterGroup(&param_group_peripheral);
iotWebConf.getApTimeoutParameter()->visible = true; iotWebConf.getApTimeoutParameter()->visible = true;
iotWebConf.setConfigSavedCallback(on_config_saved); iotWebConf.setConfigSavedCallback(on_config_saved);
iotWebConf.setWifiConnectionCallback(on_connected); iotWebConf.setWifiConnectionCallback(on_connected);
@@ -456,19 +354,16 @@ void setup()
if (camera_init_result == ESP_OK) if (camera_init_result == ESP_OK)
update_camera_settings(); update_camera_settings();
else else
log_e("Failed to initialize camera: 0x%0x. Type: %s, frame size: %s, frame rate: %d ms, jpeg quality: %d", camera_init_result, param_board.value(), param_frame_size.value(), param_frame_duration.value(), param_jpg_quality.value()); log_e("Failed to initialize camera: 0x%0x. Frame size: %s, frame rate: %d ms, jpeg quality: %d", camera_init_result, param_frame_size.value(), param_frame_duration.value(), param_jpg_quality.value());
// Set up required URL handlers on the web server // Set up required URL handlers on the web server
web_server.on("/", HTTP_GET, handle_root); web_server.on("/", HTTP_GET, handle_root);
web_server.on("/config", [] web_server.on("/config", []
{ iotWebConf.handleConfig(); }); { iotWebConf.handleConfig(); });
web_server.on("/restart", HTTP_GET, handle_restart);
// Camera snapshot // Camera snapshot
web_server.on("/snapshot", HTTP_GET, handle_snapshot); web_server.on("/snapshot", HTTP_GET, handle_snapshot);
// Camera stream // Camera stream
web_server.on("/stream", HTTP_GET, handle_stream); web_server.on("/stream", HTTP_GET, handle_stream);
// Camera flash light
web_server.on("/flash", HTTP_GET, handle_flash);
web_server.onNotFound([]() web_server.onNotFound([]()
{ iotWebConf.handleNotFound(); }); { iotWebConf.handleNotFound(); });
@@ -492,9 +387,6 @@ void setup()
case OTA_END_ERROR: log_e("OTA: End Failed"); break; case OTA_END_ERROR: log_e("OTA: End Failed"); break;
default: log_e("OTA error: %u", error); default: log_e("OTA error: %u", error);
} }); } });
// Set flash led intensity
analogWrite(LED_FLASH, param_led_intensity.value());
} }
void loop() void loop()