∞
Arduino Mega 2560 Práctica 2 Maqueta Interactiva con millis() • Salidas 13-5 & Entradas 28-22
Temporización No Bloqueante Multitarea con millis()

Secuenciador de 9 LEDs con Arduino Mega

Control de 9 diodos LED con 4 pulsadores independientes. Implementación estructurada con máquina de estados finitos y sin interrupciones bloqueantes delay(), permitiendo una lectura instantánea y receptiva de los mandos en todo momento.

Panel Virtual de la Maqueta
Modo Actual: Todos Apagados millis() Virtual: 0 ms

Barra de 9 LEDs (Salidas Digitales Arduino Mega)

Conexión: Pin 13 al Pin 5 con resistencias limitadoras a GND

D13
LED 1 Rojo
D12
LED 2 Ámbar
D11
LED 3 Verde
D10
LED 4 Rojo
D9
LED 5 Ámbar
D8
LED 6 Verde
D7
LED 7 Rojo
D6
LED 8 Ámbar
D5
LED 9 Verde
LEDs activos actualmente: 0 de 9
Paso de secuencia: - Frecuencia: 150 ms

4 Pulsadores de Control (Entradas Digitales Pin 28, 26, 24, 22)

Haz clic en cada botón o usa las teclas 1, 2, 3, 4 de tu teclado

Velocidad / Intervalo: 150 ms

🔌 Asignación de Pines y Circuito Eléctrico

Detalle del montaje en la maqueta Arduino Mega 2560 y cálculo de resistencias.

Salidas Digitales (9 Diodos LED) pinMode(pin, OUTPUT)
LED Pin Arduino Color Conexión Resistencia
LED 1Pin 13RojoÁnodo Pin 13 / Cátodo GND220Ω - 330Ω
LED 2Pin 12ÁmbarÁnodo Pin 12 / Cátodo GND220Ω - 330Ω
LED 3Pin 11VerdeÁnodo Pin 11 / Cátodo GND220Ω - 330Ω
LED 4Pin 10RojoÁnodo Pin 10 / Cátodo GND220Ω - 330Ω
LED 5Pin 9ÁmbarÁnodo Pin 9 / Cátodo GND220Ω - 330Ω
LED 6Pin 8VerdeÁnodo Pin 8 / Cátodo GND220Ω - 330Ω
LED 7Pin 7RojoÁnodo Pin 7 / Cátodo GND220Ω - 330Ω
LED 8Pin 6ÁmbarÁnodo Pin 6 / Cátodo GND220Ω - 330Ω
LED 9Pin 5VerdeÁnodo Pin 5 / Cátodo GND220Ω - 330Ω
Entradas Digitales (4 Pulsadores) pinMode(pin, INPUT)
Pulsador Pin Arduino Acción Ejecutada Estado Activo
Pulsador 1Pin 28Enciende todos los LEDsHIGH (+5V)
Pulsador 2Pin 26Apaga todos los LEDsHIGH (+5V)
Pulsador 3Pin 24Secuencia 3 (Marquesina cíclica)HIGH (+5V)
Pulsador 4Pin 22Secuencia 4 (Acumulativo cíclico)HIGH (+5V)

⚡ ¿Cómo está conectado el circuito Pull-Down en la maqueta?

En la maqueta web, cada pulsador tiene un terminal conectado a la línea de +5V y el otro terminal conectado simultáneamente al pin digital del Arduino y a GND a través de una resistencia de 4.7 kΩ (amarillo-violeta-rojo).

EN REPOSO (SOLTADO): digitalRead() == LOW (0V)
ACCIONADO (PULSADO): digitalRead() == HIGH (5V)

💻 Código Fuente Arduino (.ino)

Implementación limpia, modular, documentada y lista para cargar en Arduino IDE.

Descargar .ino
maqueta_mega_practica2.ino C++ / Wiring • Arduino Mega 2560
/*
  =============================================================================
  PROYECTO: Maqueta Arduino Mega 2560 - Práctica 2
  SECUENCIA DE LEDS Y PULSADORES MULTITAREA CON MILLIS()
  =============================================================================
*/

// --- ASIGNACIÓN DE PINES ---
const uint8_t PINES_LEDS[] = {13, 12, 11, 10, 9, 8, 7, 6, 5};
const uint8_t NUM_LEDS     = sizeof(PINES_LEDS) / sizeof(PINES_LEDS[0]);

// Pulsadores reasignados (orden invertido: 28, 26, 24, 22)
const uint8_t PIN_PUL1 = 28; // Ahora el pin 28 enciende todos los LEDs
const uint8_t PIN_PUL2 = 26; // Ahora el pin 26 apaga todos los LEDs
const uint8_t PIN_PUL3 = 24; // Ahora el pin 24 activa la Secuencia 3
const uint8_t PIN_PUL4 = 22; // Ahora el pin 22 activa la Secuencia 4

// --- MÁQUINA DE ESTADOS ---
enum ModoOperacion {
  MODO_APAGADO,
  MODO_ENCENDIDO,
  MODO_SECUENCIA_3,
  MODO_SECUENCIA_4
};

ModoOperacion modoActual = MODO_APAGADO;

// --- TEMPORIZACIÓN CON MILLIS() ---
unsigned long tiempoUltimoPaso = 0;
const unsigned long INTERVALO_ANIMACION = 150; // milisegundos por paso

int pasoActual = 0; // Índice de avance en las secuencias

// --- ANTIRREBOTE (DEBOUNCE) Y FLANCOS ---
unsigned long tiempoUltimoRebote = 0;
const unsigned long TIEMPO_DEBOUNCE = 50; // 50 ms

int estadoPrevioPul1 = LOW;
int estadoPrevioPul2 = LOW;
int estadoPrevioPul3 = LOW;
int estadoPrevioPul4 = LOW;

void setup() {
  Serial.begin(9600);
  
  // Salidas para los 9 LEDs
  for (uint8_t i = 0; i < NUM_LEDS; i++) {
    pinMode(PINES_LEDS[i], OUTPUT);
  }

  // Entradas para los 4 pulsadores (Pull-Down externa)
  pinMode(PIN_PUL1, INPUT);
  pinMode(PIN_PUL2, INPUT);
  pinMode(PIN_PUL3, INPUT);
  pinMode(PIN_PUL4, INPUT);

  apagarTodos();
}

void loop() {
  leerPulsadores();

  switch (modoActual) {
    case MODO_ENCENDIDO:
      // Permanecen encendidos
      break;

    case MODO_APAGADO:
      // Permanecen apagados
      break;

    case MODO_SECUENCIA_3:
      secuencia3(INTERVALO_ANIMACION);
      break;

    case MODO_SECUENCIA_4:
      secuencia4(INTERVALO_ANIMACION);
      break;
  }
}

// Apaga todos los 9 LEDs
void apagarTodos() {
  for (uint8_t i = 0; i < NUM_LEDS; i++) {
    digitalWrite(PINES_LEDS[i], LOW);
  }
}

// Enciende todos los 9 LEDs
void encenderTodos() {
  for (uint8_t i = 0; i < NUM_LEDS; i++) {
    digitalWrite(PINES_LEDS[i], HIGH);
  }
}

// SECUENCIA 3: Enciende un LED y apaga el anterior (Cíclico)
void secuencia3(unsigned long retardo) {
  unsigned long tiempoActual = millis();

  if (tiempoActual - tiempoUltimoPaso >= retardo) {
    tiempoUltimoPaso = tiempoActual;

    apagarTodos();
    digitalWrite(PINES_LEDS[pasoActual], HIGH);

    pasoActual++;
    if (pasoActual >= NUM_LEDS) {
      pasoActual = 0; // Vuelve al primer LED (Pin 13)
    }
  }
}

// SECUENCIA 4: Enciende acumulando hasta el último y luego apaga todo (Cíclico)
void secuencia4(unsigned long retardo) {
  unsigned long tiempoActual = millis();

  if (tiempoActual - tiempoUltimoPaso >= retardo) {
    tiempoUltimoPaso = tiempoActual;

    if (pasoActual < NUM_LEDS) {
      digitalWrite(PINES_LEDS[pasoActual], HIGH);
      pasoActual++;
    } else {
      apagarTodos();
      pasoActual = 0; // Reinicia el ciclo
    }
  }
}

// LECTURA DE PULSADORES CON DEBOUNCE
void leerPulsadores() {
  unsigned long tiempoActual = millis();
  if (tiempoActual - tiempoUltimoRebote < TIEMPO_DEBOUNCE) return;

  int lecturaPul1 = digitalRead(PIN_PUL1);
  int lecturaPul2 = digitalRead(PIN_PUL2);
  int lecturaPul3 = digitalRead(PIN_PUL3);
  int lecturaPul4 = digitalRead(PIN_PUL4);

  // Pulsador 1: Encender todos
  if (lecturaPul1 == HIGH && estadoPrevioPul1 == LOW) {
    modoActual = MODO_ENCENDIDO;
    encenderTodos();
    tiempoUltimoRebote = tiempoActual;
  }
  // Pulsador 2: Apagar todos
  else if (lecturaPul2 == HIGH && estadoPrevioPul2 == LOW) {
    modoActual = MODO_APAGADO;
    apagarTodos();
    tiempoUltimoRebote = tiempoActual;
  }
  // Pulsador 3: Secuencia 3 (Marquesina cíclica)
  else if (lecturaPul3 == HIGH && estadoPrevioPul3 == LOW) {
    modoActual = MODO_SECUENCIA_3;
    apagarTodos();
    pasoActual = 0;
    tiempoUltimoPaso = tiempoActual - INTERVALO_ANIMACION;
    tiempoUltimoRebote = tiempoActual;
  }
  // Pulsador 4: Secuencia 4 (Acumulativo cíclico)
  else if (lecturaPul4 == HIGH && estadoPrevioPul4 == LOW) {
    modoActual = MODO_SECUENCIA_4;
    apagarTodos();
    pasoActual = 0;
    tiempoUltimoPaso = tiempoActual - INTERVALO_ANIMACION;
    tiempoUltimoRebote = tiempoActual;
  }

  estadoPrevioPul1 = lecturaPul1;
  estadoPrevioPul2 = lecturaPul2;
  estadoPrevioPul3 = lecturaPul3;
  estadoPrevioPul4 = lecturaPul4;
}

🧠 Fundamentos: ¿Por qué usar millis() y no delay()?

Comparativa arquitectónica de temporización en sistemas embebidos.

❌ El problema de delay() [Bloqueante]

Anti-Patrón

Cuando se invoca delay(150), el microcontrolador entra en un bucle ciego de conteo de ciclos de reloj. Durante ese tiempo:

  • No se pueden leer pulsadores: Si el usuario pulsa un botón durante el delay, la pulsación se pierde por completo.
  • Paralización total: Ninguna otra tarea, comunicación serial o monitoreo puede ejecutarse.
  • Animaciones toscas: Cambiar de secuencia requiere esperar a que termine el ciclo completo.

✅ La ventaja de millis() [No Bloqueante]

Multitarea

La función millis() devuelve el número de milisegundos transcurridos desde que arrancó el Arduino. Funciona como un cronómetro de fondo:

  • Respuesta inmediata: El bucle loop() se ejecuta decenas de miles de veces por segundo, detectando cualquier pulsación al instante.
  • Cambio de modo fluido: Al pulsar otro botón, se resetea el contador y la nueva animación comienza inmediatamente.
  • Escalable a nivel industrial: Permite coordinar múltiples temporizadores, alarmas y sensores concurrentemente.