Saltar al contenido
Nivel intermedio

Dos semáforos sincronizados en una intersección

Armar el circuito de la intersección

Para armar la intersección con dos semáforos debemos conectar a la protoboard tres LED más que en el circuito anterior (rojo, amarillo y verde), con sus respectivas resistencias de 220 ohm hacia la línea de GND.

Esquema de conexión: Arduino UNO con seis LED en la protoboard formando dos semáforos, rotulados Semáforo 1 y Semáforo 2, cada LED con su resistencia de 220 ohm.
Esquema de la intersección: los seis LED forman el semáforo 1 y el semáforo 2, controlados por la misma placa (figura original del proyecto).

El primer semáforo conserva sus pines del nivel inicial (rojo 13, amarillo 12, verde 11). Para el segundo semáforo conectaremos el LED rojo al pin 10, el amarillo al pin 9 y el verde al pin 8.

En el ESP32, cada uno de los seis LED va a un GPIO de salida seguro. Los números exactos están en las constantes del comienzo del código (PIN_S1_ROJO, PIN_S1_AMARILLO, PIN_S1_VERDE, PIN_S2_ROJO, PIN_S2_AMARILLO, PIN_S2_VERDE): conectá cada LED al GPIO que indica su constante.

Podés recorrer el circuito completo, con los dos semáforos funcionando en la esquina, en el simulador:

Simulación del comportamiento

La misma máquina de estados del firmware, portada a TypeScript, maneja el comportamiento del dispositivo. No se ejecuta el binario real: la ejecución del .hex con avr8js está en el roadmap.

Simulación lista. Presioná «Iniciar» para arrancar la secuencia.

Programar la secuencia para dos semáforos

En este momento vamos a sumar la secuencia de nuestro segundo semáforo al código previamente armado. La planificación de la secuencia para dos semáforos es más compleja, por lo que requiere que se le dedique un poco más de tiempo a su armado y comprensión.

Para pensar cómo armar el código, es posible imaginar esta secuencia como viñetas de un cómic en donde hay una serie de acciones a realizar y una demora entre cuadro y cuadro. Cada viñeta muestra la esquina congelada en un instante: qué luz tiene cada semáforo y cuánto tiempo dura ese cuadro antes de pasar al siguiente.

ViñetaSemáforo 1Semáforo 2Demora
Nº 1VerdeRojo5 s
Nº 2AmarilloRojo1 s
Nº 3RojoRojo1 s
Nº 4RojoRojo + amarillo1 s
Nº 5RojoVerde5 s
Nº 6RojoAmarillo1 s
Nº 7RojoRojo1 s
Nº 8Rojo + amarilloRojo1 s
Nº 9VerdeRojo

Como se puede ver en la tabla, el cuadro Nº 9 es igual al cuadro Nº 1: es en ese punto donde comienza a repetirse la secuencia. Por lo tanto, en total tenemos ocho pasos que se repiten y una determinada demora en cada paso.

Dos detalles de esta coreografía merecen atención:

  • Nunca las dos calles tienen verde a la vez: cuando un semáforo habilita el paso, el otro está en rojo.
  • En las viñetas Nº 3 y Nº 7 ambos semáforos están en rojo durante un segundo. Ese respiro le da tiempo al último vehículo a despejar la bocacalle antes de habilitar a la calle transversal.

Traducimos las ocho viñetas al programa: cada viñeta es un grupo de órdenes de encendido y apagado seguido de su demora, en estructura secuencial.

Ver el código completo · interseccion.ino
interseccion.ino Arduino UNO
// Intersección con dos semáforos sincronizados (6 LED).
// Recorre los 8 cuadros del "cómic" de la Fig. 10 de la guía original:
//
//   Cuadro | Semáforo 1      | Semáforo 2      | Duración
//   -------+-----------------+-----------------+---------
//     1    | VERDE           | ROJO            |   5 s
//     2    | AMARILLO        | ROJO            |   1 s
//     3    | ROJO            | ROJO            |   1 s   (todo rojo)
//     4    | ROJO            | ROJO + AMARILLO |   1 s
//     5    | ROJO            | VERDE           |   5 s
//     6    | ROJO            | AMARILLO        |   1 s
//     7    | ROJO            | ROJO            |   1 s   (todo rojo)
//     8    | ROJO + AMARILLO | ROJO            |   1 s
//
// El cuadro 9 es igual al 1: ahí la secuencia vuelve a empezar.
// Los cuadros 3 y 7 dejan TODO en rojo un instante: es el margen de
// seguridad para que la intersección se vacíe antes de habilitar la
// otra calle. Ningún semáforo real pasa de verde a verde sin ese margen.
//
// Plataforma: Arduino UNO.
// Conexión: semáforo 1 → verde 11, amarillo 12, rojo 13;
//           semáforo 2 → verde 8, amarillo 9, rojo 10 (como en la guía).
//
// Basado en: Fragmento de código 3 — Sistema de Semaforización,
// Saberes Digitales (INET/EDUCAR 2019)
// Modernización: se reemplazó el código generado por mBlock3 (_delay, _loop)
// por C++ idiomático de Arduino con constantes nombradas. Además, cada
// cuadro escribe las SEIS luces completas (el original solo escribía los
// pines que cambiaban): así cada bloque de código se corresponde 1 a 1
// con un cuadro del cómic y puede leerse de forma aislada.

// Semáforo 1 (calle principal).
const int PIN_SEM1_VERDE    = 11;
const int PIN_SEM1_AMARILLO = 12;
const int PIN_SEM1_ROJO     = 13;

// Semáforo 2 (calle transversal).
const int PIN_SEM2_VERDE    = 8;
const int PIN_SEM2_AMARILLO = 9;
const int PIN_SEM2_ROJO     = 10;

// Tiempos de la secuencia (los mismos de toda la guía y del simulador).
const unsigned long DURACION_VERDE_MS         = 5000;
const unsigned long DURACION_AMARILLO_MS      = 1000;
const unsigned long DURACION_TODO_ROJO_MS     = 1000; // margen de seguridad
const unsigned long DURACION_ROJO_AMARILLO_MS = 1000;

void setup() {
  pinMode(PIN_SEM1_VERDE, OUTPUT);
  pinMode(PIN_SEM1_AMARILLO, OUTPUT);
  pinMode(PIN_SEM1_ROJO, OUTPUT);
  pinMode(PIN_SEM2_VERDE, OUTPUT);
  pinMode(PIN_SEM2_AMARILLO, OUTPUT);
  pinMode(PIN_SEM2_ROJO, OUTPUT);
}

void loop() {
  // Cuadro 1: Semáforo 1 VERDE / Semáforo 2 ROJO.
  digitalWrite(PIN_SEM1_VERDE, HIGH);
  digitalWrite(PIN_SEM1_AMARILLO, LOW);
  digitalWrite(PIN_SEM1_ROJO, LOW);
  digitalWrite(PIN_SEM2_VERDE, LOW);
  digitalWrite(PIN_SEM2_AMARILLO, LOW);
  digitalWrite(PIN_SEM2_ROJO, HIGH);
  delay(DURACION_VERDE_MS);

  // Cuadro 2: Semáforo 1 AMARILLO / Semáforo 2 ROJO.
  digitalWrite(PIN_SEM1_VERDE, LOW);
  digitalWrite(PIN_SEM1_AMARILLO, HIGH);
  digitalWrite(PIN_SEM1_ROJO, LOW);
  digitalWrite(PIN_SEM2_VERDE, LOW);
  digitalWrite(PIN_SEM2_AMARILLO, LOW);
  digitalWrite(PIN_SEM2_ROJO, HIGH);
  delay(DURACION_AMARILLO_MS);

  // Cuadro 3: TODO ROJO — la intersección se vacía antes del cambio.
  digitalWrite(PIN_SEM1_VERDE, LOW);
  digitalWrite(PIN_SEM1_AMARILLO, LOW);
  digitalWrite(PIN_SEM1_ROJO, HIGH);
  digitalWrite(PIN_SEM2_VERDE, LOW);
  digitalWrite(PIN_SEM2_AMARILLO, LOW);
  digitalWrite(PIN_SEM2_ROJO, HIGH);
  delay(DURACION_TODO_ROJO_MS);

  // Cuadro 4: Semáforo 1 ROJO / Semáforo 2 ROJO + AMARILLO (por arrancar).
  digitalWrite(PIN_SEM1_VERDE, LOW);
  digitalWrite(PIN_SEM1_AMARILLO, LOW);
  digitalWrite(PIN_SEM1_ROJO, HIGH);
  digitalWrite(PIN_SEM2_VERDE, LOW);
  digitalWrite(PIN_SEM2_AMARILLO, HIGH);
  digitalWrite(PIN_SEM2_ROJO, HIGH);
  delay(DURACION_ROJO_AMARILLO_MS);

  // Cuadro 5: Semáforo 1 ROJO / Semáforo 2 VERDE — ahora circula la otra calle.
  digitalWrite(PIN_SEM1_VERDE, LOW);
  digitalWrite(PIN_SEM1_AMARILLO, LOW);
  digitalWrite(PIN_SEM1_ROJO, HIGH);
  digitalWrite(PIN_SEM2_VERDE, HIGH);
  digitalWrite(PIN_SEM2_AMARILLO, LOW);
  digitalWrite(PIN_SEM2_ROJO, LOW);
  delay(DURACION_VERDE_MS);

  // Cuadro 6: Semáforo 1 ROJO / Semáforo 2 AMARILLO.
  digitalWrite(PIN_SEM1_VERDE, LOW);
  digitalWrite(PIN_SEM1_AMARILLO, LOW);
  digitalWrite(PIN_SEM1_ROJO, HIGH);
  digitalWrite(PIN_SEM2_VERDE, LOW);
  digitalWrite(PIN_SEM2_AMARILLO, HIGH);
  digitalWrite(PIN_SEM2_ROJO, LOW);
  delay(DURACION_AMARILLO_MS);

  // Cuadro 7: TODO ROJO — otra vez el margen de seguridad.
  digitalWrite(PIN_SEM1_VERDE, LOW);
  digitalWrite(PIN_SEM1_AMARILLO, LOW);
  digitalWrite(PIN_SEM1_ROJO, HIGH);
  digitalWrite(PIN_SEM2_VERDE, LOW);
  digitalWrite(PIN_SEM2_AMARILLO, LOW);
  digitalWrite(PIN_SEM2_ROJO, HIGH);
  delay(DURACION_TODO_ROJO_MS);

  // Cuadro 8: Semáforo 1 ROJO + AMARILLO / Semáforo 2 ROJO.
  digitalWrite(PIN_SEM1_VERDE, LOW);
  digitalWrite(PIN_SEM1_AMARILLO, HIGH);
  digitalWrite(PIN_SEM1_ROJO, HIGH);
  digitalWrite(PIN_SEM2_VERDE, LOW);
  digitalWrite(PIN_SEM2_AMARILLO, LOW);
  digitalWrite(PIN_SEM2_ROJO, HIGH);
  delay(DURACION_ROJO_AMARILLO_MS);

  // loop() vuelve a empezar: el cuadro 9 del cómic es igual al cuadro 1.
}
interseccion.ino ESP32
// Intersección con dos semáforos sincronizados (6 LED).
// Recorre los 8 cuadros del "cómic" de la Fig. 10 de la guía original:
//
//   Cuadro | Semáforo 1      | Semáforo 2      | Duración
//   -------+-----------------+-----------------+---------
//     1    | VERDE           | ROJO            |   5 s
//     2    | AMARILLO        | ROJO            |   1 s
//     3    | ROJO            | ROJO            |   1 s   (todo rojo)
//     4    | ROJO            | ROJO + AMARILLO |   1 s
//     5    | ROJO            | VERDE           |   5 s
//     6    | ROJO            | AMARILLO        |   1 s
//     7    | ROJO            | ROJO            |   1 s   (todo rojo)
//     8    | ROJO + AMARILLO | ROJO            |   1 s
//
// El cuadro 9 es igual al 1: ahí la secuencia vuelve a empezar.
// Los cuadros 3 y 7 dejan TODO en rojo un instante: es el margen de
// seguridad para que la intersección se vacíe antes de habilitar la
// otra calle. Ningún semáforo real pasa de verde a verde sin ese margen.
//
// Plataforma: ESP32 (DevKit v1).
// Conexión: semáforo 1 → verde GPIO 16, amarillo GPIO 17, rojo GPIO 18;
//           semáforo 2 → verde GPIO 19, amarillo GPIO 21, rojo GPIO 22.
// Nota eléctrica: el ESP32 trabaja a 3,3 V (no 5 V como el UNO); nunca
// inyectar 5 V en un GPIO.
//
// Basado en: Fragmento de código 3 — Sistema de Semaforización,
// Saberes Digitales (INET/EDUCAR 2019)
// Modernización: se reemplazó el código generado por mBlock3 (_delay, _loop)
// por C++ idiomático de Arduino con constantes nombradas y se portó a ESP32
// con GPIO seguros (se evitan los pines de arranque o "strapping"). Además,
// cada cuadro escribe las SEIS luces completas (el original solo escribía
// los pines que cambiaban): así cada bloque de código se corresponde 1 a 1
// con un cuadro del cómic y puede leerse de forma aislada.

// Semáforo 1 (calle principal).
const int PIN_SEM1_VERDE    = 16;
const int PIN_SEM1_AMARILLO = 17;
const int PIN_SEM1_ROJO     = 18;

// Semáforo 2 (calle transversal).
const int PIN_SEM2_VERDE    = 19;
const int PIN_SEM2_AMARILLO = 21;
const int PIN_SEM2_ROJO     = 22;

// Tiempos de la secuencia (los mismos de toda la guía y del simulador).
const unsigned long DURACION_VERDE_MS         = 5000;
const unsigned long DURACION_AMARILLO_MS      = 1000;
const unsigned long DURACION_TODO_ROJO_MS     = 1000; // margen de seguridad
const unsigned long DURACION_ROJO_AMARILLO_MS = 1000;

void setup() {
  pinMode(PIN_SEM1_VERDE, OUTPUT);
  pinMode(PIN_SEM1_AMARILLO, OUTPUT);
  pinMode(PIN_SEM1_ROJO, OUTPUT);
  pinMode(PIN_SEM2_VERDE, OUTPUT);
  pinMode(PIN_SEM2_AMARILLO, OUTPUT);
  pinMode(PIN_SEM2_ROJO, OUTPUT);
}

void loop() {
  // Cuadro 1: Semáforo 1 VERDE / Semáforo 2 ROJO.
  digitalWrite(PIN_SEM1_VERDE, HIGH);
  digitalWrite(PIN_SEM1_AMARILLO, LOW);
  digitalWrite(PIN_SEM1_ROJO, LOW);
  digitalWrite(PIN_SEM2_VERDE, LOW);
  digitalWrite(PIN_SEM2_AMARILLO, LOW);
  digitalWrite(PIN_SEM2_ROJO, HIGH);
  delay(DURACION_VERDE_MS);

  // Cuadro 2: Semáforo 1 AMARILLO / Semáforo 2 ROJO.
  digitalWrite(PIN_SEM1_VERDE, LOW);
  digitalWrite(PIN_SEM1_AMARILLO, HIGH);
  digitalWrite(PIN_SEM1_ROJO, LOW);
  digitalWrite(PIN_SEM2_VERDE, LOW);
  digitalWrite(PIN_SEM2_AMARILLO, LOW);
  digitalWrite(PIN_SEM2_ROJO, HIGH);
  delay(DURACION_AMARILLO_MS);

  // Cuadro 3: TODO ROJO — la intersección se vacía antes del cambio.
  digitalWrite(PIN_SEM1_VERDE, LOW);
  digitalWrite(PIN_SEM1_AMARILLO, LOW);
  digitalWrite(PIN_SEM1_ROJO, HIGH);
  digitalWrite(PIN_SEM2_VERDE, LOW);
  digitalWrite(PIN_SEM2_AMARILLO, LOW);
  digitalWrite(PIN_SEM2_ROJO, HIGH);
  delay(DURACION_TODO_ROJO_MS);

  // Cuadro 4: Semáforo 1 ROJO / Semáforo 2 ROJO + AMARILLO (por arrancar).
  digitalWrite(PIN_SEM1_VERDE, LOW);
  digitalWrite(PIN_SEM1_AMARILLO, LOW);
  digitalWrite(PIN_SEM1_ROJO, HIGH);
  digitalWrite(PIN_SEM2_VERDE, LOW);
  digitalWrite(PIN_SEM2_AMARILLO, HIGH);
  digitalWrite(PIN_SEM2_ROJO, HIGH);
  delay(DURACION_ROJO_AMARILLO_MS);

  // Cuadro 5: Semáforo 1 ROJO / Semáforo 2 VERDE — ahora circula la otra calle.
  digitalWrite(PIN_SEM1_VERDE, LOW);
  digitalWrite(PIN_SEM1_AMARILLO, LOW);
  digitalWrite(PIN_SEM1_ROJO, HIGH);
  digitalWrite(PIN_SEM2_VERDE, HIGH);
  digitalWrite(PIN_SEM2_AMARILLO, LOW);
  digitalWrite(PIN_SEM2_ROJO, LOW);
  delay(DURACION_VERDE_MS);

  // Cuadro 6: Semáforo 1 ROJO / Semáforo 2 AMARILLO.
  digitalWrite(PIN_SEM1_VERDE, LOW);
  digitalWrite(PIN_SEM1_AMARILLO, LOW);
  digitalWrite(PIN_SEM1_ROJO, HIGH);
  digitalWrite(PIN_SEM2_VERDE, LOW);
  digitalWrite(PIN_SEM2_AMARILLO, HIGH);
  digitalWrite(PIN_SEM2_ROJO, LOW);
  delay(DURACION_AMARILLO_MS);

  // Cuadro 7: TODO ROJO — otra vez el margen de seguridad.
  digitalWrite(PIN_SEM1_VERDE, LOW);
  digitalWrite(PIN_SEM1_AMARILLO, LOW);
  digitalWrite(PIN_SEM1_ROJO, HIGH);
  digitalWrite(PIN_SEM2_VERDE, LOW);
  digitalWrite(PIN_SEM2_AMARILLO, LOW);
  digitalWrite(PIN_SEM2_ROJO, HIGH);
  delay(DURACION_TODO_ROJO_MS);

  // Cuadro 8: Semáforo 1 ROJO + AMARILLO / Semáforo 2 ROJO.
  digitalWrite(PIN_SEM1_VERDE, LOW);
  digitalWrite(PIN_SEM1_AMARILLO, HIGH);
  digitalWrite(PIN_SEM1_ROJO, HIGH);
  digitalWrite(PIN_SEM2_VERDE, LOW);
  digitalWrite(PIN_SEM2_AMARILLO, LOW);
  digitalWrite(PIN_SEM2_ROJO, HIGH);
  delay(DURACION_ROJO_AMARILLO_MS);

  // loop() vuelve a empezar: el cuadro 9 del cómic es igual al cuadro 1.
}
La secuencia de ocho estados para los dos semáforos de la intersección.

Cierre del nivel

Al terminar este nivel, la intersección completa funciona con una sola placa: los seis LED conectados, y los dos semáforos recorriendo en ciclo la secuencia de ocho estados, con sus momentos de “todo rojo” y de rojo + amarillo. Además, podés contar la secuencia cuadro a cuadro, como un cómic, que es la mejor prueba de que la entendiste. Si habías postergado la soldadura y el ensamblado de las maquetas, este es el momento de retomar el paso 5 del nivel inicial para las dos columnas. En el nivel avanzado, la central de tránsito va a poder mirar esta esquina desde internet: vamos a sumarle Internet de las Cosas.

Evidencia de cierre

Al terminar este nivel, esto tiene que estar funcionando:

  • Los seis LED de la intersección (dos semáforos completos) están conectados a una sola placa, cada uno con su resistencia.
  • Los dos semáforos recorren la secuencia completa de ocho estados en ciclo, sin habilitar nunca las dos calles al mismo tiempo.
  • Podés explicar la secuencia cuadro a cuadro, como las viñetas de un cómic, incluyendo los momentos en que ambos semáforos quedan en rojo.