🛠 Caméra OV2640 : capture et serveur MJPEG
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
- 1 tarjeta ESP32-S3 con conector de cámara (DevKitC-1 con extensión, XIAO ESP32S3 Sense, o módulo tipo ESP32-S3-CAM)
- 1 módulo de cámara OV2640 (DCW o estándar)
- Preferiblemente una variante con PSRAM (N8R8/N16R8) para JPEG en SVGA/XGA
- Una red Wi-Fi de 2,4 GHz
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
- PSRAM ausente: sin PSRAM (variantes N8 sin R), limitar frame_size a QQVGA/QVGA y fb_count a 1; de lo contrario, esp_camera_init falla o la imagen está corrupta.
- Pines incorrectos: cada tarjeta tiene su propio cableado de cámara (XIAO, DevKitC, ESP32-S3-EYE difieren); copia la tabla de la ficha del fabricante, no adivines.
- Atención a los pines ocupados: en los módulos N8R8/N16R8, la PSRAM octal ocupa GPIO35/36 (y 37 según la configuración) y el WS2812 integrado ocupa el GPIO48; cruza los esquemas.
- Wi-Fi 5 GHz: el S3 solo ve la banda de 2,4 GHz; un fallo de conexión suele deberse a esto.
- Alimentación insuficiente: cámara + Wi-Fi + PSRAM en pico a veces superan la capacidad de un puerto USB desgastado; si la imagen salta, alimenta correctamente.
Otros tutoriales para esta placa
Ejemplos de código para esta placa
clignotante.inoArduinobouton-led.inoArduinorespiration.inoArduinodht22.inoArduinooled-i2c.inoArduinoserveur-web.inoArduino