Skip to Content

Cámara de vigilancia con ESP32-CAM

Cámara de vigilancia con ESP32-CAM

En este tutorial aprenderás a construir un sistema de cámara de vigilancia activado por movimiento con el ESP32-CAM. El sistema grabará vídeo siempre que se detecte movimiento y guardará la secuencia de vídeo en un archivo en tu ordenador. Este proyecto es adecuado para aplicaciones de vigilancia, seguridad o monitorización de fauna.

Componentes necesarios

A continuación encontrarás los componentes necesarios para montar el proyecto. Necesitarás un ESP32-CAM y el USB-TTL Shield o el FTDI USB-TTL Adapter para programar la placa.

He listado dos tipos distintos de sensores de movimiento. Si quieres un tamaño pequeño y un intervalo de activación más corto, elige el AM312. Pero si quieres activar la cámara solo por la noche, usa el HC-SR501, ya que puede equiparse fácilmente con un sensor de luz. Para más detalles, consulta el Motion Activated ESP32-CAM tutorial.

ESP32-CAM con USB-TTL Shield

FTDI USB-TTL Adapter

Dupont wire set

Dupont Wire Set

AM312 PIR Motion Sensor

HC-SR501 PIR Motion Sensor

USB data cable

Cable USB de datos

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.

Arquitectura del sistema

En este proyecto implementaremos un sistema de videovigilancia que utiliza el ESP32-CAM, un sensor de movimiento y un PC para recibir y almacenar los vídeos de vigilancia.

El ESP32-CAM ejecutará un servidor de streaming de vídeo que transmite fotogramas por Wi‑Fi cuando un sensor PIR conectado al ESP32-CAM detecte movimiento. Los fotogramas serán recibidos por una aplicación Python en un PC que escribe los datos de vídeo a un archivo. La imagen siguiente ilustra la configuración:

Architecture of Surveillance System
Arquitectura del sistema de vigilancia

Circuito de detección de movimiento

Empezamos montando el circuito de detección de movimiento. Esto implica conectar el sensor de infrarrojos pasivo (PIR) al ESP32-CAM. En este ejemplo usaré el AM312 PIR Sensor, pero conectar el HC-SR501 sería prácticamente lo mismo. Consulta el Motion Activated ESP32-CAM tutorial.

Conectar el sensor PIR al ESP32-CAM

Conectar el módulo sensor AM312 al ESP32-CAM es fácil. Conecta una fuente de alimentación (batería) de 5V (hasta 12V) a los pines 5V y GND del ESP32-CAM como se muestra abajo (cables rojo y azul).

Connecting AM312 PIR Sensor to ESP32-CAM
Conexión del AM312 al ESP32-CAM

Puedes suministrar entre 5V y 12V para alimentar la placa. No te preocupes. Hay un regulador de voltaje conectado al pin 5V que reducirá la tensión de entrada al nivel requerido por la placa.

De forma similar, conecta el módulo AM312 a la fuente de alimentación (cables rojo y azul). También admite hasta 12V, pero presta atención a la polaridad, ya que el AM312 no tiene protección. Luego conecta la salida S u OUT del AM312 al pin GPIO13 del ESP32-CAM (cable verde).

Si usas el módulo HC-SR501 en lugar del AM312, conéctalo de la misma manera (GND a GND, VCC a 5V–12V).

Circuito en Breadboard

Si programas el ESP32-CAM mediante el USB-TTL Shield y quieres probar el circuito en una breadboard, puedes alimentar el ESP32-CAM y el sensor AM312 como se muestra a continuación:

Circuit on Breadboard
Circuito en Breadboard

Tendrás que dejar un pequeño hueco entre el ESP32-CAM y el Programming Shield y conectar los cables a los pines expuestos en ese hueco:

Connect wires to pins in gap
Conectar cables a los pines en el hueco

Las conexiones son las mismas que antes. El pin GND se conecta al pin ‘-‘, el pin 5V al ‘+’ y GPIO13 se conecta al pin ‘s’ del AM312.

Código para probar el sensor PIR

Antes de implementar el código completo del servidor de streaming de vídeo, vamos a probar el sensor PIR y el circuito. Sube el siguiente código a tu ESP32-CAM:

void setup() {
  Serial.begin(115200);
  pinMode(GPIO_NUM_13, INPUT);
}

void loop() {
  bool isMotion = digitalRead(GPIO_NUM_13);
  Serial.println(isMotion ? "ON" : "OFF");
  delay(1000);
}

Si abres el Serial Monitor deberías ver impreso el texto «ON» cuando el sensor PIR detecte movimiento y «OFF» en caso contrario. Si no es así, revisa el circuito. También puedes encontrar consejos útiles en el Motion Activated ESP32-CAM tutorial.

Transmisión de vídeo

En esta sección implementamos el Video Streaming Server en el módulo ESP32-CAM que transmite fotogramas cuando el sensor PIR detecta movimiento. Echa un vistazo rápido al código completo abajo y luego analizaremos sus detalles.

#include "WebServer.h"
#include "WiFi.h"
#include "esp32cam.h"

const char* WIFI_SSID = "SSID";
const char* WIFI_PASS = "PASSWORD";
const char* URL = "/video";
const auto RESOLUTION = esp32cam::Resolution::find(800, 600);
const int FRAMERATE = 10;
const byte pinPIR = GPIO_NUM_13;
const byte pinFlash = GPIO_NUM_4;

WebServer server(80);

bool isMotion() {
  return digitalRead(pinPIR);
}

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()) {
    if (isMotion()) {
      analogWrite(pinFlash, 20);
      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);
      }
    } else {
      analogWrite(pinFlash, 0);
    }
  }
  analogWrite(pinFlash, 0);
}

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);
  pinMode(pinPIR, INPUT);
  pinMode(pinFlash, OUTPUT);
  initWifi();
  initCamera();
  initServer();
}

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

Librerías

Comenzamos incluyendo las librerías necesarias:

#include "WebServer.h"
#include "WiFi.h"
#include "esp32cam.h"

La WebServer.hlibrería se usa para manejar las funcionalidades del servidor HTTP, y la WiFi.hlibrería permite al ESP32 conectarse a una red inalámbrica. La esp32cam librería proporciona una interfaz sencilla para configurar y usar el módulo de cámara en la placa ESP32-CAM. Tendrás que instalarla vía el Library Manager:

esp32cam library installed via Library Manager
Biblioteca esp32cam instalada mediante Library Manager

Constantes

A continuación definimos varias constantes de configuración:

const char* WIFI_SSID = "SSID";
const char* WIFI_PASS = "PASSWORD";
const char* URL = "/video";
const auto RESOLUTION = esp32cam::Resolution::find(800, 600);
const int FRAMERATE = 10;
const byte pinPIR = GPIO_NUM_13;
const byte pinFlash = GPIO_NUM_4;

La WIFI_SSID y WIFI_PASS variables almacenan las credenciales de la red Wi‑Fi. Tendrás que reemplazar los valores ficticios SSID y PASSWORD por las credenciales de tu red Wi‑Fi.

La URLvariable especifica la ruta en el servidor a la que los clientes accederán para ver el stream de vídeo. Puedes cambiarla por otro nombre, p. ej. «/video-frontdoor», pero asegúrate de que el nombre en el servidor (ESP32-CAM) y en el cliente (app Python) coincida.

La RESOLUTION variable establece la resolución de la cámara a 800×600 píxeles. Esto también lo puedes cambiar, pero la resolución en servidor y cliente debe ser idéntica.

También puedes ajustar el FRAMERATE, que define el número de fotogramas por segundo que el servidor intentará transmitir. Como antes, las tasas de frames del servidor y del cliente deben coincidir.

Finalmente, el pinPIR y pinFlash variables se refieren a los pines GPIO conectados al sensor PIR y al LED de flash integrado, respectivamente.

Objetos

A continuación creamos un servidor web que escucha en el puerto 80:

WebServer server(80);

Función isMotion

La función isMotion() comprueba la detección de movimiento:

bool isMotion() {
  return digitalRead(pinPIR);
}

Esta función lee la señal digital del sensor PIR. Si el sensor entrega una señal alta, hay movimiento y la función devuelve true. Recuerda que el sensor PIR tiene un tiempo de activación que puede ajustarse en el HC-SR501.

Función handleStream

La función handleStream() se encarga de gestionar el streaming de vídeo cuando un cliente accede a la URL /video:

Comenzamos declarando un búfer llamado headque contendrá las cabeceras HTTP de cada fotograma. A continuación recuperamos el objeto client asociado a la petición HTTP actual:

void handleStream() {
  static char head[128];
  WiFiClient client = server.client();

El servidor responde al cliente con una cabecera HTTP que indica que enviará una serie de imágenes JPEG separadas por delimitadores marcados como --frame. Esta técnica es conocida comúnmente como streaming MJPEG (Motion JPEG).

  server.sendContent("HTTP/1.1 200 OK\r\n"
                     "Content-Type: multipart/x-mixed-replace; "
                     "boundary=frame\r\n\r\n");

Mientras el cliente permanezca conectado, el bucle continúa. Dentro del bucle llamamos a la isMotion() función para determinar si se detecta movimiento:

  while (client.connected()) {
    if (isMotion()) {
      analogWrite(pinFlash, 20);

Si lo está, encendemos el LED de flash con baja luminosidad usando PWM, escribiendo un ciclo de trabajo de 20 a pinFlash. Ten cuidado con la intensidad del flash. Si pones el flash a máxima intensidad (255) durante mucho tiempo, ¡el LED podría quemarse!

Luego intentamos capturar un fotograma de la cámara usando esp32cam::capture():

      auto frame = esp32cam::capture();

Si se captura un fotograma con éxito, preparamos las cabeceras HTTP para ese fotograma:

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

La función sprintf() formatea una cadena con el marcador de delimitador, el tipo de contenido y la longitud del contenido de la imagen. Esta cabecera se escribe al cliente con client.write(). La imagen JPEG se envía con frame->writeTo(client), seguida de un retorno de carro y salto de línea. Se introduce un breve retraso según la tasa de frames deseada para controlar la velocidad del streaming.

    } else {
      analogWrite(pinFlash, 0);
    }
  }
  analogWrite(pinFlash, 0);
}

Si no se detecta movimiento, apagamos el LED de flash. Tras salir del bucle, lo que ocurre cuando el cliente se desconecta, volvemos a apagar el flash para asegurarnos de que no quede encendido.

Función initCamera

La función initCamera() inicializa 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);
}

Dentro de esta función se crea un objeto Config llamado cfg. Se llama al método setPins() con pins::AiThinker para configurar los pines GPIO para el modelo AI Thinker del ESP32-CAM. También puedes usar el código en otras placas de cámara eligiendo una configuración de pines distinta. Consulta el Stream Video with with XIAO-ESP32-S3-Sense y el Stream Video with ESP32-WROVER CAM, por ejemplo.

La resolución se establece con el valor definido anteriormente. El número de buffers se fija en 2, permitiendo double buffering. La calidad de compresión JPEG se establece al 80%. Finalmente, la cámara se inicia con Camera.begin(cfg).

Función initWifi

La inicialización de Wi‑Fi se realiza en la función initWifi():

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

Se desactiva la persistencia de Wi‑Fi para evitar almacenar credenciales en la flash. El ESP32 se pone en modo estación y comienza a conectarse a la red Wi‑Fi especificada. El bucle espera hasta establecer la conexión. Una vez conectado, el dispositivo imprime la URL del stream en el Serial Monitor:

Stream at: http://192.168.2.42/video

Necesitarás usar esta URL en la aplicación cliente (app Python) que graba la secuencia de vídeo en un archivo.

Ten en cuenta que puedes probar el streaming pegando esta URL en la barra de direcciones de tu navegador web. Pero asegúrate de usar el cliente o el navegador, NO ambos, ya que solo un cliente a la vez puede conectarse al stream.

Función initServer

El servidor HTTP se configura en la función initServer():

void initServer() {
  server.on(URL, handleStream);
  server.begin();
}

Esta función registra el endpoint /video con handleStream() y arranca el servidor.

Función setup

La función setup() prepara el ESP32-CAM para su funcionamiento:

void setup() {
  Serial.begin(115200);
  pinMode(pinPIR, INPUT);
  pinMode(pinFlash, OUTPUT);
  initWifi();
  initCamera();
  initServer();
}

La comunicación serial se inicializa a 115200 baudios. El pin del sensor PIR se configura como entrada y el pin del LED de flash como salida. Se inicializan, en orden, la conexión Wi‑Fi, la configuración de la cámara y el servidor web.

Función loop

Por último, la función loop() contiene la lógica principal en tiempo de ejecución:

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

Esta función llama continuamente a server.handleClient() para gestionar las peticiones HTTP entrantes de los clientes.

Y eso es todo del lado del servidor. En la siguiente sección implementamos el cliente que recibe el stream de vídeo y lo guarda en un archivo.

Grabación de vídeo

El cliente se implementa como un script Python que se conecta al stream MJPEG proporcionado por un ESP32-CAM y escribe el vídeo entrante en un archivo local .aviarchivo. La grabación se segmenta automáticamente en archivos diarios: uno por día del calendario. El script además puede mostrar opcionalmente el stream en una ventana.

A continuación está el código completo. Échale un vistazo rápido y luego entraremos en detalles:

import cv2
import requests
import numpy as np
import datetime
import os

# Replace with your ESP32-CAM stream URL
stream_url = 'http://192.168.2.42/video' 

# match your ESP32-CAM setting
frame_width = 800  
frame_height = 600
fps = 10
fourcc = cv2.VideoWriter_fourcc(*'XVID')

def get_output_filename():
    date_str = datetime.datetime.now().strftime("%Y-%m-%d")
    return f'recording_{date_str}.avi'

# Initialize variables
current_date = datetime.date.today()
output_file = get_output_filename()
out = cv2.VideoWriter(output_file, fourcc, fps, (frame_width, frame_height))

# Connect to MJPEG stream
print(f"Connecting to {stream_url}")
stream = requests.get(stream_url, stream=True)

if stream.status_code != 200:
    print(f"Failed to connect to ESP32-CAM. Status code: {stream.status_code}")
    exit()

bytes_buffer = b''
try:
    for chunk in stream.iter_content(chunk_size=1024):
        bytes_buffer += chunk
        a = bytes_buffer.find(b'\xff\xd8')  # JPEG start
        b = bytes_buffer.find(b'\xff\xd9')  # JPEG end
        if a != -1 and b != -1 and b > a:
            jpg = bytes_buffer[a:b+2]
            bytes_buffer = bytes_buffer[b+2:]

            # Decode the JPEG image to OpenCV format
            img_array = np.frombuffer(jpg, dtype=np.uint8)
            frame = cv2.imdecode(img_array, cv2.IMREAD_COLOR)

            if frame is not None:
                new_date = datetime.date.today()
                if new_date != current_date:
                    out.release()
                    current_date = new_date
                    output_file = get_output_filename()
                    out = cv2.VideoWriter(output_file, fourcc, fps, (frame_width, frame_height))
                    print(f"Started new file: {output_file}")

                out.write(frame)

                # Optional: Show live video
                cv2.imshow('ESP32-CAM Stream', frame)
                if cv2.waitKey(1) & 0xFF == ord('q'):
                    break
except KeyboardInterrupt:
    pass
finally:
    out.release()
    cv2.destroyAllWindows()
    print(f"Video saved to {output_file}")

Librerías

El script comienza importando las librerías necesarias:

import cv2
import requests
import numpy as np
import datetime
import os

El módulo cv2 (OpenCV) se usa para manejar operaciones de imagen y vídeo. La librería requests permite al script establecer la conexión con el stream HTTP MJPEG del ESP32-CAM. La librería numpy se utiliza para convertir los bytes en bruto en una matriz de imagen. El módulo datetime gestiona la hora y fecha, en especial para organizar los archivos de vídeo diarios. Por último, se importa os para posibles manipulaciones de rutas, aunque no se usa en este script.

Tendrás que instalar la cv2, requests y numpy librerías en tu ordenador, preferiblemente en un entorno virtual. Consulta el Object Detection with ESP32-CAM and YOLO para un ejemplo con más detalles.

Constantes

La dirección IP del ESP32-CAM se define en la stream_urlvariable:

stream_url = 'http://192.168.2.42/video'

Esta URL corresponde al endpoint definido en el sketch de Arduino (específicamente const char* URL = "/video"). El ESP32-CAM debe ser accesible en esta dirección IP para que el script funcione.

Las siguientes constantes definen la resolución de la cámara y los ajustes de grabación:

frame_width = 800  
frame_height = 600
fps = 10
fourcc = cv2.VideoWriter_fourcc(*'XVID')

Los valores frame_width y frame_height deben coincidir con la resolución establecida en el ESP32-CAM (esp32cam::Resolution::find(800, 600)). La variable fps especifica la tasa de frames, que debe coincidir con la configuración FRAMERATE del ESP32-CAM. La variable fourcc define el códec de vídeo; aquí se usa XVID, un códec común para archivos .avi.

Función get_output_filename

A continuación definimos una función auxiliar para generar nombres de archivo basados en la fecha actual:

def get_output_filename():
    date_str = datetime.datetime.now().strftime("%Y-%m-%d")
    return f'recording_{date_str}.avi'

Esta función usa el módulo datetime para crear una cadena con el formato YYYY-MM-DD, que luego se usa para nombrar el archivo de salida, p. ej. recording_2025-06-01. Esto asegura que cada grabación se guarde en un archivo con el nombre de la fecha en que se grabó y limita el tamaño de los archivos. De lo contrario podrías acabar con un archivo de vídeo enorme si la cámara se activa frecuentemente.

Variables

El script inicializa entonces variables para gestionar la segmentación diaria de vídeo:

current_date = datetime.date.today()
output_file = get_output_filename()
out = cv2.VideoWriter(output_file, fourcc, fps, (frame_width, frame_height))

La variable current_date almacena la fecha de hoy. La variable output_file contiene el nombre de archivo generado por get_output_filename(). El objeto out es un OpenCV VideoWriter, que se usa para escribir fotogramas individuales en el archivo .avi.

Conectar al stream

A continuación, el script se conecta al stream de vídeo del ESP32-CAM:

print(f"Connecting to {stream_url}")
stream = requests.get(stream_url, stream=True)

Se envía una petición GET al ESP32-CAM. El stream=Trueparámetro asegura que la respuesta se trate como streaming, permitiendo recibir datos en fragmentos.

Comprobar estado

Inmediatamente después del intento de conexión comprobamos el estado de la respuesta:

if stream.status_code != 200:
    print(f"Failed to connect to ESP32-CAM. Status code: {stream.status_code}")
    exit()

Si el código HTTP no es 200 (OK), el script imprime un mensaje de error y termina. En ese caso comprueba que el ESP32-CAM esté funcionando, conectado al Wi‑Fi y que la URL y la dirección IP del servidor y el cliente coincidan, p. ej. http://192.168.2.42/video

Buffer

El script inicializa a continuación un buffer de bytes vacío para almacenar los datos entrantes del stream:

bytes_buffer = b''

Este buffer se usará para acumular los datos del stream y extraer imágenes JPEG individuales.

Bucle principal

El bucle principal comienza en un bloque trybloque:

try:
    for chunk in stream.iter_content(chunk_size=1024):
        bytes_buffer += chunk

El script lee el stream en fragmentos de 1024 bytes y añade cada fragmento a bytes_buffer. Este enfoque trata el stream como una secuencia continua de bytes.

Dentro del bucle, el script busca los marcadores de inicio y fin de una imagen JPEG:

        a = bytes_buffer.find(b'\xff\xd8')  # JPEG start
        b = bytes_buffer.find(b'\xff\xd9')  # JPEG end

El formato JPEG comienza con la secuencia de bytes 0xFF 0xD8 y termina con 0xFF 0xD9. Estos marcadores permiten al script aislar imágenes JPEG completas del flujo de bytes.

Si ambos marcadores se encuentran y están en el orden correcto, el script extrae la imagen JPEG:

        if a != -1 and b != -1 and b > a:
            jpg = bytes_buffer[a:b+2]
            bytes_buffer = bytes_buffer[b+2:]

La porción jpg contiene la imagen JPEG en bruto. La parte procesada se elimina del buffer para que el script pueda procesar la siguiente imagen.

Los datos JPEG se convierten luego en una imagen OpenCV:

            img_array = np.frombuffer(jpg, dtype=np.uint8)
            frame = cv2.imdecode(img_array, cv2.IMREAD_COLOR)

Los bytes en bruto se transforman en un array NumPy de enteros sin signo de 8 bits. Este array se decodifica con cv2.imdecode() en un fotograma de imagen adecuado para mostrar o guardar.

Si se obtiene un fotograma válido, el script comprueba si la fecha ha cambiado:

            if frame is not None:
                new_date = datetime.date.today()
                if new_date != current_date:
                    out.release()
                    current_date = new_date
                    output_file = get_output_filename()
                    out = cv2.VideoWriter(output_file, fourcc, fps, (frame_width, frame_height))
                    print(f"Started new file: {output_file}")

Compara la fecha actual con la almacenada previamente. Si ha empezado un nuevo día, el archivo de vídeo actual se cierra con out.release(). Se genera un nuevo nombre de archivo y se inicializa un nuevo VideoWriter para empezar a grabar en el nuevo archivo.

El fotograma actual se escribe a continuación en el vídeo de salida:

                out.write(frame)

Además, el script muestra el vídeo en una ventana de vista previa en tiempo real:

                cv2.imshow('ESP32-CAM Stream', frame)
                if cv2.waitKey(1) & 0xFF == ord('q'):
                    break

El fotograma se muestra con la función imshow() de OpenCV. Si el usuario pulsa la tecla ‘q’, el bucle termina y se detiene el stream.

Manejo de excepciones

De forma similar, si el cliente se detiene por una interrupción de teclado (p. ej. Ctrl+C), el script asegura en el bloque finally que el archivo de salida y la ventana de OpenCV se cierren:

except KeyboardInterrupt:
    pass
finally:
    out.release()
    cv2.destroyAllWindows()
    print(f"Video saved to {output_file}")

¡Y ese es el servidor! Si ahora ejecutas el ESP32-CAM (serve) y luego arrancas el cliente (app Python), tendrás un sistema de vigilancia que captura vídeo cuando se detecta movimiento y escribe la secuencia a un archivo con sello de fecha para su inspección posterior.

Conclusiones

En este tutorial aprendiste a construir un sistema de cámara de vigilancia activado por movimiento con el ESP32-CAM, un sensor de movimiento PIR y un PC.

El ESP32-CAM opera como un motion-activated servidor de vídeo MJPEG streaming server. Cuando un cliente visita el /videoURL, el servidor comienza a enviar fotogramas de vídeo. Sin embargo, los fotogramas solo se capturan y envían cuando el sensor PIR detecta movimiento. Para más información sobre detección de movimiento consulta el Motion Activated ESP32-CAM tutorial.

Además de iniciar el stream de vídeo, se activa el LED de flash para ayudar con la iluminación. Si tienes problemas para controlar el LED de flash, echa un vistazo al Control ESP32-CAM Flash LED tutorial.

En el lado del cliente un script Python recibe el stream MJPEG activado por movimiento del ESP32-CAM y escribe cada fotograma en un .aviarchivo. Crea un nuevo archivo cada día natural, lo que facilita organizar y archivar las grabaciones. El script también muestra opcionalmente la transmisión en vivo y puede interrumpirse de forma limpia por el usuario.

Si quieres recibir notificaciones cada vez que se detecte movimiento, echa un vistazo a nuestro ESP32 send Telegram Message tutorial, que te enseña a enviar mensajes a la app Telegram en tu teléfono.

Si quieres detectar objetos dentro del stream de vídeo, mira el Object Detection with ESP32-CAM and YOLO tutorial. Y si necesitas más pines GPIO para otras entradas o salidas, el More GPIO pins for ESP32-CAM puede tener algunos consejos útiles.

No dudes en dejar tus preguntas en la sección de comentarios.

¡Feliz bricolaje ; )