🛠 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.

Objetivos

Utilizar la interfaz de cámara DVP del ESP32-S3 con un módulo OV2640 (2 MP, interfaz paralela de 8 bits) para capturar imágenes JPEG y servirlas como flujo MJPEG a un navegador a través de Wi-Fi. Esta capacidad es la que ha convertido al S3 en el chip de referencia para las «pantallas inteligentes» y los proyectos ESP32-CAM de nueva generación. Aprenderás a elegir la variante adecuada (el bus de cámara ocupa muchos pines, de ahí el interés de las tarjetas con conector de cámara integrado o con PSRAM para las grandes resoluciones).

Material

Código completo

// 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); }

Verificación esperada

Tras la conexión Wi-Fi, la dirección IP se muestra en la consola; abrir http://IP/stream en un navegador muestra un flujo de imágenes actualizado continuamente (MJPEG). La calidad y la fluidez dependen de la resolución (VGA = 640×480 es un buen compromiso sin PSRAM) y de la calidad JPEG (12 aquí, más bajo = mejor pero más pesado).

Errores frecuentes

Otros tutoriales para esta placa

Ejemplos de código para esta placa

← Volver a la ficha 🛠 Todos los tutoriales