arduino-semaforo-basico

Cómo hacer un semáforo con Arduino

  • 5 min

Si el “Blink” (parpadeo de un LED) es el “Hola Mundo” de Arduino, el semáforo es, sin duda, el segundo paso lógico en nuestro aprendizaje.

Aparentemente es sencillo: encender y apagar tres luces. Sin embargo, este proyecto es fundamental porque nos obliga a dejar de pensar en un solo elemento para pensar en una secuencia de estados.

En este tutorial vamos a montar un semáforo para coches estándar, gestionando los tiempos de cada luz (Verde, Amarillo y Rojo) y asegurándonos de que la transición entre ellos sea fluida y correcta.

Este proyecto es la base ideal para entender la función delay() y la estructura secuencial de un programa, antes de saltar a conceptos más complejos como millis() o la multitarea.

Secuencias y tiempos

Un semáforo no es más que una máquina de estados muy sencilla que se repite en bucle infinito. No enciende luces “al azar”, sino que sigue un orden estricto para garantizar la seguridad.

La secuencia estándar que vamos a programar es la europea:

Verde: Paso permitido. (Duración larga).

Amarillo: Precaución, va a cambiar a rojo. (Duración corta).

Rojo: Stop. (Duración larga).

(Vuelta al Verde).

El reto aquí no es electrónico, sino de orden. Si encendemos el rojo sin apagar el verde, tendremos un problema conceptual. Debemos asegurarnos de que el microcontrolador gestiona las transiciones apagando el estado anterior antes de activar el siguiente.

Para este montaje vamos a necesitar gestionar 3 salidas digitales.

Componentes

  • [1x|blue] Arduino Uno | El cerebro del proyecto
  • [1x|red] LED Rojo (5mm) | Indica parada
  • [1x|yellow] LED Amarillo o Ámbar | Indica precaución
  • [1x|green] LED Verde | Indica paso permitido
  • [3x|gray] Resistencia 220Ω o 330Ω | Limitan corriente de LEDs
  • [Varios|gray] Protoboard y cables | Conexiones

¿Por qué esas resistencias?

Un LED estándar suele tener una caída de tensión aproximada de 2 V (rojo o amarillo) a 3 V (verde o azul). Como los pines del Arduino Uno trabajan a 5 V, necesitamos limitar la corriente para no dañar el LED ni forzar la salida.

Por seguridad y para no forzar el pin del Arduino, usamos valores comerciales superiores como 220Ω o 330Ω.

Esquema de conexión

El montaje es muy limpio. Vamos a conectar cada LED a un pin digital independiente.

LEDs del semáforo

Conectamos el ánodo de cada LED a su pin mediante una resistencia, y llevamos todos los cátodos al carril de GND.

[ { “from”: “LED Verde + 220Ω”, “to”: “Pin 11”, “color”: “green”, “note”: “Ánodo al pin, cátodo a GND” }, { “from”: “LED Amarillo + 220Ω”, “to”: “Pin 12”, “color”: “yellow” }, { “from”: “LED Rojo + 220Ω”, “to”: “Pin 13”, “color”: “red” }, { “from”: “Cátodos LED”, “to”: “GND Arduino”, “color”: “black” } ]

Usar pines consecutivos facilita el montaje y el orden visual de los cables, algo muy importante cuando los proyectos crecen.

El código

Vamos a escribir el código de una manera limpia y legible. Un error de principiante sería poner números de pines “a fuego” (hardcoded) dentro del loop. Nosotros vamos a usar constantes para que sea fácil de modificar.

Definición y setup

Definimos los pines y los configuramos como salida.

// Definición de pines
const int pinVerde = 11;
const int pinAmarillo = 12;
const int pinRojo = 13;

// Definición de tiempos (en milisegundos)
const int tiempoVerde = 5000;    // 5 segundos
const int tiempoAmarillo = 2000; // 2 segundos
const int tiempoRojo = 5000;     // 5 segundos

void setup() {
  // Configuramos los pines como salida
  pinMode(pinVerde, OUTPUT);
  pinMode(pinAmarillo, OUTPUT);
  pinMode(pinRojo, OUTPUT);
  
  // Nos aseguramos de empezar con todo apagado
  digitalWrite(pinVerde, LOW);
  digitalWrite(pinAmarillo, LOW);
  digitalWrite(pinRojo, LOW);
}
Copied!

El bucle principal (loop)

Aquí implementamos la secuencia. La lógica es siempre la misma:

  1. Encender el color actual.
  2. Esperar el tiempo definido.
  3. Apagar el color actual (limpieza).
void loop() {
  // --- FASE 1: VERDE ---
  digitalWrite(pinVerde, HIGH);   // Encendemos Verde
  delay(tiempoVerde);             // Esperamos
  digitalWrite(pinVerde, LOW);    // Apagamos Verde

  // --- FASE 2: AMARILLO ---
  digitalWrite(pinAmarillo, HIGH); // Encendemos Amarillo
  delay(tiempoAmarillo);           // Esperamos
  digitalWrite(pinAmarillo, LOW);  // Apagamos Amarillo

  // --- FASE 3: ROJO ---
  digitalWrite(pinRojo, HIGH);     // Encendemos Rojo
  delay(tiempoRojo);               // Esperamos
  digitalWrite(pinRojo, LOW);      // Apagamos Rojo
}
Copied!

Refactorizando el código

El código anterior funciona, pero si quisiéramos añadir un semáforo para peatones, el loop se volvería kilométrico. Una buena práctica en programación es modularizar.

Vamos a crear una función para cambiar las luces. Esto hace que el loop se lea como si fuera lenguaje natural.

void loop() {
  cambiarLuz(pinVerde, tiempoVerde);
  cambiarLuz(pinAmarillo, tiempoAmarillo);
  cambiarLuz(pinRojo, tiempoRojo);
}

// Función auxiliar que agrupa la lógica
void cambiarLuz(int pin, int duracion) {
  digitalWrite(pin, HIGH); // Enciende
  delay(duracion);         // Espera
  digitalWrite(pin, LOW);  // Apaga y listo para el siguiente
}
Copied!

¡Fíjate qué diferencia! Ahora el loop nos dice qué está pasando, y la función cambiarLuz se encarga del cómo.

Siguientes pasos

Este semáforo es “tonto”, es decir, ciego a lo que ocurre en el entorno. Funciona por tiempos fijos. ¿Cómo podríamos mejorarlo?

Aquí os dejo algunas ideas para evolucionar este proyecto:

  1. Añadir un botón de peatones: El semáforo siempre está en verde para los coches y solo cambia a rojo cuando un peatón pulsa el botón. Con delay() no podremos atenderlo mientras esperamos; necesitaremos consultar el botón sin bloquear, por ejemplo mediante millis().
  2. Semáforo doble: Simular un cruce de dos calles. Cuando uno está en verde, el otro obligatoriamente debe estar en rojo.
  3. Sonido: Añadir un buzzer que emita pitidos cuando el semáforo de peatones esté en verde (para invidentes).

Es un montaje sencillo, pero dominar la secuencia lógica es el primer paso para la robótica.