diff --git a/README.md b/README.md index 705766b..a14adba 100644 --- a/README.md +++ b/README.md @@ -7,20 +7,20 @@ Easy configuration through the web interface. 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**. -### RTSP +**RTSP** The RTSP protocol is an industry standard and allows many CCTV systems and applications (like for example [VLC](https://www.videolan.org/vlc/)) to connect directly to the ESP32CAM camera stream. 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 of the module>:554/mjpeg/1 -### HTTP Motion JPEG +**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 of the module>/stream -### HTTP image server -The HTTP Image server returns an HTTP jpeg image of the camera. +**HTTP image** +The HTTP Image returns an HTTP JPEG image of the camera. The URL is http://<ip address of the module>/snapshot @@ -45,6 +45,8 @@ This software provides a **configuration web server**, that can be used to: - 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 @@ -259,23 +261,55 @@ Make sure the power is 5 volts and stable, although the ESP32 is a 3.3V module, 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. -### 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 | +|--- |--- | +| ESP32CAM | Yes | +| ESP32CAM (USB-C) | No | +| AI THINKER | Yes | +| TTGO T-CAM | No | +| M5 STACK| | No | +| WROVER KIT | Yes | + +Depending on the image resolution, framerate and quality, the PSRAM must be enabled and/or the number of frame buffers increased to keep up with the data generated by the sensor. +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 +- March 2023 + - Added options to set PSRAM / Frame buffers + - Added JPEG Motion streaming - Feb 2023 - Added additional settings for camera configuration - Nov 2022 diff --git a/html/index.html b/html/index.html index 7276492..f09319b 100644 --- a/html/index.html +++ b/html/index.html @@ -26,6 +26,10 @@
ESP32
+
+
SDK Version:
+
{{SDKVersion}}
+
CPU model:
{{ChipModel}} rev. {{ChipRevision}}
@@ -125,12 +129,12 @@
{{#NetworkState.ApMode}} {{/NetworkState.ApMode}} {{#NetworkState.OnLine}} {{/NetworkState.OnLine}}
@@ -148,18 +152,18 @@
Frame size:
{{FrameSize}}
-
-
Frame buffer location:
-
{{FrameBufferLocation}}
-
-
-
Frame buffers:
-
{{FrameBuffers}}
-
JPEG quality:
{{JpegQuality}} [1-100]
+
+
Enable PSRAM:
+
{{#EnablePSRAM}}Enabled{{/EnablePSRAM}}{{^EnablePSRAM}}Disabled{{/EnablePSRAM}}
+
+
+
Number of frame buffers:
+
{{FrameBuffers}}
+
Brightness:
{{Brightness}} [-2,2]
@@ -178,11 +182,11 @@
White balance:
-
{{WhiteBal}}
+
{{#WhiteBal}}Auto{{/WhiteBal}}{{^WhiteBal}}Manual{{/WhiteBal}}
AWB gain:
-
{{AwbGain}}
+
{{#AwbGain}}Auto{{/AwbGain}}{{^AwbGain}}Manual{{/AwbGain}}
WB mode:
@@ -190,11 +194,12 @@
Exposure control:
-
{{ExposureCtrl}}
+
+ {{#ExposureCtrl}}Auto{{/ExposureCtrl}}{{^ExposureCtrl}}Manual{{/ExposureCtrl}}
Auto exposure control (dsp):
-
{{Aec2}}
+
{{#Aec2}}Enabled{{/Aec2}}{{^Aec2}}Disabled{{/Aec2}}
Auto Exposure level:
@@ -206,7 +211,7 @@
Gain control:
-
{{GainCtrl}}
+
{{#GainCtrl}}Auto{{/GainCtrl}}{{^GainCtrl}}Manual{{/GainCtrl}}
AGC gain:
@@ -218,48 +223,47 @@
Black pixel correct:
-
{{Bpc}}
+
{{#Bpc}}Auto{{/Bpc}}{{^Bpc}}Manual{{/Bpc}}
White pixel correct:
-
{{Wpc}}
+
{{#Wpc}}Auto{{/Wpc}}{{^Wpc}}Manual{{/Wpc}}
Gamma correct:
-
{{RawGma}}
+
{{#RawGma}}Enabled{{/RawGma}}{{^RawGma}}Disabled{{/RawGma}}
Lens correction:
-
{{Lenc}}
+
{{#Lenc}}Enabled{{/Lenc}}{{^Lenc}}Disabled{{/Lenc}}
Horizontal mirror:
-
{{HMirror}}
+
{{#HMirror}}Mirrored{{/HMirror}}{{^HMirror}}Normal{{/HMirror}}
Vertical flip:
-
{{VFlip}}
+
{{#VFlip}}Flipped{{/VFlip}}{{^VFlip}}Normal{{/VFlip}}
Downsize enable:
-
{{Dcw}}
+
{{#Dcw}}Enabled{{/Dcw}}{{^Dcw}}Disabled{{/Dcw}}
Color bar:
-
{{ColorBar}}
+
{{#ColorBar}}Enabled{{/ColorBar}}{{^ColorBar}}Camera{{/ColorBar}}
{{#CameraInitialized}} {{/CameraInitialized}} {{^CameraInitialized}} {{/CameraInitialized}} @@ -288,16 +292,12 @@
- The intensity of the flash led (0-255) can be controlled using: - http://{{IpV4}}/flash?v=value. - Authentication is required and if no value is present, the configuration value is used. + Intensity of the flash led (0-255): http://{{IpV4}}/flash?v=0. Authentication is required.
- Restarting the camera can be done using: - http://{{IpV4}}/restart. - Authentication is required. + Restart the camera: http://{{IpV4}}/restart. Authentication is required.
diff --git a/include/html_data.h b/include/html_data.h index 792a21d..9aa833e 100644 --- a/include/html_data.h +++ b/include/html_data.h @@ -4,5 +4,5 @@ // ****************************************************************************** -constexpr char file_data_index_html[] = "{{AppTitle}} v{{AppVersion}}

{{ThingName}}


{{#ConfigChanged}} {{/ConfigChanged}}
ESP32
CPU model:
{{ChipModel}} rev. {{ChipRevision}}
CPU speed:
{{CpuFreqMHz}} Mhz
CPU cores:
{{CpuCores}}
RAM size:
{{HeapSize}}
PSRAM size:
{{PsRamSize}}
Flash size:
{{FlashSize}}
Diagnostics
Uptime:
{{Uptime}}
RTSP sessions:
{{NumRTSPSessions}}
Free heap:
{{FreeHeap}}
Max free block:
{{MaxAllocHeap}}
Peripheral
Board type:
{{BoardType}}
LED intensity:
{{LedIntensity}} [0-100]
Network
Host name:
{{HostName}}
Mac address:
{{MacAddress}}
Wifi mode:
{{WifiMode}}
Access point:
{{AccessPoint}}
Signal strength:
{{SignalStrength}} dbm
IPv4 address:
{{IpV4}}
IPv6 address:
{{IpV6}}
{{#NetworkState.ApMode}} {{/NetworkState.ApMode}} {{#NetworkState.OnLine}} {{/NetworkState.OnLine}}
Camera
Frame rate:
{{FrameDuration}} ms ({{FrameFrequency}} f/s)
Frame size:
{{FrameSize}}
Frame buffer location:
{{FrameBufferLocation}}
Frame buffers:
{{FrameBuffers}}
JPEG quality:
{{JpegQuality}} [1-100]
Brightness:
{{Brightness}} [-2,2]
Contrast:
{{Contrast}} [-2,2]
Saturation:
{{Saturation}} [-2,2]
Special effect:
{{SpecialEffect}}
White balance:
{{WhiteBal}}
AWB gain:
{{AwbGain}}
WB mode:
{{WbMode}}
Exposure control:
{{ExposureCtrl}}
Auto exposure control (dsp):
{{Aec2}}
Auto Exposure level:
{{AeLevel}}
Manual exposure value:
{{AecValue}}
Gain control:
{{GainCtrl}}
AGC gain:
{{AgcGain}}
Gain ceiling:
{{GainCeiling}}
Black pixel correct:
{{Bpc}}
White pixel correct:
{{Wpc}}
Gamma correct:
{{RawGma}}
Lens correction:
{{Lenc}}
Horizontal mirror:
{{HMirror}}
Vertical flip:
{{VFlip}}
Downsize enable:
{{Dcw}}
Color bar:
{{ColorBar}}
{{#CameraInitialized}} {{/CameraInitialized}} {{^CameraInitialized}} {{/CameraInitialized}}
Special URLs / API
JPEG Motion stream: http://{{IpV4}}/stream
Snapshot of the camera: http://{{IpV4}}/snapshot
The intensity of the flash led (0-255) can be controlled using: http://{{IpV4}}/flash?v=value. Authentication is required and if no value is present, the configuration value is used.
Restarting the camera can be done using: http://{{IpV4}}/restart. Authentication is required.
"; +constexpr char file_data_index_html[] = "{{AppTitle}} v{{AppVersion}}

{{ThingName}}


{{#ConfigChanged}} {{/ConfigChanged}}
ESP32
SDK Version:
{{SDKVersion}}
CPU model:
{{ChipModel}} rev. {{ChipRevision}}
CPU speed:
{{CpuFreqMHz}} Mhz
CPU cores:
{{CpuCores}}
RAM size:
{{HeapSize}}
PSRAM size:
{{PsRamSize}}
Flash size:
{{FlashSize}}
Diagnostics
Uptime:
{{Uptime}}
RTSP sessions:
{{NumRTSPSessions}}
Free heap:
{{FreeHeap}}
Max free block:
{{MaxAllocHeap}}
Peripheral
Board type:
{{BoardType}}
LED intensity:
{{LedIntensity}} [0-100]
Network
Host name:
{{HostName}}
Mac address:
{{MacAddress}}
Wifi mode:
{{WifiMode}}
Access point:
{{AccessPoint}}
Signal strength:
{{SignalStrength}} dbm
IPv4 address:
{{IpV4}}
IPv6 address:
{{IpV6}}
{{#NetworkState.ApMode}} {{/NetworkState.ApMode}} {{#NetworkState.OnLine}} {{/NetworkState.OnLine}}
Camera
Frame rate:
{{FrameDuration}} ms ({{FrameFrequency}} f/s)
Frame size:
{{FrameSize}}
JPEG quality:
{{JpegQuality}} [1-100]
Enable PSRAM:
{{#EnablePSRAM}}Enabled{{/EnablePSRAM}}{{^EnablePSRAM}}Disabled{{/EnablePSRAM}}
Number of frame buffers:
{{FrameBuffers}}
Brightness:
{{Brightness}} [-2,2]
Contrast:
{{Contrast}} [-2,2]
Saturation:
{{Saturation}} [-2,2]
Special effect:
{{SpecialEffect}}
White balance:
{{#WhiteBal}}Auto{{/WhiteBal}}{{^WhiteBal}}Manual{{/WhiteBal}}
AWB gain:
{{#AwbGain}}Auto{{/AwbGain}}{{^AwbGain}}Manual{{/AwbGain}}
WB mode:
{{WbMode}}
Exposure control:
{{#ExposureCtrl}}Auto{{/ExposureCtrl}}{{^ExposureCtrl}}Manual{{/ExposureCtrl}}
Auto exposure control (dsp):
{{#Aec2}}Enabled{{/Aec2}}{{^Aec2}}Disabled{{/Aec2}}
Auto Exposure level:
{{AeLevel}}
Manual exposure value:
{{AecValue}}
Gain control:
{{#GainCtrl}}Auto{{/GainCtrl}}{{^GainCtrl}}Manual{{/GainCtrl}}
AGC gain:
{{AgcGain}}
Gain ceiling:
{{GainCeiling}}
Black pixel correct:
{{#Bpc}}Auto{{/Bpc}}{{^Bpc}}Manual{{/Bpc}}
White pixel correct:
{{#Wpc}}Auto{{/Wpc}}{{^Wpc}}Manual{{/Wpc}}
Gamma correct:
{{#RawGma}}Enabled{{/RawGma}}{{^RawGma}}Disabled{{/RawGma}}
Lens correction:
{{#Lenc}}Enabled{{/Lenc}}{{^Lenc}}Disabled{{/Lenc}}
Horizontal mirror:
{{#HMirror}}Mirrored{{/HMirror}}{{^HMirror}}Normal{{/HMirror}}
Vertical flip:
{{#VFlip}}Flipped{{/VFlip}}{{^VFlip}}Normal{{/VFlip}}
Downsize enable:
{{#Dcw}}Enabled{{/Dcw}}{{^Dcw}}Disabled{{/Dcw}}
Color bar:
{{#ColorBar}}Enabled{{/ColorBar}}{{^ColorBar}}Camera{{/ColorBar}}
{{#CameraInitialized}} {{/CameraInitialized}} {{^CameraInitialized}} {{/CameraInitialized}}
Special URLs / API
JPEG Motion stream: http://{{IpV4}}/stream
Snapshot of the camera: http://{{IpV4}}/snapshot
Intensity of the flash led (0-255): http://{{IpV4}}/flash?v=0. Authentication is required.
Restart the camera: http://{{IpV4}}/restart. Authentication is required.
"; constexpr char file_data_restart_html[] = "{{AppTitle}} v{{AppVersion}}

{{ThingName}}


Restart

The device is restarting.


In some cases, the device requires a hard reset (power cycle).

If this page takes longer than a minute, consider performing a power cycle.

Restarting...
"; diff --git a/include/settings.h b/include/settings.h index 86aa5a6..db14151 100644 --- a/include/settings.h +++ b/include/settings.h @@ -5,15 +5,17 @@ #define WIFI_SSID "ESP32CAM-RTSP" #define WIFI_PASSWORD nullptr -#define CONFIG_VERSION "1.4" +#define CONFIG_VERSION "1.5" #define OTA_PASSWORD "ESP32CAM-RTSP" #define RTSP_PORT 554 #define DEFAULT_CAMERA_CONFIG "AI THINKER" +#define DEFAULT_ENABLE_PSRAM psramFound() +#define DEFAULT_BUFFERS (psramFound() ? 2 : 1) #define DEFAULT_FRAME_DURATION 100 -#define DEFAULT_FRAME_SIZE (psramFound() ? "UXGA (1600x1200)" : "VGA (640x480)") +#define DEFAULT_FRAME_SIZE (psramFound() ? "UXGA (1600x1200)" : "SVGA (800x600)") #define DEFAULT_JPEG_QUALITY (psramFound() ? 10 : 12) #define DEFAULT_BRIGHTNESS 0 diff --git a/src/main.cpp b/src/main.cpp index 08074ae..3e94416 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -25,6 +25,8 @@ auto param_group_camera = iotwebconf::ParameterGroup("camera", "Camera settings" auto param_frame_duration = iotwebconf::Builder>("fd").label("Frame duration (ms)").defaultValue(DEFAULT_FRAME_DURATION).min(10).build(); auto param_frame_size = iotwebconf::Builder>("fs").label("Frame size").optionValues((const char *)&frame_sizes).optionNames((const char *)&frame_sizes).optionCount(sizeof(frame_sizes) / sizeof(frame_sizes[0])).nameLength(sizeof(frame_sizes[0])).defaultValue(DEFAULT_FRAME_SIZE).build(); auto param_jpg_quality = iotwebconf::Builder>("q").label("JPG quality").defaultValue(DEFAULT_JPEG_QUALITY).min(1).max(100).build(); +auto param_enable_psram = iotwebconf::Builder("eps").label("Enable PSRAM if available").defaultValue(DEFAULT_ENABLE_PSRAM).build(); +auto param_frame_buffers = iotwebconf::Builder>("fb").label("Buffers").defaultValue(DEFAULT_BUFFERS).min(1).max(4).build(); auto param_brightness = iotwebconf::Builder>("b").label("Brightness").defaultValue(DEFAULT_BRIGHTNESS).min(-2).max(2).build(); auto param_contrast = iotwebconf::Builder>("c").label("Contrast").defaultValue(DEFAULT_CONTRAST).min(-2).max(2).build(); auto param_saturation = iotwebconf::Builder>("s").label("Saturation").defaultValue(DEFAULT_SATURATION).min(-2).max(2).build(); @@ -98,6 +100,7 @@ void handle_root() {"AppTitle", APP_TITLE}, {"AppVersion", APP_VERSION}, {"ThingName", iotWebConf.getThingName()}, + {"SDKVersion", ESP.getSdkVersion()}, {"ChipModel", ESP.getChipModel()}, {"ChipRevision", String(ESP.getChipRevision())}, {"CpuFreqMHz", String(ESP.getCpuFreqMHz())}, @@ -109,7 +112,7 @@ 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", camera_server != nullptr ? String(camera_server->num_connected()) : "RTSP server disabled"}, // Network {"HostName", hostname}, {"MacAddress", WiFi.macAddress()}, @@ -125,10 +128,11 @@ void handle_root() {"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())}, + {"EnablePSRAM", String(param_enable_psram.value())}, + {"FrameBuffers", String(param_frame_buffers.value())}, {"CameraInitialized", String(camera_init_result == ESP_OK)}, + {"CameraInitResult", String(camera_init_result)}, {"CameraInitResultText", esp_err_to_name(camera_init_result)}, // Settings {"Brightness", String(param_brightness.value())}, @@ -194,7 +198,7 @@ void handle_snapshot() return; } - // Remove old images stored in the framebuffer + // Remove old images stored in the frame buffer auto frame_buffers = psramFound() ? 2 : 1; while (frame_buffers--) cam.run(); @@ -273,24 +277,33 @@ 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()); + auto camera_config_template = lookup_camera_config(param_board.value()); + // Copy the settings + camera_config_t camera_config; + memset(&camera_config, 0, sizeof(camera_config_t)); + memcpy(&camera_config, &camera_config_template, sizeof(camera_config_t)); 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()); 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.grab_mode = CAMERA_GRAB_LATEST; + log_i("Enable PSRAM: %d", param_enable_psram.value()); + log_i("Frame buffers: %d", param_frame_buffers.value()); + camera_config.fb_count = param_frame_buffers.value(); + + if (param_enable_psram.value() && psramFound()) { - camera_config.fb_count = 2; camera_config.fb_location = CAMERA_FB_IN_PSRAM; + log_i("PSRAM enabled!"); } else { - camera_config.fb_count = 1; camera_config.fb_location = CAMERA_FB_IN_DRAM; + log_i("PSRAM disabled"); } + return cam.init(camera_config); } @@ -344,9 +357,11 @@ void on_connected() 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() @@ -379,14 +394,20 @@ void setup() log_i("CPU Freq: %d Mhz", getCpuFrequencyMhz()); log_i("Free heap: %d bytes", ESP.getFreeHeap()); + log_i("SDK version: %s", ESP.getSdkVersion()); log_i("Starting " APP_TITLE "..."); + if (psramFound()) + psramInit(); + 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_jpg_quality); + param_group_camera.addItem(¶m_enable_psram); + param_group_camera.addItem(¶m_frame_buffers); param_group_camera.addItem(¶m_brightness); param_group_camera.addItem(¶m_contrast); param_group_camera.addItem(¶m_saturation);