Cómo Crear un Semáforo con Arduino y un Botón: Aprendamos Juntos🚦
En este tutorial aprenderemos a crear un semáforo con Arduino, resolveremos un problema común relacionado con el uso del botón y descubriremos cómo optimizar nuestro código usando millis() ⏱️ en lugar de delay() ⏳. ¡Sencillo, práctico y perfecto para empezar a dominar Arduino! 💡

En este tutorial, vamos a construir un semáforo con Arduino, pero no será cualquier semáforo: vamos a enfrentarnos a un pequeño problema común y aprender una valiosa lección en el camino. 😎
📋 ¿Qué Haremos?
Crearemos un semáforo simple con tres LEDs (rojo, amarillo, verde) y un botón que permitirá iniciar o detener la secuencia de luces. Las luces seguirán el orden tradicional: verder → amarillo → rojo, el amarillo parpadeará antes de cambiar a rojo. Además, vamos a resolver un problema interesante que surgió cuando intentábamos que el botón respondiera de inmediato. 🔍
🛠️ Materiales Necesarios:
- 1 Arduino Uno
- 3 LEDs (rojo, amarillo, verde)
- 3 Resistencias de 220Ω (una para cada LED)
- 1 Push Button (botón)
- 1 Resistencia de 1kΩ para el botón
- Cables de conexión
- 1 Protoboard
🔧 Circuito

🧩 El Problema del Botón: Una Historia de Memoria y Paciencia
Cuando construimos nuestro semáforo por primera vez, nos topamos con un problema curioso. Queríamos que el botón pudiera detener la secuencia de luces en cualquier momento. Sin embargo, cuando lo presionábamos, ¡nada pasaba! 😮
¿Alguna vez has estado en una fila muy larga, y aunque quieres cambiar de fila, no puedes moverte hasta que alguien adelante termine? Bueno, eso es básicamente lo que estaba pasando con nuestro semáforo. Usábamos la función delay() para hacer que los LEDs esperaran su turno, pero mientras esperaban, Arduino estaba ocupado y no escuchaba al botón. Esto se debe a cómo Arduino gestiona su tiempo: cuando hay un delay(), el microcontrolador se queda “esperando” sin hacer otra cosa.
Pero… ¡queremos que Arduino sea multitarea! 💡
🌟 La Solución: Millis() al Rescate
En lugar de usar delay(), que hace que Arduino se quede esperando sin hacer nada más, podemos usar la función millis(). Esta función es como un cronómetro que sigue contando en segundo plano, mientras Arduino puede seguir haciendo otras cosas. De esta manera, el botón siempre será escuchado y responderá cuando lo necesitemos. 🕰️
👨💻 Código: Cómo Funciona el Semáforo
Aquí tienes el código que hace todo eso posible. Hemos creado una clase LED para organizar mejor las funciones de encender, apagar y parpadear los LEDs. Además, hemos utilizado millis() para manejar el tiempo y que el botón responda rápidamente, sin que el Arduino se quede “esperando”:
class LED {
private:
int pin;
public:
// Constructor
LED(int pinLED) : pin(pinLED) {
pinMode(pin, OUTPUT);
}
// Encender el LED
void encender() const {
digitalWrite(pin, HIGH);
}
// Apagar el LED
void apagar() const {
digitalWrite(pin, LOW);
}
// Parpadear el LED (no bloqueante)
void parpadear(int veces, int intervalo) const {
for (int i = 0; i < veces; i++) {
encender();
delay(intervalo / 2);
apagar();
delay(intervalo / 2);
}
}
};
// Definir LEDs del semáforo
LED ledRojo = 11;
LED ledAmarillo = 12;
LED ledVerde = 13;
// Pin del botón
const int buttonPin = 7;
bool esSecuenciaActiva = false;
bool estadoBotonAnterior = LOW;
bool botonPresionado = false;
unsigned long ultimoTiempoDeRebote = 0;
unsigned long retardoDeRebote = 50;
// Duraciones de las fases del semáforo en milisegundos
const int duracionVerde = 5000;
const int duracionAmarillo = 1000;
const int duracionRojo = 5000;
const int numeroParpadeos = 5;
const int intervaloParpadeos = 200;
// Variables para manejar el estado de la secuencia sin delay()
unsigned long tiempoAnterior = 0;
// 0 = Verde, 1 = Amarillo, 2 = Parpadeo Amarillo, 3 = Rojo
int estadoActualSemaforo = 0;
void setup() {
pinMode(buttonPin, INPUT);
// Inicializar monitor serie para depuración
Serial.begin(9600);
}
void loop() {
manejarBoton();
if (esSecuenciaActiva) {
secuenciaSemaforo();
}
}
void secuenciaSemaforo() {
unsigned long tiempoActual = millis();
switch (estadoActualSemaforo) {
case 0: // Fase verde
if (tiempoActual - tiempoAnterior >= duracionVerde) {
estadoActualSemaforo = 1;
tiempoAnterior = tiempoActual;
ledVerde.apagar();
ledAmarillo.encender();
} else {
ledVerde.encender();
ledAmarillo.apagar();
ledRojo.apagar();
}
break;
case 1: // Fase amarilla
if (tiempoActual - tiempoAnterior >= duracionAmarillo) {
estadoActualSemaforo = 2;
tiempoAnterior = tiempoActual;
}
break;
case 2: // Parpadeo del LED amarillo antes de cambiar a rojo
ledAmarillo.parpadear(numeroParpadeos, intervaloParpadeos);
estadoActualSemaforo = 3;
tiempoAnterior = tiempoActual;
break;
case 3: // Fase roja
if (tiempoActual - tiempoAnterior >= duracionRojo) {
estadoActualSemaforo = 0;
tiempoAnterior = tiempoActual;
} else {
ledRojo.encender();
ledVerde.apagar();
ledAmarillo.apagar();
}
break;
}
}
// Función para manejar el estado del botón
void manejarBoton() {
int estadoBotonActual = digitalRead(buttonPin);
if (estadoBotonActual != estadoBotonAnterior) {
ultimoTiempoDeRebote = millis();
}
if ((millis() - ultimoTiempoDeRebote) > retardoDeRebote) {
if (estadoBotonActual == HIGH && !botonPresionado) {
// Cambiar el estado de la secuencia
esSecuenciaActiva = !esSecuenciaActiva;
Serial.print("Secuencia ");
Serial.println(esSecuenciaActiva ? "activada" : "detenida");
botonPresionado = true;
}
else if (estadoBotonActual == LOW) {
botonPresionado = false;
}
}
estadoBotonAnterior = estadoBotonActual;
}
🤔 ¿Qué Cambió?
El uso de millis() nos permite hacer que Arduino siga contando el tiempo, pero sin bloquear las demás acciones. Así, el botón siempre podrá detener o iniciar la secuencia, sin importar en qué parte del ciclo estemos.
🎯 ¿Qué Aprendimos?
Al resolver este problema, aprendimos algo muy importante: si queremos que Arduino haga varias cosas al mismo tiempo (como escuchar un botón y controlar LEDs), tenemos que evitar funciones que lo “detengan” por completo, como delay(). La función millis() es nuestra herramienta clave para que Arduino pueda hacer más de una cosa a la vez. 💡✨
Con este tutorial, no solo sabemos cómo crear un semáforo funcional, sino que también hemos aprendido a optimizar nuestro código para que sea más eficiente y responda de manera inmediata a nuestros comandos.
¡Sigue experimentando y jugando con Arduino! 🔥