ESP32/System/80. Camera capture
Your chip
Your language
System · 80 of 81

Camera capture

A camera on a five pound board, and one hard limit - a frame has to exist in memory before it can be sent. That is why the resolution you pick decides whether the thing works at all, with no error worth reading.

/esp32/camera-capture · arduino · S3

Memory decides the resolution

VGA 640×480, JPEG
13 fps
Frame buffers2
One frame
54 kB
Total
108 kB
Frame rate
13 fps
54 kB a frame, 13 fps. Two habits that prevent most camera bugs: always call esp_camera_fb_return() — miss it once and the next fb_get returns null for ever — and take JPEG unless you are doing pixel work, because the sensor compresses it for free and the chip never has to.

Take JPEG unless you have a reason not to

The sensor compresses on its own, before the data ever reaches the chip. A VGA JPEG is around 25 kB where the raw frame is 600 kB, and there is no CPU cost because the ESP32 never does the work.

Take raw RGB565 only when you need pixels — face detection, motion detection, drawing on the frame — and then expect to drop the resolution.

PSRAM is not optional

Without it you are limited to about QVGA JPEG with one buffer. That is a board-buying decision, not a code one: check for PSRAM before ordering, and enable it in the board menu, because a board that has it and is configured without it behaves exactly like a board that does not.

Streaming to a browser

The classic approach is multipart/x-mixed-replace — a boundary, a JPEG, a boundary, forever. It is a few dozen lines on top of the async web server, and it is what every ESP32-CAM example does. Two viewers at once will halve the frame rate; three will make it unusable.

On your S3
ChipXtensa LX7 · 2 × 240 MHz
Board settingESP32S3 Dev Module
Default I2CSDA 8 · SCL 9
Watch out forThe port vanishes after upload

The code

Init, grab, return. The return is the line people forget, and forgetting it once means every later capture returns null - which looks like a camera that died.

camera_snap.ino
#include "esp_camera.h"

void setup() {
  Serial.begin(115200);

  camera_config_t c = {};
  // ... pin assignments for your board go here ...
  c.pixel_format = PIXFORMAT_JPEG;      // let the sensor compress it
  c.frame_size   = FRAMESIZE_VGA;       // 640x480
  c.jpeg_quality = 12;                  // lower number, bigger file
  c.fb_count     = psramFound() ? 2 : 1;
  c.grab_mode    = CAMERA_GRAB_LATEST;

  esp_err_t err = esp_camera_init(&c);
  if (err != ESP_OK) {
    Serial.printf("init failed 0x%x (0x101 = out of memory)\n", err);
    return;
  }
}

void loop() {
  camera_fb_t *fb = esp_camera_fb_get();
  if (!fb) { Serial.println("capture failed"); return; }

  Serial.printf("%u bytes, %ux%u\n", fb->len, fb->width, fb->height);
  // ... send fb->buf somewhere ...

  esp_camera_fb_return(fb);            // ALWAYS. Miss it once and it stops.
  delay(1000);
}

grab_mode LATEST rather than WHEN_EMPTY gives you the newest frame instead of a stale one from the queue. It matters the moment anything is looking at the picture.

When it does not work

esp_camera_init returns 0x101

Out of memory. The frame does not fit. Lower the resolution, use one frame buffer instead of two, or check that PSRAM is actually enabled in the board menu.

The first capture works and the second returns null

A frame buffer was never returned. Every esp_camera_fb_get needs a matching esp_camera_fb_return, on every path including the error ones.

The image is stale by a second or two

Frames are queued. Use CAMERA_GRAB_LATEST, or grab twice and discard the first.

The picture is pink, striped or half missing

Nearly always the ribbon connector - not seated, or dirty. Reseat it. If it persists, the XCLK frequency may be wrong for that sensor.

Where this goes next

A camera fills memory with pictures. The last page is about getting them onto a screen big enough to matter.

MIPI displays

Edit this page — content/esp32/camera-capture.mdx

Discuss this article

Ask about this page. The answer stays here, on the page it belongs to, for whoever hits the same wall next.

Browse ESP32 on the forum