diff --git a/examples/single_chip/camera_web_server/main/Kconfig.projbuild b/examples/single_chip/camera_web_server/main/Kconfig.projbuild index 738788e..0d62338 100644 --- a/examples/single_chip/camera_web_server/main/Kconfig.projbuild +++ b/examples/single_chip/camera_web_server/main/Kconfig.projbuild @@ -38,6 +38,59 @@ config ESP_MAXIMUM_RETRY Set the Maximum retry to avoid station reconnecting to the AP unlimited when the AP is really inexistent. endmenu +menu "LED Illuminator" +config LED_ILLUMINATOR_ENABLED + bool "LED Illuminator Enabled" + default n + help + Enable an LED Flash or IR Illuminator + +config LED_LEDC_PIN + depends on LED_ILLUMINATOR_ENABLED + int "LED Illuminator GPIO Pin" + range 0 33 + default 4 + help + Set a pin to illuminate an onboard LED or IR Illuminator when streaming or taking snapshots. + +config LED_MAX_INTENSITY + depends on LED_ILLUMINATOR_ENABLED + int "LED Maximum Intensity (0-255)" + range 0 255 + default 255 + help + Limit the maximum intensity of the LED while streaming to prevent overheating (0-255). + +choice LED_LEDC_SPEED_MODE + depends on LED_ILLUMINATOR_ENABLED + bool "Select LEDC Timer Speed Mode" + default LED_LEDC_LOW_SPEED_MODE + help + Select a speed mode for the LEDC channel + +config LED_LEDC_LOW_SPEED_MODE + bool "LOW_SPEED_MODE" +config LED_LEDC_HIGH_SPEED_MODE + bool "HIGH_SPEED_MODE" +endchoice + +config LED_LEDC_TIMER + depends on LED_ILLUMINATOR_ENABLED + int "LEDC Timer" + range 0 3 + default 1 + help + Select the LEDC Timer (0-3) + +config LED_LEDC_CHANNEL + depends on LED_ILLUMINATOR_ENABLED + int "LEDC Channel" + range 0 7 + default 1 + help + Select the LEDC Channel (0-7) +endmenu + menu "Camera Pins" choice CAMERA_MODEL bool "Select Camera Pinout" diff --git a/examples/single_chip/camera_web_server/main/app_camera.c b/examples/single_chip/camera_web_server/main/app_camera.c index 6682487..0c03df5 100755 --- a/examples/single_chip/camera_web_server/main/app_camera.c +++ b/examples/single_chip/camera_web_server/main/app_camera.c @@ -20,6 +20,7 @@ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "esp_log.h" +#include "driver/ledc.h" #include "esp_camera.h" #include "app_camera.h" #include "sdkconfig.h" @@ -44,6 +45,36 @@ void app_camera_main () gpio_config(&conf); #endif +#ifdef CONFIG_LED_ILLUMINATOR_ENABLED + gpio_set_direction(CONFIG_LED_LEDC_PIN,GPIO_MODE_OUTPUT); + ledc_timer_config_t ledc_timer = { + .duty_resolution = LEDC_TIMER_8_BIT, // resolution of PWM duty + .freq_hz = 1000, // frequency of PWM signal + .speed_mode = LEDC_LOW_SPEED_MODE, // timer mode + .timer_num = CONFIG_LED_LEDC_TIMER // timer index + }; + ledc_channel_config_t ledc_channel = { + .channel = CONFIG_LED_LEDC_CHANNEL, + .duty = 0, + .gpio_num = CONFIG_LED_LEDC_PIN, + .speed_mode = LEDC_LOW_SPEED_MODE, + .hpoint = 0, + .timer_sel = CONFIG_LED_LEDC_TIMER + }; + #ifdef CONFIG_LED_LEDC_HIGH_SPEED_MODE + ledc_timer.speed_mode = ledc_channel.speed_mode = LEDC_HIGH_SPEED_MODE; + #endif + switch (ledc_timer_config(&ledc_timer)) { + case ESP_ERR_INVALID_ARG: ESP_LOGE(TAG, "ledc_timer_config() parameter error"); break; + case ESP_FAIL: ESP_LOGE(TAG, "ledc_timer_config() Can not find a proper pre-divider number base on the given frequency and the current duty_resolution"); break; + case ESP_OK: if (ledc_channel_config(&ledc_channel) == ESP_ERR_INVALID_ARG) { + ESP_LOGE(TAG, "ledc_channel_config() parameter error"); + } + break; + default: break; + } +#endif + camera_config_t config; config.ledc_channel = LEDC_CHANNEL_0; config.ledc_timer = LEDC_TIMER_0; diff --git a/examples/single_chip/camera_web_server/main/app_httpd.c b/examples/single_chip/camera_web_server/main/app_httpd.c index 59dc7b8..c4797e9 100644 --- a/examples/single_chip/camera_web_server/main/app_httpd.c +++ b/examples/single_chip/camera_web_server/main/app_httpd.c @@ -17,6 +17,7 @@ #include "esp_camera.h" #include "img_converters.h" #include "fb_gfx.h" +#include "driver/ledc.h" //#include "camera_index.h" #include "sdkconfig.h" @@ -47,6 +48,16 @@ static const char* TAG = "camera_httpd"; #define FACE_COLOR_PURPLE (FACE_COLOR_BLUE | FACE_COLOR_RED) #endif +#ifdef CONFIG_LED_ILLUMINATOR_ENABLED +int led_duty = 0; +bool isStreaming = false; +#ifdef CONFIG_LED_LEDC_LOW_SPEED_MODE +#define CONFIG_LED_LEDC_SPEED_MODE LEDC_LOW_SPEED_MODE +#else +#define CONFIG_LED_LEDC_SPEED_MODE LEDC_HIGH_SPEED_MODE +#endif +#endif + typedef struct { httpd_req_t *req; size_t len; @@ -59,6 +70,7 @@ static const char* _STREAM_PART = "Content-Type: image/jpeg\r\nContent-Length: % httpd_handle_t stream_httpd = NULL; httpd_handle_t camera_httpd = NULL; + #if CONFIG_ESP_FACE_DETECT_ENABLED static mtmn_config_t mtmn_config = {0}; static int8_t detection_enabled = 0; @@ -225,6 +237,19 @@ static int run_face_recognition(dl_matrix3du_t *image_matrix, box_array_t *net_b } #endif #endif + +#ifdef CONFIG_LED_ILLUMINATOR_ENABLED +void enable_led(bool en) { // Turn LED On or Off + int duty = en ? led_duty : 0; + if (en && isStreaming && (led_duty > CONFIG_LED_MAX_INTENSITY)) { + duty = CONFIG_LED_MAX_INTENSITY; + } + ledc_set_duty(CONFIG_LED_LEDC_SPEED_MODE, CONFIG_LED_LEDC_CHANNEL, duty); + ledc_update_duty(CONFIG_LED_LEDC_SPEED_MODE, CONFIG_LED_LEDC_CHANNEL); + ESP_LOGI(TAG, "Set LED intensity to %d", duty); +} +#endif + static size_t jpg_encode_stream(void * arg, size_t index, const void* data, size_t len){ jpg_chunking_t *j = (jpg_chunking_t *)arg; if(!index){ @@ -242,7 +267,15 @@ static esp_err_t capture_handler(httpd_req_t *req){ esp_err_t res = ESP_OK; int64_t fr_start = esp_timer_get_time(); + #ifdef CONFIG_LED_ILLUMINATOR_ENABLED + enable_led(true); + vTaskDelay(150 / portTICK_PERIOD_MS); // The LED needs to be turned on ~150ms before the call to esp_camera_fb_get() + fb = esp_camera_fb_get(); // or it won't be visible in the frame. A better way to do this is needed. + enable_led(false); + #else fb = esp_camera_fb_get(); + #endif + if (!fb) { ESP_LOGE(TAG, "Camera capture failed"); httpd_resp_send_500(req); @@ -359,11 +392,17 @@ static esp_err_t stream_handler(httpd_req_t *req){ httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*"); +#ifdef CONFIG_LED_ILLUMINATOR_ENABLED + enable_led(true); + isStreaming = true; +#endif + while(true){ #if CONFIG_ESP_FACE_DETECT_ENABLED detected = false; face_id = 0; #endif + fb = esp_camera_fb_get(); if (!fb) { ESP_LOGE(TAG, "Camera capture failed"); @@ -489,6 +528,12 @@ static esp_err_t stream_handler(httpd_req_t *req){ ); } + +#ifdef CONFIG_LED_ILLUMINATOR_ENABLED + isStreaming = false; + enable_led(false); +#endif + last_frame = 0; return res; } @@ -556,6 +601,9 @@ static esp_err_t cmd_handler(httpd_req_t *req){ else if(!strcmp(variable, "special_effect")) res = s->set_special_effect(s, val); else if(!strcmp(variable, "wb_mode")) res = s->set_wb_mode(s, val); else if(!strcmp(variable, "ae_level")) res = s->set_ae_level(s, val); +#ifdef CONFIG_LED_ILLUMINATOR_ENABLED + else if(!strcmp(variable, "led_intensity")) { led_duty = val; if (isStreaming) enable_led(true); } +#endif #if CONFIG_ESP_FACE_DETECT_ENABLED else if(!strcmp(variable, "face_detect")) { @@ -619,7 +667,11 @@ static esp_err_t status_handler(httpd_req_t *req){ p+=sprintf(p, "\"hmirror\":%u,", s->status.hmirror); p+=sprintf(p, "\"dcw\":%u,", s->status.dcw); p+=sprintf(p, "\"colorbar\":%u", s->status.colorbar); - +#ifdef CONFIG_LED_ILLUMINATOR_ENABLED + p+= sprintf(p, ",\"led_intensity\":%u", led_duty); +#else + p+= sprintf(p, ",\"led_intensity\":%d", -1); +#endif #if CONFIG_ESP_FACE_DETECT_ENABLED p+=sprintf(p, ",\"face_detect\":%u", detection_enabled); #if CONFIG_ESP_FACE_RECOGNITION_ENABLED diff --git a/examples/single_chip/camera_web_server/main/www/index_ov2640.html b/examples/single_chip/camera_web_server/main/www/index_ov2640.html index 8460f32..3477ebb 100644 --- a/examples/single_chip/camera_web_server/main/www/index_ov2640.html +++ b/examples/single_chip/camera_web_server/main/www/index_ov2640.html @@ -531,6 +531,12 @@ +
+ +
0
+ +
255
+
@@ -612,6 +618,8 @@ document.addEventListener('DOMContentLoaded', function (event) { value ? show(wb) : hide(wb) } else if(el.id === "face_recognize"){ value ? enable(enrollButton) : disable(enrollButton) + } else if(el.id == "led_intensity"){ + value > -1 ? show(ledGroup) : hide(ledGroup) } } } @@ -670,6 +678,7 @@ document.addEventListener('DOMContentLoaded', function (event) { const enrollButton = document.getElementById('face_enroll') const closeButton = document.getElementById('close-stream') const saveButton = document.getElementById('save-still') + const ledGroup = document.getElementById('led-group') const stopStream = () => { window.stop(); diff --git a/examples/single_chip/camera_web_server/main/www/index_ov2640.html.gz b/examples/single_chip/camera_web_server/main/www/index_ov2640.html.gz index 4d8c535..60cd5e6 100644 Binary files a/examples/single_chip/camera_web_server/main/www/index_ov2640.html.gz and b/examples/single_chip/camera_web_server/main/www/index_ov2640.html.gz differ diff --git a/examples/single_chip/camera_web_server/main/www/index_ov3660.html b/examples/single_chip/camera_web_server/main/www/index_ov3660.html index e7fa8e3..37f2dfd 100644 --- a/examples/single_chip/camera_web_server/main/www/index_ov3660.html +++ b/examples/single_chip/camera_web_server/main/www/index_ov3660.html @@ -560,6 +560,12 @@
+
+ +
0
+ +
255
+
@@ -639,6 +645,8 @@ document.addEventListener('DOMContentLoaded', function (event) { value ? show(wb) : hide(wb) } else if(el.id === "face_recognize"){ value ? enable(enrollButton) : disable(enrollButton) + } else if(el.id == "led_intensity"){ + value > -1 ? show(ledGroup) : hide(ledGroup) } } } @@ -697,6 +705,7 @@ document.addEventListener('DOMContentLoaded', function (event) { const enrollButton = document.getElementById('face_enroll') const closeButton = document.getElementById('close-stream') const saveButton = document.getElementById('save-still') + const ledGroup = document.getElementById('led-group') const stopStream = () => { window.stop(); diff --git a/examples/single_chip/camera_web_server/main/www/index_ov3660.html.gz b/examples/single_chip/camera_web_server/main/www/index_ov3660.html.gz index 411189c..db85d56 100644 Binary files a/examples/single_chip/camera_web_server/main/www/index_ov3660.html.gz and b/examples/single_chip/camera_web_server/main/www/index_ov3660.html.gz differ