En este tutorial aprenderás cómo construir un ESP32-CAM (AI-Thinker) activado por movimiento y alimentado por batería, que toma fotos cada vez que un sensor PIR detecta movimiento. Hablaremos sobre los diferentes sensores PIR, opciones de alimentación por batería y los problemas más comunes a tener en cuenta.
Este proyecto es perfecto para aplicaciones de vigilancia, seguridad o incluso para el monitoreo de fauna silvestre. Si prefieres grabar vídeos en vez de tomar fotos individuales, echa un vistazo a nuestro Surveillance Camera with ESP32-CAM tutorial.
Componentes necesarios
A continuación encontrarás los componentes necesarios para montar el proyecto. Es posible que ya tengas algunos, como el cable USB, la tarjeta micro SD o el lector de tarjetas SD. No hace falta comprarlos si ya los tienes, ya que no son específicos para este proyecto. Eso sí, la tarjeta SD no debe ser mayor de 16 GB; las más pequeñas (4GB u 8GB) funcionan perfectamente.
Necesitas o bien el USB-TTL Shield o el FTDI USB-TTL Adapter, pero no ambos. Aunque el módulo ESP32-CAM viene con el USB-TTL Shield, el FTDI USB-TTL Adapter es más cómodo para programar.
He listado dos tipos diferentes de sensores de movimiento. Si buscas un tamaño pequeño, elige el AM312. Pero si quieres que la cámara se active solo de noche, elige el HC-SR501, ya que se le puede añadir fácilmente un sensor de luz.

ESP32-CAM con USB-TTL Shield

FTDI USB-TTL Adapter

Juego de cables Dupont

Sensor de movimiento PIR AM312

Sensor de movimiento PIR HC-SR501

Tarjeta MicroSD 16GB

Lector de tarjetas SD

Cable de datos USB

Arduino IDE
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.
Fundamentos de los sensores de movimiento PIR
Existen muchos métodos y sensores para detectar objetos en movimiento, pero los más comunes son los sensores PIR ( P asivo I nfra R ojo) que detectan el movimiento de objetos calientes. Un sensor PIR típico consta de dos elementos piroeléctricos y una lente de Fresnel .

Elemento piroeléctrico
Los elementos piroeléctricos detectan el calor y están dispuestos y cableados de forma que un objeto en movimiento genera una señal de salida positiva o negativa. Si el objeto no se mueve, las señales de los dos elementos piroeléctricos se cancelan entre sí.
Lente de Fresnel
La lente de Fresnel (la tapa blanca) concentra la débil radiación térmica de un cuerpo humano y hace que el sensor sea más sensible. Puedes usar un sensor PIR sin la lente de Fresnel, pero será menos sensible y puede activarse falsamente con más frecuencia.

Ten en cuenta que los elementos PIR no funcionan detrás de cristales (ventanas) y no pueden detectar objetos que tengan la misma temperatura que la habitación donde están instalados. Además, asegúrate de montar el sensor en posición horizontal para obtener la máxima sensibilidad. Para más información, echa un vistazo here .
Tiempo de calibración
Los sensores PIR suelen tener un tiempo de calibración durante el cual se ajustan a la temperatura ambiente actual. Esto puede tardar hasta 15 segundos y durante ese tiempo pueden producirse activaciones falsas. Evita moverte durante la calibración para que el sensor se ajuste correctamente.
Tiempo de bloqueo y retardo
Además, los sensores PIR tienen un bloqueo y un retardo de tiempo. El tiempo de bloqueo o de cierre de un sensor PIR es el periodo durante el cual el sensor ignora cualquier movimiento detectado después de una activación inicial. Esto evita que el sensor se active constantemente por movimientos continuos, permitiéndole reiniciarse y evitar falsas alarmas. Por ejemplo, si el tiempo de bloqueo está ajustado a 30 segundos, el sensor no detectará ningún movimiento durante ese periodo, aunque haya movimiento en su rango de detección.
El tiempo de retardo , por otro lado, es el intervalo que el sensor espera antes de activar una acción tras detectar movimiento. Este retardo puede ser útil en situaciones donde no quieres una respuesta inmediata, como encender una luz solo después de unos segundos de movimiento continuo. Por ejemplo, si el tiempo de retardo está ajustado a 10 segundos, el sensor esperará ese tiempo antes de activar un dispositivo o enviar una señal.
Algunos módulos de sensores PIR permiten ajustar el tiempo de bloqueo y de retardo, mientras que otros tienen tiempos fijos. Existen muchos módulos de sensores PIR, pero la mayoría se basan en el sensor PIR AS312, que veremos en la siguiente sección.
Sensor PIR AS312
La imagen de abajo muestra un sensor PIR AS312 y una vista superior con la ubicación de los dos elementos piroeléctricos marcada. La lente de Fresnel ha sido retirada.

El AS312 funciona entre 2.7 y 3.3V y consume muy poca corriente (15 μA). Tiene un tiempo de bloqueo y retardo de 2,3 segundos.
El AS312 ya integra mucha electrónica (ver Datasheet ), pero necesitarás una tensión de alimentación estable (regulador de voltaje) y algunos componentes adicionales para usarlo en un circuito. Por eso existen varios módulos de sensores PIR que añaden estos componentes necesarios y ofrecen funciones extra como tiempos de retardo superiores a 2,3 segundos y control de sensibilidad.
En la siguiente sección veremos tres módulos de sensores PIR comunes y te daré algunas recomendaciones sobre cuáles usar con el ESP32-CAM.
Módulo PIR AM312
El módulo PIR AM312 es el más simple y pequeño de los tres módulos de sensores PIR que comentamos aquí. Tiene un tiempo de retardo y bloqueo fijo de unos 2 segundos y no permite ajustar la sensibilidad.

Consiste únicamente en el sensor PIR AS312, un HT7530 regulador de voltaje y una resistencia de salida (20K). Consulta el esquema interno más abajo. Ten en cuenta que no tiene protección de polaridad y es fácil dañar el sensor si lo conectas al revés.

El módulo tiene tres pines: alimentación positiva (VIN), tierra (GND) y salida (OUT). El voltaje de alimentación va de 2,7 a 12 voltios y la salida es de 3,3 voltios (TTL) cuando se detecta movimiento. El alcance de detección es de 3-5m y el ángulo de detección es de unos 100°.
Este es el sensor que recomiendo para el ESP32-CAM activado por movimiento. Tiene un tiempo de retardo y bloqueo corto (2 seg), lo que nos permite tomar fotos cada 4 segundos aproximadamente. Se puede conectar directamente a un pin GPIO del ESP32-CAM y funcionó de forma fiable en mis pruebas.
Sin embargo, si necesitas mayor alcance y detección solo por la noche, te recomiendo el HC-SR501, que veremos en la siguiente sección.
Módulo PIR HC-SR501
El HC-SR501 es el sensor de movimiento que más se encuentra en aplicaciones domésticas, como luces nocturnas activadas por movimiento. Es fácil de reconocer por su lente de Fresnel grande y característica. Mira la imagen de abajo.

Esquema del HC-SR501
El esquema del módulo HC-SR501 es mucho más complejo que el del AM312, ya que permite ajustar la sensibilidad (alcance) y el tiempo de retardo. Además, utiliza el RE200B PIR Sensor en lugar del sensor PIR AS312. Consulta el esquema más abajo.

El tiempo de retardo se puede ajustar desde unos 0,3 segundos hasta 300 segundos. Aun así, habrá un tiempo de bloqueo de unos 2,5 segundos. Sin embargo, el HC-SR501 te permitirá tomar fotos un poco más seguido que el AS312 (3 segundos frente a 4 segundos).
Ajustar el retardo mínimo
Como vamos a controlar el tiempo de retardo por software, queremos poner el retardo por hardware al mínimo. Para ello, gira el potenciómetro izquierdo en sentido antihorario hasta la posición que se muestra abajo. Así tendrás el retardo mínimo de unos 0,3 segundos.

Lo mejor es probar esto sin las complicaciones de un ESP32-CAM al principio. Consulta nuestro tutorial How to use HC-SR501 PIR Motion Sensor . Tiene un circuito de ejemplo sencillo que muestra cómo encender un LED cuando el sensor detecta movimiento.
El otro potenciómetro del módulo HC-SR501 te permite cambiar el alcance de detección (sensibilidad) de unos 2m hasta un máximo de unos 7m. El HC-SR501 también tiene un jumper para cambiar el modo de disparo. Para más detalles sobre esto, consulta nuestro tutorial How to use HC-SR501 PIR Motion Sensor o este Datasheet of the HC-SR501 .
Activación nocturna mediante LDR
La mayor ventaja (aparte del mayor alcance) del HC-SR501 frente al AM312 es que puedes añadirle una LDR (sensor de luz) para que el sensor solo se active de noche. Esto es ideal para monitorear animales nocturnos. Esta función no está bien documentada, pero si quitas la lente de Fresnel verás dos puertos de orificio pasante marcados como RL y RT. Mira la imagen de abajo.

El RT (Resistor Temperature) permite añadir un termistor, lo que hace que el HC-SR501 sea menos propenso a falsas alarmas por cambios de temperatura ambiente. El RL (Resistor Light) permite añadir una LDR (resistencia dependiente de la luz), lo que asegura que la detección de movimiento solo se active de noche.
El valor específico de la resistencia para la LDR dependerá de cuánta oscuridad quieres que haya para que el sensor se active. Esto tendrás que ajustarlo por prueba y error. Aquí tienes un enlace a un LDR kit with various resistor values que puedes probar. Como referencia, el divisor de tensión en el esquema mostrado arriba tiene una resistencia de 1M Ω . Probablemente quieras elegir una LDR de un valor similar para empezar.
Entradas y salidas
Las entradas y salidas del HC-SR501 son básicamente las mismas que las del AM312. Debes suministrar un voltaje de entre 4,5V y 20V en los pines VCC y GND, y el pin de salida se pondrá en alto (3,3V) cuando detecte movimiento. La imagen de abajo muestra el pinout.

En resumen, si quieres un sensor PIR pequeño, simple y barato, elige el AM312. Funcionará perfectamente. Si quieres mayor alcance y limitar la detección de movimiento solo a la noche, elige el HC-SR501.
En la siguiente sección comentaré brevemente el HC-SR505, que resultó no ser adecuado y no lo recomiendo para esta aplicación.
Módulo PIR HC-SR505
El módulo PIR HC-SR505 es un poco más largo que el AM312, pero usa el mismo sensor PIR AS312 y tiene el mismo pinout. Mira la imagen de abajo del HC-SR505

El voltaje de alimentación va de 4,5V a 20V y la salida es TTL (3,3V). El alcance de detección es de 3-4 metros (9-12 pies) y el tiempo de retardo es fijo, de unos 8 segundos.
Además del largo tiempo de retardo, que te limitaría a tomar una foto solo cada 8 segundos, también encontré que el sensor daba problemas cuando usé un power bank de 5V como fuente. Probé varios sensores del mismo tipo (HC-SR505), pero la salida siempre permanecía en alto. Sospecho que esto se debe al ripple de alta frecuencia del power bank (hablaremos más de esto después).
El HC-SR501 y el AM312 no tuvieron problemas con este power bank, pero el módulo PIR HC-SR505 se negó a funcionar correctamente. Además, incluso usando el ESP32-CAM con una batería de 9V o un par de baterías 18650, que no tienen ripple, el HC-SR505 tampoco funcionó y siempre estaba activado. De nuevo, sospecho que hay alguna interferencia de alta frecuencia del ESP32-CAM con el HC-SR505.
En resumen, el módulo PIR HC-SR505 no es adecuado para tomar fotos activadas por movimiento usando el ESP32-CAM. Sin embargo, he usado el módulo PIR HC-SR505 en otros proyectos y no tuve problemas en esas aplicaciones.
A continuación, vamos a ver rápidamente el propio módulo ESP32-CAM.
Presentando el AI-Thinker ESP32-CAM
La placa de desarrollo AI-Thinker ESP32-CAM es un módulo compacto que combina un chip ESP32-S, una cámara, un flash integrado y una ranura para tarjeta microSD. La placa tiene Wi-Fi y Bluetooth integrados y soporta una cámara OV2640 u OV7670 de hasta 2 megapíxeles de resolución.


Puedes encontrar más detalles sobre la placa ESP32-CAM y especialmente cómo subirle código en nuestro tutorial Programming the ESP32-CAM . Léelo sin falta, ya que subir código a la placa ESP32-CAM tiene su truco. En este tutorial me centraré en implementar la activación por movimiento para el ESP32-CAM.
Pinout
La siguiente imagen muestra el pinout del ESP32-CAM. Ten en cuenta que, en teoría, el ESP32-CAM puede funcionar a 3,3V, pero se han reportado comportamientos inestables. Puedes usar de 5V hasta 15V, pero no recomendaría pasar de 12V, ya que el regulador de voltaje podría calentarse bastante. Más adelante hablaremos en detalle sobre las diferentes opciones de alimentación.

El pin P_OUT está etiquetado como VCC en algunas placas. Es un pin de salida de energía que entrega 3,3V o 5V dependiendo de un solder pad . ¡No puedes usar este pin para alimentar la placa! Usa el pin de 5V para eso.
Ten en cuenta que la mayoría de los pines GPIO del ESP32-CAM ya están en uso por la cámara y el lector de tarjetas SD. Además, deberías evitar usar GPIO1, GPIO3 y GPIO0, ya que se necesitan para programar la placa. Además, GPIO0 está conectado al pin XCLK de la cámara y debe dejarse sin conectar cuando el ESP32 está en funcionamiento.
Por último, los pines GPIO 2, 4, 12, 13, 14 y 15 están parcialmente usados por el lector de tarjetas SD. Si no usas el lector de tarjetas SD, están disponibles como GPIO. Para más detalles, consulta nuestro More GPIO pins for ESP32-CAM tutorial.
En lo que sigue, usaremos el GPIO 13 para la activación por movimiento. Los detalles sobre cómo conectar un sensor de movimiento PIR al ESP32-CAM se describen en la siguiente sección.
Conectando el sensor PIR al ESP32-CAM
Conectar el módulo sensor PIR AM312 al ESP32-CAM es muy sencillo. 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).

Puedes suministrar más de 5V para alimentar la placa. No te preocupes. Un regulador de voltaje está conectado al pin de 5V y reducirá la tensión de entrada al nivel requerido por la placa. Pero no pases de 12V, o el regulador podría calentarse.
De igual forma, conecta el módulo sensor PIR AM312 a la fuente de alimentación (cables rojo y azul). 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).
No conectes el AM312 al pin de 3,3V para la alimentación. En teoría debería funcionar, pero en la práctica tuve problemas de detección al usar el AM312 a 3,3V. Si usas el módulo sensor PIR HC-SR501 en vez del AM312, conéctalo igual (GND a GND, VCC a 5V-12V).
Tanto el AM312 como el HC-SR501 pueden manejar voltajes de alimentación de 5V a 12V. Así que puedes alimentar el ESP32-CAM y el sensor PIR con la misma fuente (batería) y el mismo voltaje.
Circuito con transistor
Ten en cuenta que hay muchos tutoriales que proponen un circuito con transistor al conectar un sensor PIR al ESP32-CAM. ¡Esto NO es necesario! La salida del sensor PIR (incluso la del AM312) puede manejar fácilmente la entrada GPIO. Conectar el sensor PIR directamente al ESP32-CAM está bien, funciona y es mucho más sencillo.
Subida de código
Si usas un FTDI USB-TTL Adapter puedes dejar el sensor PIR conectado mientras subes y pruebas el código. Si usas el USB-TTL Shield, tendrás que desconectar y volver a conectar el sensor PIR cada vez que subas y ejecutes nuevo código, lo cual es bastante molesto.
Por eso recomiendo usar un FTDI USB-TTL Adapter. La imagen de abajo muestra cómo queda. Ni siquiera necesitas una breadboard; solo unos cables Dupont.

Los detalles sobre cómo conectar el FTDI USB-TTL Adapter y cómo subir el código los encontrarás en nuestro tutorial Programming the ESP32-CAM . En la siguiente sección te muestro el código para el ESP32-CAM activado por movimiento.
Código para el ESP32-CAM activado por movimiento
El siguiente código es una implementación completa para un ESP32-CAM activado por movimiento. Échale un vistazo rápido primero para tener una idea general antes de entrar en los detalles.
Este código pone el ESP32-CAM en deep sleep y espera una señal en el GPIO13, indicando que el sensor PIR ha detectado movimiento. Si se detecta movimiento, el ESP32-CAM se despierta, toma una foto, la guarda con un número consecutivo en la tarjeta SD y vuelve a dormir.
// Motion activated ES32-CAM
// Makerguides.com
#include "esp_camera.h"
#include "soc/rtc_cntl_reg.h"
#include "driver/rtc_io.h"
#include "SD_MMC.h"
#include "EEPROM.h"
// CAMERA_MODEL_AI_THINKER
#define PWDN_GPIO_NUM 32
#define RESET_GPIO_NUM -1
#define XCLK_GPIO_NUM 0
#define SIOD_GPIO_NUM 26
#define SIOC_GPIO_NUM 27
#define Y9_GPIO_NUM 35
#define Y8_GPIO_NUM 34
#define Y7_GPIO_NUM 39
#define Y6_GPIO_NUM 36
#define Y5_GPIO_NUM 21
#define Y4_GPIO_NUM 19
#define Y3_GPIO_NUM 18
#define Y2_GPIO_NUM 5
#define VSYNC_GPIO_NUM 25
#define HREF_GPIO_NUM 23
#define PCLK_GPIO_NUM 22
void configCamera() {
camera_config_t config;
config.ledc_channel = LEDC_CHANNEL_0;
config.ledc_timer = LEDC_TIMER_0;
config.pin_d0 = Y2_GPIO_NUM;
config.pin_d1 = Y3_GPIO_NUM;
config.pin_d2 = Y4_GPIO_NUM;
config.pin_d3 = Y5_GPIO_NUM;
config.pin_d4 = Y6_GPIO_NUM;
config.pin_d5 = Y7_GPIO_NUM;
config.pin_d6 = Y8_GPIO_NUM;
config.pin_d7 = Y9_GPIO_NUM;
config.pin_xclk = XCLK_GPIO_NUM;
config.pin_pclk = PCLK_GPIO_NUM;
config.pin_vsync = VSYNC_GPIO_NUM;
config.pin_href = HREF_GPIO_NUM;
config.pin_sscb_sda = SIOD_GPIO_NUM;
config.pin_sscb_scl = SIOC_GPIO_NUM;
config.pin_pwdn = PWDN_GPIO_NUM;
config.pin_reset = RESET_GPIO_NUM;
config.xclk_freq_hz = 20000000;
config.pixel_format = PIXFORMAT_JPEG;
config.frame_size = FRAMESIZE_UXGA;
config.jpeg_quality = 10;
config.fb_count = 2;
esp_err_t err = esp_camera_init(&config);
sensor_t* s = esp_camera_sensor_get();
s->set_brightness(s, 0);
s->set_contrast(s, 0);
s->set_saturation(s, 0);
s->set_special_effect(s, 0);
s->set_whitebal(s, 1);
s->set_awb_gain(s, 1);
s->set_wb_mode(s, 0);
s->set_exposure_ctrl(s, 1);
s->set_aec2(s, 0);
s->set_ae_level(s, 0);
s->set_aec_value(s, 300);
s->set_gain_ctrl(s, 1);
s->set_agc_gain(s, 0);
s->set_gainceiling(s, (gainceiling_t)0);
s->set_bpc(s, 0);
s->set_wpc(s, 1);
s->set_raw_gma(s, 1);
s->set_lenc(s, 1);
s->set_hmirror(s, 0);
s->set_vflip(s, 0);
s->set_dcw(s, 1);
s->set_colorbar(s, 0);
}
unsigned int incCounter() {
unsigned int cnt = 0;
EEPROM.get(0, cnt);
EEPROM.put(0, cnt + 1);
EEPROM.commit();
return cnt;
}
void enableFlash(bool enable) {
if (enable) {
rtc_gpio_hold_dis(GPIO_NUM_4);
} else {
pinMode(GPIO_NUM_4, OUTPUT);
digitalWrite(GPIO_NUM_4, LOW);
rtc_gpio_hold_en(GPIO_NUM_4);
}
}
void skipPictures(int n) {
for(int i=0; i<n; i++) {
camera_fb_t* fb = esp_camera_fb_get();
esp_camera_fb_return(fb);
}
}
void takePicture() {
camera_fb_t* fb = esp_camera_fb_get();
unsigned int cnt = incCounter();
String path = "/img" + String(cnt) + ".jpg";
File file = SD_MMC.open(path.c_str(), FILE_WRITE);
file.write(fb->buf, fb->len);
file.close();
esp_camera_fb_return(fb);
}
void deepSleep(int atleast) {
esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON);
rtc_gpio_pulldown_en(GPIO_NUM_13);
rtc_gpio_pullup_dis(GPIO_NUM_13);
esp_sleep_enable_ext0_wakeup(GPIO_NUM_13, 1);
delay(atleast);
esp_deep_sleep_start();
}
void setup() {
WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0);
enableFlash(true);
EEPROM.begin(8);
SD_MMC.begin();
configCamera();
skipPictures(10);
takePicture();
enableFlash(false);
deepSleep(5000);
}
void loop() {
}
Vamos a desglosar el código paso a paso para entender cómo funciona la configuración de la cámara y el proceso de captura de imágenes.
Includes
En la primera parte incluimos las librerías necesarias para controlar la cámara, la tarjeta SD y la EEPROM. También necesitamos algunas funciones relacionadas con el reloj en tiempo real (rtc) durante el deep sleep.
#include "esp_camera.h" #include "soc/rtc_cntl_reg.h" #include "driver/rtc_io.h" #include "SD_MMC.h" #include "EEPROM.h"
Constantes
Existen varios modelos de placas ESP32-CAM. Este código es específico para el modelo AI-Thinker, pero funcionará bien en la mayoría de placas de cámara basadas en ESP32, siempre que tengan PSRAM (ver model definitions here ).
En la sección de código de abajo definimos los pines específicos para el modelo AI-Thinker. Si tienes otra placa ESP32-CAM probablemente tendrás que cambiar estas constantes. Aquí puedes encontrar el pin definitions para otros modelos soportados.
// CAMERA_MODEL_AI_THINKER #define PWDN_GPIO_NUM 32 #define RESET_GPIO_NUM -1 #define XCLK_GPIO_NUM 0 #define SIOD_GPIO_NUM 26 #define SIOC_GPIO_NUM 27 #define Y9_GPIO_NUM 35 #define Y8_GPIO_NUM 34 #define Y7_GPIO_NUM 39 #define Y6_GPIO_NUM 36 #define Y5_GPIO_NUM 21 #define Y4_GPIO_NUM 19 #define Y3_GPIO_NUM 18 #define Y2_GPIO_NUM 5 #define VSYNC_GPIO_NUM 25 #define HREF_GPIO_NUM 23 #define PCLK_GPIO_NUM 22
Configuración de la cámara
La función configCamera() configura la cámara con ajustes específicos como el canal LEDC, pines para las líneas de datos, señales de reloj y ajustes de imagen como formato, tamaño y calidad. Si no te convence la calidad de imagen del ESP32-CAM, prueba a modificar estos parámetros.
void configCamera() {
sensor_t* s = esp_camera_sensor_get();
s->set_brightness(s, 0);
s->set_contrast(s, 0);
...
s->set_colorbar(s, 0);
}
Incrementar contador
La función incCounter() lee e incrementa un contador almacenado en la memoria EEPROM. Este contador se usa para nombrar las imágenes capturadas de forma secuencial. Cada vez que se llama, incrementa el contador. El contador debe almacenarse en EEPROM para que no se pierda durante el deep sleep.
unsigned int incCounter() {
unsigned int cnt = 0;
EEPROM.get(0, cnt);
EEPROM.put(0, cnt + 1);
EEPROM.commit();
return cnt;
}
El código es muy sencillo. Primero obtenemos el valor actual del contador con EEPROM.get(0, cnt) , luego almacenamos el contador incrementado con EEPROM.put(0, cnt + 1 ) y guardamos el cambio usando EEPROM.commit() . Finalmente, devolvemos el contador actual. La próxima vez que se llame la función, devolverá el contador incrementado que acabamos de guardar y lo incrementará de nuevo.
Ten en cuenta que hay que reservar memoria para el contador en la EEPROM. Esto se hace más adelante, llamando a EEPROM.begin(8) en la función setup(). El contador es un valor entero sin signo de 4 bytes. Pero por si acaso, reservo 8 bytes de memoria.
Un contador de 4 bytes sin signo puede contar hasta 4.294.967.295 antes de desbordarse. Eso es más que suficiente para enumerar todas las imágenes que queramos guardar en la tarjeta SD.
Control del flash
La función enableFlash() activa o desactiva el flash según el parámetro enable . Este es un punto delicado. El problema con el ESP32-CAM es que al escribir en la tarjeta SD, el LED del flash se enciende y, al entrar en deep sleep después, el LED del flash se queda encendido.
Simplemente escribir LOW en GPIO4 , donde está conectado el LED del flash, no es suficiente. Durante el deep sleep se pierde ese nivel de salida. Para mantener GPIO4 bajo durante el deep sleep, hay que llamar a rtc_gpio_hold_en(GPIO_NUM_4) , que le dice al ESP32 que preserve el estado del GPIO4 durante el deep sleep.
Pero GPIO4 también forma parte de la interfaz de la tarjeta SD, así que no podemos dejarlo congelado en ese nivel. Así que, cuando el ESP32-CAM se despierta, desactivamos el hold de GPIO4 mediante rtc_gpio_hold_dis(GPIO_NUM_4) y dejamos que el ESP32-CAM lo controle para tomar la foto y escribir en la tarjeta SD.
Antes de poner el ESP32-CAM en deep sleep, llamamos a enableFlash(false) , que apaga el LED del flash y preserva ese estado durante el deep sleep.
void enableFlash(bool enable) {
if (enable) {
rtc_gpio_hold_dis(GPIO_NUM_4);
} else {
pinMode(GPIO_NUM_4, OUTPUT);
digitalWrite(GPIO_NUM_4, LOW);
rtc_gpio_hold_en(GPIO_NUM_4);
}
}
Saltar imágenes
La función SkipPictures(n) captura n imágenes y las descarta. El motivo es el siguiente: la cámara tiene varias funciones automáticas integradas, como el balance de blancos automático y otras, que tardan un poco y varios fotogramas en ajustarse al entorno. Tras un reinicio desde deep sleep, hay que darle tiempo a la cámara para que se ajuste.
void skipPictures(int n) {
for(int i=0; i<n; i++) {
camera_fb_t* fb = esp_camera_fb_get();
esp_camera_fb_return(fb);
}
}
Si tomas la primera imagen tras un reinicio, verás que la calidad es horrible. Normalmente tiene un fuerte tinte azul, o es demasiado oscura o brillante. Sin embargo, tras descartar los primeros fotogramas, normalmente (aunque no siempre) se obtienen fotos de buena calidad.
También podrías intentar desactivar el balance de blancos automático y otras funciones automáticas en los ajustes de la cámara, pero entonces necesitarás un entorno bastante estable para poder ajustar el resto de parámetros. Si no, será difícil conseguir fotos de buena calidad.
Tomar foto
La función takePicture() captura una imagen usando la cámara y la guarda en la tarjeta SD con un nombre de archivo único basado en el valor del contador. Primero creamos un frame buffer fb llamando a esp_camera_fb_get() . Luego creamos un nombre de archivo único para la foto y guardamos el frame buffer como archivo JPG (img{cnt}.jpg) en la tarjeta SD. Finalmente, liberamos la memoria usada por el frame buffer llamando a esp_camera_fb_return .
void takePicture() {
camera_fb_t* fb = esp_camera_fb_get();
unsigned int cnt = incCounter();
String path = "/img" + String(cnt) + ".jpg";
File file = SD_MMC.open(path.c_str(), FILE_WRITE);
file.write(fb->buf, fb->len);
file.close();
esp_camera_fb_return(fb);
}
Deep Sleep
La función deepSleep() pone el ESP32 en modo deep sleep durante un tiempo especificado tras capturar la imagen. Esto ayuda a ahorrar energía cuando el dispositivo no está tomando fotos.
Las tres primeras líneas ( esp_sleep_pd_config, rtc_gpio_pulldown_en, rtc_gpio_pullup_dis ) activan la resistencia pull-down interna en GPIO13 , donde está conectado el sensor PIR. El código/ESP32-CAM funciona sin usar la resistencia pull-down interna, pero por seguridad la he activado aquí.
Después especificamos la fuente de activación mediante esp_sleep_enable_ext0_wakeup(GPIO_NUM_13, 1) . Esto significa que si GPIO13 se pone en alto (1 = el sensor PIR ha detectado movimiento), despertamos el ESP32-CAM del deep sleep.
Antes de entrar en deep sleep, delay durante unos segundos ( atleast ) para evitar tomar fotos demasiado seguido. Yo usé 5 segundos y no se puede ir mucho más rápido debido a los tiempos de bloqueo y retardo del propio sensor PIR. Pero puedes aumentar el tiempo, por ejemplo a 10 o 60 segundos, si quieres reducir la cantidad de fotos tomadas.
void deepSleep(int atleast) {
esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON);
rtc_gpio_pulldown_en(GPIO_NUM_13);
rtc_gpio_pullup_dis(GPIO_NUM_13);
esp_sleep_enable_ext0_wakeup(GPIO_NUM_13, 1);
delay(atleast);
esp_deep_sleep_start();
}
Función Setup
La función setup() es donde se une todo. Primero llamamos a WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0) para desactivar la detección de brownout . Si la fuente de alimentación es inestable o el ESP32-CAM consume demasiada energía al iniciar el WiFi o escribir en la tarjeta SD, esto evita un reinicio automático. Para más detalles, consulta esta entrada de blog Disabling the ESP32 Brownout detector .
Yo no tuve problemas con brownouts y probablemente puedas desactivar esa línea. La ventaja de la detección de brownout es que reduce el riesgo de corrupción de la tarjeta SD. La desventaja es que puede causar reinicios accidentales no deseados. Dependerá de la fuente de alimentación que uses cuál opción es mejor.
Después de especificar el manejo de brownout, activamos el LED del flash mediante enableFlash(true) . Luego reservamos 8 bytes de memoria en la EEPROM para guardar el contador de archivos, iniciamos la tarjeta SD con SD_MMC.begin() y configuramos la cámara.
Por último, tomamos la foto llamando a nuestra función takePicture() . Después desactivamos el LED del flash y mandamos el ESP32-CAM a deep sleep (al menos 5 segundos).
void setup() {
WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0);
enableFlash(true);
EEPROM.begin(8);
SD_MMC.begin();
configCamera();
takePicture();
enableFlash(false);
deepSleep(5000);
}
Función Loop
La función loop() está vacía en este código, ya que la funcionalidad principal está implementada en la función setup. El ESP32 ejecuta la función setup, luego entra en deep sleep, se despierta cuando se detecta movimiento y vuelve a ejecutar la función setup. La función loop nunca se ejecuta.
void loop() {
// Empty loop
}
Y eso es todo. Ya tienes el código completo para un ESP32-CAM activado por movimiento.
Cuando ejecutes el código, puede que notes que el contador de archivos empieza en un número arbitrario y no en cero. Puedes reiniciar el contador de archivos ejecutando el siguiente código.
Reiniciar contador de archivos
Subir y ejecutar el código de abajo reiniciará el contador de archivos almacenado en la dirección 0 a cero. Después tendrás que volver a subir el código original para tomar fotos.
#include "EEPROM.h"
void setup() {
EEPROM.begin(512);
unsigned int cnt = 0;
EEPROM.put(0, cnt);
EEPROM.commit();
}
void loop() {
}
Ya tenemos el código y el cableado, ahora hablemos de la alimentación para el proyecto.
ESP32-CAM alimentado por batería
A menudo querrás usar un ESP32-CAM activado por movimiento con batería, por ejemplo para monitoreo de fauna. Encontrar la fuente de alimentación adecuada puede ser complicado. A continuación te muestro cuatro opciones que probé.
Power Bank USB 1
La primera opción es usar un power bank USB común. Cualquiera que tenga móvil suele tener uno para recargarlo en caso de emergencia. Yo probé el power bank que ves abajo.

Este es un power bank de 30000mAh que podría alimentar el ESP32-CAM durante mucho tiempo. Sin embargo, este power bank tiene apagado automático. Si la carga en la salida es muy baja (<80mA), como ocurre cuando el ESP32-CAM entra en deep sleep (6mA), el power bank se apaga.
El ESP32-CAM funcionaba unos 10-15 segundos, tomaba algunas fotos y luego no volvía a encenderse. Me costó bastante tiempo darme cuenta de esto, pensando que el problema era el código o el cableado : (
Lección aprendida: no uses un power bank con apagado automático para esto.
Power Bank USB 2
Tenía un segundo power bank, el de la foto de abajo, con 10000mAh de capacidad.

Este power bank no se apaga automáticamente, pero al probarlo con el módulo PIR HC-SR505, no conseguí que el sensor funcionara bien. La salida del sensor siempre estaba en alto, incluso usando el módulo solo (sin el ESP32-CAM), solo con la fuente y un LED.
Al mirar con el osciloscopio, vi que este power bank tiene un ripple visible en su salida de 5V, con una frecuencia de unos 160Hz y una amplitud de 88mV. Sospecho que esto causó los problemas con el módulo PIR HC-SR505, ya que este sensor funcionó bien con una batería de 9V, por ejemplo.

Aunque el ESP32-CAM con un sensor PIR HC-SR501 o AM312 funcionó bien con este power bank en mis pruebas, me preocupaba el comportamiento a largo plazo. ¿Sería fiable este montaje durante varios días o semanas?

Creo que la mejor opción es usar una batería recargable normal, que no tenga el circuito interno de refuerzo de un power bank USB, ya que este es el que causa esos ripples en la salida.
Batería recargable de 9V
Para alimentar el ESP32-CAM y el sensor PIR puedes usar simplemente una batería de 9V. Aquí usé una batería recargable de 9V y 1000mAh de capacidad, conectada directamente al pin de 5V de la placa ESP32-CAM y al pin VCC del sensor PIR.

Este montaje funciona bien, pero la autonomía no es muy alta. Aunque el ESP32-CAM consume solo 6mA en deep sleep, puede llegar a 240mA cuando está activo. Así que en deep sleep podríamos funcionar durante 1000mAh / 6mA = 166,67 horas = 6,9 días, lo cual no está mal. Pero si tomamos fotos con frecuencia, la autonomía será mucho menor.
El vídeo corto de abajo demuestra que el ESP32-CAM con un módulo PIR AM312 puede funcionar con una batería de 9V.

En la siguiente sección veremos baterías de mayor capacidad para aumentar la autonomía.
Pack de baterías recargables 18650
Así que vamos a por una batería más grande. Una batería de litio 18650 tiene un voltaje nominal de 3,6V y capacidades de entre 1800mAh y 2800mAh. Dos en serie nos dan 7,2V de salida, que es un rango cómodo para el ESP32-CAM y permite cierta caída de voltaje.

Con una capacidad de 2800mAh podemos casi triplicar la autonomía (comparado con la batería de 1000mAh). Y si necesitas aún más capacidad, puedes añadir otro par de 18650 y duplicar de nuevo la autonomía.
Pero ten cuidado con la polaridad al conectar las baterías al circuito. Las 18650 pueden suministrar corrientes altas y yo quemé uno de mis sensores PIR por este error.
En la siguiente sección intento hacer una estimación más precisa de la autonomía.
Autonomía
Medí el consumo en deep sleep del ESP32-CAM con el sensor PIR AM312 conectado y obtuve 3,6mA en deep sleep. Cuando está activo y toma una foto, el consumo es de unos 120mA.
Si usamos las dos 18650 lithium-ion batteries en serie con una capacidad total de 2800mAh, obtenemos 2800mAh / 3,6mA = 777,7 horas = 32 días de autonomía en deep sleep. Eso es un mes, pero sin tomar ninguna foto.
Supongamos que tomamos 100 fotos en todo el periodo. Como el ESP32-CAM está activo durante 5 segundos cada vez que toma una foto, esto suma 100 * 120mA * 5seg / 60 = 1000mAh.
Si restamos esos 1000mAh de la capacidad de la batería (2800mAh), nos quedan 1800mAh / 3,6mA = 500 horas = 20 días. Casi tres semanas; creo que está bastante bien. Por supuesto, temperaturas bajas o tomar más fotos pueden reducir mucho la autonomía. Por otro lado, añadir otro par de baterías 18650 es fácil y dobla la autonomía.
Conclusiones
En este tutorial hemos construido con éxito un ESP32-CAM activado por movimiento usando un sensor PIR. Aprendimos los conceptos básicos de los sensores de movimiento PIR y cómo conectarlos al AI-Thinker ESP32-CAM. Implementando el código proporcionado, nuestro ESP32-CAM ahora puede capturar imágenes cada vez que se detecta movimiento.
Si quieres recibir notificaciones cada vez que se detecte movimiento, echa un vistazo a nuestro ESP32 send Telegram Message tutorial, donde aprenderás a enviar mensajes a la app Telegram en tu móvil.
Además, exploramos la posibilidad de alimentar el ESP32-CAM con baterías para una solución más portátil. Este proyecto abre un abanico de posibilidades para crear sistemas de vigilancia, cámaras de fauna o cualquier aplicación que requiera detección de movimiento y captura de imágenes.
Las lecciones más importantes son que hay que tener cuidado al elegir el sensor PIR y la solución de alimentación por batería. Hacer funcionar el ESP32-CAM no es fácil, pero el resultado final merece la pena.
¡Disfruta!
Preguntas frecuentes
Aquí tienes algunas preguntas y soluciones comunes para ayudarte a resolver problemas:
P: ¿Hay sensores PIR específicos que funcionen mejor con el ESP32-CAM?
R: Sí, usa el HC-SR501 o el AM312. No uses el HC-SR505, ya que su tiempo de retardo es demasiado largo y da problemas al conectarlo al ESP32-AM.
P: ¿Cómo puedo ajustar la sensibilidad del sensor PIR?
R: Algunos sensores PIR (incluido el HC-SR501) vienen con un potenciómetro que permite ajustar la sensibilidad. Girando el potenciómetro puedes afinar el alcance de detección del sensor.
P: ¿Puedo alimentar el ESP32-CAM con una batería?
R: Sí, puedes alimentar el ESP32-CAM con una batería para un montaje portátil. Si usas un power bank USB, asegúrate de que no tenga apagado automático. La mejor opción es un par de baterías 18650 o cualquier otra solución que proporcione energía estable entre 5V y 12V.
P: ¿Tengo que modificar el código para diferentes sensores PIR?
R: El código de este tutorial es genérico y debería funcionar con la mayoría de sensores PIR.
P: ¿Cuál es el alcance de un sensor PIR para detectar movimiento?
R: Dependerá del sensor y de las condiciones ambientales, pero normalmente es de unos 5 metros. Ten en cuenta que no puedes poner el detector detrás de un cristal y que no detectará objetos con la misma temperatura que el entorno.
P: ¿Hay sensores de movimiento alternativos que se puedan usar con el ESP32-CAM?
R: Sí, hay otros sensores de movimiento como sensores ultrasónicos, barreras fotoeléctricas o sensores de microondas que también se pueden usar con el ESP32-CAM.
P: ¿Cómo puedo aumentar la autonomía del ESP32-CAM?
R: Para aumentar la autonomía del ESP32-CAM, puedes aumentar el tiempo de retardo. Además, usar una batería de mayor capacidad también alargará la duración.
P: ¿Por qué las imágenes tomadas por el ESP32-CAM tienen un tinte azul tras un reinicio?
R: La cámara tiene funciones automáticas, como el balance de blancos automático, que tardan un poco en ajustarse al entorno. Sin eso, obtendrás fotos azuladas o muy oscuras o brillantes. Una forma fácil de evitarlo es tomar varias fotos tras un reinicio y descartarlas. Consulta la función skipPictures() en el tutorial, que hace exactamente eso.

