Este breve tutorial te muestra cómo encender o apagar el LED Flash del ESP32-CAM, con y sin deep-sleep. También aprenderás a controlar el brillo del LED y qué tener en cuenta al usar el LED y la interfaz de la tarjeta SD juntos.
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. Recomiendo el adaptador FTDI USB-TTL, ya que es más cómodo para programar, pero he listado ambas opciones a continuación.
Si quieres usar el lector de tarjetas SD necesitarás una tarjeta SD que no sea mayor de 16 GB. Aunque no la necesitarás para este tutorial.

ESP32-CAM con USB-TTL Shield

Adaptador FTDI USB-TTL

Tarjeta MicroSD 16GB
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.
Parpadeo del LED Flash
Si no usas la tarjeta SD ni el modo deep-sleep del ESP32-CAM, controlar el LED flash es sencillo. El LED flash está conectado al GPIO 4 y el habitual Blink code funciona perfectamente.
void setup() {
pinMode(GPIO_NUM_4, OUTPUT);
}
void loop() {
digitalWrite(GPIO_NUM_4, HIGH);
delay(1000);
digitalWrite(GPIO_NUM_4, LOW);
delay(1000);
}
Si seleccionas la placa AI Thinker ESP32-CAM, la constante GPIO_NUM_4 para GPIO 4 está definida.

En la función setup podemos usar GPIO_NUM_4 para configurar el GPIO 4 en modo salida. En la función loop tenemos el código habitual Blink que enciende el LED durante un segundo y lo apaga durante otro segundo.
Si no sabes cómo descargar código al ESP32-CAM, echa un vistazo al tutorial Programming the ESP32-CAM.
Control del brillo del LED Flash
El GPIO 4 también soporta PWM (Modulación por Ancho de Pulso), lo que significa que puedes controlar el brillo del LED flash. El siguiente ejemplo aumenta el brillo b de 0 (apagado) al brillo máximo (255) en un bucle:
void setup() {
pinMode(GPIO_NUM_4, OUTPUT);
}
void loop() {
for (int b = 0; b < 256; b++) {
analogWrite(GPIO_NUM_4, b);
delay(10);
}
}
Parpadeo del LED Flash con deep-sleep
Las cosas se complican cuando quieres hacer parpadear el LED en combinación con deep-sleep. Por ejemplo, quieres poner el ESP32-CAM a dormir, despertarlo cuando un objeto pase, encender el LED flash, tomar una foto, apagar el LED y volver a dormir.
El problema es que el estado de salida de los pines GPIO normalmente se pierde cuando el ESP32 entra en deep-sleep. Los pines GPIO quedan flotando, sin un valor definido. Puedes evitar esto llamando a rtc_gpio_hold_en(pin) para un pin específico o a la función rtc_gpio_force_hold_en_all() para congelar todos los pines en su estado actual.
Sin embargo, cuando el ESP32 se despierta del deep-sleep, necesitas desactivar esta retención de pines, de lo contrario no podrás cambiar el estado de los pines. Para desactivar la retención puedes llamar a las funciones rtc_gpio_hold_dis(pin) o rtc_gpio_force_hold_dis_all().
El siguiente ejemplo de código muestra cómo usar estas funciones para apagar el LED durante un segundo mientras el ESP32 está despierto, y luego encender el LED mientras el ESP32 duerme durante un segundo:
#include "driver/rtc_io.h"
void setup() {
rtc_gpio_hold_dis(GPIO_NUM_4);
pinMode(GPIO_NUM_4, OUTPUT);
digitalWrite(GPIO_NUM_4, LOW);
delay(1000);
digitalWrite(GPIO_NUM_4, HIGH);
rtc_gpio_hold_en(GPIO_NUM_4);
esp_sleep_enable_timer_wakeup(1 * 1000 * 1000);
esp_deep_sleep_start();
}
void loop() {}
Vamos a analizar esto con un poco más de detalle.
Primero, importamos el archivo driver/rtc_io.h, que es necesario para las funciones rtc_gpio_hold_xxx.
En la función setup, empezamos llamando a rtc_gpio_hold_dis(GPIO_NUM_4). Esto desactiva cualquier retención que tengamos en el pin GPIO 4. Sin esto no podríamos cambiar el estado del pin 4 más adelante.
En la siguiente línea llamamos a pinMode(GPIO_NUM_4, OUTPUT) para configurar el pin GPIO 4 en modo salida. Y luego podemos poner el pin 4 en LOW (apagando el LED) llamando a digitalWrite(GPIO_NUM_4, LOW).
Después de un retraso de un segundo, ponemos el pin 4 en HIGH y aquí se pone interesante. Queremos entrar en deep-sleep pero perderíamos el estado del pin 4. Por eso necesitamos llamar a rtc_gpio_hold_en(GPIO_NUM_4) para mantener el estado actual HIGH del pin 4.
Finalmente configuramos el temporizador de deep-sleep a un segundo (esp_sleep_enable_timer_wakeup) y entramos en deep-sleep con esp_deep_sleep_start().
Cuando el ESP32 se despierta después de un segundo, comienza de nuevo desde el principio de la función setup. La función loop nunca se llama.
LED Flash e interfaz de tarjeta SD
Controlar el LED flash es especialmente complicado cuando quieres usar la tarjeta SD también. Esto se debe a que la línea de datos HS2_DATA1 de la interfaz de la tarjeta SD es compartida por el LED flash. El siguiente diagrama muestra el circuito de control del LED flash.

Puedes ver que la alimentación del LED_FLASH se conmuta mediante el transistor Q1, que a su vez es controlado por una línea de señal etiquetada HS2_DATA1. Si ahora miras el diagrama de conexiones del conector de la tarjeta SD, también verás la línea HS2_DATA1 (último cable), que se usa para transferir datos a la tarjeta SD:

Esta es la razón por la que el LED del ESP32-CAM suele parpadear brevemente cuando escribes en la tarjeta SD.
Como el LED y la tarjeta SD usan HS2_DATA1, debes asegurarte de no mantener GPIO 4 ni hacer nada con el LED/GPIO 4 mientras escribes o lees de la tarjeta SD.
El siguiente ejemplo de código demuestra cómo encender el LED, tomar una foto, guardarla en la tarjeta SD, apagar el LED y luego dormir durante 5 segundos hasta la siguiente iteración.
#include "FS.h"
#include "SD_MMC.h"
#include "driver/rtc_io.h"
#include "esp32cam.h"
const auto RES = esp32cam::Resolution::find(800, 600);
RTC_DATA_ATTR uint16_t counter = 0;
void takeAndSavePic() {
static char path[64];
auto frame = esp32cam::capture();
if (frame) {
sprintf(path, "/img%d.jpg", counter++);
File file = SD_MMC.open(path, FILE_WRITE);
frame->writeTo(file);
file.close();
Serial.printf("Wrote: %s\n", path);
delay(10);
}
}
void initCamera() {
using namespace esp32cam;
Config cfg;
cfg.setPins(pins::AiThinker);
cfg.setResolution(RES);
cfg.setJpeg(80);
Camera.begin(cfg);
}
void setup() {
Serial.begin(115200);
rtc_gpio_hold_dis(GPIO_NUM_4);
SD_MMC.begin();
initCamera();
pinMode(GPIO_NUM_4, OUTPUT);
digitalWrite(GPIO_NUM_4, HIGH);
takeAndSavePic();
digitalWrite(GPIO_NUM_4, LOW);
rtc_gpio_hold_en(GPIO_NUM_4);
esp_sleep_enable_timer_wakeup(5 * 1000 * 1000);
esp_deep_sleep_start();
}
void loop() { }
Para este código necesitarás la biblioteca esp32cam, que facilita el manejo de la cámara. Puedes instalarla a través del Library Manager en el IDE de Arduino. Solo busca «esp32cam»:

No entraré en detalles sobre cómo funciona el código de la cámara, sino que me centraré en la interacción entre tomar una foto, guardarla en la tarjeta SD, controlar el LED flash y usar deep-sleep. Todo esto ocurre en la función setup.
Como puedes ver, lo primero es llamar a rtc_gpio_hold_dis(GPIO_NUM_4) para desactivar cualquier retención en GPIO 4. De lo contrario no podemos controlar el flash ni usar la tarjeta SD.
Luego inicializamos la interfaz de la tarjeta SD con SD_MMC.begin() y también inicializamos la cámara con initCamera().
Después encendemos el LED flash llamando a digitalWrite(GPIO_NUM_4, HIGH), tomamos una foto, la guardamos en la tarjeta SD y luego apagamos el LED.
Antes de entrar en deep-sleep, debes llamar a rtc_gpio_hold_en(GPIO_NUM_4) para asegurar que GPIO 4 mantenga su estado (LOW) durante el deep-sleep y el LED flash permanezca apagado.
Después configuramos el temporizador de deep-sleep a 5 segundos y luego iniciamos el modo deep-sleep. El ESP32 se despertará automáticamente tras 5 segundos y el ciclo se repetirá.
Apagar el flash al escribir en la tarjeta SD
Como se mencionó antes, el LED flash y la tarjeta SD comparten la misma línea de señal etiquetada HS2_DATA1, y por eso el LED parpadea brevemente al escribir en la tarjeta SD. Puedes evitar esto activando el modo de 1 bit para el SD Card interface:
SD_MMC.begin("/sdcard", true);
El segundo argumento mode1bit==true, activa el modo de 1 bit. En este modo la línea HS2_DATA1 (GPIO 4) no se usa y el LED no parpadeará al escribir en la tarjeta SD. Aunque la velocidad de escritura será más lenta, ahora puedes controlar libremente el GPIO 4, ya que solo lo usa el LED.
Puedes probar esto con el siguiente código de prueba. Apaga el LED y escribe un archivo 10 veces:
#include "FS.h"
#include "SD_MMC.h"
void setup() {
Serial.begin(115200);
pinMode(GPIO_NUM_4, OUTPUT);
digitalWrite(GPIO_NUM_4, LOW);
//SD_MMC.begin(); // LED will switch on
SD_MMC.begin("/sdcard", true); // LED remains off
for (int i = 0; i < 10; i++) {
delay(1000);
File file = SD_MMC.open("/test.txt", FILE_WRITE);
file.println("TEST");
file.close();
Serial.println("File written");
}
}
void loop() { }
Con SD_MMC.begin(), el LED se encenderá (a pesar de estar configurado para apagado) tan pronto como el código comience a escribir los archivos. Con SD_MMC.begin("/sdcard", true), en cambio, el LED está apagado y permanece apagado.
Conclusiones
En este tutorial aprendiste a controlar el LED flash del ESP32-CAM en varios escenarios, especialmente en combinación con deep-sleep y soporte para tarjeta SD.
Vale la pena mencionar que deberías evitar mantener el LED flash encendido por períodos prolongados. El diagrama del circuito revela que no hay una resistencia limitadora de corriente para el LED, lo que significa que puede sobrecalentarse y quemarse fácilmente.
Si necesitas información más fundamental sobre el ESP32-CAM, echa un vistazo al tutorial Programming the ESP32-CAM. Si quieres aplicar lo aprendido aquí para construir una cámara activada por movimiento, consulta el tutorial Motion Activated ESP32-CAM. Y finalmente, si quieres usar el ESP32-CAM para detección de objetos, el Object Detection with ESP32-CAM and YOLO te será útil.
Si tienes alguna pregunta, no dudes en dejarla en la sección de comentarios.
¡Feliz bricolaje ; )

