🛠 Caméra OV2640 : capture et serveur MJPEG

ESP32-S3 · intermediaire · Schéma : IMG-esp32-s3-intermediaire-3-esp32-s3-camera-ov2640.svg — Module caméra DVP OV2640 connecté au bus parallèle d'une carte ESP32-S3 (type DevKitC-1 avec connecteur caméra ou XIAO avec extension), flux vidéo affiché dans un navigateur via Wi-Fi.

Objectives

Use the ESP32-S3 DVP camera interface with an OV2640 module (2 MP, 8-bit parallel interface) to capture JPEG images and stream them as MJPEG to a browser via Wi-Fi. This capability made the S3 the reference chip for "smart displays" and next-generation ESP32-CAM projects. You will learn to choose the right variant (the camera bus occupies many pins, hence the interest in boards with an integrated camera connector or PSRAM for high resolutions).

Hardware

Complete code

// Serveur MJPEG minimal — exemple pour XIAO ESP32S3 Sense (broches ESP32S3_CAM_XIAO)
#include "esp_camera.h"
#include <WiFi.h>
#include <WebServer.h>

#define PWDN_GPIO_NUM  -1
#define RESET_GPIO_NUM -1
#define XCLK_GPIO_NUM  10
#define SIOD_GPIO_NUM  40
#define SIOC_GPIO_NUM  39
#define Y9_GPIO_NUM    48
#define Y8_GPIO_NUM    11
#define Y7_GPIO_NUM    12
#define Y6_GPIO_NUM    14
#define Y5_GPIO_NUM    16
#define Y4_GPIO_NUM    18
#define Y3_GPIO_NUM    17
#define Y2_GPIO_NUM    15
#define VSYNC_GPIO_NUM 38
#define HREF_GPIO_NUM  47
#define PCLK_GPIO_NUM  13

const char* WIFI_SSID = "votre-ssid";
const char* WIFI_PASS = "votre-mot-de-passe";
WebServer server(80);

void handleStream {
  static const char* part =
    "--frame\r\nContent-Type: image/jpeg\r\n\r\n";
  camera_fb_t* fb;
  do {
    fb = esp_camera_fb_get;
    if (!fb) { server.send(500, "text/plain", "capture KO"); return; }
    server.setContentLength(strlen(part) + fb->len + 2, true);
    server.sendContent(part);
    server.sendContent_P((const char*)fb->buf, fb->len);
    server.sendContent("\r\n", 2);
    esp_camera_fb_return(fb);
  } while (!server.client.available);
}

void setup {
  Serial.begin(115200);
  camera_config_t cfg = {};
  cfg.ledc_channel = LEDC_CHANNEL_0;
  cfg.ledc_timer   = LEDC_TIMER_0;
  cfg.pin_d0 = Y2_GPIO_NUM;  cfg.pin_d1 = Y3_GPIO_NUM;
  cfg.pin_d2 = Y4_GPIO_NUM;  cfg.pin_d3 = Y5_GPIO_NUM;
  cfg.pin_d4 = Y6_GPIO_NUM;  cfg.pin_d5 = Y7_GPIO_NUM;
  cfg.pin_d6 = Y8_GPIO_NUM;  cfg.pin_d7 = Y9_GPIO_NUM;
  cfg.pin_xclk = XCLK_GPIO_NUM;  cfg.pin_pclk = PCLK_GPIO_NUM;
  cfg.pin_vsync = VSYNC_GPIO_NUM; cfg.pin_href = HREF_GPIO_NUM;
  cfg.pin_sccb_sda = SIOD_GPIO_NUM; cfg.pin_sccb_scl = SIOC_GPIO_NUM;
  cfg.pin_pwdn = PWDN_GPIO_NUM;  cfg.pin_reset = RESET_GPIO_NUM;
  cfg.xclk_freq_hz = 20000000;
  cfg.pixel_format = PIXFORMAT_JPEG;
  cfg.frame_size   = FRAMESIZE_VGA;
  cfg.jpeg_quality = 12;
  cfg.fb_count     = 1;
  if (esp_camera_init(&cfg) != ESP_OK) {
    Serial.println("Camera KO — verifier brochage/PSRAM"); while (true) delay(999);
  }
  WiFi.begin(WIFI_SSID, WIFI_PASS);
  while (WiFi.status != WL_CONNECTED) { delay(300); Serial.print("."); }
  Serial.print("Flux : http://"); Serial.println(WiFi.localIP);
  server.on("/stream", handleStream);
  server.begin;
}

void loop { server.handleClient; delay(2); }

Expected verification

After Wi-Fi connection, the IP address is displayed on the console; opening http://IP/stream in a browser displays a continuously refreshed image stream (MJPEG). Quality and smoothness depend on the resolution (VGA = 640×480 is a good compromise without PSRAM) and JPEG quality (12 here, lower = better but heavier).

Common errors

Other tutorials for this board

Code examples for this board

← Back to the board 🛠 All tutorials