En este tutorial aprenderás cómo transmitir vídeo desde un ESP32-CAM a través de tu red Wi-Fi local hacia tu navegador web. Esto te permitirá, por ejemplo, construir una cámara de vigilancia inalámbrica que puedas monitorear desde tu ordenador.
Piezas necesarias
Necesitarás un ESP32-CAM para probar los ejemplos de código. Puedes conseguir un ESP32-CAM con un USB-TTL Shield para programarlo o un adaptador FTDI USB-TTL. El adaptador FTDI USB-TTL es un poco más complicado de usar pero deja los pines GPIO fácilmente accesibles. Para este tutorial, recomendaría el USB-TTL Shield, pero asegúrate de obtener el correcto (ver el Programming the ESP32-CAM tutorial para más detalles).

ESP32-CAM con USB-TTL Shield

Adaptador FTDI USB-TTL
Makerguides is a participant in affiliate advertising programs designed to provide a means for sites to earn advertising fees by linking to Amazon, AliExpress, Elecrow, and other sites. As an Affiliate we may earn from qualifying purchases.
Transmisión de vídeo
En este proyecto vamos a implementar un servidor de streaming de vídeo que se ejecuta en el ESP32-CAM. El ESP32-CAM captura imágenes y las sirve a través de Wi-Fi. El router Wi-Fi transporta estas imágenes al navegador web, que las muestra como una transmisión de vídeo. La imagen a continuación ilustra la configuración:

Ten en cuenta que la transmisión de vídeo no está cifrada ni es segura, pero solo será visible en una URL dentro de tu red Wi-Fi local, por ejemplo 192.168.2.39/stream. Solo alguien con acceso a tu red Wi-Fi podrá ver la transmisión de vídeo.
Código para el servidor de vídeo
El siguiente sketch de Arduino configura el servidor de streaming de vídeo que envía un flujo MJPEG continuo desde la cámara ESP32-CAM a cualquier navegador web conectado a la misma red Wi-Fi.
Para este código usaremos la esp32cam biblioteca, que facilita el manejo de la cámara. Puedes instalarla a través del Library Manager en el IDE de Arduino. Solo busca «esp32cam» y pulsa INSTALL. La imagen a continuación muestra la instalación completada:

A continuación encontrarás el código completo para el servidor de streaming de vídeo. Échale un vistazo rápido primero y luego discutiremos sus detalles:
#include "WebServer.h"
#include "WiFi.h"
#include "esp32cam.h"
const char* WIFI_SSID = "SSID";
const char* WIFI_PASS = "PASSWORD";
const char* URL = "/stream";
const int FRAMERATE = 10;
const auto RESOLUTION = esp32cam::Resolution::find(1024, 768);
WebServer server(80);
void handleStream() {
static char head[128];
WiFiClient client = server.client();
server.sendContent("HTTP/1.1 200 OK\r\n"
"Content-Type: multipart/x-mixed-replace; "
"boundary=frame\r\n\r\n");
while (client.connected()) {
auto frame = esp32cam::capture();
if (frame) {
sprintf(head,
"--frame\r\n"
"Content-Type: image/jpeg\r\n"
"Content-Length: %ul\r\n\r\n",
frame->size());
client.write(head, strlen(head));
frame->writeTo(client);
client.write("\r\n");
}
delay(1000 / FRAMERATE);
}
}
void initCamera() {
using namespace esp32cam;
Config cfg;
cfg.setPins(pins::AiThinker);
cfg.setResolution(RESOLUTION);
cfg.setBufferCount(2);
cfg.setJpeg(80);
Camera.begin(cfg);
}
void initWifi() {
WiFi.persistent(false);
WiFi.mode(WIFI_STA);
WiFi.begin(WIFI_SSID, WIFI_PASS);
while (WiFi.status() != WL_CONNECTED) {
delay(100);
}
Serial.printf("Stream at: http://%s%s\n",
WiFi.localIP().toString().c_str(), URL);
}
void initServer() {
server.on(URL, handleStream);
server.begin();
}
void setup() {
Serial.begin(115200);
initWifi();
initCamera();
initServer();
}
void loop() {
server.handleClient();
}
Bibliotecas
El sketch comienza incluyendo las bibliotecas necesarias:
#include "WebServer.h" #include "WiFi.h" #include "esp32cam.h"
Estas bibliotecas habilitan la funcionalidad del servidor HTTP (WebServer.h), la conectividad Wi-Fi (WiFi.h) y el control de la cámara usando el ESP32-CAM (esp32cam.h).
Constantes
A continuación, se definen algunas constantes:
const char* WIFI_SSID = "SSID"; const char* WIFI_PASS = "PASSWORD"; const char* URL = "/stream"; const int FRAMERATE = 10; const auto RESOLUTION = esp32cam::Resolution::find(1024, 768);
Las variables WIFI_SSID y WIFI_PASS almacenan las credenciales para la red Wi-Fi. Deberás reemplazar SSID y PASSWORD con los datos reales de acceso a tu red Wi-Fi.
La URL es la ruta en la que se servirá la transmisión de vídeo. Puedes cambiarla por lo que quieras. Por ejemplo, «/video» o «/frontdoor», solo asegúrate de mantener la barra (‘/’) al principio.
La variable FRAMERATE define cuántos fotogramas por segundo se enviarán al cliente. Y la RESOLUTION establece la resolución deseada para la cámara usando la biblioteca esp32cam.
Aquí tienes una lista de los posibles valores para la resolución de la cámara, aunque dependiendo de la cámara no todos pueden funcionar:
- 96×96
- 160×120
- 128×128
- 176×144
- 240×176
- 240×240
- 320×240
- 320×320
- 400×296
- 480×320
- 640×480
- 800×600
- 1024×768
- 1280×720
- 1280×1024
- 1600×1200
Puedes imprimir la lista de resoluciones de la cámara usando la siguiente función:
void printResolutions() {
Serial.println("Camera resolutions:");
for (auto res : esp32cam::Camera.listResolutions()) {
Serial.printf("%dx%d\n", res.getWidth(), res.getHeight());
}
}
Ten en cuenta que la resolución afectará la tasa máxima de fotogramas. Con una resolución de 1600×1200 no obtendrás mucho más de 10 fotogramas por segundo.
Objetos
La siguiente línea crea un objeto servidor HTTP que escucha en el puerto 80. Ese es nuestro servidor web que proporciona la transmisión de vídeo.
WebServer server(80);
handleStream
La función handleStream() maneja la transmisión de vídeo cada vez que un cliente accede a la ruta /stream:
void handleStream() {
static char head[128];
WiFiClient client = server.client();
Esta línea obtiene el cliente actual conectado al servidor. Luego se prepara la respuesta:
server.sendContent("HTTP/1.1 200 OK\r\n"
"Content-Type: multipart/x-mixed-replace; "
"boundary=frame\r\n\r\n");
Esta cabecera indica al navegador que recibirá un flujo MJPEG multipartes, donde cada parte está separada por un límite llamado frame.
Dentro de un bucle, la función captura un fotograma de la cámara:
while (client.connected()) {
auto frame = esp32cam::capture();
Si se captura un fotograma con éxito, se construye una cabecera que describe la imagen JPEG:
if (frame) {
sprintf(head,
"--frame\r\n"
"Content-Type: image/jpeg\r\n"
"Content-Length: %ul\r\n\r\n",
frame->size());
client.write(head, strlen(head));
frame->writeTo(client);
client.write("\r\n");
}
La imagen se envía al cliente como JPEG, y luego el bucle espera brevemente según la tasa de fotogramas:
delay(1000 / FRAMERATE);
Ten en cuenta que esta constante FRAMERATE solo te permite reducir la tasa de fotogramas por debajo del máximo que el ESP32-CAM puede producir para la resolución dada. Si siempre quieres usar la tasa máxima, simplemente elimina esta línea.
initCamera
La función initCamera() configura y arranca el hardware del ESP32-CAM:
void initCamera() {
using namespace esp32cam;
Config cfg;
cfg.setPins(pins::AiThinker);
cfg.setResolution(RESOLUTION);
cfg.setBufferCount(2);
cfg.setJpeg(80);
Camera.begin(cfg);
}
Aquí se crea un objeto Config. La función setPins configura los pines de la cámara según el diseño de la placa AiThinker. setResolution define la resolución a usar, mientras que setBufferCount determina cuántos buffers de imagen se usan internamente.
La función setJpeg establece la calidad de compresión JPEG. 80 es un buen equilibrio entre calidad y tamaño. 90 ofrece mejor calidad pero una reducción menor en el tamaño de la imagen. Para vídeo más rápido puedes reducir la resolución y la compresión JPEG, pero obviamente obtendrás imágenes más pequeñas y de menor calidad.
Finalmente, Camera.begin(cfg) inicializa la cámara con estas configuraciones.
initWifi
La función initWifi() conecta el ESP32-CAM a la red Wi-Fi especificada:
void initWifi() {
WiFi.persistent(false);
WiFi.mode(WIFI_STA);
WiFi.begin(WIFI_SSID, WIFI_PASS);
while (WiFi.status() != WL_CONNECTED) {
delay(100);
}
Serial.printf("Stream at: http://%s%s\n",
WiFi.localIP().toString().c_str(), URL);
}
WiFi.persistent(false) desactiva la escritura de las credenciales en la memoria flash, lo que acelera la reconexión. El dispositivo se configura en modo estación (WIFI_STA), y trata de conectarse al Wi-Fi usando las credenciales dadas.
Una vez conectado, la dirección IP y la ruta de la transmisión se imprimen en el monitor serie. Necesitarás copiar y pegar esta URL impresa en la barra de direcciones de tu navegador para ver la transmisión de vídeo. La URL real dependerá de tu ESP32-CAM, pero puedes cambiar el sufijo «stream». En mi caso veo la siguiente URL en el Monitor Serie:

Por lo tanto, copio «http://192.168.2.39/stream» en la barra de direcciones de mi navegador Chrome para acceder al vídeo.
initServer
La función initServer() vincula la ruta de streaming al manejador y arranca el servidor:
void initServer() {
server.on(URL, handleStream);
server.begin();
}
Esto asigna cualquier petición a /stream a la función handleStream(), y luego inicia el servidor.
setup
La función setup() se ejecuta una vez al arrancar:
void setup() {
Serial.begin(115200);
initWifi();
initCamera();
initServer();
}
Inicia la comunicación serie, conecta al Wi-Fi, inicializa la cámara y configura el servidor web.
loop
Finalmente, la función loop() maneja continuamente las peticiones HTTP entrantes:
void loop() {
server.handleClient();
}
Esta línea permite que el ESP32-CAM responda a las solicitudes de los clientes invocando cualquier manejador registrado como handleStream().
En conjunto, este código crea un servidor de streaming MJPEG simple pero efectivo que puede ser accedido desde cualquier navegador web dentro de la misma red, proporcionando una transmisión en vivo desde la cámara ESP32-CAM.
Para descargar ese código a tu módulo ESP32-CAM, selecciona la placa AI Thinker ESP32-CAM y luego pon el ESP32-CAM en modo de descarga.

Si necesitas ayuda con esto, echa un vistazo al Programming the ESP32-CAM tutorial.
Conclusiones
En este tutorial aprendiste cómo transmitir vídeo desde un ESP32-CAM a través de tu red Wi-Fi local hacia tu navegador web.
Si quieres detectar objetos en la transmisión de vídeo, echa un vistazo a nuestro Object Detection with ESP32-CAM and YOLO tutorial. Si quieres construir un sistema de vigilancia activado por movimiento, el Motion Activated ESP32-CAM tutorial podría ser útil.
También revisa el More GPIO pins for ESP32-CAM tutorial, si necesitas más pines GPIO para controlar tu sistema de vigilancia. Alternativamente, podrías considerar una placa diferente con más GPIOs como la XIAO-ESP32-S3-Sense o la ESP32-WROVER CAM.
Finalmente, puede que quieras encender el LED flash del ESP32-CAM si usas la cámara en un entorno oscuro. Consulta el Control ESP32-CAM Flash LED tutorial para más detalles.
Si tienes alguna pregunta, no dudes en dejarlas en la sección de comentarios.
¡Feliz bricolaje ; )

