Compare commits
84 Commits
1.0.4
...
106-create
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
12332fd7eb | ||
|
|
4dc36fb1e8 | ||
|
|
4b50b52ae0 | ||
|
|
b8abcdcdee | ||
|
|
5117f0d21a | ||
|
|
f59fb8bb9a | ||
|
|
c599657882 | ||
|
|
3e688ddcfb | ||
|
|
c2dcd8b3d4 | ||
|
|
fe7195c232 | ||
|
|
86ae9af574 | ||
|
|
383d5803af | ||
|
|
312d916647 | ||
|
|
c1d92bcb36 | ||
|
|
71079f4796 | ||
|
|
d99d1c510f | ||
|
|
c61e6cd882 | ||
|
|
80a23aa5c4 | ||
|
|
e702191382 | ||
|
|
613965e425 | ||
|
|
2eb7a58e1c | ||
|
|
6119020e7d | ||
|
|
152c068f68 | ||
|
|
f05932b896 | ||
|
|
4fbba6fc96 | ||
|
|
4fb0f61e8e | ||
|
|
a760fdae38 | ||
|
|
03582b83cb | ||
|
|
a60fbc7917 | ||
|
|
1c2236466d | ||
|
|
26d1af2f28 | ||
|
|
97fc4ceb33 | ||
|
|
9ba11162d8 | ||
|
|
fd21f32870 | ||
|
|
4239de2286 | ||
|
|
60b602b077 | ||
|
|
7b4f4c056a | ||
|
|
4df6c9469d | ||
|
|
50f4b6f94e | ||
|
|
f9336ad803 | ||
|
|
8e5f4ee66c | ||
|
|
138c620296 | ||
|
|
545a824b37 | ||
|
|
e8cdf6d044 | ||
|
|
a90c4fe672 | ||
|
|
5bdfc45e3b | ||
|
|
d0ef68bbf4 | ||
|
|
51c5bf08bc | ||
|
|
7f37bff13d | ||
|
|
351c6a29d5 | ||
|
|
5e4fee3624 | ||
|
|
7a0259d878 | ||
|
|
6850c2c47f | ||
|
|
9ecdd1c05d | ||
|
|
0809f6baba | ||
|
|
ed293ebb68 | ||
|
|
f68e347c5b | ||
|
|
0d0223e8df | ||
|
|
45e9a92e3c | ||
|
|
5b7d6f9da6 | ||
|
|
3e6df1a56b | ||
|
|
a40f6a8ad8 | ||
|
|
fdc0ddd379 | ||
|
|
6917e44287 | ||
|
|
4360630835 | ||
|
|
b3cb7d1bf7 | ||
|
|
aee7cbddd8 | ||
|
|
8c263be1a6 | ||
|
|
0dc7abc338 | ||
|
|
e88c7a6f62 | ||
|
|
a711eec80c | ||
|
|
5f80cabbed | ||
|
|
621dbe466e | ||
|
|
72482080cf | ||
|
|
97acc08edf | ||
|
|
86dcf88d4e | ||
|
|
730d941988 | ||
|
|
2fdb589bfd | ||
|
|
e69ff1fbe8 | ||
|
|
064f0e9ac9 | ||
|
|
7ceb878a59 | ||
|
|
19b8f77d57 | ||
|
|
5dac9876fb | ||
|
|
1b06941ee8 |
21
.github/workflows/main.yml
vendored
@@ -5,18 +5,25 @@ jobs:
|
||||
name: Build
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Set up python
|
||||
uses: actions/setup-python@v3
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
python-version: '3.x'
|
||||
architecture: 'x64'
|
||||
submodules: 'true'
|
||||
- uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
~/.cache/pip
|
||||
~/.platformio/.cache
|
||||
key: ${{ runner.os }}-pio
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.9'
|
||||
- name: Install PlatformIO
|
||||
run: python -m pip install platformio
|
||||
- name: Build firmware
|
||||
run: platformio run
|
||||
- name: Archive
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: firmware.bin
|
||||
name: firmwares.zip
|
||||
path: .pio/build/*/firmware.bin
|
||||
|
||||
1
.gitignore
vendored
@@ -1,5 +1,6 @@
|
||||
.*/*
|
||||
.*.*
|
||||
__pycache__/
|
||||
*.log
|
||||
.DS_Store
|
||||
workspace.code-workspace
|
||||
193
README.md
@@ -2,39 +2,67 @@
|
||||
|
||||
[](https://github.com/rzeldent/esp32cam-rtsp/actions/workflows/main.yml)
|
||||
|
||||
Simple [RTSP](https://en.wikipedia.org/wiki/Real_Time_Streaming_Protocol) server.
|
||||
Easy configuration through the web interface.
|
||||
Simple [RTSP](https://en.wikipedia.org/wiki/Real_Time_Streaming_Protocol), [HTTP JPEG Streamer](https://en.wikipedia.org/wiki/Motion_JPEG) and image server with configuration through the web interface.
|
||||
|
||||
Flashing this software on a ESP32CAM module will make it a **RTSP streaming camera** server.
|
||||
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).
|
||||
This makes the module a camera server allowing recording and the stream can be stored on a disk and replayed later.
|
||||
> [!IMPORTANT]
|
||||
> New branch available! Here [branch: develop](https://github.com/rzeldent/esp32cam-rtsp/tree/develop)
|
||||
> This branch supports all the current devices and the Seeed Studio Xiao esp32s3!
|
||||
> Please use this and let me know if this works for you!
|
||||
|
||||
Flashing this software on a ESP32CAM module will make it a **RTSP streaming camera** server, a **HTTP Motion JPEG streamer** and a **HTTP image server**.
|
||||
|
||||
Supported protocols
|
||||
|
||||
- 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.
|
||||
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.
|
||||
The URL is rtsp://<ip address>:554/mjpeg/1
|
||||
|
||||
- HTTP Motion JPEG
|
||||
The HTTP JPEG streamer makes it possible to watch the camera stream directly in your browser.
|
||||
The URL is http://<ip address>/stream
|
||||
|
||||
- HTTP image
|
||||
The HTTP Image returns an HTTP JPEG image of the camera.
|
||||
The URL is http://<ip address>/snapshot
|
||||
|
||||
This software supports the following ESP32-CAM (and alike) modules:
|
||||
|
||||
- ESP32CAM
|
||||
- AI THINKER
|
||||
- TTGO T-CAM
|
||||
- WROVER-KIT
|
||||
- EspressIf ESP-EYE
|
||||
- EspressIf ESP32S2-CAM
|
||||
- EspressIf ESP32S3-CAM-LCD
|
||||
- EspressIf ESP32S3-EYE
|
||||
- Freenove WROVER KIT
|
||||
- M5STACK ESP32CAM
|
||||
- M5STACK_PSRAM
|
||||
- M5STACK_UNITCAM
|
||||
- M5STACK_UNITCAMS3
|
||||
- M5STACK_V2_PSRAM
|
||||
- M5STACK_PSRAM
|
||||
- M5STACK_WIDE
|
||||
- M5STACK
|
||||
- Seeed Studio XIAO ESP32S3 SENSE
|
||||
- TTGO T-CAMERA
|
||||
- TTGO T-JOURNAL
|
||||
|
||||

|
||||
|
||||
This 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,
|
||||
- Set the WiFi parameters,
|
||||
- Set the timeout for connecting to the access point,
|
||||
- Set an access password,
|
||||
- Select the board type,
|
||||
- Select the image size,
|
||||
- Select the frame rate,
|
||||
- Select the JPEG quality
|
||||
- Enable the use of the PSRAM
|
||||
- Set the number of frame buffers
|
||||
- Configure the camera options:
|
||||
- Brightness
|
||||
- Contrast
|
||||
- Saturation
|
||||
- Special effect (Normal, Negative, Grayscale, Red/Green/Blue tint, Sepia)
|
||||
- Special effect (Normal, Negative, Gray-scale, Red/Green/Blue tint, Sepia)
|
||||
- White balance
|
||||
- Automatic White Balance gain
|
||||
- Wite Balance mode
|
||||
@@ -63,25 +91,34 @@ 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,
|
||||
- [**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 | Image | CPU | SRAM | Flash | PSRAM | Camera | Extras | Manufacturer site |
|
||||
|--- |--- |--- |--- |--- | --- |--- |--- |--- |
|
||||
| Espressif ESP32-Wrover CAM |  | ESP32 | 520KB | 4Mb | 4MB | OV2640 | | |
|
||||
| AI-Thinker ESP32-CAM |   | ESP32-S | 520KB | 4Mb | 4MB | OV2640 | | [https://docs.ai-thinker.com/esp32-cam](https://docs.ai-thinker.com/esp32-cam) |
|
||||
| Espressif ESP-EYE |  | ESP32 | 520KB | 4Mb | 4MB | OV2640 | | |
|
||||
| Espressif ESP-S3-EYE |  | ESP32-S3 | 520KB | 4Mb | 4MB | OV2640 | | [https://www.espressif.com/en/products/devkits/esp-eye/overview](https://www.espressif.com/en/products/devkits/esp-eye/overview) |
|
||||
| LilyGo camera module |  | ESP32 Wrover | 520KB | 4Mb | 4MB | OV2640 / OV5640 | | |
|
||||
| LilyGo Simcam |  | | | | | OV2640 | | |
|
||||
| LilyGo TTGO-T Camera |  | | | | | OV2640 | | |
|
||||
| M5Stack ESP32CAM |  | ESP32 | 520Kb | 4Mb | - | OV2640 | Microphone | [https://docs.m5stack.com/en/unit/esp32cam](https://docs.m5stack.com/en/unit/esp32cam) |
|
||||
| M5Stack UnitCam |   | ESP32-WROOM-32E | 520KB | 4Mb | - | OV2640 | | [https://docs.m5stack.com/en/unit/unit_cam](https://docs.m5stack.com/en/unit/unit_cam) |
|
||||
| M5Stack Camera |  | ESP32 | 520Kb | 4Mb | - | OV2640 | | [https://docs.m5stack.com/en/unit/m5camera](https://docs.m5stack.com/en/unit/m5camera) |
|
||||
| M5Stack Camera PSRAM |  | ESP32 | 520Kb | 4Mb | 4Mb | OV2640 | | [https://docs.m5stack.com/en/unit/m5camera](https://docs.m5stack.com/en/unit/m5camera) |
|
||||
| M5Stack UnitCamS3 |   | ESP32-S3-WROOM-1-N16R8 | 520Kb | 16Mb | 8Mb | OV2640 | | [https://docs.m5stack.com/en/unit/Unit-CamS3](https://docs.m5stack.com/en/unit/Unit-CamS3) |
|
||||
| Seeed studio Xiao ESP32S3 Sense |  | ESP32-S3R8 | 520KB | 8Mb | 8MB | OV2640 | Microphone | [https://www.seeedstudio.com/XIAO-ESP32S3-Sense-p-5639.html](https://www.seeedstudio.com/XIAO-ESP32S3-Sense-p-5639.html) |
|
||||
|
||||
## 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).
|
||||
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 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.
|
||||
Install [Visual Studio Code](https://code.visualstudio.com) and install the PlatformIO plugin.
|
||||
|
||||
## Putting the ESP32-CAM in download mode
|
||||
|
||||
@@ -115,16 +152,27 @@ cd esp32cam-rtsp
|
||||
|
||||
Next, the firmware has to be build and deployed to the ESP32.
|
||||
There are to flavours to do this; using the command line or the graphical interface of Visual Studio Code.
|
||||
I recommend to use VIsual Studio Code as it is free to use and offers more insight.
|
||||
|
||||
### Using the command line
|
||||
|
||||
First the source code has to be compiled. Type:
|
||||
Make sure you have the latest version of the Espressif toolchain.
|
||||
|
||||
```sh
|
||||
pio pkg update -g -p espressif32
|
||||
```
|
||||
|
||||
First the source code has to be compiled to build all targets
|
||||
|
||||
```sh
|
||||
pio run
|
||||
```
|
||||
|
||||
if only a specific target is required, for example the ```esp32cam_ttgo_t_journal``` type:
|
||||
|
||||
```sh
|
||||
pio run -e esp32cam_ttgo_t_journal
|
||||
```
|
||||
|
||||
When finished, firmware has to be uploaded.
|
||||
Make sure the ESP32-CAM is in download mode (see previous section) and type:
|
||||
|
||||
@@ -132,6 +180,12 @@ Make sure the ESP32-CAM is in download mode (see previous section) and type:
|
||||
pio run -t upload
|
||||
```
|
||||
|
||||
or, again, for a specific target, for example ```esp32cam_ai_thinker```
|
||||
|
||||
```sh
|
||||
pio run -t upload -e esp32cam_ai_thinker
|
||||
```
|
||||
|
||||
When done remove the jumper when using a FTDI adapter or press the reset button on the ESP32-CAM.
|
||||
To monitor the output, start a terminal using:
|
||||
|
||||
@@ -190,7 +244,16 @@ If this happens, for the user enter 'admin' and for the password the value that
|
||||
RTSP stream is available at: [rtsp://esp32cam-rtsp.local:554/mjpeg/1](rtsp://esp32cam-rtsp.local:554/mjpeg/1).
|
||||
This link can be opened with for example [VLC](https://www.videolan.org/vlc/).
|
||||
|
||||
:warning: **Please be aware that there is no password present on the stream!**
|
||||
## Connecting to the JPEG motion server
|
||||
|
||||
The JPEG motion server server is available using a normal web browser at: [http://esp32cam-rtsp.local:/stream](http://esp32cam-rtsp.local/stream).
|
||||
|
||||
## Connecting to the image server
|
||||
|
||||
The image server server is available using a normal web browser at: [http://esp32cam-rtsp.local:/snapshot](http://esp32cam-rtsp.local/snapshot).
|
||||
|
||||
:bangbang: **Please be aware that there is no password present!**.
|
||||
Everybody with network access to the device can see the streams or images! Beware of :trollface:!
|
||||
|
||||
## API
|
||||
|
||||
@@ -211,16 +274,8 @@ Calling this URL will start the form for configuring the device in the browser.
|
||||
### GET: /snapshot
|
||||
|
||||
Calling this URL will return a JPEG snapshot of the camera in the browser.
|
||||
|
||||
This request can also be used (for example using cURL) to save the snapshot to a file.
|
||||
|
||||
### GET: /flash?v={intensity}
|
||||
|
||||
Calling this URL will set the intensity of the flash LED. Authentication is required.
|
||||
|
||||
The parameter v for the intensity must be between 0 (off) and 255 (max).
|
||||
If no v parameter is present, it will be set to the value of the flash LED intensity from configuration.
|
||||
|
||||
## Issues / Nice to know
|
||||
|
||||
- The red LED on the back of the device indicates the device is not connected.
|
||||
@@ -237,30 +292,82 @@ If no v parameter is present, it will be set to the value of the flash LED inten
|
||||
- When finished configuring for the first time and the access point is entered, disconnect from the wireless network provided by the device.
|
||||
This should reset the device and connect to the access point.
|
||||
Resetting is also a good alternative...
|
||||
- There are modules that have no or faulty PSRAM (despite advertised as such).
|
||||
This can be the case if the camera fails to initialize.
|
||||
It might help to disable the use of the PSRAM and reduce the buffers and the screen size.
|
||||
|
||||
### Power
|
||||
|
||||
Make sure the power is 5 volts and stable, although the ESP32 is a 3.3V module, this voltage is created on the board.
|
||||
If not stable, it has been reported that restarts occur when starting up (probably when power is required for WiFi).
|
||||
The software disables the brown out protection so there is some margin in the voltage.
|
||||
Some people suggest to add a capacitor over the 5V input to stabilize the voltage.
|
||||
|
||||
### PSRAM
|
||||
### PSRAM / Buffers / JPEG quality
|
||||
|
||||
Some esp32cam modules have additional ram on the board. This allows to use this ram as frame buffer.
|
||||
Detecting and using this special RAM is handled automatically.
|
||||
The availability of PSRAM can be seen in the HTML status overview.
|
||||
|
||||
### Camera modules
|
||||
Not all the boards are equipped with PSRAM:
|
||||
|
||||
| Board | PSRAM |
|
||||
|--- |--- |
|
||||
| WROVER_KIT | 8Mb |
|
||||
| ESP_EYE | 8Mb |
|
||||
| ESP32S3_EYE | 8Mb |
|
||||
| M5STACK_PSRAM | 8Mb |
|
||||
| M5STACK_V2_PSRAM | Version B only |
|
||||
| M5STACK_WIDE | 8Mb |
|
||||
| M5STACK_ESP32CAM | No |
|
||||
| M5STACK_UNITCAM | No |
|
||||
| M5STACK_UNITCAMS3 | 8Mb |
|
||||
| AI_THINKER | 8Mb |
|
||||
| TTGO_T_JOURNAL | No |
|
||||
| ESP32_CAM_BOARD | ? |
|
||||
| ESP32S2_CAM_BOARD | ? |
|
||||
| ESP32S3_CAM_LCD | ? |
|
||||
|
||||
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.
|
||||
There are (a lot of?) boards around with faulty PSRAM. If the camera fails to initialize, this might be a reason. See on [Reddit](https://www.reddit.com/r/esp32/comments/z2hyns/i_have_a_faulty_psram_on_my_esp32cam_what_should/).
|
||||
In this case disable the use of PSRAM in the configuration and do not use camera modes that require PSRAM,
|
||||
|
||||
For the setting JPEG quality, a lower number means higher quality.
|
||||
Be aware that a very high quality (low number) can cause the ESP32 cam to crash or return no image.
|
||||
|
||||
The default settings are:
|
||||
|
||||
- No PSRAM
|
||||
- SVGA (800x600)
|
||||
- 1 frame buffer
|
||||
- JPEG quality 12
|
||||
|
||||
- With PSRAM
|
||||
- UXGA (1600x1200)
|
||||
- 2 frame buffers
|
||||
- JPEG quality 10
|
||||
|
||||
### Camera module
|
||||
|
||||
Be careful when connecting the camera module.
|
||||
Make sure it is connected the right way around (Camera pointing away from the board) and the ribbon cable inserted to the end before locking it.
|
||||
|
||||
## Credits
|
||||
|
||||
esp32cam-ready depends on PlatformIO, Bootstrap5 and Micro-RTSP by Kevin Hester.
|
||||
esp32cam-rtsp depends on PlatformIO, Bootstrap 5 and Micro-RTSP by Kevin Hester.
|
||||
|
||||
## Change history
|
||||
|
||||
- January 2024
|
||||
- Moved settings to board definitions
|
||||
- Added new boards
|
||||
- Removed OTA to increase performance
|
||||
- Oktober 2023
|
||||
- Added support for Seeed Xiao esp32s3
|
||||
- New build system
|
||||
- Updated documentation
|
||||
- March 2023
|
||||
- Added options to set PSRAM / Frame buffers
|
||||
- Added JPEG Motion streaming
|
||||
- Feb 2023
|
||||
- Added additional settings for camera configuration
|
||||
- Nov 2022
|
||||
|
||||
|
Before Width: | Height: | Size: 29 KiB |
|
Before Width: | Height: | Size: 152 KiB |
BIN
assets/boards/ai-thinker-esp32-cam-ipex.jpg
Normal file
|
After Width: | Height: | Size: 63 KiB |
BIN
assets/boards/ai-thinker-esp32-cam-mb.jpg
Normal file
|
After Width: | Height: | Size: 30 KiB |
BIN
assets/boards/ai-thinker-esp32-cam.jpg
Normal file
|
After Width: | Height: | Size: 90 KiB |
BIN
assets/boards/ai_thinker_esp32-cam.jpg
Normal file
|
After Width: | Height: | Size: 63 KiB |
BIN
assets/boards/datasheets/ov2640_ds_1.8_.pdf
Normal file
BIN
assets/boards/esp-lyrap-cam-v1.0-3d.png
Normal file
|
After Width: | Height: | Size: 1.6 MiB |
BIN
assets/boards/esp32-wrover-cam.jpg
Normal file
|
After Width: | Height: | Size: 151 KiB |
BIN
assets/boards/espressif-esp-eye.jpg
Normal file
|
After Width: | Height: | Size: 70 KiB |
BIN
assets/boards/espressif-esps3-eye.jpg
Normal file
|
After Width: | Height: | Size: 24 KiB |
BIN
assets/boards/lilygo-camera-module.jpg
Normal file
|
After Width: | Height: | Size: 84 KiB |
BIN
assets/boards/lilygo-simcam.jpg
Normal file
|
After Width: | Height: | Size: 35 KiB |
BIN
assets/boards/lilygo-ttgo-t-camera.jpg
Normal file
|
After Width: | Height: | Size: 141 KiB |
BIN
assets/boards/m5stack-esp32-camera.jpg
Normal file
|
After Width: | Height: | Size: 180 KiB |
BIN
assets/boards/m5stack_Unitcams3.webp
Normal file
|
After Width: | Height: | Size: 14 KiB |
BIN
assets/boards/m5stack_Unitcams32.webp
Normal file
|
After Width: | Height: | Size: 28 KiB |
BIN
assets/boards/m5stack_Unitcams33webp.webp
Normal file
|
After Width: | Height: | Size: 19 KiB |
BIN
assets/boards/m5stack_esp32cam_02.webp
Normal file
|
After Width: | Height: | Size: 71 KiB |
BIN
assets/boards/m5stack_unit_cam_02.webp
Normal file
|
After Width: | Height: | Size: 79 KiB |
BIN
assets/boards/m5stack_unit_cam_03.webp
Normal file
|
After Width: | Height: | Size: 75 KiB |
BIN
assets/boards/schematics/ESP-LyraP-CAM_V1.1_SCH_20200511A.pdf
Normal file
BIN
assets/boards/schematics/UnitCamS3.png
Normal file
|
After Width: | Height: | Size: 432 KiB |
BIN
assets/boards/schematics/XIAO_ESP32S3_ExpBoard_v1.0_SCH.pdf
Normal file
BIN
assets/boards/schematics/ai_thinker_esp32cam.pdf
Normal file
BIN
assets/boards/schematics/m5stack_unit_cam_sch_01.webp
Normal file
|
After Width: | Height: | Size: 92 KiB |
BIN
assets/boards/seeed-studio-xiao-esp32s3-sense.jpg
Normal file
|
After Width: | Height: | Size: 156 KiB |
66
boards/esp32cam_ai_thinker.json
Normal file
@@ -0,0 +1,66 @@
|
||||
{
|
||||
"build": {
|
||||
"arduino":{
|
||||
"ldscript": "esp32_out.ld",
|
||||
"partitions": "huge_app.csv"
|
||||
},
|
||||
"core": "esp32",
|
||||
"extra_flags": [
|
||||
"'-D ESP32CAM_AI_THINKER'",
|
||||
"'-D BOARD_HAS_PSRAM'",
|
||||
"'-mfix-esp32-psram-cache-issue'",
|
||||
"'-D USER_LED_GPIO=33'",
|
||||
"'-D USER_LED_ON_LEVEL=LOW'",
|
||||
"'-D CAMERA_CONFIG_PIN_PWDN=32'",
|
||||
"'-D CAMERA_CONFIG_PIN_RESET=GPIO_NUM_NC'",
|
||||
"'-D CAMERA_CONFIG_PIN_XCLK=0'",
|
||||
"'-D CAMERA_CONFIG_PIN_SCCB_SDA=26'",
|
||||
"'-D CAMERA_CONFIG_PIN_SCCB_SCL=27'",
|
||||
"'-D CAMERA_CONFIG_PIN_Y9=35'",
|
||||
"'-D CAMERA_CONFIG_PIN_Y8=34'",
|
||||
"'-D CAMERA_CONFIG_PIN_Y7=39'",
|
||||
"'-D CAMERA_CONFIG_PIN_Y6=36'",
|
||||
"'-D CAMERA_CONFIG_PIN_Y5=21'",
|
||||
"'-D CAMERA_CONFIG_PIN_Y4=19'",
|
||||
"'-D CAMERA_CONFIG_PIN_Y3=18'",
|
||||
"'-D CAMERA_CONFIG_PIN_Y2=5'",
|
||||
"'-D CAMERA_CONFIG_PIN_VSYNC=25'",
|
||||
"'-D CAMERA_CONFIG_PIN_HREF=23'",
|
||||
"'-D CAMERA_CONFIG_PIN_PCLK=22'",
|
||||
"'-D CAMERA_CONFIG_CLK_FREQ_HZ=20000000'",
|
||||
"'-D CAMERA_CONFIG_LEDC_TIMER=LEDC_TIMER_0'",
|
||||
"'-D CAMERA_CONFIG_LEDC_CHANNEL=LEDC_CHANNEL_0'",
|
||||
"'-D CAMERA_CONFIG_FB_COUNT=2'",
|
||||
"'-D CAMERA_CONFIG_FB_LOCATION=CAMERA_FB_IN_PSRAM'",
|
||||
"'-D CAMERA_CONFIG_SCCB_I2C_PORT=I2C_NUM_0'"
|
||||
],
|
||||
"f_cpu": "240000000L",
|
||||
"f_flash": "40000000L",
|
||||
"flash_mode": "dio",
|
||||
"mcu": "esp32",
|
||||
"variant": "esp32"
|
||||
},
|
||||
"connectivity": [
|
||||
"wifi",
|
||||
"bluetooth",
|
||||
"ethernet",
|
||||
"can"
|
||||
],
|
||||
"debug": {
|
||||
"openocd_board": "esp-wroom-32.cfg"
|
||||
},
|
||||
"frameworks": [
|
||||
"arduino",
|
||||
"espidf"
|
||||
],
|
||||
"name": "ESP32CAM AI Thinker",
|
||||
"upload": {
|
||||
"flash_size": "4MB",
|
||||
"maximum_ram_size": 327680,
|
||||
"maximum_size": 4194304,
|
||||
"require_upload_port": true,
|
||||
"speed": 460800
|
||||
},
|
||||
"url": "https://docs.ai-thinker.com/esp32-cam",
|
||||
"vendor": "Anxinke"
|
||||
}
|
||||
63
boards/esp32cam_espressif_esp32s2_cam_board.json
Normal file
@@ -0,0 +1,63 @@
|
||||
{
|
||||
"build": {
|
||||
"arduino": {
|
||||
"ldscript": "esp32s2_out.ld"
|
||||
},
|
||||
"core": "esp32",
|
||||
"extra_flags": [
|
||||
"'-D ESP32CAM_ESPRESSIF_ESP32S2_CAM_BOARD'",
|
||||
"'-D BOARD_HAS_PSRAM'",
|
||||
"'-D ARDUINO_USB_MODE=0'",
|
||||
"'-D ARDUINO_USB_CDC_ON_BOOT=1'",
|
||||
"'-D ARDUINO_RUNNING_CORE=1'",
|
||||
"'-D ARDUINO_EVENT_RUNNING_CORE=1'",
|
||||
"'-D CAMERA_CONFIG_PIN_PWDN=1'",
|
||||
"'-D CAMERA_CONFIG_PIN_RESET=2'",
|
||||
"'-D CAMERA_CONFIG_PIN_XCLK=42'",
|
||||
"'-D CAMERA_CONFIG_PIN_SCCB_SDA=41'",
|
||||
"'-D CAMERA_CONFIG_PIN_SCCB_SCL=18'",
|
||||
"'-D CAMERA_CONFIG_PIN_Y9=16'",
|
||||
"'-D CAMERA_CONFIG_PIN_Y8=39'",
|
||||
"'-D CAMERA_CONFIG_PIN_Y7=40'",
|
||||
"'-D CAMERA_CONFIG_PIN_Y6=15'",
|
||||
"'-D CAMERA_CONFIG_PIN_Y5=12'",
|
||||
"'-D CAMERA_CONFIG_PIN_Y4=5'",
|
||||
"'-D CAMERA_CONFIG_PIN_Y3=13'",
|
||||
"'-D CAMERA_CONFIG_PIN_Y2=14'",
|
||||
"'-D CAMERA_CONFIG_PIN_VSYNC=38'",
|
||||
"'-D CAMERA_CONFIG_PIN_HREF=4'",
|
||||
"'-D CAMERA_CONFIG_PIN_PCLK=3'",
|
||||
"'-D CAMERA_CONFIG_CLK_FREQ_HZ=20000000'",
|
||||
"'-D CAMERA_CONFIG_LEDC_TIMER=LEDC_TIMER_0'",
|
||||
"'-D CAMERA_CONFIG_LEDC_CHANNEL=LEDC_CHANNEL_0'",
|
||||
"'-D CAMERA_CONFIG_FB_COUNT=2'",
|
||||
"'-D CAMERA_CONFIG_FB_LOCATION=CAMERA_FB_IN_PSRAM'",
|
||||
"'-D CAMERA_CONFIG_SCCB_I2C_PORT=I2C_NUM_0'"
|
||||
],
|
||||
"f_cpu": "240000000L",
|
||||
"f_flash": "80000000L",
|
||||
"flash_mode": "qio",
|
||||
"mcu": "esp32s2",
|
||||
"variant": "esp32s2"
|
||||
},
|
||||
"connectivity": [
|
||||
"wifi"
|
||||
],
|
||||
"debug": {
|
||||
"openocd_target": "esp32s2.cfg"
|
||||
},
|
||||
"frameworks": [
|
||||
"arduino",
|
||||
"espidf"
|
||||
],
|
||||
"name": "Espressif ESP32-S2-Saola-1",
|
||||
"upload": {
|
||||
"flash_size": "4MB",
|
||||
"maximum_ram_size": 327680,
|
||||
"maximum_size": 4194304,
|
||||
"require_upload_port": true,
|
||||
"speed": 460800
|
||||
},
|
||||
"url": "https://docs.espressif.com/projects/esp-idf/en/latest/esp32s2/hw-reference/esp32s2/user-guide-esp-lyrap-cam-v1.1.html",
|
||||
"vendor": "Espressif"
|
||||
}
|
||||
63
boards/esp32cam_espressif_esp32s2_cam_header.json
Normal file
@@ -0,0 +1,63 @@
|
||||
{
|
||||
"build": {
|
||||
"arduino": {
|
||||
"ldscript": "esp32s2_out.ld"
|
||||
},
|
||||
"core": "esp32",
|
||||
"extra_flags": [
|
||||
"'-D ESP32CAM_ESPRESSIF_ESP32S2_CAM_BOARD'",
|
||||
"'-D BOARD_HAS_PSRAM'",
|
||||
"'-D ARDUINO_USB_MODE=0'",
|
||||
"'-D ARDUINO_USB_CDC_ON_BOOT=1'",
|
||||
"'-D ARDUINO_RUNNING_CORE=1'",
|
||||
"'-D ARDUINO_EVENT_RUNNING_CORE=1'",
|
||||
"'-D CAMERA_CONFIG_PIN_PWDN=1'",
|
||||
"'-D CAMERA_CONFIG_PIN_RESET=2'",
|
||||
"'-D CAMERA_CONFIG_PIN_XCLK=42'",
|
||||
"'-D CAMERA_CONFIG_PIN_SCCB_SDA=41'",
|
||||
"'-D CAMERA_CONFIG_PIN_SCCB_SCL=18'",
|
||||
"'-D CAMERA_CONFIG_PIN_Y9=16'",
|
||||
"'-D CAMERA_CONFIG_PIN_Y8=39'",
|
||||
"'-D CAMERA_CONFIG_PIN_Y7=40'",
|
||||
"'-D CAMERA_CONFIG_PIN_Y6=15'",
|
||||
"'-D CAMERA_CONFIG_PIN_Y5=13'",
|
||||
"'-D CAMERA_CONFIG_PIN_Y4=5'",
|
||||
"'-D CAMERA_CONFIG_PIN_Y3=12'",
|
||||
"'-D CAMERA_CONFIG_PIN_Y2=14'",
|
||||
"'-D CAMERA_CONFIG_PIN_VSYNC=38'",
|
||||
"'-D CAMERA_CONFIG_PIN_HREF=4'",
|
||||
"'-D CAMERA_CONFIG_PIN_PCLK=3'",
|
||||
"'-D CAMERA_CONFIG_CLK_FREQ_HZ=20000000'",
|
||||
"'-D CAMERA_CONFIG_LEDC_TIMER=LEDC_TIMER_0'",
|
||||
"'-D CAMERA_CONFIG_LEDC_CHANNEL=LEDC_CHANNEL_0'",
|
||||
"'-D CAMERA_CONFIG_FB_COUNT=2'",
|
||||
"'-D CAMERA_CONFIG_FB_LOCATION=CAMERA_FB_IN_PSRAM'",
|
||||
"'-D CAMERA_CONFIG_SCCB_I2C_PORT=I2C_NUM_0'"
|
||||
],
|
||||
"f_cpu": "240000000L",
|
||||
"f_flash": "80000000L",
|
||||
"flash_mode": "qio",
|
||||
"mcu": "esp32s2",
|
||||
"variant": "esp32s2"
|
||||
},
|
||||
"connectivity": [
|
||||
"wifi"
|
||||
],
|
||||
"debug": {
|
||||
"openocd_target": "esp32s2.cfg"
|
||||
},
|
||||
"frameworks": [
|
||||
"arduino",
|
||||
"espidf"
|
||||
],
|
||||
"name": "Espressif ESP32-S2-Saola-1",
|
||||
"upload": {
|
||||
"flash_size": "4MB",
|
||||
"maximum_ram_size": 327680,
|
||||
"maximum_size": 4194304,
|
||||
"require_upload_port": true,
|
||||
"speed": 460800
|
||||
},
|
||||
"url": "https://docs.espressif.com/projects/esp-idf/en/latest/esp32s2/hw-reference/esp32s2/user-guide-esp-lyrap-cam-v1.1.html",
|
||||
"vendor": "Espressif"
|
||||
}
|
||||
72
boards/esp32cam_espressif_esp32s3_cam_lcd.json
Normal file
@@ -0,0 +1,72 @@
|
||||
{
|
||||
"build": {
|
||||
"arduino": {
|
||||
"ldscript": "esp32s3_out.ld",
|
||||
"partitions": "default_8MB.csv",
|
||||
"memory_type": "opi_opi"
|
||||
},
|
||||
"core": "esp32",
|
||||
"extra_flags": [
|
||||
"'-D ESP32CAM_ESP32S3_CAM_LCD'",
|
||||
"'-D BOARD_HAS_PSRAM'",
|
||||
"'-D ARDUINO_USB_MODE=1'",
|
||||
"'-D ARDUINO_USB_CDC_ON_BOOT=1'",
|
||||
"'-D ARDUINO_RUNNING_CORE=1'",
|
||||
"'-D ARDUINO_EVENT_RUNNING_CORE=1'",
|
||||
"'-D CAMERA_CONFIG_PIN_PWDN=GPIO_NUM_NC'",
|
||||
"'-D CAMERA_CONFIG_PIN_RESET=GPIO_NUM_NC'",
|
||||
"'-D CAMERA_CONFIG_PIN_XCLK=40'",
|
||||
"'-D CAMERA_CONFIG_PIN_SCCB_SDA=17'",
|
||||
"'-D CAMERA_CONFIG_PIN_SCCB_SCL=18'",
|
||||
"'-D CAMERA_CONFIG_PIN_Y9=39'",
|
||||
"'-D CAMERA_CONFIG_PIN_Y8=41'",
|
||||
"'-D CAMERA_CONFIG_PIN_Y7=42'",
|
||||
"'-D CAMERA_CONFIG_PIN_Y6=12'",
|
||||
"'-D CAMERA_CONFIG_PIN_Y5=3'",
|
||||
"'-D CAMERA_CONFIG_PIN_Y4=14'",
|
||||
"'-D CAMERA_CONFIG_PIN_Y3=47'",
|
||||
"'-D CAMERA_CONFIG_PIN_Y2=13'",
|
||||
"'-D CAMERA_CONFIG_PIN_VSYNC=21'",
|
||||
"'-D CAMERA_CONFIG_PIN_HREF=38'",
|
||||
"'-D CAMERA_CONFIG_PIN_PCLK=11'",
|
||||
"'-D CAMERA_CONFIG_CLK_FREQ_HZ=20000000'",
|
||||
"'-D CAMERA_CONFIG_LEDC_TIMER=LEDC_TIMER_0'",
|
||||
"'-D CAMERA_CONFIG_LEDC_CHANNEL=LEDC_CHANNEL_0'",
|
||||
"'-D CAMERA_CONFIG_FB_COUNT=2'",
|
||||
"'-D CAMERA_CONFIG_FB_LOCATION=CAMERA_FB_IN_PSRAM'",
|
||||
"'-D CAMERA_CONFIG_SCCB_I2C_PORT=I2C_NUM_0'"
|
||||
],
|
||||
"f_cpu": "240000000L",
|
||||
"f_flash": "80000000L",
|
||||
"flash_mode": "dout",
|
||||
"hwids": [
|
||||
[
|
||||
"0X303A",
|
||||
"0x1001"
|
||||
]
|
||||
],
|
||||
"mcu": "esp32s3",
|
||||
"variant": "esp32s3camlcd"
|
||||
},
|
||||
"connectivity": [
|
||||
"bluetooth",
|
||||
"wifi"
|
||||
],
|
||||
"debug": {
|
||||
"openocd_target": "esp32s3.cfg"
|
||||
},
|
||||
"frameworks": [
|
||||
"arduino",
|
||||
"espidf"
|
||||
],
|
||||
"name": "ESP32S3-CAM LCD",
|
||||
"upload": {
|
||||
"flash_size": "8MB",
|
||||
"maximum_ram_size": 327680,
|
||||
"maximum_size": 8388608,
|
||||
"require_upload_port": true,
|
||||
"speed": 460800
|
||||
},
|
||||
"url": "https://www.espressif.com/en/news/Maple_Eye_ESP32-S3",
|
||||
"vendor": "Espressif"
|
||||
}
|
||||
76
boards/esp32cam_espressif_esp32s3_eye.json
Normal file
@@ -0,0 +1,76 @@
|
||||
{
|
||||
"build": {
|
||||
"arduino": {
|
||||
"ldscript": "esp32s3_out.ld",
|
||||
"partitions": "default_8MB.csv",
|
||||
"memory_type": "qio_opi"
|
||||
},
|
||||
"core": "esp32",
|
||||
"extra_flags": [
|
||||
"'-D ESP32CAM_ESPRESSIF_ESP32S3_EYE'",
|
||||
"'-D BOARD_HAS_PSRAM'",
|
||||
"'-D ARDUINO_USB_MODE=1'",
|
||||
"'-D ARDUINO_USB_CDC_ON_BOOT=1'",
|
||||
"'-D ARDUINO_RUNNING_CORE=1'",
|
||||
"'-D ARDUINO_EVENT_RUNNING_CORE=1'",
|
||||
"'-D CAMERA_CONFIG_PIN_PWDN=GPIO_NUM_NC'",
|
||||
"'-D CAMERA_CONFIG_PIN_RESET=GPIO_NUM_NC'",
|
||||
"'-D CAMERA_CONFIG_PIN_XCLK=15'",
|
||||
"'-D CAMERA_CONFIG_PIN_SCCB_SDA=4'",
|
||||
"'-D CAMERA_CONFIG_PIN_SCCB_SCL=5'",
|
||||
"'-D CAMERA_CONFIG_PIN_Y9=16'",
|
||||
"'-D CAMERA_CONFIG_PIN_Y8=17'",
|
||||
"'-D CAMERA_CONFIG_PIN_Y7=18'",
|
||||
"'-D CAMERA_CONFIG_PIN_Y6=12'",
|
||||
"'-D CAMERA_CONFIG_PIN_Y5=10'",
|
||||
"'-D CAMERA_CONFIG_PIN_Y4=8'",
|
||||
"'-D CAMERA_CONFIG_PIN_Y3=9'",
|
||||
"'-D CAMERA_CONFIG_PIN_Y2=11'",
|
||||
"'-D CAMERA_CONFIG_PIN_VSYNC=6'",
|
||||
"'-D CAMERA_CONFIG_PIN_HREF=7'",
|
||||
"'-D CAMERA_CONFIG_PIN_PCLK=13'",
|
||||
"'-D CAMERA_CONFIG_CLK_FREQ_HZ=20000000'",
|
||||
"'-D CAMERA_CONFIG_LEDC_TIMER=LEDC_TIMER_0'",
|
||||
"'-D CAMERA_CONFIG_LEDC_CHANNEL=LEDC_CHANNEL_0'",
|
||||
"'-D CAMERA_CONFIG_FB_COUNT=2'",
|
||||
"'-D CAMERA_CONFIG_FB_LOCATION=CAMERA_FB_IN_PSRAM'",
|
||||
"'-D CAMERA_CONFIG_SCCB_I2C_PORT=I2C_NUM_0'"
|
||||
],
|
||||
"f_cpu": "240000000L",
|
||||
"f_flash": "80000000L",
|
||||
"flash_mode": "qio",
|
||||
"hwids": [
|
||||
[
|
||||
"0x2886",
|
||||
"0x0056"
|
||||
],
|
||||
[
|
||||
"0x2886",
|
||||
"0x8056"
|
||||
]
|
||||
],
|
||||
"mcu": "esp32s3",
|
||||
"variant": "esp32s3"
|
||||
},
|
||||
"connectivity": [
|
||||
"bluetooth",
|
||||
"wifi"
|
||||
],
|
||||
"debug": {
|
||||
"openocd_target": "esp32s3.cfg"
|
||||
},
|
||||
"frameworks": [
|
||||
"arduino",
|
||||
"espidf"
|
||||
],
|
||||
"name": "ESP32S3_EYE",
|
||||
"upload": {
|
||||
"flash_size": "8MB",
|
||||
"maximum_ram_size": 327680,
|
||||
"maximum_size": 8388608,
|
||||
"require_upload_port": true,
|
||||
"speed": 460800
|
||||
},
|
||||
"url": "https://www.espressif.com/en/products/devkits/esp-eye/overview",
|
||||
"vendor": "Espressif"
|
||||
}
|
||||
66
boards/esp32cam_espressif_esp_eye.json
Normal file
@@ -0,0 +1,66 @@
|
||||
{
|
||||
"build": {
|
||||
"arduino": {
|
||||
"ldscript": "esp32_out.ld",
|
||||
"partitions": "huge_app.csv"
|
||||
},
|
||||
"core": "esp32",
|
||||
"extra_flags": [
|
||||
"'-D ESP32CAM_ESPRESSIF_ESP_EYE'",
|
||||
"'-D BOARD_HAS_PSRAM'",
|
||||
"'-mfix-esp32-psram-cache-issue'",
|
||||
"'-D USER_LED_GPIO=14'",
|
||||
"'-D USER_LED_ON_LEVEL=HIGH'",
|
||||
"'-D CAMERA_CONFIG_PIN_PWDN=GPIO_NUM_NC'",
|
||||
"'-D CAMERA_CONFIG_PIN_RESET=GPIO_NUM_NC'",
|
||||
"'-D CAMERA_CONFIG_PIN_XCLK=11'",
|
||||
"'-D CAMERA_CONFIG_PIN_SCCB_SDA=17'",
|
||||
"'-D CAMERA_CONFIG_PIN_SCCB_SCL=41'",
|
||||
"'-D CAMERA_CONFIG_PIN_Y9=13'",
|
||||
"'-D CAMERA_CONFIG_PIN_Y8=4'",
|
||||
"'-D CAMERA_CONFIG_PIN_Y7=10'",
|
||||
"'-D CAMERA_CONFIG_PIN_Y6=5'",
|
||||
"'-D CAMERA_CONFIG_PIN_Y5=7'",
|
||||
"'-D CAMERA_CONFIG_PIN_Y4=16'",
|
||||
"'-D CAMERA_CONFIG_PIN_Y3=15'",
|
||||
"'-D CAMERA_CONFIG_PIN_Y2=6'",
|
||||
"'-D CAMERA_CONFIG_PIN_VSYNC=42'",
|
||||
"'-D CAMERA_CONFIG_PIN_HREF=18'",
|
||||
"'-D CAMERA_CONFIG_PIN_PCLK=12'",
|
||||
"'-D CAMERA_CONFIG_CLK_FREQ_HZ=20000000'",
|
||||
"'-D CAMERA_CONFIG_LEDC_TIMER=LEDC_TIMER_0'",
|
||||
"'-D CAMERA_CONFIG_LEDC_CHANNEL=LEDC_CHANNEL_0'",
|
||||
"'-D CAMERA_CONFIG_FB_COUNT=1'",
|
||||
"'-D CAMERA_CONFIG_FB_LOCATION=CAMERA_FB_IN_PSRAM'",
|
||||
"'-D CAMERA_CONFIG_SCCB_I2C_PORT=I2C_NUM_0'"
|
||||
],
|
||||
"f_cpu": "240000000L",
|
||||
"f_flash": "40000000L",
|
||||
"flash_mode": "dio",
|
||||
"mcu": "esp32",
|
||||
"variant": "esp32"
|
||||
},
|
||||
"connectivity": [
|
||||
"wifi",
|
||||
"bluetooth",
|
||||
"ethernet",
|
||||
"can"
|
||||
],
|
||||
"debug": {
|
||||
"openocd_board": "esp-wroom-32.cfg"
|
||||
},
|
||||
"frameworks": [
|
||||
"arduino",
|
||||
"espidf"
|
||||
],
|
||||
"name": "ESP32-CAM AI Thinker",
|
||||
"upload": {
|
||||
"flash_size": "4MB",
|
||||
"maximum_ram_size": 327680,
|
||||
"maximum_size": 4194304,
|
||||
"require_upload_port": true,
|
||||
"speed": 460800
|
||||
},
|
||||
"url": "https://www.espressif.com/en/products/devkits/esp-eye/overview",
|
||||
"vendor": "Espressif"
|
||||
}
|
||||
70
boards/esp32cam_freenove_s3_wroom_n8r8.json
Normal file
@@ -0,0 +1,70 @@
|
||||
{
|
||||
"build": {
|
||||
"arduino": {
|
||||
"ldscript": "esp32s3_out.ld",
|
||||
"partitions": "default_8MB.csv",
|
||||
"memory_type": "qio_opi"
|
||||
},
|
||||
"core": "esp32",
|
||||
"extra_flags": [
|
||||
"'-D ARDUINO_ESP32S3_DEV'",
|
||||
"'-D ARDUINO_USB_MODE=1'",
|
||||
"'-D BOARD_HAS_PSRAM'",
|
||||
"'-D ARDUINO_RUNNING_CORE=1'",
|
||||
"'-D ARDUINO_EVENT_RUNNING_CORE=1'",
|
||||
"'-D ARDUINO_USB_CDC_ON_BOOT=1'",
|
||||
"'-D CAMERA_CONFIG_PIN_PWDN=GPIO_NUM_NC'",
|
||||
"'-D CAMERA_CONFIG_PIN_RESET=GPIO_NUM_NC'",
|
||||
"'-D CAMERA_CONFIG_PIN_XCLK=15'",
|
||||
"'-D CAMERA_CONFIG_PIN_SCCB_SDA=4'",
|
||||
"'-D CAMERA_CONFIG_PIN_SCCB_SCL=5'",
|
||||
"'-D CAMERA_CONFIG_PIN_Y9=16'",
|
||||
"'-D CAMERA_CONFIG_PIN_Y8=17'",
|
||||
"'-D CAMERA_CONFIG_PIN_Y7=18'",
|
||||
"'-D CAMERA_CONFIG_PIN_Y6=12'",
|
||||
"'-D CAMERA_CONFIG_PIN_Y5=10'",
|
||||
"'-D CAMERA_CONFIG_PIN_Y4=8'",
|
||||
"'-D CAMERA_CONFIG_PIN_Y3=9'",
|
||||
"'-D CAMERA_CONFIG_PIN_Y2=11'",
|
||||
"'-D CAMERA_CONFIG_PIN_VSYNC=6'",
|
||||
"'-D CAMERA_CONFIG_PIN_HREF=7'",
|
||||
"'-D CAMERA_CONFIG_PIN_PCLK=13'",
|
||||
"'-D CAMERA_CONFIG_CLK_FREQ_HZ=20000000'",
|
||||
"'-D CAMERA_CONFIG_LEDC_TIMER=LEDC_TIMER_0'",
|
||||
"'-D CAMERA_CONFIG_LEDC_CHANNEL=LEDC_CHANNEL_0'",
|
||||
"'-D CAMERA_CONFIG_FB_COUNT=2'",
|
||||
"'-D CAMERA_CONFIG_FB_LOCATION=CAMERA_FB_IN_PSRAM'"
|
||||
],
|
||||
"f_cpu": "240000000L",
|
||||
"f_flash": "80000000L",
|
||||
"flash_mode": "dio",
|
||||
"hwids": [
|
||||
[
|
||||
"0x303A",
|
||||
"0x1001"
|
||||
]
|
||||
],
|
||||
"mcu": "esp32s3",
|
||||
"variant": "esp32s3"
|
||||
},
|
||||
"connectivity": [
|
||||
"wifi"
|
||||
],
|
||||
"debug": {
|
||||
"openocd_target": "esp32s3.cfg"
|
||||
},
|
||||
"frameworks": [
|
||||
"arduino",
|
||||
"espidf"
|
||||
],
|
||||
"name": "ESP32 FREENOVE S3 WROOM",
|
||||
"upload": {
|
||||
"flash_size": "4MB",
|
||||
"maximum_ram_size": 327680,
|
||||
"maximum_size": 4194304,
|
||||
"require_upload_port": true,
|
||||
"speed": 460800
|
||||
},
|
||||
"url": "https://github.com/Freenove/Freenove_ESP32_S3_WROOM_Board",
|
||||
"vendor": "Freenove"
|
||||
}
|
||||
66
boards/esp32cam_freenove_wrover_kit.json
Normal file
@@ -0,0 +1,66 @@
|
||||
{
|
||||
"build": {
|
||||
"arduino":{
|
||||
"ldscript": "esp32_out.ld",
|
||||
"partitions": "huge_app.csv"
|
||||
},
|
||||
"core": "esp32",
|
||||
"extra_flags": [
|
||||
"'-D ESP32CAM_WROVER_KIT'",
|
||||
"'-D BOARD_HAS_PSRAM'",
|
||||
"'-mfix-esp32-psram-cache-issue'",
|
||||
"'-D USER_LED_GPIO=2'",
|
||||
"'-D USER_LED_ON_LEVEL=HIGH'",
|
||||
"'-D CAMERA_CONFIG_PIN_PWDN=GPIO_NUM_NC'",
|
||||
"'-D CAMERA_CONFIG_PIN_RESET=GPIO_NUM_NC'",
|
||||
"'-D CAMERA_CONFIG_PIN_XCLK=21'",
|
||||
"'-D CAMERA_CONFIG_PIN_SCCB_SDA=26'",
|
||||
"'-D CAMERA_CONFIG_PIN_SCCB_SCL=27'",
|
||||
"'-D CAMERA_CONFIG_PIN_Y9=35'",
|
||||
"'-D CAMERA_CONFIG_PIN_Y8=34'",
|
||||
"'-D CAMERA_CONFIG_PIN_Y7=39'",
|
||||
"'-D CAMERA_CONFIG_PIN_Y6=36'",
|
||||
"'-D CAMERA_CONFIG_PIN_Y5=19'",
|
||||
"'-D CAMERA_CONFIG_PIN_Y4=18'",
|
||||
"'-D CAMERA_CONFIG_PIN_Y3=5'",
|
||||
"'-D CAMERA_CONFIG_PIN_Y2=4'",
|
||||
"'-D CAMERA_CONFIG_PIN_VSYNC=25'",
|
||||
"'-D CAMERA_CONFIG_PIN_HREF=23'",
|
||||
"'-D CAMERA_CONFIG_PIN_PCLK=22'",
|
||||
"'-D CAMERA_CONFIG_CLK_FREQ_HZ=20000000'",
|
||||
"'-D CAMERA_CONFIG_LEDC_TIMER=LEDC_TIMER_0'",
|
||||
"'-D CAMERA_CONFIG_LEDC_CHANNEL=LEDC_CHANNEL_0'",
|
||||
"'-D CAMERA_CONFIG_FB_COUNT=2'",
|
||||
"'-D CAMERA_CONFIG_FB_LOCATION=CAMERA_FB_IN_PSRAM'",
|
||||
"'-D CAMERA_CONFIG_SCCB_I2C_PORT=I2C_NUM_0'"
|
||||
],
|
||||
"f_cpu": "240000000L",
|
||||
"f_flash": "40000000L",
|
||||
"flash_mode": "dio",
|
||||
"mcu": "esp32",
|
||||
"variant": "esp32"
|
||||
},
|
||||
"connectivity": [
|
||||
"wifi",
|
||||
"bluetooth",
|
||||
"ethernet",
|
||||
"can"
|
||||
],
|
||||
"debug": {
|
||||
"openocd_board": "esp-wroom-32.cfg"
|
||||
},
|
||||
"frameworks": [
|
||||
"arduino",
|
||||
"espidf"
|
||||
],
|
||||
"name": "ESP32-CAM WROVER kit",
|
||||
"upload": {
|
||||
"flash_size": "4MB",
|
||||
"maximum_ram_size": 327680,
|
||||
"maximum_size": 4194304,
|
||||
"require_upload_port": true,
|
||||
"speed": 460800
|
||||
},
|
||||
"url": "https://www.aliexpress.com/item/1005004960637276.html",
|
||||
"vendor": "Freenove"
|
||||
}
|
||||
64
boards/esp32cam_m5stack_camera.json
Normal file
@@ -0,0 +1,64 @@
|
||||
{
|
||||
"build": {
|
||||
"arduino": {
|
||||
"ldscript": "esp32_out.ld",
|
||||
"partitions": "huge_app.csv"
|
||||
},
|
||||
"core": "esp32",
|
||||
"extra_flags": [
|
||||
"'-D ESP32CAM_M5STACK'",
|
||||
"'-D CAMERA_CONFIG_PIN_PWDN=GPIO_NUM_NC'",
|
||||
"'-D CAMERA_CONFIG_PIN_RESET=15'",
|
||||
"'-D CAMERA_CONFIG_PIN_XCLK=27'",
|
||||
"'-D CAMERA_CONFIG_PIN_SCCB_SDA=25'",
|
||||
"'-D CAMERA_CONFIG_PIN_SCCB_SCL=23'",
|
||||
"'-D CAMERA_CONFIG_PIN_Y9=19'",
|
||||
"'-D CAMERA_CONFIG_PIN_Y8=36'",
|
||||
"'-D CAMERA_CONFIG_PIN_Y7=18'",
|
||||
"'-D CAMERA_CONFIG_PIN_Y6=39'",
|
||||
"'-D CAMERA_CONFIG_PIN_Y5=5'",
|
||||
"'-D CAMERA_CONFIG_PIN_Y4=34'",
|
||||
"'-D CAMERA_CONFIG_PIN_Y3=35'",
|
||||
"'-D CAMERA_CONFIG_PIN_Y2=32'",
|
||||
"'-D CAMERA_CONFIG_PIN_VSYNC=25'",
|
||||
"'-D CAMERA_CONFIG_PIN_HREF=26'",
|
||||
"'-D CAMERA_CONFIG_PIN_PCLK=21'",
|
||||
"'-D CAMERA_CONFIG_CLK_FREQ_HZ=20000000'",
|
||||
"'-D CAMERA_CONFIG_LEDC_TIMER=LEDC_TIMER_0'",
|
||||
"'-D CAMERA_CONFIG_LEDC_CHANNEL=LEDC_CHANNEL_0'",
|
||||
"'-D CAMERA_CONFIG_FB_COUNT=1'",
|
||||
"'-D CAMERA_CONFIG_FB_LOCATION=CAMERA_FB_IN_DRAM'",
|
||||
"'-D CAMERA_CONFIG_SCCB_I2C_PORT=I2C_NUM_0'",
|
||||
"'-D GROVE_SDA=13'",
|
||||
"'-D GROVE_SCL=4'"
|
||||
],
|
||||
"f_cpu": "240000000L",
|
||||
"f_flash": "40000000L",
|
||||
"flash_mode": "dio",
|
||||
"mcu": "esp32",
|
||||
"variant": "esp32"
|
||||
},
|
||||
"connectivity": [
|
||||
"wifi",
|
||||
"bluetooth",
|
||||
"ethernet",
|
||||
"can"
|
||||
],
|
||||
"debug": {
|
||||
"openocd_board": "esp-wroom-32.cfg"
|
||||
},
|
||||
"frameworks": [
|
||||
"arduino",
|
||||
"espidf"
|
||||
],
|
||||
"name": "ESP32-CAM M5 STACK",
|
||||
"upload": {
|
||||
"flash_size": "4MB",
|
||||
"maximum_ram_size": 327680,
|
||||
"maximum_size": 4194304,
|
||||
"require_upload_port": true,
|
||||
"speed": 460800
|
||||
},
|
||||
"url": "https://docs.m5stack.com/en/unit/m5camera",
|
||||
"vendor": "M5STACK"
|
||||
}
|
||||
66
boards/esp32cam_m5stack_camera_psram.json
Normal file
@@ -0,0 +1,66 @@
|
||||
{
|
||||
"build": {
|
||||
"arduino":{
|
||||
"ldscript": "esp32_out.ld",
|
||||
"partitions": "huge_app.csv"
|
||||
},
|
||||
"core": "esp32",
|
||||
"extra_flags": [
|
||||
"'-D ESP32CAM_M5STACK_CAMERA_PSRAM'",
|
||||
"'-D BOARD_HAS_PSRAM'",
|
||||
"'-mfix-esp32-psram-cache-issue'",
|
||||
"'-D CAMERA_CONFIG_PIN_PWDN=GPIO_NUM_NC'",
|
||||
"'-D CAMERA_CONFIG_PIN_RESET=15'",
|
||||
"'-D CAMERA_CONFIG_PIN_XCLK=27'",
|
||||
"'-D CAMERA_CONFIG_PIN_SCCB_SDA=25'",
|
||||
"'-D CAMERA_CONFIG_PIN_SCCB_SCL=23'",
|
||||
"'-D CAMERA_CONFIG_PIN_Y9=19'",
|
||||
"'-D CAMERA_CONFIG_PIN_Y8=36'",
|
||||
"'-D CAMERA_CONFIG_PIN_Y7=18'",
|
||||
"'-D CAMERA_CONFIG_PIN_Y6=39'",
|
||||
"'-D CAMERA_CONFIG_PIN_Y5=5'",
|
||||
"'-D CAMERA_CONFIG_PIN_Y4=34'",
|
||||
"'-D CAMERA_CONFIG_PIN_Y3=35'",
|
||||
"'-D CAMERA_CONFIG_PIN_Y2=32'",
|
||||
"'-D CAMERA_CONFIG_PIN_VSYNC=22'",
|
||||
"'-D CAMERA_CONFIG_PIN_HREF=26'",
|
||||
"'-D CAMERA_CONFIG_PIN_PCLK=21'",
|
||||
"'-D CAMERA_CONFIG_CLK_FREQ_HZ=20000000'",
|
||||
"'-D CAMERA_CONFIG_LEDC_TIMER=LEDC_TIMER_0'",
|
||||
"'-D CAMERA_CONFIG_LEDC_CHANNEL=LEDC_CHANNEL_0'",
|
||||
"'-D CAMERA_CONFIG_FB_COUNT=2'",
|
||||
"'-D CAMERA_CONFIG_FB_LOCATION=CAMERA_FB_IN_PSRAM'",
|
||||
"'-D CAMERA_CONFIG_SCCB_I2C_PORT=I2C_NUM_0'",
|
||||
"'-D GROVE_SDA=13'",
|
||||
"'-D GROVE_SCL=4'"
|
||||
],
|
||||
"f_cpu": "240000000L",
|
||||
"f_flash": "40000000L",
|
||||
"flash_mode": "dio",
|
||||
"mcu": "esp32",
|
||||
"variant": "esp32"
|
||||
},
|
||||
"connectivity": [
|
||||
"wifi",
|
||||
"bluetooth",
|
||||
"ethernet",
|
||||
"can"
|
||||
],
|
||||
"debug": {
|
||||
"openocd_board": "esp-wroom-32.cfg"
|
||||
},
|
||||
"frameworks": [
|
||||
"arduino",
|
||||
"espidf"
|
||||
],
|
||||
"name": "ESP32-CAM M5 STACK",
|
||||
"upload": {
|
||||
"flash_size": "4MB",
|
||||
"maximum_ram_size": 327680,
|
||||
"maximum_size": 4194304,
|
||||
"require_upload_port": true,
|
||||
"speed": 460800
|
||||
},
|
||||
"url": "https://docs.m5stack.com/en/unit/m5camera",
|
||||
"vendor": "M5STACK"
|
||||
}
|
||||
69
boards/esp32cam_m5stack_esp32cam.json
Normal file
@@ -0,0 +1,69 @@
|
||||
{
|
||||
"build": {
|
||||
"arduino": {
|
||||
"ldscript": "esp32_out.ld",
|
||||
"partitions": "huge_app.csv"
|
||||
},
|
||||
"core": "esp32",
|
||||
"extra_flags": [
|
||||
"'-D ESP32CAM_M5STACK_ESP32CAM'",
|
||||
"'-D BOARD_HAS_PSRAM'",
|
||||
"'-mfix-esp32-psram-cache-issue'",
|
||||
"'-D USER_LED_GPIO=16'",
|
||||
"'-D USER_LED_ON_LEVEL=LOW'",
|
||||
"'-D CAMERA_CONFIG_PIN_PWDN=GPIO_NUM_NC'",
|
||||
"'-D CAMERA_CONFIG_PIN_RESET=15'",
|
||||
"'-D CAMERA_CONFIG_PIN_XCLK=27'",
|
||||
"'-D CAMERA_CONFIG_PIN_SCCB_SDA=25'",
|
||||
"'-D CAMERA_CONFIG_PIN_SCCB_SCL=23'",
|
||||
"'-D CAMERA_CONFIG_PIN_Y9=19'",
|
||||
"'-D CAMERA_CONFIG_PIN_Y8=36'",
|
||||
"'-D CAMERA_CONFIG_PIN_Y7=18'",
|
||||
"'-D CAMERA_CONFIG_PIN_Y6=39'",
|
||||
"'-D CAMERA_CONFIG_PIN_Y5=5'",
|
||||
"'-D CAMERA_CONFIG_PIN_Y4=34'",
|
||||
"'-D CAMERA_CONFIG_PIN_Y3=35'",
|
||||
"'-D CAMERA_CONFIG_PIN_Y2=17'",
|
||||
"'-D CAMERA_CONFIG_PIN_VSYNC=22'",
|
||||
"'-D CAMERA_CONFIG_PIN_HREF=26'",
|
||||
"'-D CAMERA_CONFIG_PIN_PCLK=21'",
|
||||
"'-D CAMERA_CONFIG_CLK_FREQ_HZ=20000000'",
|
||||
"'-D CAMERA_CONFIG_LEDC_TIMER=LEDC_TIMER_0'",
|
||||
"'-D CAMERA_CONFIG_LEDC_CHANNEL=LEDC_CHANNEL_0'",
|
||||
"'-D CAMERA_CONFIG_FB_COUNT=2'",
|
||||
"'-D CAMERA_CONFIG_FB_LOCATION=CAMERA_FB_IN_PSRAM'",
|
||||
"'-D CAMERA_CONFIG_SCCB_I2C_PORT=I2C_NUM_0'",
|
||||
"'-D MICROPHONE_GPIO=32'",
|
||||
"'-D GROVE_SDA=13'",
|
||||
"'-D GROVE_SCL=4'"
|
||||
],
|
||||
"f_cpu": "240000000L",
|
||||
"f_flash": "40000000L",
|
||||
"flash_mode": "dio",
|
||||
"mcu": "esp32",
|
||||
"variant": "esp32"
|
||||
},
|
||||
"connectivity": [
|
||||
"wifi",
|
||||
"bluetooth",
|
||||
"ethernet",
|
||||
"can"
|
||||
],
|
||||
"debug": {
|
||||
"openocd_board": "esp-wroom-32.cfg"
|
||||
},
|
||||
"frameworks": [
|
||||
"arduino",
|
||||
"espidf"
|
||||
],
|
||||
"name": "ESP32-CAM M5STACK ESP32CAM",
|
||||
"upload": {
|
||||
"flash_size": "4MB",
|
||||
"maximum_ram_size": 327680,
|
||||
"maximum_size": 4194304,
|
||||
"require_upload_port": true,
|
||||
"speed": 460800
|
||||
},
|
||||
"url": "https://docs.m5stack.com/en/unit/esp32cam",
|
||||
"vendor": "M5STACK"
|
||||
}
|
||||
64
boards/esp32cam_m5stack_unitcam.json
Normal file
@@ -0,0 +1,64 @@
|
||||
{
|
||||
"build": {
|
||||
"arduino": {
|
||||
"ldscript": "esp32_out.ld",
|
||||
"partitions": "huge_app.csv"
|
||||
},
|
||||
"core": "esp32",
|
||||
"extra_flags": [
|
||||
"'-D ESP32CAM_M5STACK_UNITCAM'",
|
||||
"'-D USER_LED_GPIO=4'",
|
||||
"'-D USER_LED_ON_LEVEL=LOW'",
|
||||
"'-D CAMERA_CONFIG_PIN_PWDN=GPIO_NUM_NC'",
|
||||
"'-D CAMERA_CONFIG_PIN_RESET=15'",
|
||||
"'-D CAMERA_CONFIG_PIN_XCLK=27'",
|
||||
"'-D CAMERA_CONFIG_PIN_SCCB_SDA=25'",
|
||||
"'-D CAMERA_CONFIG_PIN_SCCB_SCL=23'",
|
||||
"'-D CAMERA_CONFIG_PIN_Y9=19'",
|
||||
"'-D CAMERA_CONFIG_PIN_Y8=36'",
|
||||
"'-D CAMERA_CONFIG_PIN_Y7=18'",
|
||||
"'-D CAMERA_CONFIG_PIN_Y6=39'",
|
||||
"'-D CAMERA_CONFIG_PIN_Y5=5'",
|
||||
"'-D CAMERA_CONFIG_PIN_Y4=34'",
|
||||
"'-D CAMERA_CONFIG_PIN_Y3=35'",
|
||||
"'-D CAMERA_CONFIG_PIN_Y2=32'",
|
||||
"'-D CAMERA_CONFIG_PIN_VSYNC=22'",
|
||||
"'-D CAMERA_CONFIG_PIN_HREF=26'",
|
||||
"'-D CAMERA_CONFIG_PIN_PCLK=21'",
|
||||
"'-D CAMERA_CONFIG_CLK_FREQ_HZ=20000000'",
|
||||
"'-D CAMERA_CONFIG_LEDC_TIMER=LEDC_TIMER_0'",
|
||||
"'-D CAMERA_CONFIG_LEDC_CHANNEL=LEDC_CHANNEL_0'",
|
||||
"'-D CAMERA_CONFIG_FB_COUNT=1'",
|
||||
"'-D CAMERA_CONFIG_FB_LOCATION=CAMERA_FB_IN_DRAM'",
|
||||
"'-D CAMERA_CONFIG_SCCB_I2C_PORT=I2C_NUM_0'"
|
||||
],
|
||||
"f_cpu": "240000000L",
|
||||
"f_flash": "40000000L",
|
||||
"flash_mode": "dio",
|
||||
"mcu": "esp32",
|
||||
"variant": "esp32"
|
||||
},
|
||||
"connectivity": [
|
||||
"wifi",
|
||||
"bluetooth",
|
||||
"ethernet",
|
||||
"can"
|
||||
],
|
||||
"debug": {
|
||||
"openocd_board": "esp-wroom-32.cfg"
|
||||
},
|
||||
"frameworks": [
|
||||
"arduino",
|
||||
"espidf"
|
||||
],
|
||||
"name": "ESP32-CAM M5STACK UNITCAM",
|
||||
"upload": {
|
||||
"flash_size": "4MB",
|
||||
"maximum_ram_size": 327680,
|
||||
"maximum_size": 4194304,
|
||||
"require_upload_port": true,
|
||||
"speed": 460800
|
||||
},
|
||||
"url": "https://docs.m5stack.com/en/unit/unit_cam",
|
||||
"vendor": "M5STACK"
|
||||
}
|
||||
84
boards/esp32cam_m5stack_unitcams3.json
Normal file
@@ -0,0 +1,84 @@
|
||||
{
|
||||
"build": {
|
||||
"arduino": {
|
||||
"ldscript": "esp32s3_out.ld",
|
||||
"partitions": "default_8MB.csv",
|
||||
"memory_type": "qio_opi"
|
||||
},
|
||||
"core": "esp32",
|
||||
"extra_flags": [
|
||||
"'-D ESP32CAM_M5STACK_UNITCAMS3'",
|
||||
"'-D BOARD_HAS_PSRAM'",
|
||||
"'-D ARDUINO_USB_MODE=1'",
|
||||
"'-D ARDUINO_USB_CDC_ON_BOOT=1'",
|
||||
"'-D ARDUINO_RUNNING_CORE=1'",
|
||||
"'-D ARDUINO_EVENT_RUNNING_CORE=1'",
|
||||
"'-D USER_LED_GPIO=14'",
|
||||
"'-D USER_LED_ON_LEVEL=HIGH'",
|
||||
"'-D CAMERA_CONFIG_PIN_PWDN=GPIO_NUM_NC'",
|
||||
"'-D CAMERA_CONFIG_PIN_RESET=21'",
|
||||
"'-D CAMERA_CONFIG_PIN_XCLK=11'",
|
||||
"'-D CAMERA_CONFIG_PIN_SCCB_SDA=17'",
|
||||
"'-D CAMERA_CONFIG_PIN_SCCB_SCL=41'",
|
||||
"'-D CAMERA_CONFIG_PIN_Y9=13'",
|
||||
"'-D CAMERA_CONFIG_PIN_Y8=4'",
|
||||
"'-D CAMERA_CONFIG_PIN_Y7=10'",
|
||||
"'-D CAMERA_CONFIG_PIN_Y6=5'",
|
||||
"'-D CAMERA_CONFIG_PIN_Y5=7'",
|
||||
"'-D CAMERA_CONFIG_PIN_Y4=16'",
|
||||
"'-D CAMERA_CONFIG_PIN_Y3=15'",
|
||||
"'-D CAMERA_CONFIG_PIN_Y2=6'",
|
||||
"'-D CAMERA_CONFIG_PIN_VSYNC=42'",
|
||||
"'-D CAMERA_CONFIG_PIN_HREF=18'",
|
||||
"'-D CAMERA_CONFIG_PIN_PCLK=12'",
|
||||
"'-D CAMERA_CONFIG_CLK_FREQ_HZ=20000000'",
|
||||
"'-D CAMERA_CONFIG_LEDC_TIMER=LEDC_TIMER_0'",
|
||||
"'-D CAMERA_CONFIG_LEDC_CHANNEL=LEDC_CHANNEL_0'",
|
||||
"'-D CAMERA_CONFIG_FB_COUNT=2'",
|
||||
"'-D CAMERA_CONFIG_FB_LOCATION=CAMERA_FB_IN_DRAM'",
|
||||
"'-D CAMERA_CONFIG_SCCB_I2C_PORT=I2C_NUM_0'",
|
||||
"'-D I2C_MEMS_SDA=17'",
|
||||
"'-D I2C_MEMS_SCL=41'",
|
||||
"'-D TF_CS=9'",
|
||||
"'-D TF_MOSI=38'",
|
||||
"'-D TF_CLK=39'",
|
||||
"'-D TF_MISO=40'"
|
||||
],
|
||||
"f_cpu": "240000000L",
|
||||
"f_flash": "80000000L",
|
||||
"flash_mode": "qio",
|
||||
"hwids": [
|
||||
[
|
||||
"0x2886",
|
||||
"0x0056"
|
||||
],
|
||||
[
|
||||
"0x2886",
|
||||
"0x8056"
|
||||
]
|
||||
],
|
||||
"mcu": "esp32s3",
|
||||
"variant": "esp32s3"
|
||||
},
|
||||
"connectivity": [
|
||||
"bluetooth",
|
||||
"wifi"
|
||||
],
|
||||
"debug": {
|
||||
"openocd_target": "esp32s3.cfg"
|
||||
},
|
||||
"frameworks": [
|
||||
"arduino",
|
||||
"espidf"
|
||||
],
|
||||
"name": "ESP32-CAM M5STACK UNITCAMS3",
|
||||
"upload": {
|
||||
"flash_size": "16MB",
|
||||
"maximum_ram_size": 327680,
|
||||
"maximum_size": 16777216,
|
||||
"require_upload_port": true,
|
||||
"speed": 460800
|
||||
},
|
||||
"url": "https://docs.m5stack.com/en/unit/Unit-CamS3",
|
||||
"vendor": "M5STACK"
|
||||
}
|
||||
64
boards/esp32cam_m5stack_wide.json
Normal file
@@ -0,0 +1,64 @@
|
||||
{
|
||||
"build": {
|
||||
"arduino": {
|
||||
"ldscript": "esp32_out.ld",
|
||||
"partitions": "huge_app.csv"
|
||||
},
|
||||
"core": "esp32",
|
||||
"extra_flags": [
|
||||
"'-D ESP32CAM_M5STACK_WIDE'",
|
||||
"'-D BOARD_HAS_PSRAM'",
|
||||
"'-mfix-esp32-psram-cache-issue'",
|
||||
"'-D CAMERA_CONFIG_PIN_PWDN=GPIO_NUM_NC'",
|
||||
"'-D CAMERA_CONFIG_PIN_RESET=15'",
|
||||
"'-D CAMERA_CONFIG_PIN_XCLK=27'",
|
||||
"'-D CAMERA_CONFIG_PIN_SCCB_SDA=22'",
|
||||
"'-D CAMERA_CONFIG_PIN_SCCB_SCL=23'",
|
||||
"'-D CAMERA_CONFIG_PIN_Y9=19'",
|
||||
"'-D CAMERA_CONFIG_PIN_Y8=36'",
|
||||
"'-D CAMERA_CONFIG_PIN_Y7=18'",
|
||||
"'-D CAMERA_CONFIG_PIN_Y6=39'",
|
||||
"'-D CAMERA_CONFIG_PIN_Y5=5'",
|
||||
"'-D CAMERA_CONFIG_PIN_Y4=34'",
|
||||
"'-D CAMERA_CONFIG_PIN_Y3=35'",
|
||||
"'-D CAMERA_CONFIG_PIN_Y2=32'",
|
||||
"'-D CAMERA_CONFIG_PIN_VSYNC=25'",
|
||||
"'-D CAMERA_CONFIG_PIN_HREF=26'",
|
||||
"'-D CAMERA_CONFIG_PIN_PCLK=21'",
|
||||
"'-D CAMERA_CONFIG_CLK_FREQ_HZ=20000000'",
|
||||
"'-D CAMERA_CONFIG_LEDC_TIMER=LEDC_TIMER_0'",
|
||||
"'-D CAMERA_CONFIG_LEDC_CHANNEL=LEDC_CHANNEL_0'",
|
||||
"'-D CAMERA_CONFIG_FB_COUNT=2'",
|
||||
"'-D CAMERA_CONFIG_FB_LOCATION=CAMERA_FB_IN_PSRAM'",
|
||||
"'-D CAMERA_CONFIG_SCCB_I2C_PORT=I2C_NUM_0'"
|
||||
],
|
||||
"f_cpu": "240000000L",
|
||||
"f_flash": "40000000L",
|
||||
"flash_mode": "dio",
|
||||
"mcu": "esp32",
|
||||
"variant": "esp32"
|
||||
},
|
||||
"connectivity": [
|
||||
"wifi",
|
||||
"bluetooth",
|
||||
"ethernet",
|
||||
"can"
|
||||
],
|
||||
"debug": {
|
||||
"openocd_board": "esp-wroom-32.cfg"
|
||||
},
|
||||
"frameworks": [
|
||||
"arduino",
|
||||
"espidf"
|
||||
],
|
||||
"name": "ESP32-CAM M5 STACK WIDE",
|
||||
"upload": {
|
||||
"flash_size": "4MB",
|
||||
"maximum_ram_size": 327680,
|
||||
"maximum_size": 4194304,
|
||||
"require_upload_port": true,
|
||||
"speed": 460800
|
||||
},
|
||||
"url": "https://shop.m5stack.com/collections/m5-cameras",
|
||||
"vendor": "M5STACK"
|
||||
}
|
||||
84
boards/esp32cam_seeed_xiao_esp32s3_sense.json
Normal file
@@ -0,0 +1,84 @@
|
||||
{
|
||||
"build": {
|
||||
"arduino": {
|
||||
"ldscript": "esp32s3_out.ld",
|
||||
"partitions": "default_8MB.csv",
|
||||
"memory_type": "qio_opi"
|
||||
},
|
||||
"core": "esp32",
|
||||
"extra_flags": [
|
||||
"'-D ESP32CAM_SEEED_XIAO_ESP32S3_SENSE'",
|
||||
"'-D BOARD_HAS_PSRAM'",
|
||||
"'-D ARDUINO_USB_MODE=1'",
|
||||
"'-D ARDUINO_USB_CDC_ON_BOOT=1'",
|
||||
"'-D ARDUINO_RUNNING_CORE=1'",
|
||||
"'-D ARDUINO_EVENT_RUNNING_CORE=1'",
|
||||
"'-D USER_LED_GPIO=21'",
|
||||
"'-D USER_LED_ON_LEVEL=LOW'",
|
||||
"'-D CAMERA_CONFIG_PIN_PWDN=GPIO_NUM_NC'",
|
||||
"'-D CAMERA_CONFIG_PIN_RESET=GPIO_NUM_NC'",
|
||||
"'-D CAMERA_CONFIG_PIN_XCLK=10'",
|
||||
"'-D CAMERA_CONFIG_PIN_SCCB_SDA=40'",
|
||||
"'-D CAMERA_CONFIG_PIN_SCCB_SCL=39'",
|
||||
"'-D CAMERA_CONFIG_PIN_Y9=48'",
|
||||
"'-D CAMERA_CONFIG_PIN_Y8=11'",
|
||||
"'-D CAMERA_CONFIG_PIN_Y7=12'",
|
||||
"'-D CAMERA_CONFIG_PIN_Y6=14'",
|
||||
"'-D CAMERA_CONFIG_PIN_Y5=16'",
|
||||
"'-D CAMERA_CONFIG_PIN_Y4=18'",
|
||||
"'-D CAMERA_CONFIG_PIN_Y3=17'",
|
||||
"'-D CAMERA_CONFIG_PIN_Y2=15'",
|
||||
"'-D CAMERA_CONFIG_PIN_VSYNC=38'",
|
||||
"'-D CAMERA_CONFIG_PIN_HREF=47'",
|
||||
"'-D CAMERA_CONFIG_PIN_PCLK=13'",
|
||||
"'-D CAMERA_CONFIG_CLK_FREQ_HZ=20000000'",
|
||||
"'-D CAMERA_CONFIG_LEDC_TIMER=LEDC_TIMER_0'",
|
||||
"'-D CAMERA_CONFIG_LEDC_CHANNEL=LEDC_CHANNEL_0'",
|
||||
"'-D CAMERA_CONFIG_FB_COUNT=2'",
|
||||
"'-D CAMERA_CONFIG_FB_LOCATION=CAMERA_FB_IN_PSRAM'",
|
||||
"'-D CAMERA_CONFIG_SCCB_I2C_PORT=I2C_NUM_0'",
|
||||
"'-D I2C_MEMS_SDA=41'",
|
||||
"'-D I2C_MEMS_SCL=42'",
|
||||
"'-D TF_CS=21'",
|
||||
"'-D TF_MOSI=10'",
|
||||
"'-D TF_CLK=8'",
|
||||
"'-D TF_MISO=9'"
|
||||
],
|
||||
"f_cpu": "240000000L",
|
||||
"f_flash": "80000000L",
|
||||
"flash_mode": "qio",
|
||||
"hwids": [
|
||||
[
|
||||
"0x2886",
|
||||
"0x0056"
|
||||
],
|
||||
[
|
||||
"0x2886",
|
||||
"0x8056"
|
||||
]
|
||||
],
|
||||
"mcu": "esp32s3",
|
||||
"variant": "esp32s3"
|
||||
},
|
||||
"connectivity": [
|
||||
"bluetooth",
|
||||
"wifi"
|
||||
],
|
||||
"debug": {
|
||||
"openocd_target": "esp32s3.cfg"
|
||||
},
|
||||
"frameworks": [
|
||||
"arduino",
|
||||
"espidf"
|
||||
],
|
||||
"name": "Seeed Studio XIAO ESP32S3 Sense",
|
||||
"upload": {
|
||||
"flash_size": "8MB",
|
||||
"maximum_ram_size": 327680,
|
||||
"maximum_size": 8388608,
|
||||
"require_upload_port": true,
|
||||
"speed": 460800
|
||||
},
|
||||
"url": "https://www.seeedstudio.com/XIAO-ESP32S3-p-5627.html",
|
||||
"vendor": "Seeed Studio"
|
||||
}
|
||||
66
boards/esp32cam_ttgo_t_camera.json
Normal file
@@ -0,0 +1,66 @@
|
||||
{
|
||||
"build": {
|
||||
"arduino": {
|
||||
"ldscript": "esp32_out.ld",
|
||||
"partitions": "huge_app.csv"
|
||||
},
|
||||
"core": "esp32",
|
||||
"extra_flags": [
|
||||
"'-D ESP32CAM_TTGO_T_CAMERA'",
|
||||
"'-D CAMERA_CONFIG_PIN_PWDN=26'",
|
||||
"'-D CAMERA_CONFIG_PIN_RESET=GPIO_NUM_NC'",
|
||||
"'-D CAMERA_CONFIG_PIN_XCLK=32'",
|
||||
"'-D CAMERA_CONFIG_PIN_SCCB_SDA=13'",
|
||||
"'-D CAMERA_CONFIG_PIN_SCCB_SCL=12'",
|
||||
"'-D CAMERA_CONFIG_PIN_Y9=39'",
|
||||
"'-D CAMERA_CONFIG_PIN_Y8=36'",
|
||||
"'-D CAMERA_CONFIG_PIN_Y7=23'",
|
||||
"'-D CAMERA_CONFIG_PIN_Y6=18'",
|
||||
"'-D CAMERA_CONFIG_PIN_Y5=15'",
|
||||
"'-D CAMERA_CONFIG_PIN_Y4=4'",
|
||||
"'-D CAMERA_CONFIG_PIN_Y3=14'",
|
||||
"'-D CAMERA_CONFIG_PIN_Y2=5'",
|
||||
"'-D CAMERA_CONFIG_PIN_VSYNC=27'",
|
||||
"'-D CAMERA_CONFIG_PIN_HREF=25'",
|
||||
"'-D CAMERA_CONFIG_PIN_PCLK=19'",
|
||||
"'-D CAMERA_CONFIG_CLK_FREQ_HZ=20000000'",
|
||||
"'-D CAMERA_CONFIG_LEDC_TIMER=LEDC_TIMER_0'",
|
||||
"'-D CAMERA_CONFIG_LEDC_CHANNEL=LEDC_CHANNEL_0'",
|
||||
"'-D CAMERA_CONFIG_FB_COUNT=1'",
|
||||
"'-D CAMERA_CONFIG_FB_LOCATION=CAMERA_FB_IN_DRAM'",
|
||||
"'-D CAMERA_CONFIG_SCCB_I2C_PORT=I2C_NUM_0'",
|
||||
"'-D LCD_SSD1306_PIN_SDA=21'",
|
||||
"'-D LCD_SSD1306_PIN_SCL=22'",
|
||||
"'-D BUTTON_RIGHT_PIN=34'",
|
||||
"'-D PIR_PIN=33'"
|
||||
],
|
||||
"f_cpu": "240000000L",
|
||||
"f_flash": "40000000L",
|
||||
"flash_mode": "dio",
|
||||
"mcu": "esp32",
|
||||
"variant": "esp32"
|
||||
},
|
||||
"connectivity": [
|
||||
"wifi",
|
||||
"bluetooth",
|
||||
"ethernet",
|
||||
"can"
|
||||
],
|
||||
"debug": {
|
||||
"openocd_board": "esp-wroom-32.cfg"
|
||||
},
|
||||
"frameworks": [
|
||||
"arduino",
|
||||
"espidf"
|
||||
],
|
||||
"name": "ESP32-CAM TTGO-T-CAMERA",
|
||||
"upload": {
|
||||
"flash_size": "4MB",
|
||||
"maximum_ram_size": 327680,
|
||||
"maximum_size": 4194304,
|
||||
"require_upload_port": true,
|
||||
"speed": 460800
|
||||
},
|
||||
"url": "https://www.lilygo.cc/products/",
|
||||
"vendor": "LILYGO"
|
||||
}
|
||||
62
boards/esp32cam_ttgo_t_journal.json
Normal file
@@ -0,0 +1,62 @@
|
||||
{
|
||||
"build": {
|
||||
"arduino": {
|
||||
"ldscript": "esp32_out.ld",
|
||||
"partitions": "huge_app.csv"
|
||||
},
|
||||
"core": "esp32",
|
||||
"extra_flags": [
|
||||
"'-D ESP32CAM_TTGO_T_JOURNAL'",
|
||||
"'-D CAMERA_CONFIG_PIN_PWDN=0'",
|
||||
"'-D CAMERA_CONFIG_PIN_RESET=15'",
|
||||
"'-D CAMERA_CONFIG_PIN_XCLK=27'",
|
||||
"'-D CAMERA_CONFIG_PIN_SCCB_SDA=25'",
|
||||
"'-D CAMERA_CONFIG_PIN_SCCB_SCL=23'",
|
||||
"'-D CAMERA_CONFIG_PIN_Y9=19'",
|
||||
"'-D CAMERA_CONFIG_PIN_Y8=36'",
|
||||
"'-D CAMERA_CONFIG_PIN_Y7=18'",
|
||||
"'-D CAMERA_CONFIG_PIN_Y6=39'",
|
||||
"'-D CAMERA_CONFIG_PIN_Y5=5'",
|
||||
"'-D CAMERA_CONFIG_PIN_Y4=34'",
|
||||
"'-D CAMERA_CONFIG_PIN_Y3=35'",
|
||||
"'-D CAMERA_CONFIG_PIN_Y2=17'",
|
||||
"'-D CAMERA_CONFIG_PIN_VSYNC=22'",
|
||||
"'-D CAMERA_CONFIG_PIN_HREF=26'",
|
||||
"'-D CAMERA_CONFIG_PIN_PCLK=21'",
|
||||
"'-D CAMERA_CONFIG_CLK_FREQ_HZ=20000000'",
|
||||
"'-D CAMERA_CONFIG_LEDC_TIMER=LEDC_TIMER_0'",
|
||||
"'-D CAMERA_CONFIG_LEDC_CHANNEL=LEDC_CHANNEL_0'",
|
||||
"'-D CAMERA_CONFIG_FB_COUNT=1'",
|
||||
"'-D CAMERA_CONFIG_FB_LOCATION=CAMERA_FB_IN_DRAM'",
|
||||
"'-D CAMERA_CONFIG_SCCB_I2C_PORT=I2C_NUM_0'"
|
||||
],
|
||||
"f_cpu": "240000000L",
|
||||
"f_flash": "40000000L",
|
||||
"flash_mode": "dio",
|
||||
"mcu": "esp32",
|
||||
"variant": "esp32"
|
||||
},
|
||||
"connectivity": [
|
||||
"wifi",
|
||||
"bluetooth",
|
||||
"ethernet",
|
||||
"can"
|
||||
],
|
||||
"debug": {
|
||||
"openocd_board": "esp-wroom-32.cfg"
|
||||
},
|
||||
"frameworks": [
|
||||
"arduino",
|
||||
"espidf"
|
||||
],
|
||||
"name": "ESP32-CAM TTGO-T-JOURNAL",
|
||||
"upload": {
|
||||
"flash_size": "4MB",
|
||||
"maximum_ram_size": 327680,
|
||||
"maximum_size": 4194304,
|
||||
"require_upload_port": true,
|
||||
"speed": 460800
|
||||
},
|
||||
"url": "https://www.lilygo.cc/products/",
|
||||
"vendor": "LILYGO"
|
||||
}
|
||||
1
dotnet_riscv
Submodule
@@ -1,5 +1,4 @@
|
||||
. python3 -m pip install --upgrade pip setuptools wheel
|
||||
. python3 -m pip install htmlmin
|
||||
. python3 -m pip install minify-html
|
||||
|
||||
. python3 ./html_to_cpp.py ./html ./include/html_data.h
|
||||
. python3 ./html_to_cpp_gzip.py ./html_gzip ./include/html_data_gzip.h
|
||||
. python3 ./minify.py ./html/index.html ./html/index.min.html
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/bin/sh
|
||||
python3 -m pip install --upgrade pip setuptools wheel
|
||||
python3 -m pip install htmlmin
|
||||
|
||||
python3 ./html_to_cpp.py ./html ./include/html_data.h
|
||||
python3 ./html_to_cpp_gzip.py ./html_gzip ./include/html_data_gzip.h
|
||||
python3 -m pip install --upgrade pip setuptools wheel
|
||||
python3 -m pip install minify-html
|
||||
|
||||
python3 ./minify.py ./html/index.html ./html/index.min.html
|
||||
526
html/index.html
@@ -4,307 +4,247 @@
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||
<link rel="stylesheet" href="bootstrap.min.css">
|
||||
<style>
|
||||
html,
|
||||
body {
|
||||
font-family: Arial, Verdana, Helvetica, sans-serif;
|
||||
min-height: 100%;
|
||||
}
|
||||
|
||||
.flex-table {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, 25%);
|
||||
}
|
||||
|
||||
.flex-table>.row {
|
||||
grid-column-start: 2;
|
||||
grid-column-end: 3;
|
||||
}
|
||||
|
||||
.text-center {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.alert {
|
||||
padding: 15px;
|
||||
margin-bottom: 20px;
|
||||
border: 1px solid transparent;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.alert-info {
|
||||
color: #31708f;
|
||||
background-color: #d9edf7;
|
||||
border-color: #bce8f1;
|
||||
}
|
||||
|
||||
.alert-success {
|
||||
color: #3c763d;
|
||||
background-color: #dff0d8;
|
||||
border-color: #d6e9c6;
|
||||
}
|
||||
|
||||
.alert-warning {
|
||||
color: #8a6d3b;
|
||||
background-color: #fcf8e3;
|
||||
border-color: #faebcc;
|
||||
}
|
||||
|
||||
.alert-danger {
|
||||
color: #a94442;
|
||||
background-color: #f2dede;
|
||||
border-color: #ebccd1;
|
||||
}
|
||||
|
||||
.btn {
|
||||
display: inline-block;
|
||||
margin-bottom: 0;
|
||||
font-weight: 400;
|
||||
text-align: center;
|
||||
white-space: nowrap;
|
||||
vertical-align: middle;
|
||||
border: 1px solid transparent;
|
||||
padding: 6px 12px;
|
||||
font-size: 14px;
|
||||
line-height: 1.42857143;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.btn-lg {
|
||||
padding: 10px 16px;
|
||||
font-size: 18px;
|
||||
line-height: 1.3333333;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
color: #fff;
|
||||
background-color: #337ab7;
|
||||
border-color: #2e6da4;
|
||||
}
|
||||
|
||||
.btn-danger {
|
||||
color: #fff;
|
||||
background-color: #d9534f;
|
||||
border-color: #d43f3a;
|
||||
}
|
||||
</style>
|
||||
<title>{{AppTitle}} v{{AppVersion}}</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="container">
|
||||
<h1 class="text-center">{{ThingName}}</h1>
|
||||
<hr>
|
||||
<h1 class="text-center">{{ThingName}}</h1>
|
||||
<hr>
|
||||
|
||||
{{#ConfigChanged}}
|
||||
<div class="alert alert-danger" role="alert">
|
||||
<p>The configuration has been changed.</p>
|
||||
<p>It is recommended to restart the device.</p>
|
||||
<div class="alert alert-info">
|
||||
<h3 class="text-center">
|
||||
Press on the button below to change the settings<br><br>
|
||||
<button type="button" class="btn btn-lg btn-primary" onclick="location.href='config'">Change
|
||||
settings</button>
|
||||
</h3>
|
||||
</div>
|
||||
|
||||
|
||||
<h2 class="text-center">ESP32</h2>
|
||||
<div class="flex-table">
|
||||
<div class="row">Board type:</div>
|
||||
<div>{{BoardType}}</div>
|
||||
<div class="row">SDK Version:</div>
|
||||
<div>{{SDKVersion}}</div>
|
||||
<div class="row">CPU model:</div>
|
||||
<div>{{ChipModel}} rev. {{ChipRevision}}</div>
|
||||
<div class="row">CPU speed:</div>
|
||||
<div>{{CpuFreqMHz}} Mhz</div>
|
||||
<div class="row">CPU cores:</div>
|
||||
<div>{{CpuCores}}</div>
|
||||
<div class="row">RAM size:</div>
|
||||
<div>{{HeapSize}}</div>
|
||||
<div class="row">PSRAM size:</div>
|
||||
<div>{{PsRamSize}}</div>
|
||||
<div class="row">Flash size:</div>
|
||||
<div>{{FlashSize}}</div>
|
||||
</div>
|
||||
|
||||
<h2 class="text-center">Diagnostics</h2>
|
||||
<div class="flex-table">
|
||||
<div class="row">Uptime:</div>
|
||||
<div>{{Uptime}}</div>
|
||||
<div class="row">RTSP sessions:</div>
|
||||
<div>{{NumRTSPSessions}}</div>
|
||||
<div class="row">Free heap:</div>
|
||||
<div>{{FreeHeap}}</div>
|
||||
<div class="row">Max free block:</div>
|
||||
<div>{{MaxAllocHeap}}</div>
|
||||
</div>
|
||||
|
||||
<h2 class="text-center">Network</h2>
|
||||
<div class="flex-table">
|
||||
<div class="row">Host name:</div>
|
||||
<div>{{HostName}}</div>
|
||||
<div class="row">Mac address:</div>
|
||||
<div>{{MacAddress}}</div>
|
||||
<div class="row">Wifi mode:</div>
|
||||
<div>{{WifiMode}}</div>
|
||||
<div class="row">Access point:</div>
|
||||
<div>{{AccessPoint}}</div>
|
||||
<div class="row">Signal strength:</div>
|
||||
<div>{{SignalStrength}} dbm</div>
|
||||
<div class="row">IPv4 address:</div>
|
||||
<div>{{IPv4}}</div>
|
||||
<div class="row">IPv6 address:</div>
|
||||
<div>{{IPv6}}</div>
|
||||
</div>
|
||||
|
||||
{{#NetworkState.ApMode}}
|
||||
<div class="alert alert-warning">
|
||||
<h3 class="text-center">Not connected to an access point.<br>Consider configuring the access point.</h3>
|
||||
</div>
|
||||
{{/NetworkState.ApMode}}
|
||||
|
||||
{{#NetworkState.OnLine}}
|
||||
<div class="alert alert-success">
|
||||
<h3 class="text-center">Connected to the access point</h3>
|
||||
</div>
|
||||
{{/NetworkState.OnLine}}
|
||||
|
||||
<h2 class="text-center">Camera</h2>
|
||||
<div class="flex-table">
|
||||
<div class="row">Frame rate:</div>
|
||||
<div>{{FrameDuration}} ms ({{FrameFrequency}} f/s)</div>
|
||||
<div class="row">Frame size:</div>
|
||||
<div>{{FrameSize}}</div>
|
||||
<div class="row">JPEG quality:</div>
|
||||
<div>{{JpegQuality}} [1-100]</div>
|
||||
<div class="row">Brightness:</div>
|
||||
<div>{{Brightness}} [-2,2]</div>
|
||||
<div class="row">Contrast:</div>
|
||||
<div>{{Contrast}} [-2,2]</div>
|
||||
<div class="row">Saturation:</div>
|
||||
<div>{{Saturation}} [-2,2]</div>
|
||||
<div class="row">Special effect:</div>
|
||||
<div>{{SpecialEffect}}</div>
|
||||
<div class="row">White balance:</div>
|
||||
<div>{{#WhiteBal}}Auto{{/WhiteBal}}{{^WhiteBal}}Manual{{/WhiteBal}}</div>
|
||||
<div class="row">AWB gain:</div>
|
||||
<div>{{#AwbGain}}Auto{{/AwbGain}}{{^AwbGain}}Manual{{/AwbGain}}</div>
|
||||
<div class="row">WB mode:</div>
|
||||
<div>{{WbMode}}</div>
|
||||
<div class="row">Exposure control:</div>
|
||||
<div>{{#ExposureCtrl}}Auto{{/ExposureCtrl}}{{^ExposureCtrl}}Manual{{/ExposureCtrl}}</div>
|
||||
<div class="row">Auto exposure control (dsp):</div>
|
||||
<div>{{#Aec2}}Enabled{{/Aec2}}{{^Aec2}}Disabled{{/Aec2}}</div>
|
||||
<div class="row">Auto Exposure level:</div>
|
||||
<div>{{AeLevel}}</div>
|
||||
<div class="row">Manual exposure value:</div>
|
||||
<div>{{AecValue}}</div>
|
||||
<div class="row">Gain control:</div>
|
||||
<div>{{#GainCtrl}}Auto{{/GainCtrl}}{{^GainCtrl}}Manual{{/GainCtrl}}</div>
|
||||
<div class="row">AGC gain:</div>
|
||||
<div>{{AgcGain}}</div>
|
||||
<div class="row">Gain ceiling:</div>
|
||||
<div>{{GainCeiling}}</div>
|
||||
<div class="row">Black pixel correct:</div>
|
||||
<div>{{#Bpc}}Auto{{/Bpc}}{{^Bpc}}Manual{{/Bpc}}</div>
|
||||
<div class="row">White pixel correct:</div>
|
||||
<div>{{#Wpc}}Auto{{/Wpc}}{{^Wpc}}Manual{{/Wpc}}</div>
|
||||
<div class="row">Gamma correct:</div>
|
||||
<div>{{#RawGma}}Enabled{{/RawGma}}{{^RawGma}}Disabled{{/RawGma}}</div>
|
||||
<div class="row">Lens correction:</div>
|
||||
<div>{{#Lenc}}Enabled{{/Lenc}}{{^Lenc}}Disabled{{/Lenc}}</div>
|
||||
<div class="row">Horizontal mirror:</div>
|
||||
<div>{{#HMirror}}Mirrored{{/HMirror}}{{^HMirror}}Normal{{/HMirror}}</div>
|
||||
<div class="row">Vertical flip:</div>
|
||||
<div>{{#VFlip}}Flipped{{/VFlip}}{{^VFlip}}Normal{{/VFlip}}</div>
|
||||
<div class="row">Downsize enable:</div>
|
||||
<div>{{#Dcw}}Enabled{{/Dcw}}{{^Dcw}}Disabled{{/Dcw}}</div>
|
||||
<div class="row">Color bar:</div>
|
||||
<div>{{#ColorBar}}Enabled{{/ColorBar}}{{^ColorBar}}Camera{{/ColorBar}}</div>
|
||||
</div>
|
||||
|
||||
{{#CameraInitialized}}
|
||||
<div class="alert alert-success">
|
||||
<h3 class="text-center">Camera was initialized successfully!</h3>
|
||||
</div>
|
||||
{{/CameraInitialized}}
|
||||
{{^CameraInitialized}}
|
||||
<div class="alert alert-danger">
|
||||
<h3 class="text-center">Failed to initialize the camera!<br>
|
||||
Result: {{CameraInitResult}} ({{CameraInitResultText}})<br>
|
||||
Please check hardware or correct the camera settings and restart.<br><br>
|
||||
<button type="button" class="btn btn-danger" onclick="location.href='restart'">Restart</button>
|
||||
</div>
|
||||
{{/ConfigChanged}}
|
||||
</h3>
|
||||
</div>
|
||||
{{/CameraInitialized}}
|
||||
|
||||
<div class="row">
|
||||
<div class="col-sm-6">
|
||||
<div class="card bg-light mb-3">
|
||||
<h5 class="card-header">ESP32</h5>
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<div class="col-4">CPU model:</div>
|
||||
<div class="col-8">{{ChipModel}} rev. {{ChipRevision}}</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-4">CPU speed:</div>
|
||||
<div class="col-8">{{CpuFreqMHz}} Mhz</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-4">CPU cores:</div>
|
||||
<div class="col-8">{{CpuCores}}</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-4">RAM size:</div>
|
||||
<div class="col-8">{{HeapSize}}</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-4">PSRAM size:</div>
|
||||
<div class="col-8">{{PsRamSize}}</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-4">Flash size:</div>
|
||||
<div class="col-8">{{FlashSize}}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
<div class="card bg-light mb-3">
|
||||
<h5 class="card-header">Diagnostics</h5>
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<div class="col-4">Uptime:</div>
|
||||
<div class="col-8">{{Uptime}}</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-4">RTSP sessions:</div>
|
||||
<div class="col-8">{{NumRTSPSessions}}</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-4">Free heap:</div>
|
||||
<div class="col-8">{{FreeHeap}}</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-4">Max free block:</div>
|
||||
<div class="col-8">{{MaxAllocHeap}}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-sm-6">
|
||||
<div class="card bg-light mb-3">
|
||||
<h5 class="card-header">Peripheral</h5>
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<div class="col-4">Board type:</div>
|
||||
<div class="col-8">{{BoardType}}</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-4">LED intensity:</div>
|
||||
<div class="col-8">{{LedIntensity}} [0-100]</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card bg-light mb-3">
|
||||
<h5 class="card-header">Network</h5>
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<div class="col-4">Host name:</div>
|
||||
<div class="col-8">{{HostName}}</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-4">Mac address:</div>
|
||||
<div class="col-8">{{MacAddress}}</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-4">Wifi mode:</div>
|
||||
<div class="col-8">{{WifiMode}}</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-4">Access point:</div>
|
||||
<div class="col-8">{{AccessPoint}}</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-4">Signal strength:</div>
|
||||
<div class="col-8">{{SignalStrength}} dbm</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-4">IPv4 address:</div>
|
||||
<div class="col-8">{{IpV4}}</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-4">IPv6 address:</div>
|
||||
<div class="col-8">{{IpV6}}</div>
|
||||
</div>
|
||||
{{#NetworkState.ApMode}}
|
||||
<div class="mt-4 alert alert-warning" role="alert">
|
||||
<p>Not connected to an access point. Consider configuring the access point.</p>
|
||||
</div>
|
||||
{{/NetworkState.ApMode}}
|
||||
{{#NetworkState.OnLine}}
|
||||
<div class="mt-4 alert alert-success" role="alert">
|
||||
<p>Connected to the access point</p>
|
||||
</div>
|
||||
{{/NetworkState.OnLine}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
<div class="card bg-light mb-3">
|
||||
<h5 class="card-header">Camera</h5>
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<div class="col-4">Frame rate:</div>
|
||||
<div class="col-8">{{FrameDuration}} ms ({{FrameFrequency}} f/s)</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-4">Frame size:</div>
|
||||
<div class="col-8">{{FrameSize}}</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-4">Frame buffer location:</div>
|
||||
<div class="col-8">{{FrameBufferLocation}}</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-4">Frame buffers:</div>
|
||||
<div class="col-8">{{FrameBuffers}}</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-4">JPEG quality:</div>
|
||||
<div class="col-8">{{JpegQuality}} [1-100]</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-4">Brightness:</div>
|
||||
<div class="col-8">{{Brightness}} [-2,2]</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-4">Contrast:</div>
|
||||
<div class="col-8">{{Contrast}} [-2,2]</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-4">Saturation:</div>
|
||||
<div class="col-8">{{Saturation}} [-2,2]</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-4">Special effect:</div>
|
||||
<div class="col-8">{{SpecialEffect}}</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-4">White balance:</div>
|
||||
<div class="col-8">{{WhiteBal}}</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-4">AWB gain:</div>
|
||||
<div class="col-8">{{AwbGain}}</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-4">WB mode:</div>
|
||||
<div class="col-8">{{WbMode}}</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-4">Exposure control:</div>
|
||||
<div class="col-8">{{ExposureCtrl}}</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-4">Auto exposure control (dsp):</div>
|
||||
<div class="col-8">{{Aec2}}</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-4">Auto Exposure level:</div>
|
||||
<div class="col-8">{{AeLevel}}</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-4">Manual exposure value:</div>
|
||||
<div class="col-8">{{AecValue}}</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-4">Gain control:</div>
|
||||
<div class="col-8">{{GainCtrl}}</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-4">AGC gain:</div>
|
||||
<div class="col-8">{{AgcGain}}</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-4">Gain ceiling:</div>
|
||||
<div class="col-8">{{GainCeiling}}</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-4">Black pixel correct:</div>
|
||||
<div class="col-8">{{Bpc}}</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-4">White pixel correct:</div>
|
||||
<div class="col-8">{{Wpc}}</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-4">Gamma correct:</div>
|
||||
<div class="col-8">{{RawGma}}</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-4">Lens correction:</div>
|
||||
<div class="col-8">{{Lenc}}</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-4">Horizontal mirror:</div>
|
||||
<div class="col-8">{{HMirror}}</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-4">Vertical flip:</div>
|
||||
<div class="col-8">{{VFlip}}</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-4">Downsize enable:</div>
|
||||
<div class="col-8">{{Dcw}}</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-4">Color bar:</div>
|
||||
<div class="col-8">{{ColorBar}}</div>
|
||||
</div>
|
||||
{{#CameraInitialized}}
|
||||
<div class="mt-4 alert alert-success" role="alert">
|
||||
<p>Camera was initialized successfully!</p>
|
||||
</div>
|
||||
{{/CameraInitialized}}
|
||||
{{^CameraInitialized}}
|
||||
<div class="mt-4 alert alert-danger" role="alert">
|
||||
<p>Failed to initialize the camera!</p>
|
||||
<p>Result: {{CameraInitResultText}}</p>
|
||||
<p>Please check hardware or correct the camera settings and restart.</p>
|
||||
<button type="button" class="btn btn-danger"
|
||||
onclick="location.href='restart'">Restart</button>
|
||||
</div>
|
||||
{{/CameraInitialized}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="card bg-light mb-3">
|
||||
<h5 class="card-header">Special URLs / API</h5>
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<span>
|
||||
The camera RTSP stream can be found at:
|
||||
<a href="rtsp://{{IpV4}}:{{RtspPort}}/mjpeg/1">rtsp://{{IpV4}}:{{RtspPort}}/mjpeg/1</a>
|
||||
</span>
|
||||
</div>
|
||||
<div class="row">
|
||||
<span>
|
||||
A snapshot of the camera can be found at:
|
||||
<a href="http://{{IpV4}}/snapshot">http://{{IpV4}}/snapshot</a>
|
||||
</span>
|
||||
</div>
|
||||
<div class="row">
|
||||
<span>
|
||||
The intensity of the flash led (0-255) can be controlled using:
|
||||
<a href="http://{{IpV4}}/flash?v=0">http://{{IpV4}}/flash?v=value</a>.
|
||||
Authentication is required and if no value is present, the configuration value is used.
|
||||
</span>
|
||||
</div>
|
||||
<div class="row">
|
||||
<span>
|
||||
Restarting the camera can be done using:
|
||||
<a href="http://{{IpV4}}/restart">http://{{IpV4}}/restart</a>.
|
||||
Authentication is required.
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="d-grid gap-2 col-6 mx-auto">
|
||||
<button type="button" class="btn btn-lg btn-warning" onclick="location.href='config'">Settings</button>
|
||||
</div>
|
||||
|
||||
<h2 class="text-center">Special URLs / API</h2>
|
||||
<div class="flex-table">
|
||||
<div class="row">RTSP camera stream:</div>
|
||||
<div><a href="rtsp://{{IPv4}}:{{RtspPort}}/mjpeg/1">rtsp://{{IPv4}}:{{RtspPort}}/mjpeg/1</a></div>
|
||||
<div class="row">JPEG Motion stream:</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><a href="http://{{IPv4}}/snapshot " target="_blank" rel="noopener">http://{{IPv4}}/snapshot</a> </div>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
|
||||
1
html/index.min.html
Normal file
@@ -1,32 +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">
|
||||
<link rel="stylesheet" href="bootstrap.min.css">
|
||||
<meta http-equiv="refresh" content="1;url=/index.html">
|
||||
<title>{{AppTitle}} v{{AppVersion}}</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="container">
|
||||
<h1 class="text-center">{{ThingName}}</h1>
|
||||
<hr>
|
||||
|
||||
<div class="jumbotron bg-light">
|
||||
<h1 class="display-4 text-center">Restart</h1>
|
||||
<p class="lead text-center">The device is restarting.</p>
|
||||
<hr class="my-4 ">
|
||||
<p class="text-center">In some cases, the device requires a hard reset (power cycle).</p>
|
||||
<p class="text-center">If this page takes longer than a minute, consider performing a power cycle.</p>
|
||||
<div class="d-flex justify-content-center">
|
||||
<div class="spinner-border text-danger" role="status">
|
||||
<span class="visually-hidden">Restarting...</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
7
html_gzip/bootstrap.min.css
vendored
@@ -1,42 +0,0 @@
|
||||
#!/usr/bin/python3
|
||||
|
||||
import os
|
||||
import sys
|
||||
import htmlmin
|
||||
|
||||
if (len(sys.argv) <= 2):
|
||||
print('Usage: html_to_cpp.py <input_dir> <file.h>')
|
||||
sys.exit(1)
|
||||
|
||||
input_dir = sys.argv[1]
|
||||
file_h = sys.argv[2]
|
||||
|
||||
file_names = os.listdir(input_dir)
|
||||
file_names = filter(lambda x: x[0] != '.' and os.path.isfile(os.path.join(input_dir, x)), file_names)
|
||||
file_names = sorted(file_names)
|
||||
|
||||
output_file = open(file_h, 'w')
|
||||
output_file.write('//*******************************************************************************\n'
|
||||
'// HTML import\n'
|
||||
'// Machine generated file\n'
|
||||
'// ******************************************************************************\n'
|
||||
'\n\n')
|
||||
|
||||
for file_name in file_names:
|
||||
print(f'Processing: {file_name}... ')
|
||||
|
||||
file_path = os.path.join(input_dir, file_name)
|
||||
file_data_name = f'file_data_{file_name}'.replace('.', '_')
|
||||
|
||||
file = open(file_path, 'r')
|
||||
html = file.read()
|
||||
file.close()
|
||||
|
||||
html_mimified = htmlmin.minify(html, remove_empty_space=True)
|
||||
# escape "
|
||||
html_mimified_escaped = html_mimified.replace('"', '\\"')
|
||||
output_file.write(f'constexpr char {file_data_name }[] = "{html_mimified_escaped}";\n')
|
||||
|
||||
output_file.close()
|
||||
|
||||
print('Done.')
|
||||
@@ -1,46 +0,0 @@
|
||||
#!/usr/bin/python3
|
||||
|
||||
import os
|
||||
import sys
|
||||
import htmlmin
|
||||
import gzip
|
||||
|
||||
if (len(sys.argv) <= 2):
|
||||
print('Usage: bin_to_cpp_gzip.py <input_dir> <file.h>')
|
||||
sys.exit(1)
|
||||
|
||||
input_dir = sys.argv[1]
|
||||
file_h = sys.argv[2]
|
||||
|
||||
file_names = os.listdir(input_dir)
|
||||
file_names = filter(lambda x: x[0] != '.' and
|
||||
os.path.isfile(os.path.join(input_dir, x)), file_names)
|
||||
file_names = sorted(file_names)
|
||||
|
||||
output_file = open(file_h, 'w')
|
||||
output_file.write(
|
||||
'//*******************************************************************************\n'
|
||||
'// HTML import gzipped\n'
|
||||
'// Machine generated file\n'
|
||||
'// ******************************************************************************\n'
|
||||
'\n\n')
|
||||
|
||||
for file_name in file_names:
|
||||
print(f'Processing: {file_name}... ')
|
||||
|
||||
file_path = os.path.join(input_dir, file_name)
|
||||
file_data_name = f'file_data_{file_name}'.replace('.', '_')
|
||||
|
||||
file = open(file_path, 'r')
|
||||
html = file.read()
|
||||
file.close()
|
||||
|
||||
html_mimified = htmlmin.minify(html, remove_empty_space=True)
|
||||
html_mimified_gzip = gzip.compress(bytes(html_mimified, 'utf-8'))
|
||||
html_mimified_gzip_values = ','.join(f'0x{i:02x}' for i in html_mimified_gzip)
|
||||
|
||||
output_file.write(f'constexpr unsigned char {file_data_name}[] = {{\n{html_mimified_gzip_values}\n}};\n\n')
|
||||
|
||||
output_file.close()
|
||||
|
||||
print('Done.')
|
||||
@@ -1,152 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <string.h>
|
||||
#include <esp_camera.h>
|
||||
|
||||
typedef struct
|
||||
{
|
||||
const char name[11];
|
||||
const camera_config_t config;
|
||||
} camera_config_entry_t;
|
||||
|
||||
constexpr camera_config_t esp32cam_settings = {
|
||||
.pin_pwdn = -1,
|
||||
.pin_reset = 15,
|
||||
.pin_xclk = 27,
|
||||
.pin_sscb_sda = 25,
|
||||
.pin_sscb_scl = 23,
|
||||
.pin_d7 = 19,
|
||||
.pin_d6 = 36,
|
||||
.pin_d5 = 18,
|
||||
.pin_d4 = 39,
|
||||
.pin_d3 = 5,
|
||||
.pin_d2 = 34,
|
||||
.pin_d1 = 35,
|
||||
.pin_d0 = 17,
|
||||
.pin_vsync = 22,
|
||||
.pin_href = 26,
|
||||
.pin_pclk = 21,
|
||||
.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 = 2};
|
||||
|
||||
constexpr camera_config_t esp32cam_aithinker_settings = {
|
||||
.pin_pwdn = 32,
|
||||
.pin_reset = -1,
|
||||
.pin_xclk = 0,
|
||||
.pin_sscb_sda = 26,
|
||||
.pin_sscb_scl = 27,
|
||||
.pin_d7 = 35,
|
||||
.pin_d6 = 34,
|
||||
.pin_d5 = 39,
|
||||
.pin_d4 = 36,
|
||||
.pin_d3 = 21,
|
||||
.pin_d2 = 19,
|
||||
.pin_d1 = 18,
|
||||
.pin_d0 = 5,
|
||||
.pin_vsync = 25,
|
||||
.pin_href = 23,
|
||||
.pin_pclk = 22,
|
||||
.xclk_freq_hz = 20000000,
|
||||
.ledc_timer = LEDC_TIMER_1,
|
||||
.ledc_channel = LEDC_CHANNEL_1,
|
||||
.pixel_format = PIXFORMAT_JPEG,
|
||||
.frame_size = FRAMESIZE_SVGA,
|
||||
.jpeg_quality = 12,
|
||||
.fb_count = 2};
|
||||
|
||||
constexpr camera_config_t esp32cam_ttgo_t_settings = {
|
||||
.pin_pwdn = 26,
|
||||
.pin_reset = -1,
|
||||
.pin_xclk = 32,
|
||||
.pin_sscb_sda = 13,
|
||||
.pin_sscb_scl = 12,
|
||||
.pin_d7 = 39,
|
||||
.pin_d6 = 36,
|
||||
.pin_d5 = 23,
|
||||
.pin_d4 = 18,
|
||||
.pin_d3 = 15,
|
||||
.pin_d2 = 4,
|
||||
.pin_d1 = 14,
|
||||
.pin_d0 = 5,
|
||||
.pin_vsync = 27,
|
||||
.pin_href = 25,
|
||||
.pin_pclk = 19,
|
||||
.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 = 2};
|
||||
|
||||
constexpr camera_config_t esp32cam_m5stack_settings = {
|
||||
.pin_pwdn = -1,
|
||||
.pin_reset = 15,
|
||||
.pin_xclk = 27,
|
||||
.pin_sscb_sda = 25,
|
||||
.pin_sscb_scl = 23,
|
||||
.pin_d7 = 19,
|
||||
.pin_d6 = 36,
|
||||
.pin_d5 = 18,
|
||||
.pin_d4 = 39,
|
||||
.pin_d3 = 5,
|
||||
.pin_d2 = 34,
|
||||
.pin_d1 = 35,
|
||||
.pin_d0 = 32,
|
||||
.pin_vsync = 22,
|
||||
.pin_href = 26,
|
||||
.pin_pclk = 21,
|
||||
.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 = 2};
|
||||
|
||||
constexpr camera_config_t esp32cam_wrover_kit_settings = {
|
||||
.pin_pwdn = -1,
|
||||
.pin_reset = -1,
|
||||
.pin_xclk = 21,
|
||||
.pin_sscb_sda = 26,
|
||||
.pin_sscb_scl = 27,
|
||||
.pin_d7 = 35,
|
||||
.pin_d6 = 34,
|
||||
.pin_d5 = 39,
|
||||
.pin_d4 = 36,
|
||||
.pin_d3 = 19,
|
||||
.pin_d2 = 18,
|
||||
.pin_d1 = 5,
|
||||
.pin_d0 = 4,
|
||||
.pin_vsync = 25,
|
||||
.pin_href = 23,
|
||||
.pin_pclk = 22,
|
||||
.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 = 2};
|
||||
|
||||
constexpr const camera_config_entry_t camera_configs[] = {
|
||||
{"ESP32CAM", esp32cam_settings},
|
||||
{"AI THINKER", esp32cam_aithinker_settings},
|
||||
{"TTGO T-CAM", esp32cam_ttgo_t_settings},
|
||||
{"M5 STACK", esp32cam_m5stack_settings},
|
||||
{"WROVER KIT", esp32cam_wrover_kit_settings}};
|
||||
|
||||
const camera_config_t lookup_camera_config(const char *name)
|
||||
{
|
||||
// Lookup table for the frame name to framesize_t
|
||||
for (const auto &entry : camera_configs)
|
||||
if (strncmp(entry.name, name, sizeof(entry.name)) == 0)
|
||||
return entry.config;
|
||||
|
||||
return camera_config_t{};
|
||||
}
|
||||
@@ -5,21 +5,26 @@
|
||||
|
||||
#define WIFI_SSID "ESP32CAM-RTSP"
|
||||
#define WIFI_PASSWORD nullptr
|
||||
#define CONFIG_VERSION "1.4"
|
||||
#define CONFIG_VERSION "1.6"
|
||||
|
||||
#define OTA_PASSWORD "ESP32CAM-RTSP"
|
||||
|
||||
// Time servers
|
||||
#define NTP_SERVER_1 "nl.pool.ntp.org"
|
||||
#define NTP_SERVER_2 "europe.pool.ntp.org"
|
||||
#define NTP_SERVER_3 "time.nist.gov"
|
||||
#define NTP_SERVERS NTP_SERVER_1, NTP_SERVER_2, NTP_SERVER_3
|
||||
|
||||
#define RTSP_PORT 554
|
||||
|
||||
#define DEFAULT_CAMERA_CONFIG "AI THINKER"
|
||||
#define DEFAULT_FRAME_DURATION 100
|
||||
#define DEFAULT_FRAME_SIZE (psramFound() ? "UXGA (1600x1200)" : "VGA (640x480)")
|
||||
#define DEFAULT_JPEG_QUALITY (psramFound() ? 10 : 12)
|
||||
#define DEFAULT_FRAME_DURATION 200
|
||||
#define DEFAULT_FRAME_SIZE "VGA (640x480)"
|
||||
#define DEFAULT_JPEG_QUALITY (psramFound() ? 12 : 14)
|
||||
|
||||
#define DEFAULT_BRIGHTNESS 0
|
||||
#define DEFAULT_CONTRAST 0
|
||||
#define DEFAULT_SATURATION 0
|
||||
#define DEFAULT_EFFECT "Normal"
|
||||
#define DEFAULT_BRIGHTNESS 0
|
||||
#define DEFAULT_CONTRAST 0
|
||||
#define DEFAULT_SATURATION 0
|
||||
#define DEFAULT_EFFECT "Normal"
|
||||
#define DEFAULT_WHITE_BALANCE true
|
||||
#define DEFAULT_WHITE_BALANCE_GAIN true
|
||||
#define DEFAULT_WHITE_BALANCE_MODE "Auto"
|
||||
@@ -39,4 +44,4 @@
|
||||
#define DEFAULT_DCW true
|
||||
#define DEFAULT_COLORBAR false
|
||||
|
||||
#define DEFAULT_LED_INTENSITY 0
|
||||
#define DEFAULT_LED_INTENSITY 0
|
||||
18
lib/micro-jpg/include/jpg.h
Normal file
@@ -0,0 +1,18 @@
|
||||
#include <stddef.h>
|
||||
|
||||
#include "jpg_section.h"
|
||||
|
||||
class jpg
|
||||
{
|
||||
public:
|
||||
bool decode(const uint8_t *jpg, size_t size);
|
||||
|
||||
const jpg_section_dqt_t *quantization_table_luminance_;
|
||||
const jpg_section_dqt_t *quantization_table_chrominance_;
|
||||
|
||||
const uint8_t *jpeg_data_start;
|
||||
const uint8_t *jpeg_data_end;
|
||||
|
||||
private:
|
||||
static const jpg_section_t *find_jpg_section(const uint8_t **ptr, const uint8_t *end, jpg_section_t::jpg_section_flag flag);
|
||||
};
|
||||
107
lib/micro-jpg/include/jpg_section.h
Normal file
@@ -0,0 +1,107 @@
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
// http://www.ietf.org/rfc/rfc2345.txt Each table is an array of 64 values given in zig-zag order, identical to the format used in a JFIF DQT marker segment.
|
||||
constexpr size_t jpeg_quantization_table_length = 64;
|
||||
|
||||
typedef struct __attribute__((packed))
|
||||
{
|
||||
enum jpg_section_flag : uint8_t
|
||||
{
|
||||
DATA = 0x00,
|
||||
SOF0 = 0xc0,
|
||||
SOF1 = 0xc1,
|
||||
SOF2 = 0xc2,
|
||||
SOF3 = 0xc3,
|
||||
DHT = 0xc4,
|
||||
SOF5 = 0xc5,
|
||||
SOF6 = 0xc6,
|
||||
SOF7 = 0xc7,
|
||||
JPG = 0xc8,
|
||||
SOF9 = 0xc9,
|
||||
SOF10 = 0xca,
|
||||
SOF11 = 0xcb,
|
||||
DAC = 0xcc,
|
||||
SOF13 = 0xcd,
|
||||
SOF14 = 0xce,
|
||||
SOF15 = 0xcf,
|
||||
RST0 = 0xd0,
|
||||
RST1 = 0xd1,
|
||||
RST2 = 0xd2,
|
||||
RST3 = 0xd3,
|
||||
RST4 = 0xd4,
|
||||
RST5 = 0xd5,
|
||||
RST6 = 0xd6,
|
||||
RST7 = 0xd7,
|
||||
SOI = 0xd8,
|
||||
EOI = 0xd9,
|
||||
SOS = 0xda,
|
||||
DQT = 0xdb,
|
||||
DNL = 0xdc,
|
||||
DRI = 0xdd,
|
||||
DHP = 0xde,
|
||||
EXP = 0xdf,
|
||||
APP0 = 0xe0,
|
||||
APP1 = 0xe1,
|
||||
APP2 = 0xe2,
|
||||
APP3 = 0xe3,
|
||||
APP4 = 0xe4,
|
||||
APP5 = 0xe5,
|
||||
APP6 = 0xe6,
|
||||
APP7 = 0xe7,
|
||||
APP8 = 0xe8,
|
||||
APP9 = 0xe9,
|
||||
APP10 = 0xea,
|
||||
APP11 = 0xeb,
|
||||
APP12 = 0xec,
|
||||
APP13 = 0xed,
|
||||
APP14 = 0xee,
|
||||
APP15 = 0xef,
|
||||
JPG0 = 0xf0,
|
||||
JPG1 = 0xf1,
|
||||
JPG2 = 0xf2,
|
||||
JPG3 = 0xf3,
|
||||
JPG4 = 0xf4,
|
||||
JPG5 = 0xf5,
|
||||
JPG6 = 0xf6,
|
||||
JPG7 = 0xf7,
|
||||
JPG8 = 0xf8,
|
||||
JPG9 = 0xf9,
|
||||
COM = 0xfe,
|
||||
JPG10 = 0xfa,
|
||||
JPG11 = 0xfb,
|
||||
JPG12 = 0xfc,
|
||||
JPG13 = 0xfd
|
||||
};
|
||||
|
||||
const uint8_t framing; // 0xff
|
||||
const jpg_section_flag flag;
|
||||
const uint8_t length_msb;
|
||||
const uint8_t length_lsb;
|
||||
const uint8_t data[];
|
||||
|
||||
static bool is_valid_flag(const jpg_section_flag flag);
|
||||
static const char *flag_name(const jpg_section_flag flag);
|
||||
uint16_t data_length() const;
|
||||
uint16_t section_length() const;
|
||||
} jpg_section_t;
|
||||
|
||||
typedef struct __attribute__((packed)) // 0xffe0
|
||||
{
|
||||
char identifier[5] = {'J', 'F', 'I', 'F', 0}; // JFIF identifier, zero-terminated
|
||||
uint8_t version_major = 1;
|
||||
uint8_t version_minor = 1; // JFIF version 1.1
|
||||
uint8_t density_units = 0; // no density units specified
|
||||
uint16_t density_hor = 1;
|
||||
uint16_t density_ver = 1; // density: 1 pixel "per pixel" horizontally and vertically
|
||||
uint8_t thumbnail_hor = 0;
|
||||
uint8_t thumbnail_ver = 0; // no thumbnail (size 0 x 0)
|
||||
} jpg_section_app0_t;
|
||||
|
||||
typedef struct __attribute__((packed)) // 0xffdb
|
||||
{
|
||||
uint8_t id; // 0= quantLuminance, 1= quantChrominance
|
||||
uint8_t data[jpeg_quantization_table_length];
|
||||
} jpg_section_dqt_t;
|
||||
14
lib/micro-jpg/library.json
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"name": "micro-jpg",
|
||||
"version": "1.0.0",
|
||||
"description": "JPEG library",
|
||||
"keywords": "",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/rzeldent/"
|
||||
},
|
||||
"build": {
|
||||
"srcDir": "src/",
|
||||
"includeDir": "include/"
|
||||
}
|
||||
}
|
||||
111
lib/micro-jpg/src/jpg.cpp
Normal file
@@ -0,0 +1,111 @@
|
||||
#include <esp32-hal-log.h>
|
||||
#include "jpg.h"
|
||||
|
||||
const jpg_section_t *jpg::find_jpg_section(const uint8_t **ptr, const uint8_t *end, jpg_section_t::jpg_section_flag flag)
|
||||
{
|
||||
log_d("find_jpeg_section 0x%02x (%s)", flag, jpg_section_t::flag_name(flag));
|
||||
while (*ptr < end)
|
||||
{
|
||||
// flag, len MSB, len LSB
|
||||
auto section = reinterpret_cast<const jpg_section_t *>((*ptr));
|
||||
if (section->framing != 0xff)
|
||||
{
|
||||
log_e("Expected framing 0xff but found: 0x%02x", section->framing);
|
||||
break;
|
||||
}
|
||||
|
||||
if (!jpg_section_t::is_valid_flag(section->flag))
|
||||
{
|
||||
log_d("Unknown section 0x%02x", flag);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Advance pointer section has a length, so not SOI (0xd8) and EOI (0xd9)
|
||||
*ptr += section->section_length();
|
||||
if (section->flag == flag)
|
||||
{
|
||||
log_d("Found section 0x%02x (%s), %d bytes", flag, jpg_section_t::flag_name(section->flag), section->section_length());
|
||||
return section;
|
||||
}
|
||||
|
||||
log_d("Skipping section: 0x%02x (%s), %d bytes", section->flag, jpg_section_t::flag_name(section->flag), section->section_length());
|
||||
}
|
||||
|
||||
// Not found
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// See https://create.stephan-brumme.com/toojpeg/
|
||||
bool jpg::decode(const uint8_t *data, size_t size)
|
||||
{
|
||||
log_d("decode_jpeg");
|
||||
// Look for start jpeg file (0xd8)
|
||||
auto ptr = data;
|
||||
auto end = ptr + size;
|
||||
|
||||
// Check for SOI (start of image) 0xff, 0xd8
|
||||
if (!find_jpg_section(&ptr, end, jpg_section_t::jpg_section_flag::SOI))
|
||||
{
|
||||
log_e("No valid start of image marker found");
|
||||
return false;
|
||||
}
|
||||
|
||||
// First quantization table (Luminance - black & white images)
|
||||
const jpg_section_t *quantization_table_section;
|
||||
if (!(quantization_table_section = find_jpg_section(&ptr, end, jpg_section_t::jpg_section_flag::DQT)))
|
||||
{
|
||||
log_e("No quantization_table_luminance section found");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (quantization_table_section->data_length() != sizeof(jpg_section_dqt_t))
|
||||
{
|
||||
log_w("Invalid length of quantization_table_luminance section. Expected %d but read %d", sizeof(jpg_section_dqt_t), quantization_table_section->data_length());
|
||||
return false;
|
||||
}
|
||||
|
||||
quantization_table_luminance_ = reinterpret_cast<const jpg_section_dqt_t *>(quantization_table_section->data);
|
||||
|
||||
// Second quantization table (Chrominance - color images)
|
||||
if (!(quantization_table_section = find_jpg_section(&ptr, end, jpg_section_t::jpg_section_flag::DQT)))
|
||||
{
|
||||
log_w("No quantization_table_chrominance section found");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (quantization_table_section->data_length() != sizeof(jpg_section_dqt_t))
|
||||
{
|
||||
log_w("Invalid length of quantization_table_chrominance section. Expected %d but read %d", sizeof(jpg_section_dqt_t), quantization_table_section->data_length());
|
||||
return false;
|
||||
}
|
||||
|
||||
quantization_table_chrominance_ = reinterpret_cast<const jpg_section_dqt_t *>(quantization_table_section->data);
|
||||
|
||||
// Start of scan
|
||||
if (!find_jpg_section(&ptr, end, jpg_section_t::jpg_section_flag::SOS))
|
||||
{
|
||||
log_e("No start of scan section found");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Start of the data sections
|
||||
jpeg_data_start = ptr;
|
||||
|
||||
log_d("Skipping over data sections");
|
||||
// Scan over all the sections. 0xff followed by not zero, is a new section
|
||||
while (ptr < end - 1 && (ptr[0] != 0xff || ptr[1] == 0))
|
||||
ptr++;
|
||||
|
||||
// Check if marker is an end of image marker
|
||||
if (!find_jpg_section(&ptr, end, jpg_section_t::jpg_section_flag::EOI))
|
||||
{
|
||||
log_e("No end of image marker found");
|
||||
return false;
|
||||
}
|
||||
|
||||
jpeg_data_end = ptr;
|
||||
|
||||
log_d("Total jpeg data: %d bytes", jpeg_data_end - jpeg_data_start);
|
||||
|
||||
return true;
|
||||
}
|
||||
154
lib/micro-jpg/src/jpg_section.cpp
Normal file
@@ -0,0 +1,154 @@
|
||||
#include "jpg_section.h"
|
||||
|
||||
uint16_t jpg_section_t::data_length() const
|
||||
{
|
||||
return (length_msb << 8) + length_lsb - sizeof(jpg_section_t::length_msb)- sizeof(jpg_section_t::length_lsb);
|
||||
}
|
||||
|
||||
uint16_t jpg_section_t::section_length() const
|
||||
{
|
||||
return flag == SOI || flag == EOI ? sizeof(jpg_section_t::framing) + sizeof(jpg_section_t::flag) : sizeof(jpg_section_t::framing) + sizeof(jpg_section_t::flag) + (length_msb << 8) + length_lsb;
|
||||
}
|
||||
|
||||
bool jpg_section_t::is_valid_flag(const jpg_section_flag flag)
|
||||
{
|
||||
return flag >= SOF0 && flag <= COM;
|
||||
}
|
||||
|
||||
// from: https://www.disktuna.com/list-of-jpeg-markers/
|
||||
const char *jpg_section_t::flag_name(const jpg_section_flag flag)
|
||||
{
|
||||
switch (flag)
|
||||
{
|
||||
case DATA:
|
||||
return "DATA"; // DATA
|
||||
case SOF0:
|
||||
return "SOF0"; // Start of Frame 0 Baseline DCT
|
||||
case SOF1:
|
||||
return "SOF1"; // Start of Frame 1 Extended Sequential DCT
|
||||
case SOF2:
|
||||
return "SOF2"; // Start of Frame 2 Progressive DCT
|
||||
case SOF3:
|
||||
return "SOF3"; // Start of Frame 3 Lossless (sequential)
|
||||
case DHT:
|
||||
return "DHT"; // Define Huffman Table
|
||||
case SOF5:
|
||||
return "SOF5"; // Start of Frame 5 Differential sequential DCT
|
||||
case SOF6:
|
||||
return "SOF6"; // Start of Frame 6 Differential progressive DCT
|
||||
case SOF7:
|
||||
return "SOF7"; // Start of Frame 7 Differential lossless (sequential)
|
||||
case JPG:
|
||||
return "JPG"; // JPEG Extensions
|
||||
case SOF9:
|
||||
return "SOF9"; // Start of Frame 9 Extended sequential DCT, Arithmetic coding
|
||||
case SOF10:
|
||||
return "SOF10"; // Start of Frame 10 Progressive DCT, Arithmetic coding
|
||||
case SOF11:
|
||||
return "SOF11"; // Start of Frame 11 Lossless (sequential), Arithmetic coding
|
||||
case DAC:
|
||||
return "DAC"; // Define Arithmetic Coding
|
||||
case SOF13:
|
||||
return "SOF13"; // Start of Frame 13 Differential sequential DCT, Arithmetic coding
|
||||
case SOF14:
|
||||
return "SOF14"; // Start of Frame 14 Differential progressive DCT, Arithmetic coding
|
||||
case SOF15:
|
||||
return "SOF15"; // Start of Frame 15 Differential lossless (sequential), Arithmetic coding
|
||||
case RST0:
|
||||
return "RST0"; // Restart Marker 0
|
||||
case RST1:
|
||||
return "RST1"; // Restart Marker 1
|
||||
case RST2:
|
||||
return "RST2"; // Restart Marker 2
|
||||
case RST3:
|
||||
return "RST3"; // Restart Marker 3
|
||||
case RST4:
|
||||
return "RST4"; // Restart Marker 4
|
||||
case RST5:
|
||||
return "RST5"; // Restart Marker 5
|
||||
case RST6:
|
||||
return "RST6"; // Restart Marker 6
|
||||
case RST7:
|
||||
return "RST7"; // Restart Marker 7
|
||||
case SOI:
|
||||
return "SOI"; // Start of Image
|
||||
case EOI:
|
||||
return "EOI"; // End of Image
|
||||
case SOS:
|
||||
return "SOS"; // Start of Scan
|
||||
case DQT:
|
||||
return "DQT"; // Define Quantization Table
|
||||
case DNL:
|
||||
return "DNL"; // Define Number of Lines (Not common)
|
||||
case DRI:
|
||||
return "DRI"; // Define Restart Interval
|
||||
case DHP:
|
||||
return "DHP"; // Define Hierarchical Progression (Not common)
|
||||
case EXP:
|
||||
return "EXP"; // Expand Reference Component (Not common)
|
||||
case APP0:
|
||||
return "APP0"; // Application Segment 0 JFIF – JFIF JPEG image, AVI1 – Motion JPEG (MJPG)
|
||||
case APP1:
|
||||
return "APP1"; // Application Segment 1 EXIF Metadata, TIFF IFD format, JPEG Thumbnail (160×120) Adobe XMP
|
||||
case APP2:
|
||||
return "APP2"; // Application Segment 2 ICC color profile, FlashPix
|
||||
case APP3:
|
||||
return "APP3"; // Application Segment 3 (Not common) JPS Tag for Stereoscopic JPEG images
|
||||
case APP4:
|
||||
return "APP4"; // Application Segment 4 (Not common)
|
||||
case APP5:
|
||||
return "APP5"; // Application Segment 5 (Not common)
|
||||
case APP6:
|
||||
return "APP6"; // Application Segment 6 (Not common) NITF Lossles profile
|
||||
case APP7:
|
||||
return "APP7"; // Application Segment 7 (Not common)
|
||||
case APP8:
|
||||
return "APP8"; // Application Segment 8 (Not common)
|
||||
case APP9:
|
||||
return "APP9"; // Application Segment 9 (Not common)
|
||||
case APP10:
|
||||
return "APP10"; // Application Segment 10 PhoTags (Not common) ActiveObject (multimedia messages / captions)
|
||||
case APP11:
|
||||
return "APP11"; // Application Segment 11 (Not common) HELIOS JPEG Resources (OPI Postscript)
|
||||
case APP12:
|
||||
return "APP12"; // Application Segment 12 Picture Info (older digicams), Photoshop Save for Web: Ducky
|
||||
case APP13:
|
||||
return "APP13"; // Application Segment 13 Photoshop Save As: IRB, 8BIM, IPTC
|
||||
case APP14:
|
||||
return "APP14"; // Application Segment 14 (Not common)
|
||||
case APP15:
|
||||
return "APP15"; // Application Segment 15 (Not common)
|
||||
case JPG0:
|
||||
return "JPG0"; // JPEG Extension 0
|
||||
case JPG1:
|
||||
return "JPG1"; // JPEG Extension 1
|
||||
case JPG2:
|
||||
return "JPG2"; // JPEG Extension 2
|
||||
case JPG3:
|
||||
return "JPG3"; // JPEG Extension 3
|
||||
case JPG4:
|
||||
return "JPG4"; // JPEG Extension 4
|
||||
case JPG5:
|
||||
return "JPG5"; // JPEG Extension 5
|
||||
case JPG6:
|
||||
return "JPG6"; // JPEG Extension 6
|
||||
case JPG7:
|
||||
return "JPG7"; // SOF48 JPEG Extension 7 JPEG-LS Lossless JPEG
|
||||
case JPG8:
|
||||
return "JPG8"; // LSE JPEG Extension 8 JPEG-LS Extension Lossless JPEG Extension Parameters
|
||||
case JPG9:
|
||||
return "JPG9"; // JPEG Extension 9 (Not common)
|
||||
case JPG10:
|
||||
return "JPG10"; // JPEG Extension 10 (Not common)
|
||||
case JPG11:
|
||||
return "JPG11"; // JPEG Extension 11 (Not common)
|
||||
case JPG12:
|
||||
return "JPG12"; // JPEG Extension 12 (Not common)
|
||||
case JPG13:
|
||||
return "JPG13"; // JPEG Extension 13 (Not common)
|
||||
case COM:
|
||||
return "COM"; // Comment
|
||||
}
|
||||
|
||||
return "Unknown";
|
||||
}
|
||||
25
lib/micro-rtsp-server/include/micro_rtsp_camera.h
Normal file
@@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
|
||||
#include <micro_rtsp_source.h>
|
||||
#include <esp_camera.h>
|
||||
|
||||
class micro_rtsp_camera : public micro_rtsp_source
|
||||
{
|
||||
public:
|
||||
micro_rtsp_camera();
|
||||
virtual ~micro_rtsp_camera();
|
||||
|
||||
esp_err_t initialize(camera_config_t *camera_config);
|
||||
esp_err_t deinitialize();
|
||||
|
||||
virtual void update_frame();
|
||||
|
||||
virtual uint8_t *data() const { return fb_->buf; }
|
||||
virtual size_t width() const { return fb_->width; }
|
||||
virtual size_t height() const { return fb_->height; }
|
||||
virtual size_t size() const { return fb_->len; }
|
||||
|
||||
private:
|
||||
esp_err_t init_result_;
|
||||
camera_fb_t *fb_;
|
||||
};
|
||||
56
lib/micro-rtsp-server/include/micro_rtsp_requests.h
Normal file
@@ -0,0 +1,56 @@
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
class micro_rtsp_requests
|
||||
{
|
||||
public:
|
||||
std::string process_request(const std::string& request);
|
||||
bool active() const { return stream_active_; }
|
||||
|
||||
private:
|
||||
// enum rtsp_command
|
||||
// {
|
||||
// rtsp_command_unknown,
|
||||
// rtsp_command_options, // OPTIONS
|
||||
// rtsp_command_describe, // DESCRIBE
|
||||
// rtsp_command_setup, // SETUP
|
||||
// rtsp_command_play, // PLAY
|
||||
// rtsp_command_teardown // TEARDOWN
|
||||
// };
|
||||
|
||||
static const std::string available_stream_name_;
|
||||
|
||||
//rtsp_command parse_command(const std::string &request);
|
||||
//static bool parse_cseq(const std::string &line, unsigned long &cseq);
|
||||
bool parse_client_port(const std::string &request);
|
||||
//bool parse_stream_url(const std::string &request);
|
||||
|
||||
//static std::string date_header();
|
||||
static std::string handle_rtsp_error(unsigned long cseq, unsigned short code, const std::string &message);
|
||||
|
||||
static std::string handle_options(unsigned long cseq);
|
||||
static std::string handle_describe(unsigned long cseq, const std::string &request);
|
||||
std::string handle_setup(unsigned long cseq, const std::map<std::string, std::string> &request);
|
||||
std::string handle_play(unsigned long cseq);
|
||||
std::string handle_teardown(unsigned long cseq);
|
||||
|
||||
//unsigned long cseq_;
|
||||
|
||||
// std::string host_url_;
|
||||
// unsigned short host_port_;
|
||||
// std::string stream_name_;
|
||||
|
||||
bool tcp_transport_;
|
||||
unsigned short start_client_port_;
|
||||
unsigned short end_client_port_;
|
||||
|
||||
unsigned short rtp_streamer_port_;
|
||||
unsigned short rtcp_streamer_port_;
|
||||
|
||||
unsigned long rtsp_session_id_;
|
||||
|
||||
bool stream_active_;
|
||||
bool stream_stopped_;
|
||||
};
|
||||
45
lib/micro-rtsp-server/include/micro_rtsp_server.h
Normal file
@@ -0,0 +1,45 @@
|
||||
#pragma once
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <WiFiServer.h>
|
||||
|
||||
#include <string>
|
||||
#include <list>
|
||||
|
||||
#include "micro_rtsp_camera.h"
|
||||
#include "micro_rtsp_requests.h"
|
||||
#include "micro_rtsp_streamer.h"
|
||||
|
||||
class micro_rtsp_server : WiFiServer
|
||||
{
|
||||
public:
|
||||
micro_rtsp_server(micro_rtsp_source &source);
|
||||
~micro_rtsp_server();
|
||||
|
||||
void begin(unsigned short port = 554);
|
||||
void end();
|
||||
|
||||
unsigned get_frame_interval() const { return frame_interval_; }
|
||||
unsigned set_frame_interval(unsigned value) { return frame_interval_ = value; }
|
||||
|
||||
void loop();
|
||||
|
||||
size_t clients() const { return clients_.size(); }
|
||||
|
||||
class rtsp_client : public WiFiClient, public micro_rtsp_requests
|
||||
{
|
||||
public:
|
||||
rtsp_client(const WiFiClient &client);
|
||||
~rtsp_client();
|
||||
|
||||
void handle_request();
|
||||
};
|
||||
|
||||
private:
|
||||
micro_rtsp_source &source_;
|
||||
unsigned frame_interval_;
|
||||
unsigned long next_frame_update_;
|
||||
unsigned long next_check_client_;
|
||||
micro_rtsp_streamer streamer_;
|
||||
std::list<rtsp_client> clients_;
|
||||
};
|
||||
16
lib/micro-rtsp-server/include/micro_rtsp_source.h
Normal file
@@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
// Interface for a video source
|
||||
class micro_rtsp_source
|
||||
{
|
||||
public:
|
||||
virtual void update_frame() = 0;
|
||||
|
||||
virtual uint8_t *data() const = 0;
|
||||
virtual size_t width() const = 0;
|
||||
virtual size_t height() const = 0;
|
||||
virtual size_t size() const = 0;
|
||||
};
|
||||
43
lib/micro-rtsp-server/include/micro_rtsp_streamer.h
Normal file
@@ -0,0 +1,43 @@
|
||||
#pragma once
|
||||
|
||||
#include <jpg_section.h>
|
||||
#include <micro_rtsp_camera.h> // Add this line to include the definition of micro_rtsp_camera
|
||||
#include <micro_rtsp_structs.h>
|
||||
|
||||
// https://en.wikipedia.org/wiki/Maximum_transmission_unit
|
||||
constexpr size_t max_wifi_mtu = 2304;
|
||||
// Payload JPG - https://www.ietf.org/rfc/rfc1890.txt
|
||||
constexpr uint8_t RTP_PAYLOAD_JPG = 26;
|
||||
|
||||
// One of the types below will be returned, the jpeg_packet_with_quantization_t for the first packet, then the jpeg_packet_t
|
||||
|
||||
typedef struct __attribute__((packed))
|
||||
{
|
||||
rtp_over_tcp_hdr_t rtp_over_tcp_hdr;
|
||||
rtp_hdr_t rtp_hdr;
|
||||
jpeg_hdr_t jpeg_hdr;
|
||||
jpeg_hdr_qtable_t jpeg_hdr_qtable;
|
||||
uint8_t quantization_table_luminance[jpeg_quantization_table_length];
|
||||
uint8_t quantization_table_chrominance[jpeg_quantization_table_length];
|
||||
uint8_t jpeg_data[];
|
||||
} jpeg_packet_with_quantization_t;
|
||||
|
||||
typedef struct __attribute__((packed))
|
||||
{
|
||||
rtp_over_tcp_hdr_t rtp_over_tcp_hdr;
|
||||
rtp_hdr_t rtp_hdr;
|
||||
jpeg_hdr_t jpeg_hdr;
|
||||
uint8_t jpeg_data[];
|
||||
} jpeg_packet_t;
|
||||
|
||||
class micro_rtsp_streamer
|
||||
{
|
||||
public:
|
||||
micro_rtsp_streamer(const micro_rtsp_source& source);
|
||||
rtp_over_tcp_hdr_t *create_jpg_packet(const uint8_t *jpg_scan, const uint8_t *jpg_scan_end, uint8_t **jpg_offset, const uint32_t timestamp, const uint8_t *quantization_table_luminance, const uint8_t *quantization_table_chrominance);
|
||||
|
||||
private:
|
||||
const micro_rtsp_source& source_;
|
||||
uint32_t ssrc_;
|
||||
uint16_t sequence_number_;
|
||||
};
|
||||
51
lib/micro-rtsp-server/include/micro_rtsp_structs.h
Normal file
@@ -0,0 +1,51 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
// https://www.ietf.org/rfc/rfc2326#section-10.12
|
||||
typedef struct __attribute__((packed))
|
||||
{
|
||||
char magic = '$'; // Magic encapsulation ASCII dollar sign (24 hexadecimal)
|
||||
uint8_t channel; // Channel identifier
|
||||
uint16_t length; // Network order
|
||||
} rtp_over_tcp_hdr_t;
|
||||
|
||||
// RTP data header - http://www.ietf.org/rfc/rfc3550.txt
|
||||
typedef struct __attribute__((packed))
|
||||
{
|
||||
uint16_t version : 2; // protocol version
|
||||
uint16_t padding : 1; // padding flag
|
||||
uint16_t extension : 1; // header extension flag
|
||||
uint16_t cc : 4; // CSRC count
|
||||
uint16_t marker : 1; // marker bit
|
||||
uint16_t pt : 7; // payload type
|
||||
uint16_t seq : 16; // sequence number
|
||||
uint32_t ts; // timestamp
|
||||
uint32_t ssrc; // synchronization source
|
||||
} rtp_hdr_t;
|
||||
|
||||
// https://datatracker.ietf.org/doc/html/rfc2435
|
||||
typedef struct __attribute__((packed))
|
||||
{
|
||||
uint32_t tspec : 8; // type-specific field
|
||||
uint32_t off : 24; // fragment byte offset
|
||||
uint8_t type; // id of jpeg decoder params
|
||||
uint8_t q; // Q values 0-127 indicate the quantization tables. JPEG types 0 and 1 (and their corresponding types 64 and 65)
|
||||
uint8_t width; // frame width in 8 pixel blocks
|
||||
uint8_t height; // frame height in 8 pixel blocks
|
||||
} jpeg_hdr_t;
|
||||
|
||||
typedef struct __attribute__((packed))
|
||||
{
|
||||
uint16_t dri;
|
||||
uint16_t f : 1;
|
||||
uint16_t l : 1;
|
||||
uint16_t count : 14;
|
||||
} jpeg_hdr_rst_t;
|
||||
|
||||
typedef struct __attribute__((packed))
|
||||
{
|
||||
uint8_t mbz;
|
||||
uint8_t precision;
|
||||
uint16_t length;
|
||||
} jpeg_hdr_qtable_t;
|
||||
20
lib/micro-rtsp-server/library.json
Normal file
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"name": "micro-rtsp-streamer",
|
||||
"version": "1.0.0",
|
||||
"description": "RTSP Server",
|
||||
"keywords": "",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/rzeldent/micro-rtsp-streamer"
|
||||
},
|
||||
|
||||
"build": {
|
||||
"srcDir": "src/",
|
||||
"includeDir": "include/"
|
||||
},
|
||||
|
||||
"dependencies": {
|
||||
"micro-jpg": "^1.0.0",
|
||||
"espressif/esp32-camera": "^2.0.4"
|
||||
}
|
||||
}
|
||||
39
lib/micro-rtsp-server/src/micro_rtsp_camera.cpp
Normal file
@@ -0,0 +1,39 @@
|
||||
#include <esp32-hal-log.h>
|
||||
|
||||
#include "micro_rtsp_camera.h"
|
||||
|
||||
micro_rtsp_camera::micro_rtsp_camera()
|
||||
{
|
||||
init_result_ == ESP_FAIL;
|
||||
}
|
||||
|
||||
micro_rtsp_camera::~micro_rtsp_camera()
|
||||
{
|
||||
deinitialize();
|
||||
}
|
||||
|
||||
esp_err_t micro_rtsp_camera::initialize(camera_config_t *camera_config)
|
||||
{
|
||||
log_v("camera_config={.pin_pwdn:%u,.pin_reset:%u,.pin_xclk:%u,.pin_sccb_sda:%u,.pin_sccb_scl:%u,.pin_d7:%u,.pin_d6:%u,.pin_d5:%u,.pin_d4:%u,.pin_d3:%u,.pin_d2:%u,.pin_d1:%u,.pin_d0:%u,.pin_vsync:%u,.pin_href:%u,.pin_pclk:%u,.xclk_freq_hz:%d,.ledc_timer:%u,ledc_channel:%u,.pixel_format:%d,.frame_size:%d,.jpeg_quality:%d,.fb_count:%d,.fb_location%d,.grab_mode:%d,sccb_i2c_port:%d}", camera_config->pin_pwdn, camera_config->pin_reset, camera_config->pin_xclk, camera_config->pin_sccb_sda, camera_config->pin_sccb_scl, camera_config->pin_d7, camera_config->pin_d6, camera_config->pin_d5, camera_config->pin_d4, camera_config->pin_d3, camera_config->pin_d2, camera_config->pin_d1, camera_config->pin_d0, camera_config->pin_vsync, camera_config->pin_href, camera_config->pin_pclk, camera_config->xclk_freq_hz, camera_config->ledc_timer, camera_config->ledc_channel, camera_config->pixel_format, camera_config->frame_size, camera_config->jpeg_quality, camera_config->fb_count, camera_config->fb_location, camera_config->grab_mode, camera_config->sccb_i2c_port);
|
||||
|
||||
init_result_ = esp_camera_init(camera_config);
|
||||
if (init_result_ == ESP_OK)
|
||||
update_frame();
|
||||
else
|
||||
log_e("Camera initialization failed: 0x%02x", init_result_);
|
||||
|
||||
return init_result_;
|
||||
}
|
||||
|
||||
esp_err_t micro_rtsp_camera::deinitialize()
|
||||
{
|
||||
return init_result_ == ESP_OK ? esp_camera_deinit() : ESP_OK;
|
||||
}
|
||||
|
||||
void micro_rtsp_camera::update_frame()
|
||||
{
|
||||
if (fb_)
|
||||
esp_camera_fb_return(fb_);
|
||||
|
||||
fb_ = esp_camera_fb_get();
|
||||
}
|
||||
217
lib/micro-rtsp-server/src/micro_rtsp_requests.cpp
Normal file
@@ -0,0 +1,217 @@
|
||||
#include <esp32-hal-log.h>
|
||||
|
||||
#include <iomanip>
|
||||
#include <unordered_map>
|
||||
#include <regex>
|
||||
#include "micro_rtsp_requests.h"
|
||||
|
||||
// https://datatracker.ietf.org/doc/html/rfc2326
|
||||
|
||||
const std::string micro_rtsp_requests::available_stream_name_ = "/mjpeg/1";
|
||||
|
||||
bool micro_rtsp_requests::parse_client_port(const std::string &request)
|
||||
{
|
||||
log_v("request: %s", request.c_str());
|
||||
|
||||
std::regex regex("client_port=([0-9]+)", std::regex_constants::icase);
|
||||
std::smatch match;
|
||||
if (!std::regex_match(request, match, regex))
|
||||
{
|
||||
log_e("client_port not found");
|
||||
return false;
|
||||
}
|
||||
|
||||
start_client_port_ = std::stoi(match[1].str());
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string micro_rtsp_requests::handle_rtsp_error(unsigned long cseq, unsigned short code, const std::string &message)
|
||||
{
|
||||
log_e("code: %d, message: %s", code, message.c_str());
|
||||
auto now = time(nullptr);
|
||||
std::ostringstream oss;
|
||||
oss << "RTSP/1.0 " << code << " " << message << "\r\n"
|
||||
<< "CSeq: " << cseq << "\r\n"
|
||||
<< std::put_time(std::gmtime(&now), "Date: %a, %b %d %Y %H:%M:%S GMT") << "\r\n";
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
// OPTIONS rtsp://192.168.178.247:554/mjpeg/1 RTSP/1.0
|
||||
// CSeq: 2
|
||||
// User-Agent: LibVLC/3.0.20 (LIVE555 Streaming Media v2016.11.28)
|
||||
std::string micro_rtsp_requests::handle_options(unsigned long cseq)
|
||||
{
|
||||
auto now = time(nullptr);
|
||||
std::ostringstream oss;
|
||||
oss << "RTSP/1.0 200 OK\r\n"
|
||||
<< "CSeq: " << cseq << "\r\n"
|
||||
<< std::put_time(std::gmtime(&now), "Date: %a, %b %d %Y %H:%M:%S GMT") << "\r\n"
|
||||
<< "Content-Length: 0\r\n"
|
||||
<< "Public: DESCRIBE, SETUP, TEARDOWN, PLAY, PAUSE\r\n"
|
||||
<< "\r\n";
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
// DESCRIBE rtsp://192.168.178.247:554/mjpeg/1 RTSP/1.0
|
||||
// CSeq: 3
|
||||
// User-Agent: LibVLC/3.0.20 (LIVE555 Streaming Media v2016.11.28)
|
||||
// Accept: application/sdp
|
||||
std::string micro_rtsp_requests::handle_describe(unsigned long cseq, const std::string &request)
|
||||
{
|
||||
// Parse the url
|
||||
static const std::regex regex_url("rtsp:\\/\\/([^\\/:]+)(?::(\\d+))?(\\/.*)?\\s+RTSP\\/1\\.0", std::regex_constants::icase);
|
||||
std::smatch match;
|
||||
if (!std::regex_search(request, match, regex_url))
|
||||
return handle_rtsp_error(cseq, 400, "Invalid URL");
|
||||
|
||||
auto host = match[1].str();
|
||||
auto port = match[2].str().length() > 0 ? std::stoi(match[2].str()) : 554;
|
||||
auto path = match[3].str();
|
||||
log_i("host: %s, port: %d, path: %s", host.c_str(), port, path.c_str());
|
||||
|
||||
if (path != available_stream_name_)
|
||||
return handle_rtsp_error(cseq, 404, "Stream Not Found");
|
||||
|
||||
std::ostringstream osbody;
|
||||
osbody << "v=0\r\n"
|
||||
<< "o=- " << std::rand() << " 1 IN IP4 " << host << "\r\n"
|
||||
<< "s=\r\n"
|
||||
<< "t=0 0\r\n" // start / stop - 0 -> unbounded and permanent session
|
||||
<< "m=video 0 RTP/AVP 26\r\n" // currently we just handle UDP sessions
|
||||
<< "c=IN IP4 0.0.0.0\r\n";
|
||||
auto body = osbody.str();
|
||||
|
||||
auto now = time(nullptr);
|
||||
std::ostringstream oss;
|
||||
oss << "RTSP/1.0 200 OK\r\n"
|
||||
<< "CSeq: " << cseq << "\r\n"
|
||||
<< std::put_time(std::gmtime(&now), "Date: %a, %b %d %Y %H:%M:%S GMT") << "\r\n"
|
||||
<< "Content-Base: rtsp://" << host << ":" << port << path << "/" << "\r\n"
|
||||
<< "Content-Type: application/sdp\r\n"
|
||||
<< "Content-Length: " << body.size() << "\r\n"
|
||||
<< "\r\n"
|
||||
<< body;
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
// SETUP rtsp://192.168.178.247:554/mjpeg/1 RTSP/1.0
|
||||
// CSeq: 0
|
||||
// Transport: RTP/AVP;unicast;client_port=9058-9059
|
||||
std::string micro_rtsp_requests::handle_setup(unsigned long cseq, const std::map<std::string, std::string> &lines)
|
||||
{
|
||||
log_v("request: %s", request.c_str());
|
||||
|
||||
auto it = lines.find("Transport");
|
||||
if (it == lines.end())
|
||||
return handle_rtsp_error(cseq, 400, "No Transport Header Found");
|
||||
|
||||
static const std::regex regex_transport("\\s+RTP\\/AVP(\\/TCP)?;unicast;client_port=(\\d+)-(\\d+)", std::regex_constants::icase);
|
||||
std::smatch match;
|
||||
if (!std::regex_search(it->second, match, regex_transport))
|
||||
return handle_rtsp_error(cseq, 400, "Could Not Parse Transport");
|
||||
|
||||
tcp_transport_ = match[1].str().length() > 0;
|
||||
start_client_port_ = std::stoi(match[2].str());
|
||||
end_client_port_ = std::stoi(match[3].str());
|
||||
log_i("tcp_transport: %d, start_client_port: %d, end_client_port: %d", tcp_transport_, start_client_port_, end_client_port_);
|
||||
|
||||
std::ostringstream ostransport;
|
||||
if (tcp_transport_)
|
||||
ostransport << "RTP/AVP/TCP;unicast;interleaved=0-1";
|
||||
else
|
||||
ostransport << "RTP/AVP;unicast;destination=127.0.0.1;source=127.0.0.1;client_port=" << start_client_port_ << "-" << end_client_port_ + 1 << ";server_port=" << rtp_streamer_port_ << "-" << rtp_streamer_port_/*rtcp_streamer_port_*/;
|
||||
|
||||
auto now = time(nullptr);
|
||||
std::ostringstream oss;
|
||||
oss << "RTSP/1.0 200 OK\r\n"
|
||||
<< "CSeq: " << cseq << "\r\n"
|
||||
<< std::put_time(std::gmtime(&now), "Date: %a, %b %d %Y %H:%M:%S GMT") << "\r\n"
|
||||
<< "Transport: " << ostransport.str() << "\r\n"
|
||||
<< "Session: " << rtsp_session_id_<< "\r\n";
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
std::string micro_rtsp_requests::handle_play(unsigned long cseq)
|
||||
{
|
||||
log_v("request: %s", request.c_str());
|
||||
|
||||
stream_active_ = true;
|
||||
|
||||
auto now = time(nullptr);
|
||||
std::ostringstream oss;
|
||||
oss << "RTSP/1.0 200 OK\r\n"
|
||||
<< "CSeq: " << cseq << "\r\n"
|
||||
<< std::put_time(std::gmtime(&now), "Date: %a, %b %d %Y %H:%M:%S GMT") << "\r\n"
|
||||
<< "Range: npt=0.000-\r\n"
|
||||
<< "Session: " << rtsp_session_id_ << "\r\n"
|
||||
<< "RTP-Info: url=rtsp://127.0.0.1:8554" << available_stream_name_ << "/track1" << "\r\n"
|
||||
<< "\r\n";
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
std::string micro_rtsp_requests::handle_teardown(unsigned long cseq)
|
||||
{
|
||||
log_v("request: %s", request.c_str());
|
||||
|
||||
stream_stopped_ = true;
|
||||
|
||||
auto now = time(nullptr);
|
||||
std::ostringstream oss;
|
||||
oss << "RTSP/1.0 200 OK\r\n"
|
||||
<< "CSeq: " << cseq << "\r\n"
|
||||
<< std::put_time(std::gmtime(&now), "Date: %a, %b %d %Y %H:%M:%S GMT") << "\r\n"
|
||||
<< "\r\n";
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
// Parse a request e.g.
|
||||
// Request: OPTIONS rtsp://192.168.178.247:554/mjpeg/1 RTSP/1.0
|
||||
// CSeq: 2
|
||||
// User-Agent: LibVLC/3.0.20 (LIVE555 Streaming Media v2016.11.28)
|
||||
|
||||
std::string micro_rtsp_requests::process_request(const std::string &request)
|
||||
{
|
||||
log_v("request: %s", request.c_str());
|
||||
|
||||
std::stringstream ss(request);
|
||||
// Get the request line
|
||||
std::string request_line;
|
||||
if (!std::getline(ss, request_line))
|
||||
return handle_rtsp_error(0, 400, "No Request Found");
|
||||
|
||||
// Create a map with headers
|
||||
std::string line;
|
||||
std::map<std::string, std::string> headers;
|
||||
std::size_t pos;
|
||||
while (std::getline(ss, line))
|
||||
{
|
||||
if ((pos = line.find(':')) != std::string::npos)
|
||||
headers[line.substr(0, pos)] = line.substr(pos + 1);
|
||||
// else
|
||||
// log_e("No : found for header: %s", line.c_str());
|
||||
}
|
||||
|
||||
log_i("request_line: %s", request_line.c_str());
|
||||
for (const auto &header : headers)
|
||||
log_i("header: %s: %s", header.first.c_str(), header.second.c_str());
|
||||
|
||||
// Check for CSeq
|
||||
const auto cseq_it = headers.find("CSeq");
|
||||
if (cseq_it == headers.end())
|
||||
return handle_rtsp_error(0, 400, "No Sequence Found");
|
||||
|
||||
auto cseq = std::stoul(cseq_it->second);
|
||||
|
||||
if (request_line.find("OPTIONS") == 0)
|
||||
return handle_options(cseq);
|
||||
if (request_line.find("DESCRIBE") == 0)
|
||||
return handle_describe(cseq, request_line);
|
||||
if (request_line.find("SETUP") == 0)
|
||||
return handle_setup(cseq, headers);
|
||||
if (request_line.find("PLAY") == 0)
|
||||
return handle_play(cseq);
|
||||
if (request_line.find("TEARDOWN") == 0)
|
||||
return handle_teardown(cseq);
|
||||
|
||||
return handle_rtsp_error(cseq, 400, "Unknown Command or malformed request");
|
||||
}
|
||||
108
lib/micro-rtsp-server/src/micro_rtsp_server.cpp
Normal file
@@ -0,0 +1,108 @@
|
||||
#include <micro_rtsp_server.h>
|
||||
#include <jpg.h>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
|
||||
// Check client connections every 100 milliseconds
|
||||
#define CHECK_CLIENT_INTERVAL 10
|
||||
|
||||
micro_rtsp_server::micro_rtsp_server(micro_rtsp_source &source)
|
||||
: source_(source), streamer_(source)
|
||||
{
|
||||
}
|
||||
|
||||
micro_rtsp_server::~micro_rtsp_server()
|
||||
{
|
||||
end();
|
||||
}
|
||||
|
||||
void micro_rtsp_server::begin(unsigned short port /*= 554*/)
|
||||
{
|
||||
WiFiServer::begin(port);
|
||||
}
|
||||
|
||||
void micro_rtsp_server::end()
|
||||
{
|
||||
WiFiServer::end();
|
||||
}
|
||||
|
||||
void micro_rtsp_server::loop()
|
||||
{
|
||||
auto now = millis();
|
||||
|
||||
if (next_check_client_ < now)
|
||||
{
|
||||
log_v("Check for new client");
|
||||
next_check_client_ = now + CHECK_CLIENT_INTERVAL;
|
||||
|
||||
// Check if a client wants to connect
|
||||
auto client = accept();
|
||||
if (client)
|
||||
clients_.push_back(rtsp_client(client));
|
||||
|
||||
// Check for idle clients
|
||||
clients_.remove_if([](rtsp_client &c)
|
||||
{ return !c.connected(); });
|
||||
|
||||
for (auto client : clients_)
|
||||
client.handle_request();
|
||||
}
|
||||
|
||||
if (next_frame_update_ < now)
|
||||
{
|
||||
log_v("Stream frame t=%d", next_frame_update_);
|
||||
next_frame_update_ = now + frame_interval_;
|
||||
|
||||
auto ts = time(nullptr);
|
||||
// Get next jpg frame
|
||||
source_.update_frame();
|
||||
// Decode to get quantitation- and scan data
|
||||
jpg jpg;
|
||||
auto jpg_data = source_.data();
|
||||
auto jpg_size = source_.size();
|
||||
assert(jpg.decode(jpg_data, jpg_size));
|
||||
|
||||
auto jpg_scan_current = (uint8_t *)jpg.jpeg_data_start;
|
||||
while (jpg_scan_current < jpg.jpeg_data_end)
|
||||
{
|
||||
auto packet = streamer_.create_jpg_packet(jpg.jpeg_data_start, jpg.jpeg_data_end, &jpg_scan_current, ts, jpg.quantization_table_luminance_->data, jpg.quantization_table_chrominance_->data);
|
||||
for (auto client : clients_)
|
||||
{
|
||||
log_i("Stream frame to client: 0x%08x", client);
|
||||
// RTP over TCP encapsulates in a $
|
||||
client.write((const uint8_t *)packet, packet->length + sizeof(rtp_over_tcp_hdr_t));
|
||||
// TODO: UDP
|
||||
}
|
||||
free(packet);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
micro_rtsp_server::rtsp_client::rtsp_client(const WiFiClient &wifi_client)
|
||||
: WiFiClient(wifi_client)
|
||||
{
|
||||
}
|
||||
|
||||
micro_rtsp_server::rtsp_client::~rtsp_client()
|
||||
{
|
||||
stop();
|
||||
}
|
||||
|
||||
void micro_rtsp_server::rtsp_client::handle_request()
|
||||
{
|
||||
// Read if data available
|
||||
auto bytes_available = available();
|
||||
if (bytes_available > 0)
|
||||
{
|
||||
std::string request(bytes_available, '\0');
|
||||
if (read((uint8_t *)&request[0], bytes_available) == bytes_available)
|
||||
{
|
||||
request.resize(bytes_available);
|
||||
log_i("Request: %s", request.c_str());
|
||||
auto response = process_request(request);
|
||||
log_i("Response: %s", response.c_str());
|
||||
println(response.c_str());
|
||||
println();
|
||||
}
|
||||
}
|
||||
}
|
||||
82
lib/micro-rtsp-server/src/micro_rtsp_streamer.cpp
Normal file
@@ -0,0 +1,82 @@
|
||||
#include <stddef.h>
|
||||
#include <memory.h>
|
||||
#include <esp32-hal-log.h>
|
||||
|
||||
#include "micro_rtsp_streamer.h"
|
||||
#include "esp_random.h"
|
||||
|
||||
micro_rtsp_streamer::micro_rtsp_streamer(const micro_rtsp_source &source)
|
||||
: source_(source)
|
||||
{
|
||||
// Random number
|
||||
ssrc_ = esp_random();
|
||||
sequence_number_ = 0;
|
||||
}
|
||||
|
||||
rtp_over_tcp_hdr_t *micro_rtsp_streamer::create_jpg_packet(const uint8_t *jpg_scan, const uint8_t *jpg_scan_end, uint8_t **jpg_offset, const uint32_t timestamp, const uint8_t *quantization_table_luminance, const uint8_t *quantization_table_chrominance)
|
||||
{
|
||||
log_v("jpg_scan:0x%08x, jpg_scan_end:0x%08x, jpg_offset:0x%08x, timestamp:%d, quantization_table_luminance:0x%08x, quantization_table_chrominance:0x%08x", jpg_scan, jpg_scan_end, jpg_offset, timestamp, quantization_table_luminance, quantization_table_chrominance);
|
||||
|
||||
// The MTU of wireless networks is 2,312 bytes. This size includes the packet headers.
|
||||
const auto isFirstFragment = jpg_scan == *jpg_offset;
|
||||
const auto include_quantization_tables = isFirstFragment && quantization_table_luminance != nullptr && quantization_table_chrominance != nullptr;
|
||||
// Quantization tables musty be included in the first packet
|
||||
const auto headers_size = include_quantization_tables ? sizeof(jpeg_packet_with_quantization_t) : sizeof(jpeg_packet_t);
|
||||
const auto payload_size = max_wifi_mtu - headers_size;
|
||||
|
||||
const auto jpg_bytes_left = jpg_scan_end - *jpg_offset;
|
||||
const bool isLastFragment = jpg_bytes_left <= payload_size;
|
||||
const auto jpg_bytes = isLastFragment ? jpg_bytes_left : payload_size;
|
||||
const uint16_t packet_size = headers_size + jpg_bytes;
|
||||
|
||||
const auto packet = static_cast<jpeg_packet_t *>(calloc(1, packet_size));
|
||||
|
||||
// 4 bytes RTP over TCP header
|
||||
packet->rtp_over_tcp_hdr.channel = 0;
|
||||
packet->rtp_over_tcp_hdr.length = packet_size;
|
||||
log_v("rtp_over_tcp_hdr_t={.magic=%c,.channel=%u,.length=%u}", packet->rtp_over_tcp_hdr.magic, packet->rtp_over_tcp_hdr.channel, packet->rtp_over_tcp_hdr.length);
|
||||
|
||||
// 12 bytes RTP header
|
||||
packet->rtp_hdr.version = 2;
|
||||
packet->rtp_hdr.marker = isLastFragment;
|
||||
packet->rtp_hdr.pt = RTP_PAYLOAD_JPG;
|
||||
packet->rtp_hdr.seq = sequence_number_;
|
||||
packet->rtp_hdr.ts = timestamp;
|
||||
packet->rtp_hdr.ssrc = ssrc_;
|
||||
log_v("rtp_hdr={.version:%u,.padding:%u,.extension:%u,.cc:%u,.marker:%u,.pt:%u,.seq:%u,.ts:%u,.ssrc:%u}", packet->rtp_hdr.version, packet->rtp_hdr.padding, packet->rtp_hdr.extension, packet->rtp_hdr.cc, packet->rtp_hdr.marker, packet->rtp_hdr.pt, packet->rtp_hdr.seq, packet->rtp_hdr.ts, packet->rtp_hdr.ssrc);
|
||||
|
||||
// 8 bytes JPEG payload header
|
||||
packet->jpeg_hdr.tspec = 0; // type-specific field
|
||||
packet->jpeg_hdr.off = (uint32_t)(*jpg_offset - jpg_scan); // fragment byte offset (24 bits in jpg)
|
||||
packet->jpeg_hdr.type = 0; // id of jpeg decoder params
|
||||
packet->jpeg_hdr.q = (uint8_t)(include_quantization_tables ? 0x80 : 0x5e); // quantization factor (or table id) 5eh=94d
|
||||
packet->jpeg_hdr.width = (uint8_t)(source_.width() >> 3); // frame width in 8 pixel blocks
|
||||
packet->jpeg_hdr.height = (uint8_t)(source_.height() >> 3); // frame height in 8 pixel blocks
|
||||
log_v("jpeg_hdr={.tspec:%u,.off:0x%6x,.type:0x2%x,.q:%u,.width:%u.height:%u}", packet->jpeg_hdr.tspec, packet->jpeg_hdr.off, packet->jpeg_hdr.type, packet->jpeg_hdr.q, packet->jpeg_hdr.width, packet->jpeg_hdr.height);
|
||||
|
||||
// Only in first packet of the frame
|
||||
if (include_quantization_tables)
|
||||
{
|
||||
auto packet_with_quantization = reinterpret_cast<jpeg_packet_with_quantization_t *>(packet);
|
||||
packet_with_quantization->jpeg_hdr_qtable.mbz = 0;
|
||||
packet_with_quantization->jpeg_hdr_qtable.precision = 0; // 8 bit precision
|
||||
packet_with_quantization->jpeg_hdr_qtable.length = jpeg_quantization_table_length + jpeg_quantization_table_length;
|
||||
log_v("jpeg_hdr_qtable={.mbz:%u,.precision:%u,.length:%u}", packet_with_quantization->jpeg_hdr_qtable.mbz, packet_with_quantization->jpeg_hdr_qtable.precision, packet_with_quantization->jpeg_hdr_qtable.length);
|
||||
memcpy(packet_with_quantization->quantization_table_luminance, quantization_table_luminance, jpeg_quantization_table_length);
|
||||
memcpy(packet_with_quantization->quantization_table_chrominance, quantization_table_chrominance, jpeg_quantization_table_length);
|
||||
// Copy JPG data
|
||||
memcpy(packet_with_quantization->jpeg_data, *jpg_offset, jpg_bytes);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Copy JPG data
|
||||
memcpy(packet->jpeg_data, *jpg_offset, jpg_bytes);
|
||||
}
|
||||
|
||||
// Update JPG offset
|
||||
*jpg_offset += jpg_bytes;
|
||||
// Update sequence number
|
||||
sequence_number_++;
|
||||
|
||||
return (rtp_over_tcp_hdr_t *)packet;
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"name": "RTSPServer",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"description": "RTSP Server",
|
||||
"dependencies": {
|
||||
"contrem/arduino-timer": "^2.3.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -29,7 +29,7 @@ private:
|
||||
std::shared_ptr<CRtspSession> session;
|
||||
};
|
||||
|
||||
OV2640 cam_;
|
||||
OV2640 cam_;
|
||||
std::list<std::unique_ptr<rtsp_client>> clients_;
|
||||
uintptr_t task_;
|
||||
Timer<> timer_;
|
||||
|
||||
20
minify.py
Normal file
@@ -0,0 +1,20 @@
|
||||
#!/usr/bin/python3
|
||||
|
||||
import sys
|
||||
import minify_html
|
||||
|
||||
if (len(sys.argv) <= 2):
|
||||
print('Usage: minify.py input.html output.html')
|
||||
sys.exit(1)
|
||||
|
||||
input_file = open(sys.argv[1], 'r')
|
||||
output_file = open(sys.argv[2], 'w')
|
||||
|
||||
html = input_file.read()
|
||||
input_file.close()
|
||||
|
||||
html_minified = minify_html.minify(html, minify_css=True)
|
||||
output_file.write(html_minified)
|
||||
|
||||
output_file.close()
|
||||
print('Done.')
|
||||
107
platformio.ini
@@ -8,34 +8,107 @@
|
||||
; Please visit documentation for the other options and examples
|
||||
; https://docs.platformio.org/page/projectconf.html
|
||||
|
||||
[env:esp32cam]
|
||||
###############################################################################
|
||||
[platformio]
|
||||
default_envs = esp32cam_ai_thinker
|
||||
#default_envs = esp32cam_espressif_esp_eye
|
||||
#default_envs = esp32cam_espressif_esp32s2_cam_board
|
||||
#default_envs = esp32cam_espressif_esp32s2_cam_header
|
||||
#default_envs = esp32cam_espressif_esp32s3_cam_lcd
|
||||
#default_envs = esp32cam_espressif_esp32s3_eye
|
||||
#default_envs = esp32cam_freenove_wrover_kit
|
||||
#default_envs = esp32cam_m5stack_camera_psram
|
||||
#default_envs = esp32cam_m5stack_camera
|
||||
#default_envs = esp32cam_m5stack_esp32cam
|
||||
|
||||
#default_envs = esp32cam_m5stack_unitcam
|
||||
#default_envs = esp32cam_m5stack_unitcams3
|
||||
#default_envs = esp32cam_m5stack_wide
|
||||
#default_envs = esp32cam_seeed_xiao_esp32s3_sense
|
||||
#default_envs = esp32cam_ttgo_t_camera
|
||||
#default_envs = esp32cam_ttgo_t_journal
|
||||
|
||||
[env]
|
||||
platform = espressif32
|
||||
board = esp32cam
|
||||
framework = arduino
|
||||
test_framework = unity
|
||||
|
||||
#upload_protocol = espota
|
||||
#upload_port = 192.168.50.222
|
||||
#upload_flags =
|
||||
# --auth='ESP32CAM-RTSP'
|
||||
#upload_port = 192.168.178.223
|
||||
#upload_flags = --auth='ESP32CAM-RTSP'
|
||||
|
||||
# Partition scheme for OTA
|
||||
board_build.partitions = min_spiffs.csv
|
||||
#board_build.partitions = max_spiffs.csv
|
||||
|
||||
monitor_speed = 115200
|
||||
monitor_rts = 0
|
||||
monitor_dtr = 0
|
||||
monitor_filters = log2file, time, default, esp32_exception_decoder
|
||||
#monitor_filters = log2file, time, default, esp32_exception_decoder
|
||||
monitor_filters = esp32_exception_decoder
|
||||
|
||||
build_flags =
|
||||
-O2
|
||||
-D CORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_VERBOSE
|
||||
-D LED_FLASH=4
|
||||
-D LED_BUILTIN=33
|
||||
-D BOARD_HAS_PSRAM
|
||||
-mfix-esp32-psram-cache-issue
|
||||
-D IOTWEBCONF_PASSWORD_LEN=64
|
||||
-Ofast
|
||||
-D 'BOARD_NAME="${this.board}"'
|
||||
-D 'CORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_INFO'
|
||||
-D 'IOTWEBCONF_PASSWORD_LEN=64'
|
||||
|
||||
board_build.embed_txtfiles =
|
||||
html/index.min.html
|
||||
|
||||
lib_deps =
|
||||
prampec/IotWebConf @ ^3.2.1
|
||||
geeksville/Micro-RTSP @ ^0.1.6
|
||||
rzeldent/micro-moustache@^1.0.1
|
||||
prampec/IotWebConf@^3.2.1
|
||||
geeksville/Micro-RTSP@^0.1.6
|
||||
rzeldent/micro-moustache
|
||||
rzeldent/micro-timezonedb
|
||||
|
||||
[env:esp32cam_ai_thinker]
|
||||
board = esp32cam_ai_thinker
|
||||
|
||||
[env:esp32cam_espressif_esp_eye]
|
||||
board = esp32cam_espressif_esp_eye
|
||||
|
||||
[env:esp32cam_espressif_esp32s2_cam_board]
|
||||
# Use board connection
|
||||
# The 18 pin header on the board has Y5 and Y3 swapped
|
||||
board = esp32cam_espressif_esp32s2_cam_board
|
||||
|
||||
[env:esp32cam_espressif_esp32s2_cam_header]
|
||||
# Use header connection
|
||||
# The 18 pin header on the board has Y5 and Y3 swapped
|
||||
board = esp32cam_espressif_esp32s2_cam_header
|
||||
|
||||
[env:esp32cam_espressif_esp32s3_cam_lcd]
|
||||
board = esp32cam_espressif_esp32s3_cam_lcd
|
||||
|
||||
[env:esp32cam_espressif_esp32s3_eye]
|
||||
board = esp32cam_espressif_esp32s3_eye
|
||||
|
||||
[env:esp32cam_freenove_wrover_kit]
|
||||
board = esp32cam_freenove_wrover_kit
|
||||
|
||||
[env:esp32cam_m5stack_camera_psram]
|
||||
board = esp32cam_m5stack_camera_psram
|
||||
|
||||
[env:esp32cam_m5stack_camera]
|
||||
board = esp32cam_m5stack_camera
|
||||
|
||||
[env:esp32cam_m5stack_esp32cam]
|
||||
board = esp32cam_m5stack_esp32cam
|
||||
|
||||
[env:esp32cam_m5stack_unitcam]
|
||||
board = esp32cam_m5stack_unitcam
|
||||
|
||||
[env:esp32cam_m5stack_unitcams3]
|
||||
board = esp32cam_m5stack_unitcams3
|
||||
|
||||
[env:esp32cam_m5stack_wide]
|
||||
board = esp32cam_m5stack_wide
|
||||
|
||||
[env:esp32cam_seeed_xiao_esp32s3_sense]
|
||||
board = esp32cam_seeed_xiao_esp32s3_sense
|
||||
|
||||
[env:esp32cam_ttgo_t_camera]
|
||||
board = esp32cam_ttgo_t_camera
|
||||
|
||||
[env:esp32cam_ttgo_t_journal]
|
||||
board = esp32cam_ttgo_t_journal
|
||||
306
src/main.cpp
@@ -1,12 +1,12 @@
|
||||
#include <Arduino.h>
|
||||
#include <ArduinoOTA.h>
|
||||
#include <esp_wifi.h>
|
||||
#include <soc/rtc_cntl_reg.h>
|
||||
#include <driver/i2c.h>
|
||||
#include <IotWebConf.h>
|
||||
#include <IotWebConfTParameter.h>
|
||||
#include <OV2640.h>
|
||||
#include <ESPmDNS.h>
|
||||
#include <rtsp_server.h>
|
||||
#include <lookup_camera_config.h>
|
||||
#include <lookup_camera_effect.h>
|
||||
#include <lookup_camera_frame_size.h>
|
||||
#include <lookup_camera_gainceiling.h>
|
||||
@@ -14,12 +14,13 @@
|
||||
#include <format_duration.h>
|
||||
#include <format_number.h>
|
||||
#include <moustache.h>
|
||||
#include <html_data.h>
|
||||
#include <html_data_gzip.h>
|
||||
#include <settings.h>
|
||||
|
||||
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();
|
||||
#include <micro_rtsp_camera.h>
|
||||
#include <micro_rtsp_server.h>
|
||||
|
||||
// HTML files
|
||||
extern const char index_html_min_start[] asm("_binary_html_index_min_html_start");
|
||||
|
||||
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();
|
||||
@@ -48,34 +49,25 @@ 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_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
|
||||
OV2640 cam;
|
||||
// OV2640 cam;
|
||||
// DNS Server
|
||||
DNSServer dnsServer;
|
||||
// RTSP Server
|
||||
std::unique_ptr<rtsp_server> camera_server;
|
||||
// std::unique_ptr<rtsp_server> camera_server;
|
||||
|
||||
micro_rtsp_camera camera;
|
||||
micro_rtsp_server server(camera);
|
||||
|
||||
// Web server
|
||||
WebServer web_server(80);
|
||||
IotWebConf iotWebConf(WIFI_SSID, &dnsServer, &web_server, WIFI_PASSWORD, CONFIG_VERSION);
|
||||
|
||||
// Keep track of config changes. This will allow a reset of the device
|
||||
bool config_changed = false;
|
||||
auto thingName = String(WIFI_SSID) + "-" + String(ESP.getEfuseMac(), 16);
|
||||
IotWebConf iotWebConf(thingName.c_str(), &dnsServer, &web_server, WIFI_PASSWORD, CONFIG_VERSION);
|
||||
|
||||
// Camera initialization result
|
||||
esp_err_t camera_init_result;
|
||||
|
||||
void stream_text_file_gzip(const unsigned char *content, size_t length, const char *mime_type)
|
||||
{
|
||||
// Cache for 86400 seconds (one day)
|
||||
web_server.sendHeader("Cache-Control", "max-age=86400");
|
||||
web_server.sendHeader("Content-encoding", "gzip");
|
||||
web_server.setContentLength(length);
|
||||
web_server.send(200, mime_type, "");
|
||||
web_server.sendContent(reinterpret_cast<const char *>(content), length);
|
||||
}
|
||||
|
||||
void handle_root()
|
||||
{
|
||||
log_v("Handle root");
|
||||
@@ -90,14 +82,20 @@ void handle_root()
|
||||
|
||||
// Wifi Modes
|
||||
const char *wifi_modes[] = {"NULL", "STA", "AP", "STA+AP"};
|
||||
auto ipv4 = WiFi.getMode() == WIFI_MODE_AP ? WiFi.softAPIP() : WiFi.localIP();
|
||||
auto ipv6 = WiFi.getMode() == WIFI_MODE_AP ? WiFi.softAPIPv6() : WiFi.localIPv6();
|
||||
|
||||
auto initResult = esp_err_to_name(camera_init_result);
|
||||
if (initResult == nullptr)
|
||||
initResult = "Unknown reason";
|
||||
|
||||
moustache_variable_t substitutions[] = {
|
||||
// Config Changed?
|
||||
{"ConfigChanged", String(config_changed)},
|
||||
// Version / CPU
|
||||
{"AppTitle", APP_TITLE},
|
||||
{"AppVersion", APP_VERSION},
|
||||
{"BoardType", BOARD_NAME},
|
||||
{"ThingName", iotWebConf.getThingName()},
|
||||
{"SDKVersion", ESP.getSdkVersion()},
|
||||
{"ChipModel", ESP.getChipModel()},
|
||||
{"ChipRevision", String(ESP.getChipRevision())},
|
||||
{"CpuFreqMHz", String(ESP.getCpuFreqMHz())},
|
||||
@@ -109,27 +107,25 @@ void handle_root()
|
||||
{"Uptime", String(format_duration(millis() / 1000))},
|
||||
{"FreeHeap", format_memory(ESP.getFreeHeap())},
|
||||
{"MaxAllocHeap", format_memory(ESP.getMaxAllocHeap())},
|
||||
{"NumRTSPSessions", camera_server != nullptr ? String(camera_server->num_connected()) : "N/A"},
|
||||
{"NumRTSPSessions", String(server.clients())},
|
||||
// Network
|
||||
{"HostName", hostname},
|
||||
{"MacAddress", WiFi.macAddress()},
|
||||
{"AccessPoint", WiFi.SSID()},
|
||||
{"SignalStrength", String(WiFi.RSSI())},
|
||||
{"IpV4", WiFi.localIP().toString()},
|
||||
{"IpV6", WiFi.localIPv6().toString()},
|
||||
{"WifiMode", wifi_modes[WiFi.getMode()]},
|
||||
{"IPv4", ipv4.toString()},
|
||||
{"IPv6", ipv6.toString()},
|
||||
{"NetworkState.ApMode", String(iotWebConf.getState() == iotwebconf::NetworkState::ApMode)},
|
||||
{"NetworkState.OnLine", String(iotWebConf.getState() == iotwebconf::NetworkState::OnLine)},
|
||||
// Camera
|
||||
{"BoardType", String(param_board.value())},
|
||||
{"FrameSize", String(param_frame_size.value())},
|
||||
{"FrameDuration", String(param_frame_duration.value())},
|
||||
{"FrameFrequency", String(1000.0 / param_frame_duration.value(), 1)},
|
||||
{"FrameBufferLocation", psramFound() ? "PSRAM" : "DRAM)"},
|
||||
{"FrameBuffers", String(psramFound() ? 2 : 1)},
|
||||
{"JpegQuality", String(param_jpg_quality.value())},
|
||||
{"CameraInitialized", String(camera_init_result == ESP_OK)},
|
||||
{"CameraInitResultText", esp_err_to_name(camera_init_result)},
|
||||
{"CameraInitResult", String(camera_init_result)},
|
||||
{"CameraInitResultText", initResult},
|
||||
// Settings
|
||||
{"Brightness", String(param_brightness.value())},
|
||||
{"Contrast", String(param_contrast.value())},
|
||||
@@ -153,38 +149,14 @@ void handle_root()
|
||||
{"VFlip", String(param_vflip.value())},
|
||||
{"Dcw", String(param_dcw.value())},
|
||||
{"ColorBar", String(param_colorbar.value())},
|
||||
// LED
|
||||
{"LedIntensity", String(param_led_intensity.value())},
|
||||
// RTSP
|
||||
{"RtspPort", String(RTSP_PORT)}};
|
||||
|
||||
web_server.sendHeader("Cache-Control", "no-cache, no-store, must-revalidate");
|
||||
auto html = moustache_render(file_data_index_html, substitutions);
|
||||
auto html = moustache_render(index_html_min_start, substitutions);
|
||||
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(file_data_restart_html, substitutions);
|
||||
web_server.send(200, "text/html", html);
|
||||
log_v("Restarting... Press refresh to connect again");
|
||||
sleep(100);
|
||||
ESP.restart();
|
||||
}
|
||||
|
||||
void handle_snapshot()
|
||||
{
|
||||
log_v("handle_snapshot");
|
||||
@@ -194,13 +166,13 @@ void handle_snapshot()
|
||||
return;
|
||||
}
|
||||
|
||||
// Remove old images stored in the framebuffer
|
||||
auto frame_buffers = psramFound() ? 2 : 1;
|
||||
// Remove old images stored in the frame buffer
|
||||
auto frame_buffers = CAMERA_CONFIG_FB_COUNT;
|
||||
while (frame_buffers--)
|
||||
cam.run();
|
||||
camera.update_frame();
|
||||
|
||||
auto fb_len = cam.getSize();
|
||||
auto fb = (const char *)cam.getfb();
|
||||
auto fb_len = camera.size();
|
||||
auto fb = camera.data();
|
||||
if (fb == nullptr)
|
||||
{
|
||||
web_server.send(404, "text/plain", "Unable to obtain frame buffer from the camera");
|
||||
@@ -210,58 +182,88 @@ void handle_snapshot()
|
||||
web_server.sendHeader("Cache-Control", "no-cache, no-store, must-revalidate");
|
||||
web_server.setContentLength(fb_len);
|
||||
web_server.send(200, "image/jpeg", "");
|
||||
web_server.sendContent(fb, fb_len);
|
||||
web_server.sendContent((const char *)fb, fb_len);
|
||||
}
|
||||
|
||||
void handle_flash()
|
||||
{
|
||||
log_v("handle_flash");
|
||||
#define STREAM_CONTENT_BOUNDARY "123456789000000000000987654321"
|
||||
|
||||
if (!web_server.authenticate("admin", iotWebConf.getApPasswordParameter()->valueBuffer))
|
||||
void handle_stream()
|
||||
{
|
||||
log_v("handle_stream");
|
||||
if (camera_init_result != ESP_OK)
|
||||
{
|
||||
web_server.requestAuthentication(BASIC_AUTH, APP_TITLE, "401 Unauthorized<br><br>The password is incorrect.");
|
||||
web_server.send(404, "text/plain", "Camera is not initialized");
|
||||
return;
|
||||
}
|
||||
|
||||
// If no value present, use value from config
|
||||
if (web_server.hasArg("v"))
|
||||
log_v("starting streaming");
|
||||
// Blocks further handling of HTTP server until stopped
|
||||
char size_buf[12];
|
||||
auto client = web_server.client();
|
||||
client.write("HTTP/1.1 200 OK\r\nAccess-Control-Allow-Origin: *\r\nContent-Type: multipart/x-mixed-replace; boundary=" STREAM_CONTENT_BOUNDARY "\r\n");
|
||||
while (client.connected())
|
||||
{
|
||||
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());
|
||||
client.write("\r\n--" STREAM_CONTENT_BOUNDARY "\r\n");
|
||||
camera.update_frame();
|
||||
client.write("Content-Type: image/jpeg\r\nContent-Length: ");
|
||||
sprintf(size_buf, "%d\r\n\r\n", camera.size());
|
||||
client.write(size_buf);
|
||||
client.write(camera.data(), camera.size());
|
||||
}
|
||||
|
||||
web_server.sendHeader("Cache-Control", "no-cache, no-store, must-revalidate");
|
||||
web_server.send(200);
|
||||
log_v("client disconnected");
|
||||
client.stop();
|
||||
log_v("stopped streaming");
|
||||
}
|
||||
|
||||
esp_err_t initialize_camera()
|
||||
{
|
||||
log_v("initialize_camera");
|
||||
log_i("Camera config: %s", param_board.value());
|
||||
auto camera_config = lookup_camera_config(param_board.value());
|
||||
|
||||
log_i("Frame size: %s", param_frame_size.value());
|
||||
auto frame_size = lookup_frame_size(param_frame_size.value());
|
||||
log_i("JPEG quality: %d", param_jpg_quality.value());
|
||||
auto jpeg_quality = param_jpg_quality.value();
|
||||
log_i("Frame duration: %d ms", param_frame_duration.value());
|
||||
|
||||
camera_config.frame_size = frame_size;
|
||||
camera_config.jpeg_quality = param_jpg_quality.value();
|
||||
if (psramFound())
|
||||
{
|
||||
camera_config.fb_count = 2;
|
||||
camera_config.fb_location = CAMERA_FB_IN_PSRAM;
|
||||
}
|
||||
else
|
||||
{
|
||||
camera_config.fb_count = 1;
|
||||
camera_config.fb_location = CAMERA_FB_IN_DRAM;
|
||||
}
|
||||
return cam.init(camera_config);
|
||||
// Set frame duration
|
||||
server.set_frame_interval(param_frame_duration.value());
|
||||
|
||||
camera_config_t camera_config = {
|
||||
.pin_pwdn = CAMERA_CONFIG_PIN_PWDN, // GPIO pin for camera power down line
|
||||
.pin_reset = CAMERA_CONFIG_PIN_RESET, // GPIO pin for camera reset line
|
||||
.pin_xclk = CAMERA_CONFIG_PIN_XCLK, // GPIO pin for camera XCLK line
|
||||
.pin_sccb_sda = CAMERA_CONFIG_PIN_SCCB_SDA, // GPIO pin for camera SDA line
|
||||
.pin_sccb_scl = CAMERA_CONFIG_PIN_SCCB_SCL, // GPIO pin for camera SCL line
|
||||
.pin_d7 = CAMERA_CONFIG_PIN_Y9, // GPIO pin for camera D7 line
|
||||
.pin_d6 = CAMERA_CONFIG_PIN_Y8, // GPIO pin for camera D6 line
|
||||
.pin_d5 = CAMERA_CONFIG_PIN_Y7, // GPIO pin for camera D5 line
|
||||
.pin_d4 = CAMERA_CONFIG_PIN_Y6, // GPIO pin for camera D4 line
|
||||
.pin_d3 = CAMERA_CONFIG_PIN_Y5, // GPIO pin for camera D3 line
|
||||
.pin_d2 = CAMERA_CONFIG_PIN_Y4, // GPIO pin for camera D2 line
|
||||
.pin_d1 = CAMERA_CONFIG_PIN_Y3, // GPIO pin for camera D1 line
|
||||
.pin_d0 = CAMERA_CONFIG_PIN_Y2, // GPIO pin for camera D0 line
|
||||
.pin_vsync = CAMERA_CONFIG_PIN_VSYNC, // GPIO pin for camera VSYNC line
|
||||
.pin_href = CAMERA_CONFIG_PIN_HREF, // GPIO pin for camera HREF line
|
||||
.pin_pclk = CAMERA_CONFIG_PIN_PCLK, // GPIO pin for camera PCLK line
|
||||
.xclk_freq_hz = CAMERA_CONFIG_CLK_FREQ_HZ, // Frequency of XCLK signal, in Hz. EXPERIMENTAL: Set to 16MHz on ESP32-S2 or ESP32-S3 to enable EDMA mode
|
||||
.ledc_timer = CAMERA_CONFIG_LEDC_TIMER, // LEDC timer to be used for generating XCLK
|
||||
.ledc_channel = CAMERA_CONFIG_LEDC_CHANNEL, // LEDC channel to be used for generating XCLK
|
||||
.pixel_format = PIXFORMAT_JPEG, // Format of the pixel data: PIXFORMAT_ + YUV422|GRAYSCALE|RGB565|JPEG
|
||||
.frame_size = frame_size, // Size of the output image: FRAMESIZE_ + QVGA|CIF|VGA|SVGA|XGA|SXGA|UXGA
|
||||
.jpeg_quality = jpeg_quality, // Quality of JPEG output. 0-63 lower means higher quality
|
||||
.fb_count = CAMERA_CONFIG_FB_COUNT, // Number of frame buffers to be allocated. If more than one, then each frame will be acquired (double speed)
|
||||
.fb_location = CAMERA_CONFIG_FB_LOCATION, // The location where the frame buffer will be allocated
|
||||
.grab_mode = CAMERA_GRAB_LATEST, // When buffers should be filled
|
||||
#if CONFIG_CAMERA_CONVERTER_ENABLED
|
||||
conv_mode = CONV_DISABLE, // RGB<->YUV Conversion mode
|
||||
#endif
|
||||
.sccb_i2c_port = CAMERA_CONFIG_SCCB_I2C_PORT // If pin_sccb_sda is -1, use the already configured I2C bus by number
|
||||
};
|
||||
|
||||
return camera.initialize(&camera_config);
|
||||
|
||||
// return cam.init(camera_config);
|
||||
}
|
||||
|
||||
void update_camera_settings()
|
||||
@@ -300,33 +302,27 @@ void update_camera_settings()
|
||||
void start_rtsp_server()
|
||||
{
|
||||
log_v("start_rtsp_server");
|
||||
camera_server = std::unique_ptr<rtsp_server>(new rtsp_server(cam, param_frame_duration.value(), RTSP_PORT));
|
||||
// Add service to mDNS - rtsp
|
||||
MDNS.addService("rtsp", "tcp", 554);
|
||||
|
||||
server.begin(RTSP_PORT);
|
||||
// Add RTSP service to mDNS
|
||||
// HTTP is already set by iotWebConf
|
||||
MDNS.addService("rtsp", "tcp", RTSP_PORT);
|
||||
}
|
||||
|
||||
void on_connected()
|
||||
{
|
||||
log_v("on_connected");
|
||||
// Turn LED off (has inverted logic GPIO33) => red LED off => connected
|
||||
digitalWrite(LED_BUILTIN, true);
|
||||
// Set flash led intensity
|
||||
analogWrite(LED_FLASH, param_led_intensity.value());
|
||||
// Start (OTA) Over The Air programming when connected
|
||||
ArduinoOTA.begin();
|
||||
// Start the RTSP Server if initializef
|
||||
// Start the RTSP Server if initialized
|
||||
if (camera_init_result == ESP_OK)
|
||||
start_rtsp_server();
|
||||
else
|
||||
log_e("Not starting RTSP server: camera not initialized");
|
||||
}
|
||||
|
||||
void 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();
|
||||
config_changed = true;
|
||||
}
|
||||
|
||||
void setup()
|
||||
@@ -334,25 +330,28 @@ void setup()
|
||||
// Disable brownout
|
||||
WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0);
|
||||
|
||||
pinMode(LED_BUILTIN, OUTPUT);
|
||||
// Turn LED on (has inverted logic GPIO33) => red LED on => not connected
|
||||
digitalWrite(LED_BUILTIN, false);
|
||||
|
||||
pinMode(LED_FLASH, OUTPUT);
|
||||
// Turn flash led off
|
||||
analogWrite(LED_FLASH, 0);
|
||||
|
||||
#ifdef CORE_DEBUG_LEVEL
|
||||
Serial.begin(115200);
|
||||
Serial.setDebugOutput(true);
|
||||
#ifdef USER_LED_GPIO
|
||||
pinMode(USER_LED_GPIO, OUTPUT);
|
||||
digitalWrite(USER_LED_GPIO, !USER_LED_ON_LEVEL);
|
||||
#endif
|
||||
|
||||
log_i("CPU Freq: %d Mhz", getCpuFrequencyMhz());
|
||||
Serial.begin(115200);
|
||||
Serial.setDebugOutput(true);
|
||||
|
||||
#ifdef ARDUINO_USB_CDC_ON_BOOT
|
||||
// Delay for USB to connect/settle
|
||||
delay(5000);
|
||||
#endif
|
||||
|
||||
log_i("Core debug level: %d", CORE_DEBUG_LEVEL);
|
||||
log_i("CPU Freq: %d Mhz, %d core(s)", getCpuFrequencyMhz(), ESP.getChipCores());
|
||||
log_i("Free heap: %d bytes", ESP.getFreeHeap());
|
||||
log_i("SDK version: %s", ESP.getSdkVersion());
|
||||
log_i("Board: %s", BOARD_NAME);
|
||||
log_i("Starting " APP_TITLE "...");
|
||||
|
||||
param_group_board.addItem(¶m_board);
|
||||
iotWebConf.addParameterGroup(¶m_group_board);
|
||||
if (CAMERA_CONFIG_FB_LOCATION == CAMERA_FB_IN_PSRAM && !psramInit())
|
||||
log_e("Failed to initialize PSRAM");
|
||||
|
||||
param_group_camera.addItem(¶m_frame_duration);
|
||||
param_group_camera.addItem(¶m_frame_size);
|
||||
@@ -381,63 +380,48 @@ void setup()
|
||||
param_group_camera.addItem(¶m_colorbar);
|
||||
iotWebConf.addParameterGroup(¶m_group_camera);
|
||||
|
||||
param_group_peripheral.addItem(¶m_led_intensity);
|
||||
iotWebConf.addParameterGroup(¶m_group_peripheral);
|
||||
|
||||
iotWebConf.getApTimeoutParameter()->visible = true;
|
||||
iotWebConf.setConfigSavedCallback(on_config_saved);
|
||||
iotWebConf.setWifiConnectionCallback(on_connected);
|
||||
#ifdef USER_LED_GPIO
|
||||
iotWebConf.setStatusPin(USER_LED_GPIO, USER_LED_ON_LEVEL);
|
||||
#endif
|
||||
iotWebConf.init();
|
||||
|
||||
camera_init_result = initialize_camera();
|
||||
if (camera_init_result != ESP_OK)
|
||||
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());
|
||||
else
|
||||
update_camera_settings();
|
||||
// Set the time servers
|
||||
configTime(0, 0, NTP_SERVERS);
|
||||
|
||||
// Try to initialize 3 times
|
||||
for (auto i = 0; i < 3; i++)
|
||||
{
|
||||
log_i("Initializing camera...");
|
||||
camera_init_result = initialize_camera();
|
||||
if (camera_init_result == ESP_OK)
|
||||
break;
|
||||
|
||||
esp_camera_deinit();
|
||||
log_e("Failed to initialize camera. Error: 0x%04x. 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());
|
||||
delay(500);
|
||||
}
|
||||
|
||||
update_camera_settings();
|
||||
|
||||
// Set up required URL handlers on the web server
|
||||
web_server.on("/", HTTP_GET, handle_root);
|
||||
web_server.on("/config", []
|
||||
{ iotWebConf.handleConfig(); });
|
||||
web_server.on("/restart", HTTP_GET, handle_restart);
|
||||
// Camera snapshot
|
||||
web_server.on("/snapshot", HTTP_GET, handle_snapshot);
|
||||
// Camera flash light
|
||||
web_server.on("/flash", HTTP_GET, handle_flash);
|
||||
|
||||
// bootstrap
|
||||
web_server.on("/bootstrap.min.css", HTTP_GET, []()
|
||||
{ stream_text_file_gzip(file_data_bootstrap_min_css, sizeof(file_data_bootstrap_min_css), "text/css"); });
|
||||
// Camera stream
|
||||
web_server.on("/stream", HTTP_GET, handle_stream);
|
||||
|
||||
web_server.onNotFound([]()
|
||||
{ iotWebConf.handleNotFound(); });
|
||||
|
||||
ArduinoOTA
|
||||
.onStart([]()
|
||||
{ log_w("Starting OTA update: %s", ArduinoOTA.getCommand() == U_FLASH ? "sketch" : "filesystem"); })
|
||||
.onEnd([]()
|
||||
{ log_w("OTA update done!"); })
|
||||
.onProgress([](unsigned int progress, unsigned int total)
|
||||
{ log_i("OTA Progress: %u%%\r", (progress / (total / 100))); })
|
||||
.onError([](ota_error_t error)
|
||||
{
|
||||
switch (error)
|
||||
{
|
||||
case OTA_AUTH_ERROR: log_e("OTA: Auth Failed"); break;
|
||||
case OTA_BEGIN_ERROR: log_e("OTA: Begin Failed"); break;
|
||||
case OTA_CONNECT_ERROR: log_e("OTA: Connect Failed"); break;
|
||||
case OTA_RECEIVE_ERROR: log_e("OTA: Receive Failed"); break;
|
||||
case OTA_END_ERROR: log_e("OTA: End Failed"); break;
|
||||
default: log_e("OTA error: %u", error);
|
||||
} });
|
||||
ArduinoOTA.setPassword(OTA_PASSWORD);
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
iotWebConf.doLoop();
|
||||
ArduinoOTA.handle();
|
||||
|
||||
if (camera_server)
|
||||
camera_server->doLoop();
|
||||
server.loop();
|
||||
}
|
||||