Skip to content

Commit

Permalink
Working demo.
Browse files Browse the repository at this point in the history
  • Loading branch information
clausqr committed Sep 7, 2023
1 parent 9797073 commit abf4e2e
Show file tree
Hide file tree
Showing 5 changed files with 289 additions and 27 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@
.vscode/c_cpp_properties.json
.vscode/launch.json
.vscode/ipch
secrets.h
10 changes: 6 additions & 4 deletions sdkconfig.esp32cam
Original file line number Diff line number Diff line change
Expand Up @@ -1149,12 +1149,14 @@ CONFIG_IEEE802154_PENDING_TABLE_SIZE=20
# CONFIG_LOG_DEFAULT_LEVEL_NONE is not set
# CONFIG_LOG_DEFAULT_LEVEL_ERROR is not set
# CONFIG_LOG_DEFAULT_LEVEL_WARN is not set
# CONFIG_LOG_DEFAULT_LEVEL_INFO is not set
CONFIG_LOG_DEFAULT_LEVEL_INFO=y
# CONFIG_LOG_DEFAULT_LEVEL_DEBUG is not set
CONFIG_LOG_DEFAULT_LEVEL_VERBOSE=y
CONFIG_LOG_DEFAULT_LEVEL=5
# CONFIG_LOG_DEFAULT_LEVEL_VERBOSE is not set
CONFIG_LOG_DEFAULT_LEVEL=3
CONFIG_LOG_MAXIMUM_EQUALS_DEFAULT=y
CONFIG_LOG_MAXIMUM_LEVEL=5
# CONFIG_LOG_MAXIMUM_LEVEL_DEBUG is not set
# CONFIG_LOG_MAXIMUM_LEVEL_VERBOSE is not set
CONFIG_LOG_MAXIMUM_LEVEL=3
# CONFIG_LOG_COLORS is not set
CONFIG_LOG_TIMESTAMP_SOURCE_RTOS=y
# CONFIG_LOG_TIMESTAMP_SOURCE_SYSTEM is not set
Expand Down
161 changes: 138 additions & 23 deletions src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,30 @@

// ================================ CODE ======================================

#include <esp_log.h>
#include <esp_system.h>
#include <nvs_flash.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/gpio.h"

#include "esp_camera.h"
#include "esp_http_server.h"
#include "esp_timer.h"

#include "wifi.h"

static const char *TAG = "esp32-cam Webserver";

#define PART_BOUNDARY "123456789000000000000987654321"
static const char* _STREAM_CONTENT_TYPE = "multipart/x-mixed-replace;boundary=" PART_BOUNDARY;
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";


#include <esp_log.h>
#include <sys/param.h>
#include <string.h>

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"

// support IDF 5.x
#ifndef portTICK_RATE_MS
Expand Down Expand Up @@ -93,7 +109,6 @@

#endif

static const char *TAG = "example:take_picture";

#if ESP_CAMERA_SUPPORTED
static camera_config_t camera_config = {
Expand All @@ -120,12 +135,16 @@ static camera_config_t camera_config = {
.ledc_timer = LEDC_TIMER_0,
.ledc_channel = LEDC_CHANNEL_0,

.pixel_format = PIXFORMAT_RGB565, //YUV422,GRAYSCALE,RGB565,JPEG
.frame_size = FRAMESIZE_QVGA, //QQVGA-UXGA, For ESP32, do not use sizes above QVGA when not JPEG. The performance of the ESP32-S series has improved a lot, but JPEG mode always gives better frame rates.
// .pixel_format = PIXFORMAT_RGB565, //YUV422,GRAYSCALE,RGB565,JPEG
.pixel_format = PIXFORMAT_JPEG, //YUV422,GRAYSCALE,RGB565,JPEG
// .frame_size = FRAMESIZE_QVGA, //QQVGA-UXGA, For ESP32, do not use sizes above QVGA when not JPEG. The performance of the ESP32-S series has improved a lot, but JPEG mode always gives better frame rates.
.frame_size = FRAMESIZE_240X240, //QQVGA-UXGA, For ESP32, do not use sizes above QVGA when not JPEG. The performance of the ESP32-S series has improved a lot, but JPEG mode always gives better frame rates.

// .jpeg_quality = 12, //0-63, for OV series camera sensors, lower number means higher quality
.jpeg_quality = 12, //0-63, for OV series camera sensors, lower number means higher quality
.fb_count = 1, //When jpeg mode is used, if fb_count more than one, the driver will work in continuous mode.
.grab_mode = CAMERA_GRAB_WHEN_EMPTY,
.fb_count = 2, //When jpeg mode is used, if fb_count more than one, the driver will work in continuous mode.
// .grab_mode = CAMERA_GRAB_WHEN_EMPTY,
.grab_mode = CAMERA_GRAB_LATEST,
};

static esp_err_t init_camera(void)
Expand All @@ -142,26 +161,122 @@ static esp_err_t init_camera(void)
}
#endif

void app_main(void)


esp_err_t jpg_stream_httpd_handler(httpd_req_t *req){
camera_fb_t * fb = NULL;
esp_err_t res = ESP_OK;
size_t _jpg_buf_len;
uint8_t * _jpg_buf;
char * part_buf[64];
static int64_t last_frame = 0;
if(!last_frame) {
last_frame = esp_timer_get_time();
}

res = httpd_resp_set_type(req, _STREAM_CONTENT_TYPE);
if(res != ESP_OK){
return res;
}

while(true){
fb = esp_camera_fb_get();
if (!fb) {
ESP_LOGE(TAG, "Camera capture failed");
res = ESP_FAIL;
break;
}
if(fb->format != PIXFORMAT_JPEG){
bool jpeg_converted = frame2jpg(fb, 80, &_jpg_buf, &_jpg_buf_len);
if(!jpeg_converted){
ESP_LOGE(TAG, "JPEG compression failed");
esp_camera_fb_return(fb);
res = ESP_FAIL;
}
} else {
_jpg_buf_len = fb->len;
_jpg_buf = fb->buf;
}

if(res == ESP_OK){
res = httpd_resp_send_chunk(req, _STREAM_BOUNDARY, strlen(_STREAM_BOUNDARY));
}
if(res == ESP_OK){
size_t hlen = snprintf((char *)part_buf, 64, _STREAM_PART, _jpg_buf_len);

res = httpd_resp_send_chunk(req, (const char *)part_buf, hlen);
}
if(res == ESP_OK){
res = httpd_resp_send_chunk(req, (const char *)_jpg_buf, _jpg_buf_len);
}
if(fb->format != PIXFORMAT_JPEG){
free(_jpg_buf);
}
esp_camera_fb_return(fb);
if(res != ESP_OK){
break;
}
int64_t fr_end = esp_timer_get_time();
int64_t frame_time = fr_end - last_frame;
last_frame = fr_end;
frame_time /= 1000;
ESP_LOGI(TAG, "MJPG: %uKB %ums (%.1ffps)",
(unsigned int)(uint32_t)(_jpg_buf_len/1024),
(unsigned int)(uint32_t)frame_time, 1000.0 / (unsigned int)(uint32_t)frame_time);
}

last_frame = 0;
return res;
}


httpd_uri_t uri_get = {
.uri = "/",
.method = HTTP_GET,
.handler = jpg_stream_httpd_handler,
.user_ctx = NULL};


httpd_handle_t setup_server(void)
{
#if ESP_CAMERA_SUPPORTED
if(ESP_OK != init_camera()) {
return;
httpd_config_t config = HTTPD_DEFAULT_CONFIG();
httpd_handle_t stream_httpd = NULL;

if (httpd_start(&stream_httpd , &config) == ESP_OK)
{
httpd_register_uri_handler(stream_httpd , &uri_get);
}

while (1)
return stream_httpd;
}



void app_main()
{
esp_err_t err;

// Initialize NVS
esp_err_t ret = nvs_flash_init();
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND)
{
ESP_LOGI(TAG, "Taking picture...");
camera_fb_t *pic = esp_camera_fb_get();
ESP_ERROR_CHECK(nvs_flash_erase());
ret = nvs_flash_init();
}

// use pic->buf to access the image
ESP_LOGI(TAG, "Picture taken! Its size was: %zu bytes", pic->len);
esp_camera_fb_return(pic);
connect_wifi();

vTaskDelay(5000 / portTICK_RATE_MS);
if (wifi_connect_status)
{
err = init_camera();
if (err != ESP_OK)
{
printf("err: %s\n", esp_err_to_name(err));
return;
}
setup_server();
ESP_LOGI(TAG, "ESP32 CAM Web Server is up and running\n");
}
#else
ESP_LOGE(TAG, "Camera support is not available for this chip");
return;
#endif
else
ESP_LOGI(TAG, "Failed to connected with Wi-Fi, check your network Credentials\n");
}
118 changes: 118 additions & 0 deletions src/wifi.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
#include "wifi.h"
#include "secrets.h"

int wifi_connect_status = 0;
static const char *TAG = "Connect_WiFi";
int s_retry_num = 0;



#define MAXIMUM_RETRY 5
/* FreeRTOS event group to signal when we are connected*/
EventGroupHandle_t s_wifi_event_group;

/* The event group allows multiple bits for each event, but we only care about two events:
* - we are connected to the AP with an IP
* - we failed to connect after the maximum amount of retries */
#define WIFI_CONNECTED_BIT BIT0
#define WIFI_FAIL_BIT BIT1

static void event_handler(void *arg, esp_event_base_t event_base,
int32_t event_id, void *event_data)
{
if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START)
{
esp_wifi_connect();
}
else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED)
{
if (s_retry_num < MAXIMUM_RETRY)
{
esp_wifi_connect();
s_retry_num++;
ESP_LOGI(TAG, "retry to connect to the AP");
}
else
{
xEventGroupSetBits(s_wifi_event_group, WIFI_FAIL_BIT);
}
wifi_connect_status = 0;
ESP_LOGI(TAG, "connect to the AP fail");
}
else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP)
{
ip_event_got_ip_t *event = (ip_event_got_ip_t *)event_data;
ESP_LOGW(TAG, "got ip:" IPSTR, IP2STR(&event->ip_info.ip));
s_retry_num = 0;
xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT);
wifi_connect_status = 1;
}
}

void connect_wifi(void)
{
s_wifi_event_group = xEventGroupCreate();

ESP_ERROR_CHECK(esp_netif_init());

ESP_ERROR_CHECK(esp_event_loop_create_default());
esp_netif_create_default_wifi_sta();

wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_wifi_init(&cfg));

esp_event_handler_instance_t instance_any_id;
esp_event_handler_instance_t instance_got_ip;
ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT,
ESP_EVENT_ANY_ID,
&event_handler,
NULL,
&instance_any_id));
ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT,
IP_EVENT_STA_GOT_IP,
&event_handler,
NULL,
&instance_got_ip));

wifi_config_t wifi_config = {
.sta = {
.ssid = WIFI_SSID,
.password = WIFI_PASSWORD,
/* Setting a password implies station will connect to all security modes including WEP/WPA.
* However these modes are deprecated and not advisable to be used. Incase your Access point
* doesn't support WPA2, these mode can be enabled by commenting below line */
.threshold.authmode = WIFI_AUTH_WPA2_PSK,
},
};
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config));
ESP_ERROR_CHECK(esp_wifi_start());

ESP_LOGI(TAG, "wifi_init_sta finished.");

/* Waiting until either the connection is established (WIFI_CONNECTED_BIT) or connection failed for the maximum
* number of re-tries (WIFI_FAIL_BIT). The bits are set by event_handler() (see above) */
EventBits_t bits = xEventGroupWaitBits(s_wifi_event_group,
WIFI_CONNECTED_BIT | WIFI_FAIL_BIT,
pdFALSE,
pdFALSE,
portMAX_DELAY);

/* xEventGroupWaitBits() returns the bits before the call returned, hence we can test which event actually
* happened. */
if (bits & WIFI_CONNECTED_BIT)
{
ESP_LOGI(TAG, "connected to ap SSID:%s password:%s",
WIFI_SSID, WIFI_PASSWORD);
}
else if (bits & WIFI_FAIL_BIT)
{
ESP_LOGI(TAG, "Failed to connect to SSID:%s, password:%s",
WIFI_SSID, WIFI_PASSWORD);
}
else
{
ESP_LOGE(TAG, "UNEXPECTED EVENT");
}
vEventGroupDelete(s_wifi_event_group);
}
26 changes: 26 additions & 0 deletions src/wifi.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#ifndef WIFI_H
#define WIFI_H


#include <esp_system.h>
#include <nvs_flash.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_wifi.h"
#include "esp_event.h"
#include "freertos/event_groups.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include "esp_netif.h"
#include "driver/gpio.h"
#include <lwip/sockets.h>
#include <lwip/sys.h>
#include <lwip/api.h>
#include <lwip/netdb.h>

extern int wifi_connect_status;

void connect_wifi(void);


#endif /* WIFI_H */

0 comments on commit abf4e2e

Please sign in to comment.