Skip to Content

Tomar fotos con ESP32-CAM

Tomar fotos con ESP32-CAM

En este tutorial aprenderás cómo tomar fotos con un ESP32-CAM al presionar un botón y guardarlas en la tarjeta SD. Básicamente, construiremos una cámara digital sencilla.

Partes necesarias

Necesitarás un ESP32-CAM para probar los ejemplos de código. Puedes conseguir un ESP32-CAM con un USB-TTL Shield para programar o un adaptador FTDI USB-TTL. El adaptador FTDI USB-TTL es un poco más incómodo de usar pero deja los pines GPIO fácilmente accesibles. El USB-TTL Shield es más fácil de usar pero es más difícil conectar los pines GPIO.

Si quieres usar tu PC para ver las fotos tomadas por el ESP32-CAM y guardadas en la tarjeta SD, también podrías necesitar un lector de tarjetas SD.

ESP32-CAM con USB-TTL Shield

Adaptador FTDI USB-TTL

Dupont wire set

Juego de cables Dupont

Half_breadboard56a

Protoboard

Lector de tarjetas SD

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.

Diagrama de conexiones

Queremos tomar una foto con el ESP32-CAM cada vez que se presione un botón. En el siguiente diagrama de conexiones, conectamos un pulsador entre el pin GND y el pin GPIO13:

Button wired to ESP32-CAM
Botón conectado al ESP32-CAM

Cada vez que se presiona el botón (cerrado), GPIO13 se conecta a tierra. Esto funciona porque GPIO13 (y otros pines GPIO) tiene resistencias pull-up internas que mantienen GPIO13 en alto cuando no está conectado nada. Esto significa que la función del botón está invertida (cuando está cerradoGPIO13 == LOW) y lo verás reflejado en la sección de código.

La siguiente imagen muestra cómo puedes montar este circuito con una protoboard y el ESP32-CAM:

Circuit on breadboard with button and ESP32-CAM
Circuito en protoboard con botón y ESP32-CAM

Como puedes ver, usé el USB-TTL Shield y pude dejar un pequeño espacio entre el ESP32-CAM y el shield para conectar los cables del botón a GND y GPIO13:

Connecting wires at gap between ESP32-CAM and USB-TTL Shield
Conexión de cables en el espacio entre ESP32-CAM y USB-TTL Shield

Alternativamente, podrías usar el adaptador FTDI USB-TTL o conectar el ESP32-CAM al USB-TTL Shield mediante un juego de cables Dupont.

Código para tomar fotos y guardarlas

El siguiente sketch de Arduino toma una foto con la cámara del ESP32-CAM y la guarda en la tarjeta SD cuando se presiona el botón conectado a GPIO13 (cerrado).

Para este código necesitarás la esp32cam biblioteca. Puedes instalarla a través del LIBRARY MANAGER en el IDE de Arduino. Solo busca «esp32cam» y presiona INSTALL. La imagen abajo muestra la instalación completada:

esp32cam library installed via Library Manager
Biblioteca esp32cam instalada vía LIBRARY MANAGER

A continuación está el código completo para nuestra pequeña aplicación de captura. Échale un vistazo primero y luego discutiremos sus detalles:

#include "FS.h"
#include "SD_MMC.h"
#include "esp32cam.h"

const byte btnPin = GPIO_NUM_13;
const auto RES = esp32cam::Resolution::find(1600, 1200);

void takePicAndSave() {
  static int cnt = 0;

  auto frame = esp32cam::capture();
  if (frame == nullptr) {
    Serial.println("Capture failed!");
    return;
  }
  
  String path = "/img" + String(cnt++) + ".jpg";
  File file = SD_MMC.open(path.c_str(), FILE_WRITE);
  if (!file) {
    Serial.println("Failed to open file!");
  }
  frame->writeTo(file);
  Serial.printf("Wrote: %s\n", path.c_str());
  file.close();
}

void enableFlash(bool enable) {
  digitalWrite(GPIO_NUM_4, enable ? HIGH : LOW);
}

void initCamera() {
    using namespace esp32cam;
    Config cfg;
    cfg.setPins(pins::AiThinker);
    cfg.setResolution(RES);
    cfg.setJpeg(80);

    bool ok = Camera.begin(cfg);
    Serial.println(ok ? "CAMERA OK" : "CAMERA FAIL");
}

void initSDCard() {
  if (!SD_MMC.begin("/sdcard", true)) {
    Serial.println("SD Card Mount Failed!");
  } else if (SD_MMC.cardType() == CARD_NONE) {
    Serial.println("No SD card inserted!");
  }
  Serial.println("SD card ready.");
}

void setup() {
  Serial.begin(115200);
  initCamera();
  initSDCard();
  pinMode(btnPin, INPUT);
  pinMode(GPIO_NUM_4, OUTPUT);
  enableFlash(false);  
  delay(1000);
}

void loop() {
  if (!digitalRead(btnPin)) {
    enableFlash(true);
    takePicAndSave();
    enableFlash(false);
    delay(500);
  }
}

Bibliotecas

Al inicio del código, se incluyen tres bibliotecas:

#include "FS.h"
#include "SD_MMC.h"
#include "esp32cam.h"

Estas bibliotecas son esenciales para el proyecto ESP32-CAM. LaFS.h biblioteca proporciona operaciones del sistema de archivos, SD_MMC.h permite que el ESP32 se comunique con tarjetas SD a través del bus MMC (MultiMediaCard), y esp32cam.h es una biblioteca de nivel superior que simplifica la configuración de la cámara y la captura de imágenes en el módulo ESP32-CAM.

Constantes

Luego, se define una constante para el pin del botón y una configuración de resolución de cámara:

const byte btnPin = GPIO_NUM_13;
const auto RES = esp32cam::Resolution::find(1600, 1200);

Aquí, btnPin está asignado a GPIO 13, donde está conectado el botón. También podrías usar GPIO 12, pero en ambos casos debes ejecutar la interfaz de la tarjeta SD en modo de 1 bit. Para más detalles, consulta el More GPIO pins for ESP32-CAM tutorial.

ElRES objeto define la resolución de la imagen, en este caso, 1600×1200 píxeles (UXGA), que se usará más adelante para configurar la cámara. 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

takePicAndSave

La takePicAndSave() función es la principal que captura una imagen y la escribe en la tarjeta SD:

void takePicAndSave() {
  static int cnt = 0;

Lacnt variable se declarastatic, lo que significa que mantiene su valor entre llamadas a la función. La usamos para generar nombres de archivo únicos para cada foto.

  auto frame = esp32cam::capture();
  if (frame == nullptr) {
    Serial.println("Capture failed!");
    return;
  }

La función llama aesp32cam::capture() para capturar un fotograma de la cámara. Si la captura falla (es decir, frame es nullptr), se imprime un mensaje de error y la función termina temprano.

  String path = "/img" + String(cnt++) + ".jpg";
  File file = SD_MMC.open(path.c_str(), FILE_WRITE);

El código construye un nombre de archivo concatenando «img» con el valor incremental decnt y añadiendo «.jpg». Luego, el archivo se abre en la tarjeta SD para escritura.

  if (!file) {
    Serial.println("Failed to open file!");
  }

Si el archivo no se pudo abrir por alguna razón (por ejemplo, tarjeta SD no presente, error en el sistema de archivos), imprime un mensaje de error.

  frame->writeTo(file);
  Serial.printf("Wrote: %s\n", path.c_str());
  file.close();
}

Si el archivo se abrió correctamente, el fotograma capturado se escribe en el archivo, se imprime un mensaje de confirmación en el Monitor Serial mostrando el nombre del archivo guardado, y finalmente, se cierra el archivo para asegurar la integridad de los datos.

enableFlash

La enableFlash(bool enable) función controla el LED flash incorporado del ESP32-CAM:

void enableFlash(bool enable) {
  digitalWrite(GPIO_NUM_4, enable ? HIGH : LOW);
}

Esta función establece el nivel de voltaje en GPIO 4. Cuando enable es verdadero, pone GPIO 4 en HIGH, encendiendo el LED flash; de lo contrario, lo pone en LOW, apagando el flash.

Para más información sobre cómo controlar el LED flash, consulta el Control ESP32-CAM Flash LED tutorial.

initCamera

La initCamera() función inicializa el módulo de la cámara:

void initCamera() {
    using namespace esp32cam;
    Config cfg;
    cfg.setPins(pins::AiThinker);
    cfg.setResolution(RES);
    cfg.setJpeg(80);

Dentro de initCamera(), primero se trae el espacio de nombresesp32cam al ámbito local. Luego, crea un objeto Config, cfg, y lo configura para usar los pines predefinidos para un diseño de placa Ai-Thinker, que es el tipo típico para módulos ESP32-CAM. Establece la resolución de la cámara aRES (1600×1200) y la calidad de compresión JPEG a 80 (donde 100 es la mejor calidad, y valores más bajos significan más compresión y peor calidad).

    bool ok = Camera.begin(cfg);
    Serial.println(ok ? "CAMERA OK" : "CAMERA FAIL");
}

La Camera.begin(cfg) función intenta iniciar la cámara usando la configuración especificada. Imprime «CAMERA OK» si tiene éxito, o «CAMERA FAIL» si no.

initSDCard

La initSDCard() función monta la tarjeta SD:

void initSDCard() {
  if (!SD_MMC.begin("/sdcard", true)) {
    Serial.println("SD Card Mount Failed!");
  } else if (SD_MMC.cardType() == CARD_NONE) {
    Serial.println("No SD card inserted!");
  }
  Serial.println("SD card ready.");
}

Aquí, SD_MMC.begin() inicializa el bus SD_MMC e intenta montar el sistema de archivos bajo la ruta /sdcard. El segundo argumentotrue habilita el modo de 1 línea, que simplifica el cableado a costa de menor velocidad. Si el montaje falla, imprime «SD Card Mount Failed!». Si la tarjeta está montada pero no hay tarjeta físicamente insertada, imprime «No SD card inserted!». De lo contrario, asume que todo está bien e imprime «SD card ready.»

setup

La setup() función prepara todo una vez al iniciar:

void setup() {
  Serial.begin(115200);
  initCamera();
  initSDCard();
  pinMode(btnPin, INPUT);
  pinMode(GPIO_NUM_4, OUTPUT);
  enableFlash(false);  
  delay(1000);
}

En setup(), el puerto serial se inicia a 115200 baudios para depuración. La cámara y la tarjeta SD se inicializan llamando a las funciones initCamera() y initSDCard(). Luego, el pin del botón (btnPin) se configura como entrada, y el pin de control del flash (GPIO 4) como salida. enableFlash(false) asegura que el LED flash comience apagado. Se añade un retardo de 1 segundo (1000 milisegundos) al final para que el sistema se estabilice antes de iniciar el bucle principal.

loop

Finalmente, la loop() función verifica constantemente si el botón está presionado:

void loop() {
  if (!digitalRead(btnPin)) {
    enableFlash(true);
    takePicAndSave();
    enableFlash(false);
    delay(500);
  }
}

En loop(), lee el estado debtnPin. Si el botón está presionado, digitalRead(btnPin) devuelve LOW porque el pin se conecta a tierra al presionar. Cuando se detecta el botón presionado, se enciende el flash llamando a enableFlash(true), luego se captura y guarda una foto llamando a takePicAndSave(), y finalmente se apaga el flash co enableFlash(false). Sigue un retardo de 500 milisegundos para evitar tomar múltiples fotos por una sola pulsación.

Conclusiones

En este tutorial aprendiste a construir una pequeña cámara digital usando el ESP32-CAM. El ejemplo de código configura un ESP32-CAM para monitorear un botón. Cuando se presiona el botón, enciende el flash, captura una foto a 1600×1200 de resolución, la guarda en la tarjeta SD con un nombre secuencial como /img0.jpg, /img1.jpg, etc., apaga el flash y espera brevemente antes de escuchar de nuevo.

Si quieres poner el ESP32 en deep-sleep entre fotos, echa un vistazo al Motion Activated ESP32-CAM tutorial y para streaming de video consulta el Stream Video with ESP32-CAM artículo.

Además, si quieres recibir notificaciones cuando se detecte movimiento, mira nuestro ESP32 send Telegram Message tutorial, que te enseña cómo enviar mensajes a la app Telegram en tu teléfono.

Y si tienes alguna pregunta, no dudes en dejarla en la sección de comentarios.

¡Feliz bricolaje! 😉