ESP32-CAM ESP-IDF Live Streaming Web Server

In this tutorial, we will create a video live streaming web server using ESP32-CAM and ESP-IDF. We will use the esp_http_server library to send captured frames to a web server.

ESP32-CAM Live Streaming Web Server ESP-IDF

The ESP32-CAM is a famous camera module with a built-in ESP32 chip that can take photos and record video. To stream live video from the ESP32-CAM, we will set up a web server on the ESP32.

Before we move ahead, make sure you have the latest version of VS Code installed on your system with the ESP-IDF extension configured.

Set up ESP32-CAM Live Streaming Project

Before starting, ESP32-CAM live Streaming Web Server project, we need to set up ESP32-CAM board.

In this section, we will see how to connect ESP32-CAM with a USB to TTL converter to upload the esp32-cam live streaming code to ESP.

We will require the following components for this project

Required Components

  • ESP32-CAM board with a camera
  • USB to TTL converter
  • microSD card
  • Push button
  • Connecting Wires
  • External 5V Power Supply (optional)

The ESP32-CAM does not come with a USB port attached to it. Therefore, to upload a program sketch to the ESP32-CAM, we will need to use an FTDI programmer/USB to TTL Serial converter.

Connect the 5V pin of the USB to TTL converter with the 5V pin of ESP32. Connect the TX pin of the USB to TTL converter with the UOR (GPIO13) of ESP32-CAM. Similarly, connect RX pin of USB to TTL converter with the UOT(GPIO1) of ESP32-CAM. Grounds will be common.

Follow the connections given in the table below to connect ESP32-CAM with the USB to TTL converter.

ESP32-CAMUSB to TTL Converter
5V5V
UOR (GPIO3)TX
UOT (GPIO1)RX
GNDGND

To upload code, connect GPIO0 of ESP32-CAM with GND pin of ESP32-CAM in order to enable it to go in flashing mode. After the code is successfully uploaded, remove this connection.

On uploading the code, if you get a brown-out detector error then it means the USB power supply does not meet power requirement of ESP32-CAM. Therefore, you may use an external 5V power supply to power the ESP32-CAM module. Follow the schematic diagram below for connections:

ESP32-CAM with FTDI Programmer

Add ESP32 Camera Driver in ESP-IDF

Before creating this live streaming webserver project, let us first add the ESP32 camera driver in ESP-IDF. Open the following link:(https://github.com/espressif/esp32-camera) which opens the GitHub page where the repository is provided. Go to Code > Download ZIP and download the zip file. This ESP32 series Soc compatible driver is built for image sensors which also allows the captured framed data to be converted to JPEG/BMP files.

Download ESP32-CAM Driver

After the folder is downloaded, extract it and paste it in the Components folder of ESP-IDF.

Add ESP32-CAM Driver in components ESP-IDF

Create ESP32-CAM Live Streaming Web Server Project in ESP-IDF

In this section, we will create a esp-idf project for esp32-cam live streaming over a webserver.

Open your VS Code and head over to View > Command Palette. Type ESP-IDF: New Project in the search bar and press enter. We will create a project in VS Code using ESP-IDF extension. The ESP32 board connected with a push button will take a photo whenever the push button is pressed. The photo will get saved on the microSD card which is inserted in the microSD card slot.

Specify the project name and directory. We have named our project ‘esp32_cam_live_streaming.’ For the ESP-IDF board, we have chosen the custom board option. For the ESP-IDF target, we have chosen ESP32 module. Click the ‘Choose Template’ button to proceed forward.

In the Extension, select the ESP-IDF option:

ESP-IDF in VS Code New Project 2

We will click the ‘sample_project’ under the get-started tab. Now click ‘Create project using template sample_project.’

ESP-IDF in VS Code New Project 3

You will get a notification that the project has been created. To open the project in a new window, click ‘Yes.’

This opens the project that we created inside the EXPLORER tab. There are several folders inside our project folder. This is the same for every project which you will create through ESP-IDF Explorer.

Enable PSRAM

To use the ESP32 camera driver we will first have to enable PSRAM. You can enable it through menuconfig or open sdkconfig and add the following line in that file and save it.

CONFIG_ESP32_SPIRAM_SUPPORT=y

ESP32-CAM ESP-IDF Live Streaming Web Server Code

This code sets up a web server on an ESP32-CAM module for live streaming video over the local network using esp-idf framework.

#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 "camera_pins.h"
#include "connect_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";

#define CONFIG_XCLK_FREQ 20000000 

static esp_err_t init_camera(void)
{
    camera_config_t camera_config = {
        .pin_pwdn  = CAM_PIN_PWDN,
        .pin_reset = CAM_PIN_RESET,
        .pin_xclk = CAM_PIN_XCLK,
        .pin_sccb_sda = CAM_PIN_SIOD,
        .pin_sccb_scl = CAM_PIN_SIOC,

        .pin_d7 = CAM_PIN_D7,
        .pin_d6 = CAM_PIN_D6,
        .pin_d5 = CAM_PIN_D5,
        .pin_d4 = CAM_PIN_D4,
        .pin_d3 = CAM_PIN_D3,
        .pin_d2 = CAM_PIN_D2,
        .pin_d1 = CAM_PIN_D1,
        .pin_d0 = CAM_PIN_D0,
        .pin_vsync = CAM_PIN_VSYNC,
        .pin_href = CAM_PIN_HREF,
        .pin_pclk = CAM_PIN_PCLK,

        .xclk_freq_hz = CONFIG_XCLK_FREQ,
        .ledc_timer = LEDC_TIMER_0,
        .ledc_channel = LEDC_CHANNEL_0,

        .pixel_format = PIXFORMAT_JPEG,
        .frame_size = FRAMESIZE_VGA,

        .jpeg_quality = 10,
        .fb_count = 1,
        .grab_mode = CAMERA_GRAB_WHEN_EMPTY};//CAMERA_GRAB_LATEST. Sets when buffers should be filled
    esp_err_t err = esp_camera_init(&camera_config);
    if (err != ESP_OK)
    {
        return err;
    }
    return ESP_OK;
}

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)",
            (uint32_t)(_jpg_buf_len/1024),
            (uint32_t)frame_time, 1000.0 / (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)
{
    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);
    }

    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_ERROR_CHECK(nvs_flash_erase());
        ret = nvs_flash_init();
    }

    connect_wifi();

    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_LOGI(TAG, "Failed to connected with Wi-Fi, check your network Credentials\n");
}

How does the Code Work?

Now let’s explore all functions and parts of code and see how esp32-cam live streaming webserver code works.

These are header files that provide functions and definitions required by the code.

esp_system.h: This header file provides functions for interacting with the system, such as resetting the device or getting the device’s chip ID.

nvs_flash.h: This header file provides functions for interacting with the device’s Non-Volatile Storage (NVS), which is used to store persistent data on the device.

freertos/FreeRTOS.h: This header file provides functions for using the FreeRTOS real-time operating system, which is used to schedule tasks on the device.

freertos/task.h: This header file provides functions for creating and manipulating tasks in the FreeRTOS operating system.

driver/gpio.h: This header file provides functions for interacting with the device’s General-Purpose Input/Output (GPIO) pins.

esp_camera.h: This header file provides functions for interacting with the ESP32-CAM camera module. We will use it perform live streaming over a webserver.

esp_http_server.h: This header file provides functions for setting up an HTTP webserver on the device.

esp_timer.h: This header file provides functions for interacting with the device’s hardware timer.

camera_pins.h: This header file defines constants for the pin assignments for the ESP32-CAM module.

#ifndef CAMERA_PINS_H_
#define CAMERA_PINS_H_

//#define CONFIG_BOARD_ESP32CAM_AITHINKER 1

// Freenove ESP32-WROVER CAM Board PIN Map
#if CONFIG_BOARD_WROVER_KIT
#define CAM_PIN_PWDN -1  //power down is not used
#define CAM_PIN_RESET -1 //software reset will be performed
#define CAM_PIN_XCLK 21
#define CAM_PIN_SIOD 26
#define CAM_PIN_SIOC 27

#define CAM_PIN_D7 35
#define CAM_PIN_D6 34
#define CAM_PIN_D5 39
#define CAM_PIN_D4 36
#define CAM_PIN_D3 19
#define CAM_PIN_D2 18
#define CAM_PIN_D1 5
#define CAM_PIN_D0 4
#define CAM_PIN_VSYNC 25
#define CAM_PIN_HREF 23
#define CAM_PIN_PCLK 22
#endif

// ESP-EYE PIN Map
#if CONFIG_BOARD_CAMERA_MODEL_ESP_EYE
#define PWDN_GPIO_NUM    -1
#define RESET_GPIO_NUM   -1
#define XCLK_GPIO_NUM    4
#define SIOD_GPIO_NUM    18
#define SIOC_GPIO_NUM    23

#define Y9_GPIO_NUM      36
#define Y8_GPIO_NUM      37
#define Y7_GPIO_NUM      38
#define Y6_GPIO_NUM      39
#define Y5_GPIO_NUM      35
#define Y4_GPIO_NUM      14
#define Y3_GPIO_NUM      13
#define Y2_GPIO_NUM      34
#define VSYNC_GPIO_NUM   5
#define HREF_GPIO_NUM    27
#define PCLK_GPIO_NUM    25
#endif


// AiThinker ESP32Cam PIN Map
#if CONFIG_BOARD_ESP32CAM_AITHINKER
#define CAM_PIN_PWDN 32
#define CAM_PIN_RESET -1 //software reset will be performed
#define CAM_PIN_XCLK 0
#define CAM_PIN_SIOD 26
#define CAM_PIN_SIOC 27

#define CAM_PIN_D7 35
#define CAM_PIN_D6 34
#define CAM_PIN_D5 39
#define CAM_PIN_D4 36
#define CAM_PIN_D3 21
#define CAM_PIN_D2 19
#define CAM_PIN_D1 18
#define CAM_PIN_D0 5
#define CAM_PIN_VSYNC 25
#define CAM_PIN_HREF 23
#define CAM_PIN_PCLK 22
#endif



// TTGO T-Journal ESP32 Camera PIN Map
#if CONFIG_BOARD_CAMERA_MODEL_TTGO_T_JOURNAL
#define PWDN_GPIO_NUM      0
#define RESET_GPIO_NUM    15
#define XCLK_GPIO_NUM     27
#define SIOD_GPIO_NUM     25
#define SIOC_GPIO_NUM     23

#define Y9_GPIO_NUM       19
#define Y8_GPIO_NUM       36
#define Y7_GPIO_NUM       18
#define Y6_GPIO_NUM       39
#define Y5_GPIO_NUM        5
#define Y4_GPIO_NUM       34
#define Y3_GPIO_NUM       35
#define Y2_GPIO_NUM       17
#define VSYNC_GPIO_NUM    22
#define HREF_GPIO_NUM     26
#define PCLK_GPIO_NUM     21
#endif

#endif

connect_wifi.h: This header file provides functions for connecting the device to a Wi-Fi network. You can refer to this article for more explanation on functions and APIs used in connnect_wifi.h library:

#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 "camera_pins.h"
#include "connect_wifi.h"

Define HTTP Request Handle Type

For live streaming, we will be sending ESP32-CAM captured frames to a webserver with HTTP requests. Hence, we need to define http response structure and type related to live streaming by sending each frame one by one.

PART_BOUNDARY is a string constant that defines the boundary string used to separate parts in the HTTP response.

_STREAM_CONTENT_TYPE is a string constant that defines the content type of the HTTP response. The value of this constant is “multipart/x-mixed-replace”, which specifies that the response will contain multiple parts that are intended to replace each other. The boundary string is specified using the PART_BOUNDARY constant.

_STREAM_BOUNDARY is a string constant that defines the boundary string used to separate parts in the HTTP response. The value of this constant is the PART_BOUNDARY string preceded by “\r\n–“. That means we will be sending continue stream of data which will be live streaming frames for our case.

_STREAM_PART is a string constant that defines the format of a part in the HTTP response. It specifies the content type (image/jpeg) and the content length (specified using the %u placeholder). The content type and content length are separated by “\r\n” and the part is terminated with “\r\n\r\n”.

#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";

Initialize ESP32-CAM (init_camera())

This init_camera() function is initializing a camera using the ESP-EYE, an ESP32-based development board with a built-in camera. The function init_camera takes in no arguments and returns an esp_err_t value indicating the status of the initialization.

The function starts by defining a camera_config variable of type camera_config_t. This variable is initialized with a number of fields that specify the configuration for the camera. These fields include:

  • pin_pwdn: the pin number for the power-down pin of the camera.
  • pin_reset: the pin number for the reset pin of the camera.
  • pin_xclk: the pin number for the clock pin of the camera.
  • pin_sccb_sda and pin_sccb_scl: the pin numbers for the SCCB (I2C-like) data and clock pins of the camera, respectively.
  • pin_d7 through pin_d0: the pin numbers for the data pins of the camera.
  • pin_vsync, pin_href, and pin_pclk: the pin numbers for the vertical sync, horizontal reference, and pixel clock pins of the camera, respectively.
  • xclk_freq_hz: the frequency of the clock signal, in Hz.
  • ledc_timer and ledc_channel: the timer and channel numbers for the LEDC (LED PWM controller) of the ESP32.
  • pixel_format: the pixel format of the camera, specified as PIXFORMAT_JPEG.
  • frame_size: the frame size of the camera, specified as FRAMESIZE_UXGA.
  • jpeg_quality: the JPEG quality of the camera, specified as 12.
  • fb_count: the number of frame buffers, specified as 1.
  • grab_mode: the grab mode of the camera, specified as CAMERA_GRAB_WHEN_EMPTY. This specifies that the camera should fill its buffers when they are empty.

After the camera_config variable is initialized, the function calls the esp_camera_init function, passing it the address of the camera_config variable as an argument. This function initializes the camera using the configuration specified in camera_config. If the initialization is successful, the function returns ESP_OK. If an error occurs, it returns an error code.

static esp_err_t init_camera(void)
{
    camera_config_t camera_config = {
        .pin_pwdn  = CAM_PIN_PWDN,
        .pin_reset = CAM_PIN_RESET,
        .pin_xclk = CAM_PIN_XCLK,
        .pin_sccb_sda = CAM_PIN_SIOD,
        .pin_sccb_scl = CAM_PIN_SIOC,

        .pin_d7 = CAM_PIN_D7,
        .pin_d6 = CAM_PIN_D6,
        .pin_d5 = CAM_PIN_D5,
        .pin_d4 = CAM_PIN_D4,
        .pin_d3 = CAM_PIN_D3,
        .pin_d2 = CAM_PIN_D2,
        .pin_d1 = CAM_PIN_D1,
        .pin_d0 = CAM_PIN_D0,
        .pin_vsync = CAM_PIN_VSYNC,
        .pin_href = CAM_PIN_HREF,
        .pin_pclk = CAM_PIN_PCLK,

        .xclk_freq_hz = CONFIG_XCLK_FREQ,
        .ledc_timer = LEDC_TIMER_0,
        .ledc_channel = LEDC_CHANNEL_0,

        .pixel_format = PIXFORMAT_JPEG,
        .frame_size = FRAMESIZE_UXGA,

        .jpeg_quality = 12,
        .fb_count = 1,
        .grab_mode = CAMERA_GRAB_WHEN_EMPTY};//CAMERA_GRAB_LATEST. Sets when buffers should be filled
    esp_err_t err = esp_camera_init(&camera_config);
    if (err != ESP_OK)
    {
        return err;
    }
    return ESP_OK;
}

Setup HTTP WebServer on ESP32-CAM

This code sets up an HTTP server that listens for GET requests on the root URI (“/”). When a GET request is received, the server calls the jpg_stream_httpd_handler function to handle the request.

The setup_server function creates an HTTP server using the default configuration and starts it. If the server was started successfully, it registers the uri_get structure as a handler for GET requests on the root URI. The uri_get structure contains the URI, the HTTP method (GET), the handler function (jpg_stream_httpd_handler), and a user context (which is set to NULL in this case).

The setup_server function returns a handle to the HTTP server, which can be used to control the server (e.g., stop it, unregister URI handlers).

httpd_uri_t uri_get = {
    .uri = "/",
    .method = HTTP_GET,
    .handler = jpg_stream_httpd_handler,
    .user_ctx = NULL};
httpd_handle_t setup_server(void)
{
    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);
    }

    return stream_httpd;
}

jpg_stream_httpd_handler Live Streaming Handler Function

The jpg_stream_httpd_handler function is the handler function that is called to handle GET requests on the root URI of the HTTP server. It streams live video contents by sending camera’s frame buffer as JPEG images in the HTTP response.

The function starts by setting the content type of the HTTP response to _STREAM_CONTENT_TYPE using the httpd_resp_set_type function.

It then enters a loop that captures a frame from the camera, converts it to JPEG format if necessary, and sends it as a chunk in the HTTP response. The loop continues until an error occurs or the connection is closed.

For each iteration of the loop, the function first captures a frame from the camera using the esp_camera_fb_get function. If the capture fails, it logs an error message and breaks out of the loop.

If the frame was captured successfully, the function checks the format of the frame. If it is not in JPEG format, it converts it to JPEG using the frame2jpg function. If the conversion fails, it logs an error message, returns the frame buffer to the camera using esp_camera_fb_return, and breaks out of the loop.

If the frame is already in JPEG format or the conversion to JPEG was successful, the function sends the boundary string, the part header (which includes the content type and content length), and the JPEG image as chunks in the HTTP response using the httpd_resp_send_chunk function. If any of these operations fails, it breaks out of the loop.

After sending the chunks, the function returns the frame buffer to the camera using esp_camera_fb_return and logs the size and processing time of the JPEG image.

Finally, the function sets the last_frame variable to 0 and returns the result of the last operation. In other words, this function actually performs live streaming of video.

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)",
            (uint32_t)(_jpg_buf_len/1024),
            (uint32_t)frame_time, 1000.0 / (uint32_t)frame_time);
    }

    last_frame = 0;
    return res;
}

App main Function

This function is the entry point of the esp32-cam live streaming webserver application and is called when the device boots up.

It starts by initializing the Non-Volatile Storage (NVS) on the device. If the NVS is not initialized or a new version of the NVS is found, it is erased and re-initialized.

It then calls the connect_wifi function to connect to a Wi-Fi network using the credentials specified in the connect_wifi.h header file.

If the connection to the Wi-Fi network was successful (as indicated by the wifi_connect_status flag), the function initializes the camera using the init_camera function. If the initialization fails, it prints an error message and returns.

If the camera was initialized successfully, the function sets up the HTTP live streaming webserver using the setup_server function and logs a message indicating that the server is up and running. If the connection to the Wi-Fi network was not successful, it logs a message indicating the failure and does not start the server.

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_ERROR_CHECK(nvs_flash_erase());
        ret = nvs_flash_init();
    }

    connect_wifi();

    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_LOGI(TAG, "Failed to connected with Wi-Fi, check your network Credentials\n");
}

ESP32-CAM ESP-IDF Live Streaming Web Server Demo

In this section, we will see a demo with ESP32-CAM live streaming web server. First of all download complete project for the following link:

After downloading project, open wifi_connect.c file and add your Wi-Fi credentials in the following two lines:

#define WIFI_SSID "Enter your SSIF"
#define WIFI_PASSWORD "Enter your Password"

Also select, ESP32-CAM board type from menuconfig. In this example, we are using ESP32CAM_AITHINKER. But you can select from options whatever you are using.

Now build the project. Now flash code to ESP32-CAM board. To flash your chip, type the following command in the serial terminal. Remember to replace the COM port with the one through which your board is connected.

idf.py -p COMX flash monitor

After you have successfully uploaded your code to the board, remove the connecting wire from GPIO0 and GND.

As soon as you remove the connecting wire from GPIO0 and GND. You will see an IP address on serial console of VS code as shown below. Copy this IP address.

ESP32-CAM ESP-IDF Live Streaming Web Server IP address

Now open any web browser and paste this IP address, you will be able to see live streaming from ESP32-CAM as shown below:

ESP32-CAM ESP-IDF Live Streaming Web Server

You may also like to read:

10 thoughts on “ESP32-CAM ESP-IDF Live Streaming Web Server”

  1. How can I use a ESP32-WROVER CAM Board for this tutorial? Your init_camera function doesnt specify which board it used when assigning the camera pins.

    Reply
  2. Everything seems to work fine except that I am literally watching slideshow (1fps). The logs show:
    I (2356) esp32-cam-test: ESP32 CAM Web Server is up and running

    I (7876) esp32-cam-test: MJPG: 16KB 855ms (1.2fps)
    I (9526) esp32-cam-test: MJPG: 34KB 1658ms (0.6fps)
    I (11756) esp32-cam-test: MJPG: 85KB 2226ms (0.4fps)
    I (14516) esp32-cam-test: MJPG: 99KB 2760ms (0.4fps)
    I (17226) esp32-cam-test: MJPG: 124KB 2706ms (0.4fps)
    I (21906) esp32-cam-test: MJPG: 97KB 4686ms (0.2fps)
    I (24586) esp32-cam-test: MJPG: 87KB 2678ms (0.4fps)

    How increase the FPS ?

    Reply
  3. Hi ,
    The project works on my ESP32 CAM (AiThinker board). But when I run it on ESP32-S3-eye board, it has the following errors:
    I (1900) cam_hal: Allocating 15360 Byte frame buffer in PSRAM
    E (1900) cam_hal: cam_dma_config(300): frame buffer malloc failed
    E (1910) cam_hal: cam_config(384): cam_dma_config failed
    E (1910) camera: Camera config failed with error 0xffffffff
    err: ESP_FAIL
    I (1920) main_task: Returned from app_main()

    Could you advise how to fix it?
    Thanks!
    PJ

    Reply
  4. Looking at the html file everything that uses port 80 uses a fetch cmd to communicate with the esp32 so that’s understandable. However streaming which is on on port 81 does not communicate with the esp32 via fetch command. So I am wondering how streaming actually works. If the src attribute of the img tag is populated with the local_address:81/stream, streaming will start. If the same src tag is assigned an empty string “” the streaming will stop. This is complete magic to me. Is this being handled by browser code because it is in an img tag ? That would be too much of a black box to me. Also do not know how the boundary string is used.

    Reply
  5. How would you alter this if you wanted to manually assign a static IP?

    also would this work with the Arduino IDE or is VS Code absolutely necessary ?

    Reply

Leave a Comment