Arduino Mega 2560 Control 9 LEDs

Arquitectura No Bloqueante & Máquina de Estados

Documentación del Sistema de Control de LEDs

Aprende cómo controlar 9 LEDs de forma concurrente con 4 pulsadores en lógica invertida utilizando una Máquina de Estados Finitos (FSM) y temporización asíncrona mediante millis().

No Bloqueante (millis)

Sustituye completamente la función delay(). Esto permite que el микроcontrolador ejecute la lectura de pulsadores en tiempo real sin detener el flujo del programa durante las secuencias temporizadas.

Máquina de Estados (FSM)

Organiza el comportamiento del sistema en 4 modos definidos (MODO_APAGADO, MODO_ENCENDIDO, MODO_LLENADO y MODO_SECUENCIA_1SEG) garantizando un cambio seguro e inmediato de rutina.

Pulls-Up & Debounce

Usa las resistencias internas INPUT_PULLUP (activación por nivel LOW) junto a un filtro por software de 50 ms para eliminar el ruido eléctrico de rebotes en las pulsaciones.

Panel de Pruebas

Simulador Virtual en Tiempo Real

Modo Actual: MODO_APAGADO
PANEL DE LEDS (PINES DIGITALES 13 AL 5) 1x Velocidad
P13
LED 1
P12
LED 2
P11
LED 3
P10
LED 4
P9
LED 5
P8
LED 6
P7
LED 7
P6
LED 8
P5
LED 9
Monitor Serie Virtual (115200 baud)
[0 ms] Sistema iniciado correctamente.

Asignación de Pines & Configuración de Hardware

Mapeo físico de conexiones del microcontrolador Arduino Mega 2560.

Salidas: Matriz de 9 LEDs

Índice Arreglo Pin Mega Función Lógica
PINES_LEDS[0] Pin 13 LED 1 (Inicio) HIGH = ON
PINES_LEDS[1..7] Pines 12 a 6 LEDs 2 al 8 HIGH = ON
PINES_LEDS[8] Pin 5 LED 9 (Fin) HIGH = ON

Entradas: 4 Pulsadores (Invertidos)

Pin Mega Variable Modo Asignado Modo Entrada
Pin 28 PIN_BTN_ON MODO_ENCENDIDO INPUT_PULLUP
Pin 26 PIN_BTN_OFF MODO_APAGADO INPUT_PULLUP
Pin 24 PIN_BTN_FILL MODO_LLENADO INPUT_PULLUP
Pin 22 PIN_BTN_CHASE MODO_SECUENCIA_1SEG INPUT_PULLUP

Análisis del Código Paso a Paso

Explicación por bloques funcionales del programa C++.

BLOQUE 1

Definiciones & Constantes

Se define el vector de pines PINES_LEDS y se calcula dinámicamente el total con sizeof. Además, se crea un tipo de dato enumerado enum ModoOperacion que define claramente cada estado.

// Lista de pines para los 9 LEDs (Del 13 al 5)
const uint8_t PINES_LEDS[] = {13, 12, 11, 10, 9, 8, 7, 6, 5};

// Estados posibles del sistema
enum ModoOperacion {
  MODO_APAGADO,
  MODO_ENCENDIDO,
  MODO_LLENADO,
  MODO_SECUENCIA_1SEG
};
ModoOperacion modoActual = MODO_APAGADO;
BLOQUE 2

Setup e Inicialización

Configura las salidas de los LEDs mediante un bucle for y asigna la entrada de los botones de forma dinámica según la variable ESTADO_ACTIVO (INPUT_PULLUP).

void setup() {
  Serial.begin(115200);
  for (uint8_t i = 0; i < TOTAL_LEDS; i++) {
    pinMode(PINES_LEDS[i], OUTPUT);
    digitalWrite(PINES_LEDS[i], LOW);
  }
  uint8_t modoEntrada = (ESTADO_ACTIVO == LOW) ? INPUT_PULLUP : INPUT;
  pinMode(PIN_BTN_ON,   modoEntrada); // Pin 28
  // ... resto de botones
}
BLOQUE 3

Lectura con Debounce

La función verificarPulsadores() mide la diferencia entre millis() y ultimoDebounce. Solo responde cuando han transcurrido más de 50 ms y detecta el flanco de bajada.

if (ahora - ultimoDebounce > TIEMPO_DEBOUNCE) {
  if (lOn == ESTADO_ACTIVO && estadoPrevioBtnOn != ESTADO_ACTIVO) {
    modoActual = MODO_ENCENDIDO;
    encenderTodosLeds();
    ultimoDebounce = ahora;
  }
  // ... evaluación de demás botones
}
BLOQUE 4

Actualización de Secuencias

La función actualizarSecuencias() ejecuta un switch(modoActual) que conmuta el estado de los LEDs según el intervalo correspondiente (250ms, 600ms o 1000ms).

case MODO_SECUENCIA_1SEG: {
  if (ahora - tiempoUltimaAccion >= INTERVALO_CHASE) { // 1000 ms
    tiempoUltimaAccion = ahora;
    digitalWrite(PINES_LEDS[pasoSecuencia], LOW);  // Apaga anterior
    pasoSecuencia = (pasoSecuencia + 1) % TOTAL_LEDS;
    digitalWrite(PINES_LEDS[pasoSecuencia], HIGH); // Enciende siguiente
  }
  break;
}

Código Completo C++

Listo para compilar en Arduino IDE para la placa Arduino Mega 2560.

Control_9LEDs_Mega2560.ino C++ / Arduino IDE
/*
 * Control de 9 LEDs con 4 Pulsadores - Arduino Mega 2560
 * Arquitectura no bloqueante con millis() y Máquina de Estados
 * (Pulsadores en orden y lógica inversa)
 */

const uint8_t PINES_LEDS[] = {13, 12, 11, 10, 9, 8, 7, 6, 5};
const uint8_t TOTAL_LEDS   = sizeof(PINES_LEDS) / sizeof(PINES_LEDS[0]); // 9 LEDs

// ==========================================
// ASIGNACIÓN DE PINES (ORDEN INVERTIDO)
// ==========================================
const uint8_t PIN_BTN_ON    = 28; // Ahora 28: Todos encendidos
const uint8_t PIN_BTN_OFF   = 26; // Ahora 26: Todos apagados
const uint8_t PIN_BTN_FILL  = 24; // Ahora 24: Llenado progresivo (13 -> 5)
const uint8_t PIN_BTN_CHASE = 22; // Ahora 22: Secuencia individual 1 seg (13 -> 5)

// LÓGICA ELÉCTRICA:
// LOW  -> si el pulsador va conectado a GND (con INPUT_PULLUP interno)
// HIGH -> si el pulsador va conectado a 5V con resistencia pull-down externa
const bool ESTADO_ACTIVO = LOW;

enum ModoOperacion {
  MODO_APAGADO,
  MODO_ENCENDIDO,
  MODO_LLENADO,
  MODO_SECUENCIA_1SEG
};

ModoOperacion modoActual = MODO_APAGADO;

unsigned long tiempoUltimaAccion = 0;
uint8_t pasoSecuencia = 0;

const unsigned long INTERVALO_LLENADO_PASO = 250;  // 250 ms entre LEDs en llenado
const unsigned long INTERVALO_LLENADO_HOLD = 600;  // Pausa con todos encendidos
const unsigned long INTERVALO_CHASE        = 1000; // 1 segundo exacto por LED

// Antirrebote (debounce)
unsigned long ultimoDebounce = 0;
const unsigned long TIEMPO_DEBOUNCE = 50;

bool estadoPrevioBtnOn    = !ESTADO_ACTIVO;
bool estadoPrevioBtnOff   = !ESTADO_ACTIVO;
bool estadoPrevioBtnFill  = !ESTADO_ACTIVO;
bool estadoPrevioBtnChase = !ESTADO_ACTIVO;

void apagarTodosLeds() {
  for (uint8_t i = 0; i < TOTAL_LEDS; i++) {
    digitalWrite(PINES_LEDS[i], LOW);
  }
}

void encenderTodosLeds() {
  for (uint8_t i = 0; i < TOTAL_LEDS; i++) {
    digitalWrite(PINES_LEDS[i], HIGH);
  }
}

void setup() {
  Serial.begin(115200);

  // Configurar pines de LEDs como salidas
  for (uint8_t i = 0; i < TOTAL_LEDS; i++) {
    pinMode(PINES_LEDS[i], OUTPUT);
    digitalWrite(PINES_LEDS[i], LOW);
  }

  // Configurar modo de entrada según la lógica elegida
  uint8_t modoEntrada = (ESTADO_ACTIVO == LOW) ? INPUT_PULLUP : INPUT;
  pinMode(PIN_BTN_ON,    modoEntrada);
  pinMode(PIN_BTN_OFF,   modoEntrada);
  pinMode(PIN_BTN_FILL,  modoEntrada);
  pinMode(PIN_BTN_CHASE, modoEntrada);

  Serial.println(F("Sistema iniciado correctamente."));
}

void verificarPulsadores() {
  unsigned long ahora = millis();

  bool lOn    = digitalRead(PIN_BTN_ON);
  bool lOff   = digitalRead(PIN_BTN_OFF);
  bool lFill  = digitalRead(PIN_BTN_FILL);
  bool lChase = digitalRead(PIN_BTN_CHASE);

  if (ahora - ultimoDebounce > TIEMPO_DEBOUNCE) {
    if (lOn == ESTADO_ACTIVO && estadoPrevioBtnOn != ESTADO_ACTIVO) {
      modoActual = MODO_ENCENDIDO;
      encenderTodosLeds();
      ultimoDebounce = ahora;
      Serial.println(F("[PIN 28] MODO_ENCENDIDO activo"));
    }
    else if (lOff == ESTADO_ACTIVO && estadoPrevioBtnOff != ESTADO_ACTIVO) {
      modoActual = MODO_APAGADO;
      apagarTodosLeds();
      ultimoDebounce = ahora;
      Serial.println(F("[PIN 26] MODO_APAGADO activo"));
    }
    else if (lFill == ESTADO_ACTIVO && estadoPrevioBtnFill != ESTADO_ACTIVO) {
      modoActual = MODO_LLENADO;
      pasoSecuencia = 0;
      tiempoUltimaAccion = ahora;
      apagarTodosLeds();
      digitalWrite(PINES_LEDS[0], HIGH); // Enciende Pin 13
      ultimoDebounce = ahora;
      Serial.println(F("[PIN 24] MODO_LLENADO activo"));
    }
    else if (lChase == ESTADO_ACTIVO && estadoPrevioBtnChase != ESTADO_ACTIVO) {
      modoActual = MODO_SECUENCIA_1SEG;
      pasoSecuencia = 0;
      tiempoUltimaAccion = ahora;
      apagarTodosLeds();
      digitalWrite(PINES_LEDS[0], HIGH); // Enciende Pin 13
      ultimoDebounce = ahora;
      Serial.println(F("[PIN 22] MODO_SECUENCIA_1SEG activo"));
    }
  }

  estadoPrevioBtnOn    = lOn;
  estadoPrevioBtnOff   = lOff;
  estadoPrevioBtnFill  = lFill;
  estadoPrevioBtnChase = lChase;
}

void actualizarSecuencias() {
  unsigned long ahora = millis();

  switch (modoActual) {
    case MODO_APAGADO:
    case MODO_ENCENDIDO:
      break;

    case MODO_LLENADO: {
      unsigned long lapso = (pasoSecuencia < TOTAL_LEDS) 
                            ? INTERVALO_LLENADO_PASO 
                            : INTERVALO_LLENADO_HOLD;

      if (ahora - tiempoUltimaAccion >= lapso) {
        tiempoUltimaAccion = ahora;
        pasoSecuencia++;

        if (pasoSecuencia < TOTAL_LEDS) {
          digitalWrite(PINES_LEDS[pasoSecuencia], HIGH); // Acumula encendido
        } else if (pasoSecuencia == TOTAL_LEDS) {
          // Pausa con todos encendidos (espera INTERVALO_LLENADO_HOLD)
        } else {
          apagarTodosLeds();
          pasoSecuencia = 0;
          digitalWrite(PINES_LEDS[0], HIGH); // Reinicia ciclo con Pin 13
        }
      }
      break;
    }

    case MODO_SECUENCIA_1SEG: {
      if (ahora - tiempoUltimaAccion >= INTERVALO_CHASE) {
        tiempoUltimaAccion = ahora;
        digitalWrite(PINES_LEDS[pasoSecuencia], LOW); // Apaga el actual
        
        pasoSecuencia++;
        if (pasoSecuencia >= TOTAL_LEDS) {
          pasoSecuencia = 0;
        }
        
        digitalWrite(PINES_LEDS[pasoSecuencia], HIGH); // Enciende el siguiente
      }
      break;
    }
  }
}

void loop() {
  verificarPulsadores();
  actualizarSecuencias();
}