No aclaras cosas como qué pasa si ya todos los LEDs están encendidos y continuamos dándole al botón. Ni tampoco qué ha de hacer si le damos una vez, se enciende el primero, esperamos a que se apague y le volvemos a dar al botón. ¿Se ha encender el segundo, que es el que "le tocaba", o ha de volver a empezar por el primero? Así que como no aclaras cómo ha de funcionar, y aprovechando que han de estar encendidos siempre el mismo tiempo cada uno por separado, he asumido que se han de encender uno detrás de otro y si ya están todos encendidos ha de esperar a que se apague "el que le toca" para que haga caso del botón.
Yo habría usado clases, pero no quiero "liarlo" más con conceptos que no controla mucha gente (ver cómo se haría en el enlace que puse en mi primer comentario de este post). Así que me he limitado a usar una estructura para contener los datos de cada LED y crear un array de dicha estructura para "trabajar" con todos ellos. En el ejemplo he puesto sólo dos LEDs, pero si se desea se pueden añadir fácilmente más LEDs a controlar, con sólo añadir más elementos al array leds[] en su definición.
Tampoco he usado funciones miembro de la estructura (ya para eso habría hecho una clase y punto) sino que uso dos funciones para "tratar" los LEDs. Una es loopLed() que se encarga de controlar el tiempo que lleva encendido cada LED para apagarlo cuando deba. La otra es encenderLed() que controla si el LED está apagado o encendido para no "encenderlo" si ya lo está, a la vez que retorna 1 o 0 para que incremente o no el índice que controla cual va a ser el siguiente LED del array que se tiene que encender (cuando esté apagado).
En el loop() principal controlo el pulsador y sus posibles rebotes. Para ello digamos que uso una pequeña máquina de estados que controla si está "apagado", "encendido" o en "transición" para apagarse. Si está "apagado" en cuanto lo detecte pulsado pasa a "encendido" y trata de encender el LED pertinente. Si en ese momento hay rebotes, no importa, ya que pasa a "transición" y antes de llegar a "apagado" volverá a el estado "encendido" si finalmente se deja pulsado. Si el tiempo que está en "transición" es el suficiente (está sin pulsar el tiempo estipulado por TIEMPO_ANTI_REBOTE) pasará finalmente a "apagado". Sólo cuando pasa de "apagado" a "encendido" es cuando "manda la orden" de encender el LED, mientras que el paso de "transición" a "encendido" no hace nada (así es como nos libramos de un posible "efecto rebote").
La variable que controla cual es el siguiente LED a encender, indiceLedActual, se incrementa si el LED que se ha de encender estaba apagado. Y, para "volver" a cero cuando sobrepasa el último elemento del array, se le asigna el resto de la división entre el número de elementos del array leds[]. Si, por ejemplo, fueran 3 elementos el valor debería de estar entre 0 y 2. El resto de dividir 0 entre 3 es 0, el de 1 entre 3 es 1, el de 2 entre 3 es 2, el resto de dividir 3 entre 3 es 0 (con lo que "volvemos al principio"). El operador del resto de la división es %
Como puedes observar, no hay ningún delay(), sólo algún que otro millis(). Y el programa no se queda "bloqueado" en ningún bucle de gran duración o infinito.
Espero que esto te oriente y te de ideas de cómo hacer lo que deseas.
Aquí tienes el código:
const byte PIN_BOTON = 2; // Pin al que está conectado el botón. El otro lado del botón ha de estar a GND
const byte PIN_LED_1 = 3; // Pin al que se conecta el LED
const byte PIN_LED_2 = 6; // Pin al que se conecta el LED
const unsigned long TIEMPO_ANTI_REBOTE = 150; // Tiempo en milisegundos que ha de permanecer el pulsador inactivo para que filtre los posibles rebotes
const unsigned long TIEMPO_ENCENDIDO = 6000; // Tiempo en milisegundos que permanecerá encendido el LED
struct led_t {
byte pin;
bool encendido; // Controla si el LED está encendido
unsigned long instanteEncendido; // Guarda el valor de millis() cuando se encendió el LED por última vez
};
led_t leds[] = { // Definimos un array con tantos LEDs como se quiera controlar
{PIN_LED_1, false, 0}, // Siempre ha de inicializar con false
{PIN_LED_2, false, 0}
};
const byte CANTIDAD_LEDS = sizeof(leds) / sizeof(leds[0]); // Esto calcula la constante con el número de elementos del array
enum estadoPulsador_t { // Estados para controlar los posibles rebotes del pulsador
ESTADO_REPOSO, // Cuando no está pulsado
ESTADO_PULSADO, // Cuando está pulsado
ESTADO_TRANSICION // Cuando no está pulsado pero aún no ha pasado el tiempo para evitar el posible efecto rebote
};
estadoPulsador_t estadoPulsador = ESTADO_REPOSO; // Variable que controla los estados del botón
unsigned long instanteInicioTransicion; // Variable para controlar el tiempo de del rebote del botón
byte indiceLedActual = 0; // Índice que controla cual será el próximo LED a encender
// Esta función se ha de llamar frecuentemente para que controle el tiempo que están los LEDs encendidos (sin bloquear)
void loopLed(led_t &led) {
// Verificamos si el LED está encendido y ha transcurrido el tiempo que ha de estar encendido
if ((led.encendido == true) && ((millis() - led.instanteEncendido) >= TIEMPO_ENCENDIDO)) {
digitalWrite(led.pin, LOW); // Apagamos el LED
led.encendido = false; // Indicamos que el LED no está encendido
}
}
// Para encender el LED se llama a esta función.
// Si el LED ya estaba encendido no hace nada y retorna 0 para que no incremente el índice.
// Si enciende el LED retorna 1 para que incremente el índice que controla el próximo LED a encender
byte encenderLed(led_t &led) {
if (led.encendido) {
// Si el LED ya está encendido, habrá que esperar a que se apague
return 0;
}
led.instanteEncendido = millis(); // Guardamos el instante en que encendemos el LED
digitalWrite(led.pin, HIGH); // Encendemos el led
led.encendido = true; // Indicamos que el LED está encendido
return 1; // Pasa al siguiente LED
}
void setup() {
pinMode(PIN_BOTON, INPUT_PULLUP);
for (byte i = 0; i < CANTIDAD_LEDS; i++) { // Con este bucle configuramos como salida el pin de todos los LEDs
pinMode(leds[i].pin, OUTPUT);
}
}
void loop() {
// Controlamos el botón
if (digitalRead(PIN_BOTON) == LOW) {
// En este instante el botón está pulsado
if (estadoPulsador == ESTADO_REPOSO) {
// Hasta ahora no estaba pulsado
estadoPulsador = ESTADO_PULSADO; // Ahora está pulsado
// Se llama a la funcíón para encender el LED "actual".
// Si ya estaba encendido retornará 0 y no incrementa indiceLedActual.
// Si estaba apagado enciende el LED, retorna 1 e incrementa indiceLedActual.
// Para que "vuelva" a 0 cuando se "apunta" más allá del último LED,
// se le asigna el resto de la división de (indiceLedActual + encenderLed()) entre CANTIDAD_LEDS
indiceLedActual = (indiceLedActual + encenderLed(leds[indiceLedActual])) % CANTIDAD_LEDS;
}
else if (estadoPulsador == ESTADO_TRANSICION) {
// Se encontraba en medio de la transición (un posible rebote) así que lo "mantiene" pulsado y no hace nada con los LEDs
estadoPulsador = ESTADO_PULSADO;
}
}
else {
// En este instante el botón no está pulsado
if (estadoPulsador == ESTADO_PULSADO) {
// Hasta ahora el botón estaba pulsado, con lo que a partir de este instante comienza "la transición"
estadoPulsador = ESTADO_TRANSICION;
instanteInicioTransicion = millis(); // Guardamos el instante en que comienza la transición para controlar el tiempo
}
else if (estadoPulsador == ESTADO_TRANSICION) {
// Hasta ahora el botón ha estado sin pulsar durante la trancisión
// Verifiamos si ha transcurrido el tiempo "sin rebotes"
if ((millis() - instanteInicioTransicion) >= TIEMPO_ANTI_REBOTE) {
// Como ha transcurrido el tiempo de transición sin rebotes, pasamos al estado de "reposo"
estadoPulsador = ESTADO_REPOSO;
}
}
}
// Con este bulce controlamos el tiempo que ha de estar encendido todos los LEDs
for (byte i = 0; i < CANTIDAD_LEDS; i++) {
loopLed(leds[i]);
}
}