forked from external-repos/esp32cam-rtsp
Merge pull request #40 from rzeldent/feature/update_config
Added options for camera to web GUI - colors/sepia, hmirror/vflip, white balance options etc..
This commit is contained in:
38
README.md
38
README.md
@@ -31,6 +31,29 @@ This software provides a **configuration web server**, that can be used to:
|
||||
- Select the frame rate,
|
||||
- Select number of frame buffers
|
||||
- Select the JPEG quality
|
||||
- Configure the camera options
|
||||
- Brightness
|
||||
- Contrast
|
||||
- Saturation
|
||||
- Special effect (Normal, Negative, Grayscale, Red/Green/Blue tint, Sepia)
|
||||
- White balance
|
||||
- Automatic White Balance gain
|
||||
- Wite Balance mode
|
||||
- Exposure control
|
||||
- AEC2
|
||||
- AE level
|
||||
- AEC value
|
||||
- Gain control
|
||||
- AGC gain
|
||||
- Gain ceiling
|
||||
- BPC
|
||||
- WPC
|
||||
- Raw gamma
|
||||
- Lens correction
|
||||
- Horizontal mirror
|
||||
- Vertical flip
|
||||
- DCW
|
||||
- Color bar
|
||||
|
||||
The software provides contains also a mDNS server to be easily discoverable on the local network.
|
||||
It advertises HTTP (port 80) and RTSP (port 554)
|
||||
@@ -236,3 +259,18 @@ Make sure it is connected the right way around (Camera pointing away from the bo
|
||||
## Credits
|
||||
|
||||
esp32cam-ready depends on PlatformIO, Bootstrap5 and Micro-RTSP by Kevin Hester.
|
||||
|
||||
## Change history
|
||||
|
||||
- Feb 2023
|
||||
- Added additional settings for camera configuration
|
||||
- Nov 2022
|
||||
- Added OTA
|
||||
- Fix for grabbing frame
|
||||
- Fixed bug: Increased WiFi password length
|
||||
- Sep 2022
|
||||
- Added GUI with bootstrap
|
||||
- More information in web page
|
||||
- Added camera preview in HTML
|
||||
- Jul 2022
|
||||
- Initial version
|
||||
|
||||
112
html/index.html
112
html/index.html
@@ -79,6 +79,19 @@
|
||||
</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">
|
||||
@@ -125,12 +138,8 @@
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
<div class="card bg-light mb-3">
|
||||
<h5 class="card-header">Settings</h5>
|
||||
<h5 class="card-header">Camera</h5>
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<div class="col-4">Camera type:</div>
|
||||
<div class="col-8">{{CameraType}}</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-4">Frame rate:</div>
|
||||
<div class="col-8">{{FrameDuration}} ms ({{FrameFrequency}} f/s)</div>
|
||||
@@ -149,11 +158,95 @@
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-4">JPEG quality:</div>
|
||||
<div class="col-8">{{JpegQuality}} (1-100)</div>
|
||||
<div class="col-8">{{JpegQuality}} [1-100]</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-4">Flash LED intensity:</div>
|
||||
<div class="col-8">{{FlashLedIntensity}} (0-100)</div>
|
||||
<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">AEC2:</div>
|
||||
<div class="col-8">{{Aec2}}</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-4">AE level:</div>
|
||||
<div class="col-8">{{AeLevel}}</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-4">AEC 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">BPC:</div>
|
||||
<div class="col-8">{{Bpc}}</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-4">WPC:</div>
|
||||
<div class="col-8">{{Wpc}}</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-4">Raw gamma:</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">DCW:</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">
|
||||
@@ -181,8 +274,7 @@
|
||||
<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>
|
||||
<a href="rtsp://{{IpV4}}:{{RtspPort}}/mjpeg/1">rtsp://{{IpV4}}:{{RtspPort}}/mjpeg/1</a>
|
||||
</span>
|
||||
</div>
|
||||
<div class="row">
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -3,11 +3,9 @@
|
||||
#include <string.h>
|
||||
#include <esp_camera.h>
|
||||
|
||||
typedef char camera_config_name_t[18];
|
||||
|
||||
typedef struct camera_config_entry
|
||||
typedef struct
|
||||
{
|
||||
const camera_config_name_t name;
|
||||
const char name[11];
|
||||
const camera_config_t config;
|
||||
} camera_config_entry_t;
|
||||
|
||||
@@ -137,18 +135,17 @@ constexpr camera_config_t esp32cam_wrover_kit_settings = {
|
||||
.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 *pin)
|
||||
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, pin, sizeof(camera_config_name_t)) == 0)
|
||||
if (strncmp(entry.name, name, sizeof(entry.name)) == 0)
|
||||
return entry.config;
|
||||
|
||||
return camera_config_t{};
|
||||
28
include/lookup_camera_effect.h
Normal file
28
include/lookup_camera_effect.h
Normal file
@@ -0,0 +1,28 @@
|
||||
#pragma once
|
||||
|
||||
#include <string.h>
|
||||
|
||||
typedef struct
|
||||
{
|
||||
const char name[11];
|
||||
const int value;
|
||||
} camera_effect_entry_t;
|
||||
|
||||
constexpr const camera_effect_entry_t camera_effects[] = {
|
||||
{"Normal", 0},
|
||||
{"Negative", 1},
|
||||
{"Grayscale", 2},
|
||||
{"Red tint", 3},
|
||||
{"Green tint", 4},
|
||||
{"Blue tint", 5},
|
||||
{"Sepia", 6}};
|
||||
|
||||
const int lookup_camera_effect(const char *name)
|
||||
{
|
||||
// Lookup table for the frame name to framesize_t
|
||||
for (const auto &entry : camera_effects)
|
||||
if (strncmp(entry.name, name, sizeof(entry.name)) == 0)
|
||||
return entry.value;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -3,11 +3,9 @@
|
||||
#include <string.h>
|
||||
#include <sensor.h>
|
||||
|
||||
typedef char frame_size_name_t[18];
|
||||
|
||||
typedef struct frame_size_entry
|
||||
{
|
||||
const frame_size_name_t name;
|
||||
const char name[17];
|
||||
const framesize_t frame_size;
|
||||
} frame_size_entry_t;
|
||||
|
||||
@@ -30,7 +28,7 @@ const framesize_t lookup_frame_size(const char *pin)
|
||||
{
|
||||
// Lookup table for the frame name to framesize_t
|
||||
for (const auto &entry : frame_sizes)
|
||||
if (strncmp(entry.name, pin, sizeof(frame_size_name_t)) == 0)
|
||||
if (strncmp(entry.name, pin, sizeof(entry.name)) == 0)
|
||||
return entry.frame_size;
|
||||
|
||||
return FRAMESIZE_INVALID;
|
||||
29
include/lookup_camera_gainceiling.h
Normal file
29
include/lookup_camera_gainceiling.h
Normal file
@@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
#include <string.h>
|
||||
#include <esp_camera.h>
|
||||
|
||||
typedef struct
|
||||
{
|
||||
const char name[5];
|
||||
const gainceiling_t value;
|
||||
} camera_gainceiling_entry_t;
|
||||
|
||||
constexpr const camera_gainceiling_entry_t camera_gain_ceilings[] = {
|
||||
{"2X", GAINCEILING_2X},
|
||||
{"4X", GAINCEILING_4X},
|
||||
{"8X", GAINCEILING_8X},
|
||||
{"16X", GAINCEILING_16X},
|
||||
{"32X", GAINCEILING_32X},
|
||||
{"64X", GAINCEILING_64X},
|
||||
{"128X", GAINCEILING_128X}};
|
||||
|
||||
const gainceiling_t lookup_camera_gainceiling(const char *name)
|
||||
{
|
||||
// Lookup table for the frame name to framesize_t
|
||||
for (const auto &entry : camera_gain_ceilings)
|
||||
if (strncmp(entry.name, name, sizeof(entry.name)) == 0)
|
||||
return entry.value;
|
||||
|
||||
return GAINCEILING_2X;
|
||||
}
|
||||
26
include/lookup_camera_wb_mode.h
Normal file
26
include/lookup_camera_wb_mode.h
Normal file
@@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
|
||||
#include <string.h>
|
||||
|
||||
typedef struct
|
||||
{
|
||||
const char name[7];
|
||||
const int value;
|
||||
} camera_wb_mode_entry_t;
|
||||
|
||||
constexpr const camera_wb_mode_entry_t camera_wb_modes[] = {
|
||||
{"Auto", 0},
|
||||
{"Sunny", 1},
|
||||
{"Cloudy", 2},
|
||||
{"Office", 3},
|
||||
{"Home", 4}};
|
||||
|
||||
const int lookup_camera_wb_mode(const char *name)
|
||||
{
|
||||
// Lookup table for the frame name to framesize_t
|
||||
for (const auto &entry : camera_wb_modes)
|
||||
if (strncmp(entry.name, name, sizeof(entry.name)) == 0)
|
||||
return entry.value;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -5,14 +5,39 @@
|
||||
|
||||
#define WIFI_SSID "ESP32CAM-RTSP"
|
||||
#define WIFI_PASSWORD nullptr
|
||||
#define CONFIG_VERSION "1.3"
|
||||
#define CONFIG_VERSION "1.4"
|
||||
|
||||
#define OTA_PASSWORD "ESP32CAM-RTSP"
|
||||
|
||||
#define RTSP_PORT 554
|
||||
|
||||
#define DEFAULT_CAMERA_CONFIG "AI THINKER"
|
||||
#define DEFAULT_FRAME_DURATION "20"
|
||||
#define DEFAULT_FRAME_BUFFERS "2"
|
||||
#define DEFAULT_FRAME_DURATION 20
|
||||
#define DEFAULT_FRAME_BUFFERS 2
|
||||
#define DEFAULT_FRAME_SIZE "SVGA (800x600)"
|
||||
#define DEFAULT_JPEG_QUALITY "12"
|
||||
#define DEFAULT_LIGHT_INTENSITY "1"
|
||||
#define DEFAULT_JPEG_QUALITY 12
|
||||
|
||||
#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"
|
||||
#define DEFAULT_EXPOSURE_CONTROL true
|
||||
#define DEFAULT_AEC2 true
|
||||
#define DEFAULT_AE_LEVEL 0
|
||||
#define DEFAULT_AEC_VALUE 300
|
||||
#define DEFAULT_GAIN_CONTROL true
|
||||
#define DEFAULT_AGC_GAIN 0
|
||||
#define DEFAULT_GAIN_CEILING "2X"
|
||||
#define DEFAULT_BPC false
|
||||
#define DEFAULT_WPC true
|
||||
#define DEFAULT_RAW_GAMMA true
|
||||
#define DEFAULT_LENC true
|
||||
#define DEFAULT_HORIZONTAL_MIRROR false
|
||||
#define DEFAULT_VERTICAL_MIRROR false
|
||||
#define DEFAULT_DCW true
|
||||
#define DEFAULT_COLORBAR false
|
||||
|
||||
#define DEFAULT_LED_INTENSITY 0
|
||||
|
||||
223
src/main.cpp
223
src/main.cpp
@@ -6,8 +6,11 @@
|
||||
#include <OV2640.h>
|
||||
#include <ESPmDNS.h>
|
||||
#include <rtsp_server.h>
|
||||
#include <frame_size.h>
|
||||
#include <camera_config.h>
|
||||
#include <lookup_camera_config.h>
|
||||
#include <lookup_camera_effect.h>
|
||||
#include <lookup_camera_frame_size.h>
|
||||
#include <lookup_camera_gainceiling.h>
|
||||
#include <lookup_camera_wb_mode.h>
|
||||
#include <format_duration.h>
|
||||
#include <format_number.h>
|
||||
#include <moustache.h>
|
||||
@@ -15,20 +18,39 @@
|
||||
#include <html_data_gzip.h>
|
||||
#include <settings.h>
|
||||
|
||||
char camera_config_val[sizeof(camera_config_entry)];
|
||||
char frame_duration_val[6];
|
||||
char frame_size_val[sizeof(frame_size_entry_t)];
|
||||
char frame_buffers_val[3];
|
||||
char jpeg_quality_val[4];
|
||||
char flash_led_intensity_val[4];
|
||||
auto param_group_board = iotwebconf::ParameterGroup("board", "Board settings");
|
||||
auto param_board = iotwebconf::Builder<iotwebconf::SelectTParameter<sizeof(camera_configs[0])>>("bt").label("Board").optionValues((const char *)&camera_configs).optionNames((const char *)&camera_configs).optionCount(sizeof(camera_configs) / sizeof(camera_configs[0])).nameLength(sizeof(camera_configs[0])).defaultValue(DEFAULT_CAMERA_CONFIG).build();
|
||||
|
||||
auto config_group_stream_settings = iotwebconf::ParameterGroup("settings", "Streaming settings");
|
||||
auto config_camera_config = iotwebconf::SelectParameter("Camera config", "config", camera_config_val, sizeof(camera_config_val), (const char *)camera_configs, (const char *)camera_configs, sizeof(camera_configs) / sizeof(camera_configs[0]), sizeof(camera_configs[0]), DEFAULT_CAMERA_CONFIG);
|
||||
auto config_frame_rate = iotwebconf::NumberParameter("Frame duration (ms)", "fd", frame_duration_val, sizeof(frame_duration_val), DEFAULT_FRAME_DURATION, nullptr, "min=\"10\"");
|
||||
auto config_frame_size = iotwebconf::SelectParameter("Frame size", "fs", frame_size_val, sizeof(frame_size_val), (const char *)frame_sizes, (const char *)frame_sizes, sizeof(frame_sizes) / sizeof(frame_sizes[0]), sizeof(frame_sizes[0]), DEFAULT_FRAME_SIZE);
|
||||
auto config_frame_buffers = iotwebconf::NumberParameter("Frame buffers", "fb", frame_buffers_val, sizeof(frame_buffers_val), DEFAULT_FRAME_BUFFERS, nullptr, "min=\"1\" max=\"16\"");
|
||||
auto config_jpg_quality = iotwebconf::NumberParameter("JPEG quality", "q", jpeg_quality_val, sizeof(jpeg_quality_val), DEFAULT_JPEG_QUALITY, nullptr, "min=\"1\" max=\"100\"");
|
||||
auto config_flash_led_intensity = iotwebconf::NumberParameter("Flash LED intensity", "li", flash_led_intensity_val, sizeof(flash_led_intensity_val), DEFAULT_LIGHT_INTENSITY, nullptr, "min=\"0\" max=\"100\"");
|
||||
auto param_group_camera = iotwebconf::ParameterGroup("camera", "Camera settings");
|
||||
auto param_frame_duration = iotwebconf::Builder<iotwebconf::UIntTParameter<unsigned long>>("fd").label("Frame duration (ms)").defaultValue(DEFAULT_FRAME_DURATION).min(10).build();
|
||||
auto param_frame_size = iotwebconf::Builder<iotwebconf::SelectTParameter<sizeof(frame_sizes[0])>>("fs").label("Frame size").optionValues((const char *)&frame_sizes).optionNames((const char *)&frame_sizes).optionCount(sizeof(frame_sizes) / sizeof(frame_sizes[0])).nameLength(sizeof(frame_sizes[0])).defaultValue(DEFAULT_FRAME_SIZE).build();
|
||||
auto param_frame_buffers = iotwebconf::Builder<iotwebconf::UIntTParameter<byte>>("fb").label("Frame buffers").defaultValue(DEFAULT_FRAME_BUFFERS).min(1).max(16).build();
|
||||
auto param_jpg_quality = iotwebconf::Builder<iotwebconf::UIntTParameter<byte>>("q").label("JPG quality").defaultValue(DEFAULT_JPEG_QUALITY).min(1).max(100).build();
|
||||
auto param_brightness = iotwebconf::Builder<iotwebconf::IntTParameter<int>>("b").label("Brightness").defaultValue(DEFAULT_BRIGHTNESS).min(-2).max(2).build();
|
||||
auto param_contrast = iotwebconf::Builder<iotwebconf::IntTParameter<int>>("c").label("Contrast").defaultValue(DEFAULT_CONTRAST).min(-2).max(2).build();
|
||||
auto param_saturation = iotwebconf::Builder<iotwebconf::IntTParameter<int>>("s").label("Saturation").defaultValue(DEFAULT_SATURATION).min(-2).max(2).build();
|
||||
auto param_special_effect = iotwebconf::Builder<iotwebconf::SelectTParameter<sizeof(camera_effects[0])>>("e").label("Effect").optionValues((const char *)&camera_effects).optionNames((const char *)&camera_effects).optionCount(sizeof(camera_effects) / sizeof(camera_effects[0])).nameLength(sizeof(camera_effects[0])).defaultValue(DEFAULT_EFFECT).build();
|
||||
auto param_whitebal = iotwebconf::Builder<iotwebconf::CheckboxTParameter>("wb").label("White balance").defaultValue(DEFAULT_WHITE_BALANCE).build();
|
||||
auto param_awb_gain = iotwebconf::Builder<iotwebconf::CheckboxTParameter>("awbg").label("Automatic white balance gain").defaultValue(DEFAULT_WHITE_BALANCE_GAIN).build();
|
||||
auto param_wb_mode = iotwebconf::Builder<iotwebconf::SelectTParameter<sizeof(camera_wb_modes[0])>>("wbm").label("White balance mode").optionValues((const char *)&camera_wb_modes).optionNames((const char *)&camera_wb_modes).optionCount(sizeof(camera_wb_modes) / sizeof(camera_wb_modes[0])).nameLength(sizeof(camera_wb_modes[0])).defaultValue(DEFAULT_WHITE_BALANCE_MODE).build();
|
||||
auto param_exposure_ctrl = iotwebconf::Builder<iotwebconf::CheckboxTParameter>("ec").label("Exposure control").defaultValue(DEFAULT_EXPOSURE_CONTROL).build();
|
||||
auto param_aec2 = iotwebconf::Builder<iotwebconf::CheckboxTParameter>("aec2").label("AEC2").defaultValue(DEFAULT_AEC2).build();
|
||||
auto param_ae_level = iotwebconf::Builder<iotwebconf::IntTParameter<int>>("ael").label("AE level").defaultValue(DEFAULT_AE_LEVEL).min(-2).max(2).build();
|
||||
auto param_aec_value = iotwebconf::Builder<iotwebconf::IntTParameter<int>>("aecv").label("AEC value").defaultValue(DEFAULT_AEC_VALUE).min(9).max(1200).build();
|
||||
auto param_gain_ctrl = iotwebconf::Builder<iotwebconf::CheckboxTParameter>("gc").label("Gain control").defaultValue(DEFAULT_GAIN_CONTROL).build();
|
||||
auto param_agc_gain = iotwebconf::Builder<iotwebconf::IntTParameter<int>>("agcg").label("AGC gain").defaultValue(DEFAULT_AGC_GAIN).min(0).max(30).build();
|
||||
auto param_gain_ceiling = iotwebconf::Builder<iotwebconf::SelectTParameter<sizeof(camera_gain_ceilings[0])>>("gcl").label("Gain ceilings").optionValues((const char *)&camera_gain_ceilings).optionNames((const char *)&camera_gain_ceilings).optionCount(sizeof(camera_gain_ceilings) / sizeof(camera_gain_ceilings[0])).nameLength(sizeof(camera_gain_ceilings[0])).defaultValue(DEFAULT_GAIN_CEILING).build();
|
||||
auto param_bpc = iotwebconf::Builder<iotwebconf::CheckboxTParameter>("bpc").label("BPC").defaultValue(DEFAULT_BPC).build();
|
||||
auto param_wpc = iotwebconf::Builder<iotwebconf::CheckboxTParameter>("wpc").label("WPC").defaultValue(DEFAULT_WPC).build();
|
||||
auto param_raw_gma = iotwebconf::Builder<iotwebconf::CheckboxTParameter>("rg").label("Raw gamma").defaultValue(DEFAULT_RAW_GAMMA).build();
|
||||
auto param_lenc = iotwebconf::Builder<iotwebconf::CheckboxTParameter>("lenc").label("Lens correction").defaultValue(DEFAULT_LENC).build();
|
||||
auto param_hmirror = iotwebconf::Builder<iotwebconf::CheckboxTParameter>("hm").label("Horizontal mirror").defaultValue(DEFAULT_HORIZONTAL_MIRROR).build();
|
||||
auto param_vflip = iotwebconf::Builder<iotwebconf::CheckboxTParameter>("vm").label("Vertical mirror").defaultValue(DEFAULT_VERTICAL_MIRROR).build();
|
||||
auto param_dcw = iotwebconf::Builder<iotwebconf::CheckboxTParameter>("dcw").label("DCW").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;
|
||||
@@ -100,16 +122,40 @@ void handle_root()
|
||||
{"NetworkState.ApMode", String(iotWebConf.getState() == iotwebconf::NetworkState::ApMode)},
|
||||
{"NetworkState.OnLine", String(iotWebConf.getState() == iotwebconf::NetworkState::OnLine)},
|
||||
// Camera
|
||||
{"CameraType", camera_config_val},
|
||||
{"FrameSize", frame_size_val},
|
||||
{"FrameDuration", frame_duration_val},
|
||||
{"FrameFrequency", String(1000.0 / atol(frame_duration_val), 1)},
|
||||
{"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", frame_buffers_val},
|
||||
{"JpegQuality", jpeg_quality_val},
|
||||
{"FrameBuffers", String(param_frame_buffers.value())},
|
||||
{"JpegQuality", String(param_jpg_quality.value())},
|
||||
{"CameraInitialized", String(camera_init_result == ESP_OK)},
|
||||
{"CameraInitResultText", esp_err_to_name(camera_init_result)},
|
||||
{"FlashLedIntensity", flash_led_intensity_val},
|
||||
// Settings
|
||||
{"Brightness", String(param_brightness.value())},
|
||||
{"Contrast", String(param_contrast.value())},
|
||||
{"Saturation", String(param_saturation.value())},
|
||||
{"SpecialEffect", String(param_special_effect.value())},
|
||||
{"WhiteBal", String(param_whitebal.value())},
|
||||
{"AwbGain", String(param_awb_gain.value())},
|
||||
{"WbMode", String(param_wb_mode.value())},
|
||||
{"ExposureCtrl", String(param_exposure_ctrl.value())},
|
||||
{"Aec2", String(param_aec2.value())},
|
||||
{"AeLevel", String(param_ae_level.value())},
|
||||
{"AecValue", String(param_aec_value.value())},
|
||||
{"GainCtrl", String(param_gain_ctrl.value())},
|
||||
{"AgcGain", String(param_agc_gain.value())},
|
||||
{"GainCeiling", String(param_gain_ceiling.value())},
|
||||
{"Bpc", String(param_bpc.value())},
|
||||
{"Wpc", String(param_wpc.value())},
|
||||
{"RawGma", String(param_raw_gma.value())},
|
||||
{"Lenc", String(param_lenc.value())},
|
||||
{"HMirror", String(param_hmirror.value())},
|
||||
{"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)}};
|
||||
|
||||
@@ -150,12 +196,12 @@ void handle_snapshot()
|
||||
}
|
||||
|
||||
// Remove old images stored in the framebuffer
|
||||
auto frame_buffers = atoi(frame_buffers_val);
|
||||
auto frame_buffers = param_frame_buffers.value();
|
||||
while (frame_buffers--)
|
||||
cam.run();
|
||||
|
||||
auto fb_len = cam.getSize();
|
||||
auto fb = (const char*)cam.getfb();
|
||||
auto fb = (const char *)cam.getfb();
|
||||
if (fb == nullptr)
|
||||
{
|
||||
web_server.send(404, "text/plain", "Unable to obtain frame buffer from the camera");
|
||||
@@ -179,56 +225,84 @@ void handle_flash()
|
||||
}
|
||||
|
||||
// If no value present, use value from config
|
||||
auto value = web_server.hasArg("v") ? web_server.arg("v") : flash_led_intensity_val;
|
||||
if (web_server.hasArg("v"))
|
||||
{
|
||||
auto v = (uint8_t)min(web_server.arg("v").toInt(), 255L);
|
||||
// If conversion fails, v = 0
|
||||
auto v = (uint8_t)min(value.toInt(), 255l);
|
||||
analogWrite(LED_FLASH, v);
|
||||
}
|
||||
else
|
||||
{
|
||||
analogWrite(LED_FLASH, param_led_intensity.value());
|
||||
}
|
||||
|
||||
web_server.sendHeader("Cache-Control", "no-cache, no-store, must-revalidate");
|
||||
web_server.send(200);
|
||||
}
|
||||
|
||||
void on_config_saved()
|
||||
{
|
||||
log_v("on_config_saved");
|
||||
// Set flash led intensity
|
||||
analogWrite(LED_FLASH, atoi(flash_led_intensity_val));
|
||||
config_changed = true;
|
||||
}
|
||||
|
||||
esp_err_t initialize_camera()
|
||||
{
|
||||
log_v("initialize_camera");
|
||||
log_i("Camera config: %s", camera_config_val);
|
||||
auto camera_config = lookup_camera_config(camera_config_val);
|
||||
log_i("Frame size: %s", frame_size_val);
|
||||
auto frame_size = lookup_frame_size(frame_size_val);
|
||||
log_i("Frame buffers: %s", frame_buffers_val);
|
||||
auto frame_buffers = atoi(frame_buffers_val);
|
||||
log_i("JPEG quality: %s", jpeg_quality_val);
|
||||
auto jpeg_quality = atoi(jpeg_quality_val);
|
||||
log_i("Frame rate: %s ms", frame_duration_val);
|
||||
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("Frame buffers: %d", param_frame_buffers.value());
|
||||
// auto frame_buffers = atoi(frame_buffers_val);
|
||||
log_i("JPEG quality: %d", param_jpg_quality.value());
|
||||
log_i("Frame duration: %d ms", param_frame_duration.value());
|
||||
|
||||
camera_config.frame_size = frame_size;
|
||||
camera_config.fb_count = frame_buffers;
|
||||
camera_config.fb_count = param_frame_buffers.value();
|
||||
camera_config.fb_location = psramFound() ? CAMERA_FB_IN_PSRAM : CAMERA_FB_IN_DRAM;
|
||||
camera_config.jpeg_quality = jpeg_quality;
|
||||
camera_config.jpeg_quality = param_jpg_quality.value();
|
||||
|
||||
return cam.init(camera_config);
|
||||
}
|
||||
|
||||
void update_camera_settings()
|
||||
{
|
||||
auto camera = esp_camera_sensor_get();
|
||||
camera->set_brightness(camera, param_brightness.value());
|
||||
camera->set_contrast(camera, param_contrast.value());
|
||||
camera->set_saturation(camera, param_saturation.value());
|
||||
camera->set_special_effect(camera, lookup_camera_effect(param_special_effect.value()));
|
||||
camera->set_whitebal(camera, param_whitebal.value());
|
||||
camera->set_awb_gain(camera, param_awb_gain.value());
|
||||
camera->set_wb_mode(camera, lookup_camera_wb_mode(param_wb_mode.value()));
|
||||
camera->set_exposure_ctrl(camera, param_exposure_ctrl.value());
|
||||
camera->set_aec2(camera, param_aec2.value());
|
||||
camera->set_ae_level(camera, param_ae_level.value());
|
||||
camera->set_aec_value(camera, param_aec_value.value());
|
||||
camera->set_gain_ctrl(camera, param_gain_ctrl.value());
|
||||
camera->set_agc_gain(camera, param_agc_gain.value());
|
||||
camera->set_gainceiling(camera, lookup_camera_gainceiling(param_gain_ceiling.value()));
|
||||
camera->set_bpc(camera, param_bpc.value());
|
||||
camera->set_wpc(camera, param_wpc.value());
|
||||
camera->set_raw_gma(camera, param_raw_gma.value());
|
||||
camera->set_lenc(camera, param_lenc.value());
|
||||
camera->set_hmirror(camera, param_hmirror.value());
|
||||
camera->set_vflip(camera, param_vflip.value());
|
||||
camera->set_dcw(camera, param_dcw.value());
|
||||
camera->set_colorbar(camera, param_colorbar.value());
|
||||
}
|
||||
|
||||
void start_rtsp_server()
|
||||
{
|
||||
log_v("start_rtsp_server");
|
||||
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 buffers: %s, frame rate: %s ms, jpeg quality: %s", camera_init_result, camera_config_val, frame_size_val, frame_buffers_val, frame_duration_val, jpeg_quality_val);
|
||||
log_e("Failed to initialize camera: 0x%0x. Type: %s, frame size: %s, frame buffers: %d, frame rate: %d ms, jpeg quality: %d", camera_init_result, param_board.value(), param_frame_size.value(), param_frame_buffers.value(), param_frame_duration.value(), param_jpg_quality.value());
|
||||
return;
|
||||
}
|
||||
|
||||
log_i("Camera initialized");
|
||||
auto frame_rate = atol(frame_duration_val);
|
||||
camera_server = std::unique_ptr<rtsp_server>(new rtsp_server(cam, frame_rate, RTSP_PORT));
|
||||
|
||||
update_camera_settings();
|
||||
log_i("Camera settings updated");
|
||||
|
||||
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);
|
||||
}
|
||||
@@ -239,13 +313,23 @@ void on_connected()
|
||||
// Turn LED off (has inverted logic GPIO33) => red LED off => connected
|
||||
digitalWrite(LED_BUILTIN, true);
|
||||
// Set flash led intensity
|
||||
analogWrite(LED_FLASH, atoi(flash_led_intensity_val));
|
||||
analogWrite(LED_FLASH, param_led_intensity.value());
|
||||
// Start (OTA) Over The Air programming when connected
|
||||
ArduinoOTA.begin();
|
||||
// Start the RTSP Server
|
||||
start_rtsp_server();
|
||||
}
|
||||
|
||||
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()
|
||||
{
|
||||
// Disable brownout
|
||||
@@ -268,13 +352,40 @@ void setup()
|
||||
log_i("Free heap: %d bytes", ESP.getFreeHeap());
|
||||
log_i("Starting " APP_TITLE "...");
|
||||
|
||||
config_group_stream_settings.addItem(&config_camera_config);
|
||||
config_group_stream_settings.addItem(&config_frame_rate);
|
||||
config_group_stream_settings.addItem(&config_frame_size);
|
||||
config_group_stream_settings.addItem(&config_frame_buffers);
|
||||
config_group_stream_settings.addItem(&config_jpg_quality);
|
||||
config_group_stream_settings.addItem(&config_flash_led_intensity);
|
||||
iotWebConf.addParameterGroup(&config_group_stream_settings);
|
||||
param_group_board.addItem(¶m_board);
|
||||
iotWebConf.addParameterGroup(¶m_group_board);
|
||||
|
||||
param_group_camera.addItem(¶m_frame_duration);
|
||||
param_group_camera.addItem(¶m_frame_size);
|
||||
param_group_camera.addItem(¶m_frame_buffers);
|
||||
param_group_camera.addItem(¶m_jpg_quality);
|
||||
param_group_camera.addItem(¶m_brightness);
|
||||
param_group_camera.addItem(¶m_contrast);
|
||||
param_group_camera.addItem(¶m_saturation);
|
||||
param_group_camera.addItem(¶m_special_effect);
|
||||
param_group_camera.addItem(¶m_whitebal);
|
||||
param_group_camera.addItem(¶m_awb_gain);
|
||||
param_group_camera.addItem(¶m_wb_mode);
|
||||
param_group_camera.addItem(¶m_exposure_ctrl);
|
||||
param_group_camera.addItem(¶m_aec2);
|
||||
param_group_camera.addItem(¶m_ae_level);
|
||||
param_group_camera.addItem(¶m_aec_value);
|
||||
param_group_camera.addItem(¶m_gain_ctrl);
|
||||
param_group_camera.addItem(¶m_agc_gain);
|
||||
param_group_camera.addItem(¶m_gain_ceiling);
|
||||
param_group_camera.addItem(¶m_bpc);
|
||||
param_group_camera.addItem(¶m_wpc);
|
||||
param_group_camera.addItem(¶m_raw_gma);
|
||||
param_group_camera.addItem(¶m_lenc);
|
||||
param_group_camera.addItem(¶m_hmirror);
|
||||
param_group_camera.addItem(¶m_vflip);
|
||||
param_group_camera.addItem(¶m_dcw);
|
||||
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);
|
||||
|
||||
Reference in New Issue
Block a user