Temporizador de encendido de leds

Lo primero de todo disculpa porque el código lo voy a escribir con el teléfono móvil, así que revísalo por si se me escapa algún caracter.

Mi segunda disculpa es que yo programo a mi manera. Me agobia el uso de mil variables o el uso de 10 líneas para algo que se hace en 1. Comprendo que mi código aunque más breve pueda ser difícil de leer a quienes empiezan.

Confío en que te sirva para ver un ejemplo :slight_smile:
Seguro ves algo nuevo.

/*
Indice 0. Pin boton
Indice 1. Pin led
Indice 2. Estado boton
Indice 3. Modo interruptor 
Indice 4. Contador tiempo luz
Indice 5. Contador debounce

Recuerda,  botones con resistencia 10k :) 
*/

unsigned int
btnA[6] ={2,6,0,0,0,0}, 
btnB[6] ={3,7,0,0,0,0} ;

void setup() {
   Serial.begin(9600);
   DDRD =B11110010; //Pin 2 y 3 como INPUT
}

void loop() {
   luz(btnA) ; //usando punteros
   luz(btnB) ;
} 

void luz( int  *arr ) {
   bool val = digitalRead(arr[0]); 
   if(arr[2] != val && millis() >= arr[5] + 150) { //debounce
      arr[2] = val; //valor como pulsador
      if(! arr[2] ) {
         arr[3]  = ! arr[3] ; //valor como interruptor
         if( arr[3] ) {
            arr[4] = millis() ;
         }else{
            Serial. println("El led del pin "+String(arr[1]) +" ha estado encendido " + String(millis() - arr[4]) +" milisegundos");
         } 
      } 
      arr[5] = millis() ; //reiniciar tiempo debounce
   } 
   digitalWrite(arr[1], arr[3]) ; //apagar encender led
}

Saludos.

Shadoaru, no entiendo qué pretendes hacer con la variable conteo, nunca le asignas un valor ni se lo cambias.

Por otro lado, si vas a usar variables para controlar milisegundos, mejor decláralas siempre de tipo unsigned long, para no tener problemas de desbordamientos y con números negativos. Así que declara la variable tempOn como unsigned long en lugar de int.

ArduMyth, muchas gracias por poner tu código y así se pueda ver un claro ejemplo de cómo no se ha de programar. Si ya un código medianamente "legible" es difícil de entender, depurar, mantener y adaptar; el código que tú has puesto está orientado a ofuscar y a no ser nada práctico ya que, por otro lado, derrochas memoria de mala al tratar todas las variables como tipo int. Cuando, si te fijas, en más de un caso con un byte tendrías (e incluso menos, si utilizas los campos de bits).

Pero bien pudiera ser que yo esté equivocado y la mejor manera de programar y enseñar a programar sea "tu estilo". Lo que entonces me pregunto para qué están las estructuras y las clases en un lenguaje de programación orientado a objetos como este, si con un simple array donde se mete todo junto y por igual ya tenemos más que suficiente. Así como la portabilidad que nos permite el entorno de Arduino en donde tenemos la función pinMode que nos abstrae del hardware que estamos usando y así no tener que preocuparnos más que del número asignado al pin según sea un Arduino UNO, un MEGA, un ESP8266 o un ESP32... Total, como nos conocemos al dedillo todos los registros de todos los micros que podemos manejar. Eso sin hablar de lo "fácil" que lo haces con "tu sistema" cuando decides no usar el pin 3, sino el 5 porque el cable me llega mejor a ese pin.

Pero bueno, que no descarto que yo esté equivocado. Y si es así, estoy dispuesto a retirar, entre otras, algunas de mis respuestas. Como la que hice al post Para qué sirve un enum? ya que está visto que lo de tener un código con mucha verborrea no es algo que parezca aconsejable, sino que cuanto más breve mejor.

Aunque, siguiendo tu criterio, no entiendo porqué pones comentarios. ¿No es escribir de más? El preprocesador del compilador es de lo primero que elimina del código antes de compilar.

1 Like

IgnoranteAbsoluto:
ArduMyth, muchas gracias por poner tu código y así se pueda ver un claro ejemplo de cómo no se ha de programar. Si ya un código medianamente "legible" es difícil de entender, depurar, mantener y adaptar; el código que tú has puesto está orientado a ofuscar y a no ser nada práctico ya que, por otro lado, derrochas memoria

Saludos. Curiosamente yo opino que tu código es el que está mal, incompleto, largo, ofuscado, ineficiente y ni sabes hacer lo más básico con millis()... Peeeero en ningún momento te dije que así no se debe programar, no cómo tú.

Si no sabes usar millis() ¿qué potestad tienes criticando a quien sí sólo porque tus conocimientos sean escasos?

Con otro tipo de comentario te habría explicado y me habría "adecuado" más a tu forma de programar.

Sin jaleos te deseo que consigas resolverlo. Por cierto, tema harto repetido en el foro cada semana con mil ejemplos. Vamos que si buscases un poquito...

IgnoranteAbsoluto:
Aunque, siguiendo tu criterio, no entiendo porqué pones comentarios. ¿No es escribir de más? El preprocesador del compilador es de lo primero que elimina del código antes de compilar.

Poner comentarios no es para quien hace el código es para quienes no lo han hecho y puedan guiarse. Ponerlos o no no es obligado, al igual que el resto de reglas que existen en la programación de buenas prácticas. Algo elemental y que es de lo primerito que se estudia. Suerte con tu código. Ánimo.

IgnoranteAbsoluto:
derrochas memoria de mala al tratar todas las variables como tipo int. Cuando, si te fijas, en más de un caso con un byte tendrías (e incluso menos, si utilizas los campos de bits).

Dí que sí. Guarda el valor de millis() en una variable de tipo byte y a ver que depura el IDE...

Saludos ArduMyth. Lo primero de todo es pedirte disculpas porque creo que he sido algo pedante y grosero contigo. No debería de tratar así a alguien nuevo en el foro que trata de ayudar desinteresadamente. Así que ruego sepas disculparme.

Aunque parezca sarcasmo, lo que dije:

IgnoranteAbsoluto:
… muchas gracias por poner tu código y así se pueda ver un claro ejemplo de cómo no se ha de programar.

… lo dije en serio y con toda buena intención (tal vez no con buenos modales). Creo que no se debería de poner ejemplos y explicaciones ofuscadas para aquellos que son nuevos, están aprendiendo y carecen de muchos conocimientos y experiencia en la programación. Además de ser un muy mal hábito el “programar para uno”, sin pensar en “los demás” ya que, en el caso de necesitar ayuda, tu código puede resultar “áspero” y poco legible para otros. Así que, en serio, creo que has puesto un claro ejemplo de cómo no programar.

ArduMyth:
Curiosamente yo opino que tu código es el que está mal, incompleto, largo, ofuscado, ineficiente y ni sabes hacer lo más básico con millis()... Peeeero en ningún momento te dije que así no se debe programar, no cómo tú.

Si no sabes usar millis() ¿qué potestad tienes criticando a quien sí sólo porque tus conocimientos sean escasos?

Con otro tipo de comentario te habría explicado y me habría "adecuado" más a tu forma de programar.

Creo que te has confundido, quien hace la consulta y necesita ayuda no soy yo, sino Shadoaru. Soy yo, y no él, quien ha criticado tu código. No critico la escasez de comentarios, sino el mal uso de nombres de las variables y, sobre todo, el usar un array como único contenedor de datos que son muy diferentes y heterogéneos entre sí. Ya que deberían de ser de tipos diferentes, sería preferible usar una simple estructura con la que poder especificar tipos y nombres diferentes a cada uno de los elementos que conjuntamente controlan lo que deseamos hacer. Precisamente en mi respuesta a Shadoaru le remito a un post donde explico paso a paso cómo hacerlo, así como el recorrido “lógico” que lleva al resultado final.

En cuanto a lo que dije yo, no Shadoaru, de...

IgnoranteAbsoluto:
Aunque, siguiendo tu criterio, no entiendo porqué pones comentarios. ¿No es escribir de más? El preprocesador del compilador es de lo primero que elimina del código antes de compilar.

… confieso que quería ser sarcástico. Mis disculpas.

Respecto a:

ArduMyth:
Dí que sí. Guarda el valor de millis() en una variable de tipo byte y a ver que depura el IDE...

Había dicho claramente:

IgnoranteAbsoluto:
...derrochas memoria de mala al tratar todas las variables como tipo int. Cuando, si te fijas, en más de un caso con un byte tendrías (e incluso menos, si utilizas los campos de bits).

en más de un caso”, no para todos los casos. De hecho, para controlar los “famosos” millis(), es aconsejable utilizar variables de tipo unsigned long, en lugar del tipo unsigned int, ya que con este último apenas se puede “manejar” poco más de seis segundos. Es por eso que, en mi respuesta a Shadoaru, le aconsejo el uso de unsigned long en lugar de int.

Así que, si fuera necesario controlar tiempos de más de seis segundos y medio, ¿tú lo que harías es declarar el array de tipo unsigned long? ¿Consumiendo así el doble de memoria en lugar de cuatro bytes más? (Pasaría de un array de 6 unsigned int, un total de 12 bytes, a un array de 6 unsingned long, un total de 24 bytes por array).

Mi “propuesta rápida” de “correción de estilo” a tu código, sin saber si funciona o no, y sólo sustituyendo el array por una simple estructura, sería esta:

struct Boton {
    byte pinBoton;
    byte pinLed;
    bool estadoBoton : 1;
    bool modoInterruptor : 1;
    unsigned long contadorTiempoLuz;
    unsigned long contadorDebounce;
} botonA = {2, 6, 0, 0, 0, 0},
  botonB = {3, 7, 0, 0, 0, 0};

void setup() {
   Serial.begin(9600);
   DDRD =B11110010; //Pin 2 y 3 como INPUT
}

void loop() {
   luz(botonA) ; // Usamos el paso por referencia
   luz(botonA) ;
}

void luz(Boton &b) { // Usamos el paso por referencia
   bool val = digitalRead(b.pinBoton);
   if(b.estadoBoton != val && (millis() - b.contadorDebounce >= 150)) { //debounce
      b.estadoBoton = val; //valor como pulsador
      if(! b.estadoBoton ) {
         b.modoInterruptor  = ! b.modoInterruptor ; //valor como interruptor
         if( b.modoInterruptor ) {
            b.contadorTiempoLuz = millis() ;
         }else{
            Serial. println("El led del pin "+String(b.pinLed) +" ha estado encendido " + String(millis() - b.contadorTiempoLuz) +" milisegundos");
         }
      }
      b.contadorDebounce = millis() ; //reiniciar tiempo debounce
   }
   digitalWrite(b.pinLed, b.modoInterruptor) ; //apagar encender led
}

Bueno, como no te gusta “escribir de más”, en la función luz en lugar de llamar el parámetro boton lo he llamado simplemente b. Después de todo, cada campo de la estructura nos deja claro de qué se trata. Así que nos podemos tomar esa licencia.

La estructura finalmente tiene un tamaño de 11 bytes (aprovechando los campos de bits para los dos bool). Los tiempos se controlan con unsigned long, así que podrán ser mayores a seis segundos y medio.

¡Ah! Otra cosa. Como he dicho, no sé si tu propuesta funciona o no, sólo he cambiado “el estilo” y corregido una única cosa. Mira que como tú bien dices es un tema harto repetido en el foro cada semana con mil ejemplos... y aún así siempre hay a quien se le escapa el uso correcto de los millis() a la hora de verificar si ha transcurrido el tiempo deseado, como es tu caso. No has de hacerlo así:

millis() >= arr[5] + 150

La forma correcta de hacerlo es así:

millis() - arr[5] >= 150

Busca en los foros o en Google, hay muchas explicaciones del porqué y del problema que se da cuando se desborda el contador de los millis(). Pero supongo que eso tú ya lo sabías y lo tenías más que controlado (nótese el sarcasmo).

Una vez más, ruego disculpes mi pedantería y si he sido grosero contigo. Te animo a que sigas ayudando en el foro. No todo el mundo lo sabe todo, y doy por hecho de que tú sabes mucho, así que espero que lo compartas poco a poco con todos.

Leyendo consultas y respuestas de este foro he descubierto que estaba equivocado en alguna que otra cosa. A la vez que he descubierto y aprendido cosas nuevas.

Los fallos nos pueden servir para aprender, los aciertos sólo nos sirven para confirmarnos lo que creemos saber.

No estoy muy seguro de las normas de este foro pero esto empieza a ser offtopic. Yo planteé una manera. Si no te gusta me alegro y si querías, como dices, ser irónico y pedante de entrada las posteriores disculpas son vacías. Si quieres criticarme al menos hazlo vía privado y no distorciones un ejemplo a un offtopic criticando hasta que ponga comentarios. Ya te has retratado. Be happy.

Lo que has dicho al final de milis()... Tan sólo coge una calculadora o aprende ecuaciones de primer grado.

Podría exponer otras formas partiendo con tu manera de hacer las cosas e igual más cortas, pero creo haber tropezado con un usuario un poco sobrado vía internet.

No confundas el número de post en un foro con otras cuestiones.

Saludos y fin. Evita esas respuestas conmigo cuando escriba algo. Si no te gusta no sé, ponte con otro usuario que aguante tus formas. Sólo quise exponer un ejemplo, todo lo demás desvía la duda del autor.

Y lo del desbordamiento de millis... Que sí... 50 días con el arduino encendido y más de lo mismo. Repito... No deduzcas a razón del número de post en un foro...

Más te leo y más infantil pareces. Te disculpas, explicas q eres irónico, te vuelves a burlar, animas a que colabore, y vuelves a faltar el respeto... No sé tu edad pero me das vergüenza ajena y yo no soy irónico ni pienso dejar el foro por gente así.

ArduMyth:
Más te leo y más infantil pareces. Te disculpas, explicas q eres irónico, te vuelves a burlar, animas a que colabore, y vuelves a faltar el respeto... No sé tu edad pero me das vergüenza ajena y yo no soy irónico ni pienso dejar el foro por gente así.

Tienes razón. :confused:

Creen otro hilo y debatimos NUEVAMENTE (es uno de los temas mas debatibles) el tema millis() según el enfoque de IgnoranteAbsoluto pero estoy de acuerdo con ArduMyth que ya ha sido algo mas de la cuenta, aunque yo también me excedo y no debería.
Limitemos el hilo a lo que el tema requiere.

ArduMyth:

void luz( int  *arr ) {

bool val = digitalRead(arr[0]);
  if(arr[2] != val && millis() >= arr[5] + 150) { //debounce
      arr[2] = val; //valor como pulsador
      if(! arr[2] ) {
        arr[3]  = ! arr[3] ; //valor como interruptor
        if( arr[3] ) {
            arr[4] = millis() ;
        }else{
            Serial. println("El led del pin "+String(arr[1]) +" ha estado encendido " + String(millis() - arr[4]) +" milisegundos");
        }
      }
      arr[5] = millis() ; //reiniciar tiempo debounce
  }
  digitalWrite(arr[1], arr[3]) ; //apagar encender led
}

Muchas gracias pero una pregunta el arr es un array, pero para que el asterisco de aqui

void luz( int *arr )

y otra cosa,

DDRD =B11110010;

, esto es un pinMode, pero podrias explicarmelo, me quiero enterar de esto.

Lo que tu buscas esta en un ejemplo: dentro de la IDE de Arduino, BlinkWithoutDelay.

Ahi mero esta o te recomiendo que le des una buscada en google con;

Nick, Gammon, Millis, blink, without, delay

Saludos.

-Alex.

AlexLPD:
Lo que tu buscas esta en un ejemplo: dentro de la IDE de Arduino, BlinkWithoutDelay.

Eso lo se pero no consigo implementarlo pero consegui que se apague cuando conteo llega a 6000, pero no consigo que se repita de nuevo cuando pulso, solo puedo cuando lo reinicio.
Yo hago que cuando llega a mas de 6000 apague el led y resetee la variable conteo, lo hace porque lo comprobe pero no vuelve a contar al darle al boton. No se si es por las variables de estado o salida, el codigo lo dejo para que lo veais:

#define boton 2
int led = 6;
int leds = 3;

int estado = 0;
int estadoAN = 0;
int salida = 0;
int salidaAN;

unsigned long conteo;
unsigned long tempOn = 6000;


void setup() {
  // put your setup code here, to run once:
  pinMode(boton, INPUT_PULLUP);
  pinMode(led, OUTPUT);
  pinMode(leds, OUTPUT);
  Serial.begin(9600);
}

void loop() {
  // put your main code here, to run repeatedly:
  estado = digitalRead(boton);

  if((estado == 1)&&(estadoAN == 0)){
    salida = 1 - salida;
  }

  estadoAN = estado;

  if(salida == 0){
    digitalWrite(led, HIGH);
    digitalWrite(leds, HIGH);
    conteo = millis();
    if ((conteo >= tempOn)){
      digitalWrite(led, LOW);
      digitalWrite(leds, LOW);
      conteo = 0;
      salida = 1 - salida;
    }else{
      digitalWrite(led, HIGH);
      digitalWrite(leds, HIGH);
    }
  }
  else{
    digitalWrite(led, LOW);
    digitalWrite(leds, LOW);
  }
  
}

ArduMyth:

unsigned int

btnA[6] ={2,6,0,0,0,0},
btnB[6] ={3,7,0,0,0,0} ;

void setup() {
  Serial.begin(9600);
  DDRD =B11110010; //Pin 2 y 3 como INPUT
}

void loop() {
  luz(btnA) ; //usando punteros
  luz(btnB) ;
}

void luz( int  *arr ) {
  bool val = digitalRead(arr[0]);
  if(arr[2] != val && millis() >= arr[5] + 150) { //debounce
      arr[2] = val; //valor como pulsador
      if(! arr[2] ) {
        arr[3]  = ! arr[3] ; //valor como interruptor
        if( arr[3] ) {
            arr[4] = millis() ;
        }else{
            Serial. println("El led del pin "+String(arr[1]) +" ha estado encendido " + String(millis() - arr[4]) +" milisegundos");
        }
      }
      arr[5] = millis() ; //reiniciar tiempo debounce
  }
  digitalWrite(arr[1], arr[3]) ; //apagar encender led
}

Ya intente estudiar tu codigo, pero me cuesta entenderlo incluso con comentarios, yo deje una respuesta con mi codigo, ya consegui que se apague a llegar a 6000, pero no consigo que vuelva ha hacerlo.

Shadoaru, no tengo nada claro qué pretendes hacer y cómo. ¿Para qué un botón y dos LED? ¿Cual es tu propósito final? ¿Cómo quieres que actúe el sistema ante los posibles rebotes del pulsador? ¿Qué ha de pasar si "se juega" con el pulsador? ¿Y si siempre está pulsado? Si no tengo bien claro qué es lo que quieres hacer, tal vez no te pueda ayudar tanto como quisiera. Para encontrar una solución a un problema, primero se ha de saber en qué consiste el problema.

Como ejemplo, para ver si te da alguna idea de cómo hacer lo que quieres, te pongo el código que enciende durante 6 segundos un LED desde el momento en que se pone a nivel bajo el pin del botón (se supone que cuando se presiona se pone a nivel bajo la entrada). No se tiene en cuenta para nada el posible "efecto rebote" del pulsador ya que sólo se enciende el LED y se empieza a contar los seis segundos si este previamente estaba apagado. Si el LED ya estaba encendido no tiene en cuenta el estado del pulsador. No importa si el pulsador se suelta y vuelve a apretar durante los seis segundos, no afectará al tiempo en que esté encendido el LED. Sólo si en el instante en que se completan los seis segundos el botón se encuentra pulsado, el LED se apagará durante un instante inapreciable para nosotros y volverá a encenderse durante otros seis segundos.

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 = 6;   // Pin al que se conecta el LED

const unsigned long TIEMPO_ENCENDIDO = 6000;  // Tiempo en milisegundos que permanecerá encendido el LED

bool encendido = false;           // Variable que nos indicará si el LED está encendido
unsigned long instanteEncendido;  // Variable que guardará el valor de millis() cuando se encendió el LED por última vez


void setup() {
  pinMode(PIN_BOTON, INPUT_PULLUP);
  pinMode(PIN_LED, OUTPUT);
}

void loop() {
  // Verificamos si está pulsado el botón y el LED no está encendido
  if ((digitalRead(PIN_BOTON) == LOW) && (encendido == false)) {
    instanteEncendido = millis(); // Guardamos el instante en que encendemos el LED
    digitalWrite(PIN_LED, HIGH);  // Encendemos el led
    encendido = true;             // Indicamos que el LED está encendido
  }

  // Verificamos si el LED está encendido y ha transcurrido el tiempo que ha de estar encendido
  if ((encendido == true) && ((millis() - instanteEncendido) >= TIEMPO_ENCENDIDO)) {
    digitalWrite(PIN_LED, LOW);  // Apagamos el LED
    encendido = false;           // Indicamos que el LED no está encendido
  }
}

Fíjate que con la variable encendido controlamos si hacemos algo con respecto al estado del botón o con el tiempo transcurrido. Y para saber cuanto tiempo lleva encendido lo que hacemos es guardar en instanteEncendido el valor de millis() cuando lo hemos encendido y luego restamos ese valor a millis() para calcular cuanto lleva encendido y saber si ya ha transcurrido el tiempo que deseamos.

IgnoranteAbsoluto, ese codigo lo repetí muchas veces en c#, claramente cambiando cosas, no se como no se me ocurrio, sera del tiempo que ya no programo, pero lo que me dijiste, veras, yo quiero que un boton controle 2 leds, que cuente indenpendiente al otro cuente ese tiempo tambien, por ejemplo, le doy al boton y me enciende el led 6 segundos, pero al darle otra vez empieze a contar el tiempo del otro led independientemente al otro, pero como dije, con un unico boton

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]);
    }
}

Te respondo el ultimo mensaje IgnoranteAbsoluto, aun me queda por aprender, muchas gracias, me tengo que enterarde esto, pero lo que me preguntaste fueron varias preguntas, como por ejemplo que pasa si le doy al boton cuando todos los leds estan encendidos, pus no pasa nada. Pero tambien de cual led se enciende eso es aleatorio