Control de LED con botón en ESP32 (Toggle)
Cada vez que se pulsa el botón, cambia el estado del LED: si está encendido se apaga, si está apagado se enciende. Patrón de conmutación clásico. Con debounce limpiamos el ruido mecánico del pulsador para garantizar una sola conmutación con una sola pulsación.

Video
Diagrama del circuito

Código fuente
1// ─────────────────────────────────────────
2// BlueGrays · bluegrays.com
3// Control de LED con botón en ESP32 (Toggle)
4// ─────────────────────────────────────────
5// Cada pulsación del botón alterna el LED: encendido pasa a apagado,
6// apagado pasa a encendido. Un antirrebote de 50 ms garantiza que una
7// pulsación dé exactamente un cambio.
8//
9// Pines:
10// GPIO 5 -> LED rojo (con una resistencia de 220 ohmios)
11// GPIO 4 -> Botón (otra patilla a GND)
12
13const int LED_PIN = 5; // Salida del LED
14const int BUTTON_PIN = 4; // Entrada del botón
15
16bool ledState = false; // ¿Está encendido el LED ahora?
17bool lastButton = HIGH; // Lectura anterior del botón
18unsigned long lastPress = 0; // Última pulsación válida (ms)
19const unsigned long DEBOUNCE = 50; // Tiempo antirrebote (ms)
20
21void setup() {
22 pinMode(LED_PIN, OUTPUT);
23 pinMode(BUTTON_PIN, INPUT_PULLUP); // Lee HIGH cuando no está pulsado
24}
25
26void loop() {
27 bool buttonState = digitalRead(BUTTON_PIN);
28
29 // Alternar solo en un flanco HIGH -> LOW (una pulsación nueva) y una vez pasado el tiempo de
30 // antirrebote
31 if (buttonState == LOW && lastButton == HIGH && (millis() - lastPress > DEBOUNCE)) {
32 ledState = !ledState;
33 digitalWrite(LED_PIN, ledState);
34 lastPress = millis();
35 }
36 lastButton = buttonState;
37}
38Preguntas frecuentes
¿Cuánto debe durar el tiempo de rebote?▾
Para la mayoría de los botones, basta con un tiempo de 20-50 ms. Si el tiempo es demasiado corto, se siguen detectando saltos; si es demasiado largo, se pierden las pulsaciones rápidas y sucesivas.
¿Qué estado debo capturar para el Toggle?▾
Detecta únicamente el momento en que se «pulsa» el botón (la transición de HIGH a LOW, si utilizas INPUT_PULLUP). Para no tener que cambiar el LED una y otra vez mientras se mantenga pulsado, guarda el estado anterior del botón en una variable.
¿Es correcto utilizar `delay()` para el rebote?▾
Funciona en proyectos sencillos, pero durante el tiempo que dura la función delay(), el ESP32 no puede realizar ninguna otra tarea. El rebote (debounce) que se consigue registrando el momento de la última modificación con la función millis() es más adecuado para evitar que se vean afectadas tareas como la conexión Wi-Fi.
¿El LED puede recordar su estado último al apagarse y encenderse?▾
Sí, en el ESP32 puedes guardar el estado en la memoria permanente mediante la biblioteca Preferences y leerlo al arrancar. De este modo, aunque se produzca un corte de corriente, el LED se encenderá en su estado final.
