Feature: Control a LED Illuminator or Flash from Camera_Web_Server Web Page (#104)

* Feature: Control a LED Illuminator or Flash from Camera_Web_Server

Add a "LED Illuminator" menu to menuconfig
Through menuconfig you can select a LEDC pin, LEDC Timer, LEDC Channel, and LEDC Speed Mode
The defaults work for the AIThinker ESPCAM-32 board
There is an option to limit LED intensity while streaming to prevent overheating
LED intensity is not limited when taking a still image photo
The LED intensity controls are hidden on the web page if the LED feature is not enabled
LED intensity can be adjusted while streaming and the LED will respond right away

Tested on an ESPCAM-32 board with an OV2460 sensor

* app_httpd.c: move isStreaming into macro

* app_httpd.c: Change commit re: 150ms LED delay
pull/131/head
Bond Keevil 2019-10-25 04:24:21 -04:00 committed by XiaochaoGONG
parent 65d4587d76
commit 1169211177
7 changed files with 155 additions and 1 deletions

View File

@ -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"

View File

@ -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;

View File

@ -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

View File

@ -531,6 +531,12 @@
<label class="slider" for="colorbar"></label>
</div>
</div>
<div class="input-group" id="led-group">
<label for="led_intensity">LED Intensity</label>
<div class="range-min">0</div>
<input type="range" id="led_intensity" min="0" max="255" value="0" class="default-action">
<div class="range-max">255</div>
</div>
<div class="input-group" id="face_detect-group">
<label for="face_detect">Face Detection</label>
<div class="switch">
@ -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();

View File

@ -560,6 +560,12 @@
<label class="slider" for="colorbar"></label>
</div>
</div>
<div class="input-group" id="led-group">
<label for="led_intensity">LED Intensity</label>
<div class="range-min">0</div>
<input type="range" id="led_intensity" min="0" max="255" value="0" class="default-action">
<div class="range-max">255</div>
</div>
<div class="input-group" id="face_detect-group">
<label for="face_detect">Face Detection</label>
<div class="switch">
@ -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();