MCPcopy Index your code
hub / github.com/espressif/esp32-camera

github.com/espressif/esp32-camera @v2.1.7

Chat with this repo
repository ↗ · DeepWiki ↗ · release v2.1.7 ↗ · + Follow
680 symbols 1,392 edges 86 files 42 documented · 6%
What it actually does AI analysis from the code graph — generated when you open this
loading…
README

ESP32 Camera Driver

Build examples Component Registry

General Information

This repository hosts ESP32 series Soc compatible driver for image sensors. Additionally it provides a few tools, which allow converting the captured frame data to the more common BMP and JPEG formats.

Supported Soc

  • ESP32
  • ESP32-S2
  • ESP32-S3

Supported Sensor

model max resolution color type output format Len Size
OV2640 1600 x 1200 color YUV(422/420)/YCbCr422

RGB565/555

8-bit compressed data

8/10-bit Raw RGB data | 1/4" | | OV3660 | 2048 x 1536 | color | raw RGB data

RGB565/555/444

CCIR656

YCbCr422

compression | 1/5" | | OV5640 | 2592 x 1944 | color | RAW RGB

RGB565/555/444

CCIR656

YUV422/420

YCbCr422

compression | 1/4" | | OV7670 | 640 x 480 | color | Raw Bayer RGB

Processed Bayer RGB

YUV/YCbCr422

GRB422

RGB565/555 | 1/6" | | OV7725 | 640 x 480 | color | Raw RGB

GRB 422

RGB565/555/444

YCbCr 422 | 1/4" | | NT99141 | 1280 x 720 | color | YCbCr 422

RGB565/555/444

Raw

CCIR656

JPEG compression | 1/4" | | GC032A | 640 x 480 | color | YUV/YCbCr422

RAW Bayer

RGB565 | 1/10" | | GC0308 | 640 x 480 | color | YUV/YCbCr422

RAW Bayer

RGB565

Grayscale | 1/6.5" | | GC2145 | 1600 x 1200 | color | YUV/YCbCr422

RAW Bayer

RGB565 | 1/5" | | BF3005 | 640 x 480 | color | YUV/YCbCr422

RAW Bayer

RGB565 | 1/4" | | BF20A6 | 640 x 480 | color | YUV/YCbCr422

RAW Bayer

Only Y | 1/10" | | SC101IOT| 1280 x 720 | color | YUV/YCbCr422

Raw RGB | 1/4.2" | | SC030IOT| 640 x 480 | color | YUV/YCbCr422

RAW Bayer | 1/6.5" | | SC031GS | 640 x 480 | monochrome | RAW MONO

Grayscale | 1/6" | | HM0360 | 656 x 496 | monochrome | RAW MONO

Grayscale | 1/6" | | HM1055 | 1280 x 720 | color | 8/10-bit Raw

YUV/YCbCr422

RGB565/555/444 | 1/6" |

Important to Remember

  • Except when using CIF or lower resolution with JPEG, the driver requires PSRAM to be installed and activated.
  • Using YUV or RGB puts a lot of strain on the chip because writing to PSRAM is not particularly fast. The result is that image data might be missing. This is particularly true if WiFi is enabled. If you need RGB data, it is recommended that JPEG is captured and then turned into RGB using fmt2rgb888 or fmt2bmp/frame2bmp.
  • When 1 frame buffer is used, the driver will wait for the current frame to finish (VSYNC) and start I2S DMA. After the frame is acquired, I2S will be stopped and the frame buffer returned to the application. This approach gives more control over the system, but results in longer time to get the frame.
  • When 2 or more frame buffers are used, I2S is running in continuous mode and each frame is pushed to a queue that the application can access. This approach puts more strain on the CPU/Memory, but allows for double the frame rate. Please use only with JPEG.
  • The Kconfig option CONFIG_CAMERA_PSRAM_DMA enables PSRAM DMA mode on ESP32-S2 and ESP32-S3 devices. This flag defaults to false.
  • You can switch PSRAM DMA mode at runtime using esp_camera_set_psram_mode().

Installation Instructions

Using with ESP-IDF

  • Add a dependency on espressif/esp32-camera component: bash idf.py add-dependency "espressif/esp32-camera" (or add it manually in idf_component.yml of your project)
  • Enable PSRAM in menuconfig (also set Flash and PSRAM frequiencies to 80MHz)
  • Include esp_camera.h in your code

These instructions also work for PlatformIO, if you are using framework=espidf.

Using with Arduino

Arduino IDE

If you are using the arduino-esp32 core in Arduino IDE, no installation is needed! You can use esp32-camera right away.

PlatformIO

The easy way -- on the env section of platformio.ini, add the following:

[env]
lib_deps =
  esp32-camera

Now the esp_camera.h is available to be included:

#include "esp_camera.h"

Enable PSRAM on menuconfig or type it directly on sdkconfig. Check the official doc for more info.

CONFIG_ESP32_SPIRAM_SUPPORT=y

Examples

This component comes with a basic example illustrating how to get frames from the camera. You can try out the example using the following command:

idf.py create-project-from-example "espressif/esp32-camera:camera_example"

This command will download the example into camera_example directory. It comes already pre-configured with the correct settings in menuconfig.

Initialization

#include "esp_camera.h"

//WROVER-KIT PIN Map
#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

static 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 = 20000000,
    .ledc_timer = LEDC_TIMER_0,
    .ledc_channel = LEDC_CHANNEL_0,

    .pixel_format = PIXFORMAT_JPEG,//YUV422,GRAYSCALE,RGB565,JPEG
    .frame_size = FRAMESIZE_UXGA,//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
    .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//CAMERA_GRAB_LATEST. Sets when buffers should be filled
};

esp_err_t camera_init(){
    //power up the camera if PWDN pin is defined
    if(CAM_PIN_PWDN != -1){
        pinMode(CAM_PIN_PWDN, OUTPUT);
        digitalWrite(CAM_PIN_PWDN, LOW);
    }

    //initialize the camera
    esp_err_t err = esp_camera_init(&camera_config);
    if (err != ESP_OK) {
        ESP_LOGE(TAG, "Camera Init Failed");
        return err;
    }

    return ESP_OK;
}

esp_err_t camera_capture(){
    //acquire a frame
    camera_fb_t * fb = esp_camera_fb_get();
    if (!fb) {
        ESP_LOGE(TAG, "Camera Capture Failed");
        return ESP_FAIL;
    }
    //replace this with your own function
    process_image(fb->width, fb->height, fb->format, fb->buf, fb->len);

    //return the frame buffer back to the driver for reuse
    esp_camera_fb_return(fb);
    return ESP_OK;
}

JPEG HTTP Capture

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

typedef struct {
        httpd_req_t *req;
        size_t len;
} jpg_chunking_t;

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){
        j->len = 0;
    }
    if(httpd_resp_send_chunk(j->req, (const char *)data, len) != ESP_OK){
        return 0;
    }
    j->len += len;
    return len;
}

esp_err_t jpg_httpd_handler(httpd_req_t *req){
    camera_fb_t * fb = NULL;
    esp_err_t res = ESP_OK;
    size_t fb_len = 0;
    int64_t fr_start = esp_timer_get_time();

    fb = esp_camera_fb_get();
    if (!fb) {
        ESP_LOGE(TAG, "Camera capture failed");
        httpd_resp_send_500(req);
        return ESP_FAIL;
    }
    res = httpd_resp_set_type(req, "image/jpeg");
    if(res == ESP_OK){
        res = httpd_resp_set_hdr(req, "Content-Disposition", "inline; filename=capture.jpg");
    }

    if(res == ESP_OK){
        if(fb->format == PIXFORMAT_JPEG){
            fb_len = fb->len;
            res = httpd_resp_send(req, (const char *)fb->buf, fb->len);
        } else {
            jpg_chunking_t jchunk = {req, 0};
            res = frame2jpg_cb(fb, 80, jpg_encode_stream, &jchunk)?ESP_OK:ESP_FAIL;
            httpd_resp_send_chunk(req, NULL, 0);
            fb_len = jchunk.len;
        }
    }
    esp_camera_fb_return(fb);
    int64_t fr_end = esp_timer_get_time();
    ESP_LOGI(TAG, "JPG: %uKB %ums", (uint32_t)(fb_len/1024), (uint32_t)((fr_end - fr_start)/1000));
    return res;
}

JPEG HTTP Stream

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

#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: %zu\r\n\r\n";

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 = 0;
    uint8_t * jpg_buf = NULL;
    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;
                break;
            }
        } 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){
            int hlen = snprintf(part_buf, sizeof(part_buf), _STREAM_PART, jpg_buf_len);
            if(hlen < 0 || hlen >= sizeof(part_buf)){
                ESP_LOGE(TAG, "Header truncated (%d bytes needed >= %zu buffer)",
                         hlen, sizeof(part_buf));
                res = ESP_FAIL;
            } else {
                res = httpd_resp_send_chunk(req, part_buf, (size_t)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;
        float fps = frame_time > 0 ? 1000.0f / (float)frame_time : 0.0f;
        ESP_LOGI(TAG, "MJPG: %uKB %ums (%.1ffps)",
            (uint32_t)(jpg_buf_len/1024),
            (uint32_t)frame_time, fps);
    }

    last_frame = 0;
    return res;
}

BMP HTTP Capture

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

esp_err_t bmp_httpd_handler(httpd_req_t *req){
    camera_fb_t * fb = NULL;
    esp_err_t res = ESP_OK;
    int64_t fr_start = esp_timer_get_time();

    fb = esp_camera_fb_get();
    if (!fb) {
        ESP_LOGE(TAG, "Camera capture failed");
        httpd_resp_send_500(req);
        return ESP_FAIL;
    }

    uint8_t * buf = NULL;
    size_t buf_len = 0;
    bool converted = frame2bmp(fb, &buf, &buf_len);
    esp_camera_fb_return(fb);
    if(!converted){
        ESP_LOGE(TAG, "BMP conversion failed");
        httpd_resp_send_500(req);
        return ESP_FAIL;
    }

    res = httpd_resp_set_type(req, "image/x-windows-bmp")
       || httpd_resp_set_hdr(req, "Content-Disposition", "inline; filename=capture.bmp")
       || httpd_resp_send(req, (const char *)buf, buf_len);
    free(buf);
    int64_t fr_end = esp_timer_get_time();
    ESP_LOGI(TAG, "BMP: %uKB %ums", (uint32_t)(buf_len/1024), (uint32_t)((fr_end - fr_start)/1000));
    return res;
}

Autofocus (OV5640)

This component includes an optional autofocus helper for OV5640 modules that have an AF-capable lens.

  • Enable it in menuconfig: Component configCamera configurationEnable autofocus (OV5640).
  • Include the header: #include "esp_camera_af.h".

Basic usage:

```c

include "esp_camera.h"

include "esp_camera_af.h"

// After esp_camera_init(...) sensor_t *s = esp_camera_sensor_get();

esp_camera_af_config_t af_cfg = { .mode = ESP_CAMERA_AF_

Core symbols most depended-on inside this repo

SCCB_Write
called by 102
driver/sccb.c
SCCB_Read
called by 81
driver/sccb.c
write_reg
called by 80
sensors/gc0308.c
write_reg
called by 56
sensors/ov5640.c
write_reg
called by 53
sensors/ov3660.c
write_reg
called by 49
sensors/gc2145.c
write_reg_bits
called by 47
sensors/ov2640.c
write_reg
called by 36
sensors/mega_ccm.c

Shape

Function 625
Method 40
Class 14
Enum 1

Languages

C90%
C++10%

Modules by API surface

sensors/ov5640.c50 symbols
sensors/ov3660.c50 symbols
sensors/nt99141.c49 symbols
sensors/ov2640.c44 symbols
sensors/hm1055.c38 symbols
conversions/jpge.cpp37 symbols
sensors/ov7725.c32 symbols
sensors/gc0308.c31 symbols
sensors/bf3005.c28 symbols
sensors/hm0360.c25 symbols
sensors/sc101iot.c23 symbols
sensors/sc030iot.c22 symbols

For agents

$ claude mcp add esp32-camera \
  -- python -m otcore.mcp_server <graph>

⬇ download graph artifact