Skip to content

Fix autoLamp and FPS settings #239

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
May 9, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 13 additions & 7 deletions app_httpd.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,18 @@
#include "src/logo.h"
#include "storage.h"

extern "C"{
#if __has_include("myconfig.h")
struct station { const char ssid[65]; const char password[65]; const bool dhcp;};
#include "myconfig.h"
#endif

#ifndef MIN_FRAME_TIME
#warning "MIN_FRAME_TIME undefined, using default value of 500"
#define MIN_FRAME_TIME 500
#endif

#include "cam_streamer.h"
}

// Functions from the main .ino
extern void flashLED(int flashtime);
extern void setLamp(int newVal);
Expand Down Expand Up @@ -302,7 +311,7 @@ static esp_err_t cmd_handler(httpd_req_t *req){
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);
else if(!strcmp(variable, "rotate")) myRotation = val;
else if(!strcmp(variable, "min_frame_time")) minFrameTime = val;
else if(!strcmp(variable, "min_frame_time")) cam_streamer_set_frame_delay(cam_streamer, val);
else if(!strcmp(variable, "autolamp") && (lampVal != -1)) {
autoLamp = val;
if (autoLamp) {
Expand Down Expand Up @@ -793,10 +802,7 @@ void startCameraServer(int hPort, int sPort){
httpd_register_uri_handler(stream_httpd, &info_uri);
httpd_register_uri_handler(stream_httpd, &streamviewer_uri);
cam_streamer=(cam_streamer_t *) malloc(sizeof(cam_streamer_t));
#ifndef CAM_STREAMER_DESIRED_FPS
#define CAM_STREAMER_DESIRED_FPS 2
#endif
cam_streamer_init(cam_streamer, stream_httpd, CAM_STREAMER_DESIRED_FPS);
cam_streamer_init(cam_streamer, stream_httpd, MIN_FRAME_TIME);
cam_streamer_start(cam_streamer);
}
httpd_register_uri_handler(stream_httpd, &favicon_16x16_uri);
Expand Down
135 changes: 81 additions & 54 deletions cam_streamer.c → cam_streamer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,24 @@
#include <freertos/FreeRTOS.h>
#include <freertos/queue.h>
#include <freertos/task.h>
#include <sys/socket.h>

#include "cam_streamer.h"

#if __has_include("myconfig.h")
struct station {
const char ssid[65];
const char password[65];
const bool dhcp;
};
#include "myconfig.h"
#endif

#ifndef CAM_STREAMER_MAX_CLIENTS
#warning "CAM_STREAMER_MAX_CLIENTS undefined, using default value of 10"
#define CAM_STREAMER_MAX_CLIENTS 10
#endif

#define PART_BOUNDARY "123456789000000000000987654321"

#define _STREAM_HEADERS "HTTP/1.1 200 OK\r\n"\
Expand All @@ -18,46 +33,60 @@
"Keep-Alive: timeout=15\r\n"\
"Content-Type: multipart/x-mixed-replace;boundary=" PART_BOUNDARY "\r\n"

#define _TEXT_HEADERS "HTTP/1.1 200 OK\r\n"\
"Access-Control-Allow-Origin: *\r\n"\
"Connection: Close\r\n"\
"Content-Type: text/plain\r\n\r\n"

extern bool debugData;
extern int lampVal;
extern bool autoLamp;
extern void setLamp(int);

static const char* _STREAM_BOUNDARY = "\r\n--" PART_BOUNDARY "\r\n";
static const char* _STREAM_PART = "Content-Type: image/jpeg\r\nContent-Length: %u\r\n\r\n";

static inline void print_debug(const char *fmt, ...) {
if(debugData) {
va_list l;
va_start(l, fmt);
vprintf(fmt, l);
va_end(l);
}
}

static uint8_t is_send_error(int r) {
switch(r) {
case HTTPD_SOCK_ERR_INVALID:
#ifdef DEBUG_DEFAULT_ON
printf("[cam_streamer] invalid argument occured!\n");
#endif
print_debug("[cam_streamer] invalid argument occured!\n");
return 1;
case HTTPD_SOCK_ERR_TIMEOUT:
#ifdef DEBUG_DEFAULT_ON
printf("[cam_streamer] timeout/interrupt occured!\n");
#endif
print_debug("[cam_streamer] timeout/interrupt occured!\n");
return 1;
case HTTPD_SOCK_ERR_FAIL:
#ifdef DEBUG_DEFAULT_ON
printf("[cam_streamer] unrecoverable error while send()!\n");
#endif
print_debug("[cam_streamer] unrecoverable error while send()!\n");
return 1;
case ESP_ERR_INVALID_ARG:
#ifdef DEBUG_DEFAULT_ON
printf("[text-streamer] session closed!\n");
#endif
print_debug("[text-streamer] session closed!\n");
return 1;
default:
#ifdef DEBUG_DEFAULT_ON
printf("[cam_streamer] sent %d bytes!\n", r);
#endif
print_debug("[cam_streamer] sent %d bytes!\n", r);
return 0;
}
}

void cam_streamer_init(cam_streamer_t *s, httpd_handle_t server, uint16_t fps) {
void cam_streamer_init(cam_streamer_t *s, httpd_handle_t server, uint16_t frame_delay) {
memset(s, 0, sizeof(cam_streamer_t));
s->frame_delay=1000000/fps;
s->frame_delay=1000*frame_delay;
s->clients=xQueueCreate(CAM_STREAMER_MAX_CLIENTS*2, sizeof(int));
s->server=server;
}

// frame_delay must be in ms (not us)
void cam_streamer_set_frame_delay(cam_streamer_t *s, uint16_t frame_delay) {
s->frame_delay=1000*frame_delay;
}

static void cam_streamer_update_frame(cam_streamer_t *s) {
uint8_t l=0;
while(!__atomic_compare_exchange_n(&s->buf_lock, &l, 1, 0, __ATOMIC_RELAXED, __ATOMIC_RELAXED)) {
Expand All @@ -73,17 +102,13 @@ static void cam_streamer_update_frame(cam_streamer_t *s) {
s->last_updated=esp_timer_get_time();
s->part_len=snprintf(s->part_buf, 64, _STREAM_PART, s->buf->len);
__atomic_store_n(&s->buf_lock, 0, __ATOMIC_RELAXED);
#ifdef DEBUG_DEFAULT_ON
printf("[cam_streamer] fetched new frame\n");
#endif
print_debug("[cam_streamer] fetched new frame\n");
}

static void cam_streamer_decrement_num_clients(cam_streamer_t *s) {
size_t num_clients=s->num_clients;
while(num_clients>0 && !__atomic_compare_exchange_n(&s->num_clients, &num_clients, num_clients-1, 0, __ATOMIC_RELAXED, __ATOMIC_RELAXED));
#ifdef DEBUG_DEFAULT_ON
printf("[cam_streamer] num_clients decremented\n");
#endif
print_debug("[cam_streamer] num_clients decremented\n");
}

void cam_streamer_task(void *p) {
Expand All @@ -93,35 +118,35 @@ void cam_streamer_task(void *p) {
int fd;
unsigned int n_entries;
for(;;) {
while(!(n_entries=uxQueueMessagesWaiting(s->clients)))
while(!(n_entries=uxQueueMessagesWaiting(s->clients))) {
if(autoLamp && lampVal!=-1) setLamp(0);
vTaskSuspend(NULL);
if(autoLamp && lampVal!=-1) setLamp(lampVal);
}

current_time=esp_timer_get_time();
if((current_time-last_time)<s->frame_delay)
vTaskDelay((s->frame_delay-(current_time-last_time))/(1000*portTICK_PERIOD_MS));
last_time=current_time;

cam_streamer_update_frame(s);

print_debug("[cam_streamer] frame_size: %luB %lums\n", s->buf->len, (current_time-last_time)/1000);
last_time=current_time;

for(unsigned int i=0; i<n_entries; ++i) {
if(xQueueReceive(s->clients, &fd, 10/portTICK_PERIOD_MS)==pdFALSE) {
#ifdef DEBUG_DEFAULT_ON
printf("[cam_streamer] failed to dequeue fd!\n");
#endif
print_debug("[cam_streamer] failed to dequeue fd!\n");
continue;
}

#ifdef DEBUG_DEFAULT_ON
printf("[cam_streamer] dequeued fd %d\n", fd);
printf("[cam_streamer] sending part: \"%.*s\"\n", (int) s->part_len, s->part_buf);
#endif
print_debug("[cam_streamer] fd %d dequeued\n", fd);

if(is_send_error(httpd_socket_send(s->server, fd, s->part_buf, s->part_len, 0))) {
cam_streamer_decrement_num_clients(s);
continue;
}

if(is_send_error(httpd_socket_send(s->server, fd, s->buf->buf, s->buf->len, 0))) {
if(is_send_error(httpd_socket_send(s->server, fd, (const char *) s->buf->buf, s->buf->len, 0))) {
cam_streamer_decrement_num_clients(s);
continue;
}
Expand All @@ -132,20 +157,16 @@ void cam_streamer_task(void *p) {
}

xQueueSend(s->clients, (void *) &fd, 10/portTICK_PERIOD_MS);
#ifdef DEBUG_DEFAULT_ON
printf("[cam_streamer] fd %d requeued\n", fd);
#endif
print_debug("[cam_streamer] fd %d requeued\n", fd);
}
}
}

void cam_streamer_start(cam_streamer_t *s) {
BaseType_t r=xTaskCreate(cam_streamer_task, "cam_streamer", 10*1024, (void *) s, tskIDLE_PRIORITY+3, &s->task);

#ifdef DEBUG_DEFAULT_ON
if(r!=pdPASS)
printf("[cam_streamer] failed to create task!\n");
#endif
print_debug("[cam_streamer] failed to create task!\n");
}

void cam_streamer_stop(cam_streamer_t *s) {
Expand All @@ -162,35 +183,41 @@ void cam_streamer_dequeue_all_clients(cam_streamer_t *s) {
}

bool cam_streamer_enqueue_client(cam_streamer_t *s, int fd) {
#ifdef DEBUG_DEFAULT_ON
printf("sending stream headers:\n%s\nLength: %d\n", _STREAM_HEADERS, strlen(_STREAM_HEADERS));
#endif
if(s->num_clients>=CAM_STREAMER_MAX_CLIENTS) {
if(httpd_socket_send(s->server, fd, _TEXT_HEADERS, strlen(_TEXT_HEADERS), 0)) {
print_debug("failed sending text headers!\n");
return false;
}

#define EMSG "too many clients"
if(httpd_socket_send(s->server, fd, EMSG, strlen(EMSG), 0)) {
print_debug("failed sending message\n");
return false;
}
#undef EMSG
close(fd);
return false;
}

print_debug("sending stream headers:\n%s\nLength: %d\n", _STREAM_HEADERS, strlen(_STREAM_HEADERS));
if(is_send_error(httpd_socket_send(s->server, fd, _STREAM_HEADERS, strlen(_STREAM_HEADERS), 0))) {
#ifdef DEBUG_DEFAULT_ON
printf("failed sending headers!\n");
#endif
print_debug("failed sending headers!\n");
return false;
}

if(is_send_error(httpd_socket_send(s->server, fd, _STREAM_BOUNDARY, strlen(_STREAM_BOUNDARY), 0))) {
#ifdef DEBUG_DEFAULT_ON
printf("failed sending boundary!\n");
#endif
print_debug("failed sending boundary!\n");
return false;
}

const BaseType_t r=xQueueSend(s->clients, (void *) &fd, 10*portTICK_PERIOD_MS);
if(r!=pdTRUE) {
#ifdef DEBUG_DEFAULT_ON
printf("[cam_streamer] failed to enqueue fd %d\n", fd);
#endif
print_debug("[cam_streamer] failed to enqueue fd %d\n", fd);
#define EMSG "failed to enqueue"
httpd_socket_send(s->server, fd, EMSG, strlen(EMSG), 0);
#undef EMSG
} else {
#ifdef DEBUG_DEFAULT_ON
printf("[cam_streamer] socket %d enqueued\n", fd);
#endif
print_debug("[cam_streamer] socket %d enqueued\n", fd);
__atomic_fetch_add(&s->num_clients, 1, __ATOMIC_RELAXED);
vTaskResume(s->task);
}
Expand Down
4 changes: 2 additions & 2 deletions cam_streamer.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
#include <freertos/queue.h>
#include <freertos/task.h>

#define CAM_STREAMER_MAX_CLIENTS 10
typedef struct {
QueueHandle_t clients;
TaskHandle_t task;
Expand All @@ -25,7 +24,8 @@ typedef struct {
size_t num_clients;
} cam_streamer_t;

void cam_streamer_init(cam_streamer_t *s, httpd_handle_t server, uint16_t fps);
void cam_streamer_init(cam_streamer_t *s, httpd_handle_t server, uint16_t frame_delay);
void cam_streamer_set_frame_delay(cam_streamer_t *s, uint16_t frame_delay);
void cam_streamer_task(void *p);
void cam_streamer_start(cam_streamer_t *s);
void cam_streamer_stop(cam_streamer_t *s);
Expand Down
3 changes: 1 addition & 2 deletions esp32-cam-webserver.ino
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,12 @@

// Primary config, or defaults.
#if __has_include("myconfig.h")
struct station { const char ssid[65]; const char password[65]; const bool dhcp;}; // do no edit
struct station { const char ssid[65]; const char password[65]; const bool dhcp;}; // do no edit
#include "myconfig.h"
#else
#warning "Using Defaults: Copy myconfig.sample.h to myconfig.h and edit that to use your own settings"
#define WIFI_AP_ENABLE
#define CAMERA_MODEL_AI_THINKER
struct station { const char ssid[65]; const char password[65]; const bool dhcp;}
stationList[] = {{"ESP32-CAM-CONNECT","InsecurePassword", true}};
#endif

Expand Down
5 changes: 4 additions & 1 deletion myconfig.sample.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
* just replace your ssid and password in the line below.
*/

struct station stationList[] = {{"my_ssid","my_password", true}};
const struct station stationList[] = {{"my_ssid","my_password", true}};

/*
* You can extend the stationList[] above with additional SSID+Password pairs
Expand Down Expand Up @@ -148,6 +148,9 @@ struct station stationList[] = {{"ssid1", "pass1", true},
// max_fps = 1000/min_frame_time
// #define MIN_FRAME_TIME 500

// Maximum number of clients of the stream
// #define CAM_STREAMER_MAX_CLIENTS 10

/*
* Additional Features
*
Expand Down