usar millis() junto con for() [SOLUCIONADO]

viendo algunos codigos donde usan el for() para crear una secuencia como de encendidos de led, y apagar la misma usan delay() no millis(), yo tampoco lo pude resolver, pongo como ejemplo el de la pagina de referencia de arduino, sacar delay y usar millis, alguien tiene idea como se hace??
ya que en una oportinida lo solucione con switch case, pero ahora no me sirve, preciso si se puede con for loop

//este es un ejemplo y asi encontre muchos for() con delay, pero millis() ninguno.

int PWMpin = 10; 

void setup()
{
  // no setup needed
}

void loop()
{
   for (int i=0; i <= 255; i++){
      analogWrite(PWMpin, i);
      delay(10);
   }
}

Respuesta corta: no se usa for con millis().

Respuesta larga: el uso del for está contraindicado o ha de ser "moderado" si se quiere aprovechar el uso de millis(). El sentido de usar millis() en lugar de delay() es el evitar que el programa se quede "mucho tiempo" "entretenido" en hacer una única cosa. Y entiéndase como "mucho tiempo" todo aquello que supere el milisegundo (incluso menos tiempo puede ser mucho tiempo). Se trata de que haga muchas "pequeñas cosas" y que se entretenga muy poco tiempo en cada una de ellas, dando así la sensación de "multitarea".

Así que para hacer el ejemplo que has puesto, hay que hacerlo sin usar for. Se trata de que la ejecución de la función loop() tarde lo menos posible en completarse, para una vez finalizada se vuelva a ejecutar, una y otra vez, indefinidamente. Como la función loop() ha de terminarse y volverse a ejecutar muchas veces antes de completar el ciclo de valores que ha de tomar la variable i, esta ha de ser global (también se podría declarar como estática dentro de la función loop(), pero para esta ocasión veo más pedagógico el declararla global y así de paso no liar más a la gente). Lo mismo ocurre con una de las variables auxiliares que vamos a necesitar, instanteAnterior, la cual "recordará" en qué momento cambió el valor de i por última vez, para así poder calcular cuando ha de cambiar de nuevo.

La variable instanteActual puede ser declarada dentro de la función loop(), ya que su valor no importa que se "pierda" cuando termine la función ya que se inicializa siempre al principio de cada iteración.

Calculando la diferencia entre instanteActual e instanteAnterior obtenemos el tiempo que ha transcurrido desde el último cambio de la variable i. Si ya han pasado los 10 milisegundos entonces actualizamos la variable instanteAnterior para recordar cuándo se ha hecho este cambio y cambiamos el valor de la variable i. Verificamos entonces si la variable i ya ha "terminado el bucle" viendo si supera el valor máximo, y si es así la inicializamos a cero. Ya solo resta usar su valor en analogWrite(PWMpin, i) y a esperar a que vuelvan a pasar los siguientes 10 milisegundos.

El código de la adaptación usando millis() y sin usar for:

int PWMpin = 10;
int i = 0;
unsigned long instanteAnterior = 0;

void setup()
{
    // no setup needed
}

void loop()
{
    unsigned long instanteActual = millis();
    if ( (instanteActual - instanteAnterior) > 10 ) { // Verificamos si han transcurrido 10 milisegundos desde el último cambio
        instanteAnterior = instanteActual; // Guardamos este momento para tomarlo como referencia para el siguiente periodo
        i++; // Pasamos al siguiente valor de i
        if (i > 255) { // Verificamos si se ha sobrepasado el valor final
            i = 0; // Volvemos al valor inicial
        }
        analogWrite(PWMpin, i);
    }
}

Gracias voy a ver si lo puedo implementar en este codigo

int pin;
int matriz[]{8,9,10,11,12};
int cont =5;
int tiempo=100;


void setup()
{
	for(int pin=0; pin<cont; pin++){
pinMode(matriz[pin], OUTPUT);

	}
}

void loop()
{
	for(int pin=0; pin<cont; pin++){

		digitalWrite(matriz[pin],LOW);
		delay(tiempo);
	    digitalWrite(matriz[pin], HIGH);
	    }
}
1 Like

Adrian_E:
Gracias voy a ver si lo puedo implementar en este codigo...

¿Quieres que te diga cómo quedaría o prefieres intentarlo por tu cuenta?

Te recomiendo intentarlo y que luego publiques el resultado. Si lo logras, estupendo. Y si hay algo que no te funciona, vemos qué es lo que falla. Yo aprendo más de los fallos que de los aciertos. Los aciertos sirven para verificar lo que ya sabes, mientras que los fallos te descubren las cosas que quedan por aprender.

1 Like

como quedaria, amigo??

int pin = 0; // Hay que inicializar el valor de la variable global pin para asegurarnos de que no tiene "basura"
int matriz[] = {8,9,10,11,12};
int cont = 5;
int tiempo = 100;

unsigned long instanteAnterior = 0;

void setup()
{
    // Este bucle no hay porqué cambiarlo, sólo se ejecuta una vez, al "arrancar" el Arduino
    for(int pin=0; pin < cont; pin++){ // Esta variable pin no es la misma que la variable global pin
        pinMode(matriz[pin], OUTPUT);
    }
}

void loop()
{
    unsigned long instanteActual = millis();
    if ( (instanteActual - instanteAnterior) > tiempo ) {
        instanteAnterior = instanteActual;
        digitalWrite(matriz[pin], HIGH); // Esto es lo que se hacía justo después del "delay(tiempo)"
        pin++;
        if (pin >= cont) { // Si pin es mayor o igual a cont es que ha de volver al cero
            pin = 0;
        }
        digitalWrite(matriz[pin], LOW); // Esto es lo que se hacía junsto antes del "delay(tiempo)"
    }
}

Fíjate que digitalWrite(matriz[pin], LOW) y digitalWrite(matriz[pin], HIGH) "parecen estar intercambiados". Antes se ponía a nivel alto antes del delay(tiempo) y a nivel bajo justo después de la pausa. Ahora parecen intercambiados, pero no es así, digamos que ahora la pausa es lo que "se hace fuera" del if, y dentro del if ponemos "el anterior" a nivel alto, pasamos "al siguiente" y lo ponemos a nivel bajo, para luego salir del if y continuar con el loop() hasta que se complete "la pausa".

Antes "el actual" se ponía a nivel bajo, se hacía "la pausa", bloqueando todo el loop() sin hacer otra cosa más que esperar y esperar. Transcurrida "la pausa" se ponía "el actual" a nivel alto y se continuaba con el for pasando "al siguiente" hasta que se completaba el bucle para los cinco. Finalmente, después de tanta espera, continuaba el loop(). Continuaba para volver a empezar todo una vez más.

Espero que lo hayas entendido.

se parece al mio;yo llegue hasta ahi, pero no repite solo lo hace una vez. mirando el tuyo lo mio fueron errores muy de novato

int pin;
int matriz[]{8,9,10,11,12};
int cont =5;
unsigned long valoranterior=0;


void setup()
{
  for(int pin=0;pin<=cont;pin++){

    pinMode(matriz[pin], OUTPUT);

  }
}

void loop()
{
  unsigned long valoractual=millis();
  if(valoractual- valoranterior >100){
    valoranterior = valoractual;
    pin++;

    if ( pin>cont){
      pin=0;

        }
        digitalWrite(matriz[pin],HIGH);
      
  }
}
1 Like

gracias IgnoranteAbsolut, tengo muchos errores conceptuales, por ahi coloco if(pin>cont) y me olvido de ponerle el = eso me hacia fallar parte del codigo, despues de corregir con el tuyo.

Si se quisiera "simular" un bucle descendente, for (int pin = cont - 1; pin > 0; pin--), habría que inicializar la variable pin con el índice del último elemento del array, que es uno menos que el tamaño del array porque el índice del primer elemento es cero. Y aunque en principio valdría con cambiar el incremento de pin y el if por:

        pin--;
        if (pin < 0) { // Si pin es de tipo unsigned (sin signo) nunca será menor que cero con lo que la condición nunca se cumplirá
            pin = cont - 1;
        }

Esta forma de hacerlo no funcionará si el pin se declara sin signo (unsigned) por que nunca será menor que cero. Si a una variable entera sin signo que vale cero se le resta uno, el valor que da no es menos uno, sino que sufrirá un desbordamiento "por debajo" y pasará a tener su máximo valor posible. Por ello hay que cambiar esa parte del código para que verifique primero qué valor tiene y haga el cambio de la variable según su valor: decrementar si no es cero o asignarle el valor inicial, cont - 1, si es cero. El código completo quedaría así:

// "Simulando" el bucle for (int pin = cont - 1; pin > 0; pin--)
int matriz[] = {8,9,10,11,12};
int cont = 5;
int pin = cont - 1; // <--- Hay que inicializar el valor de la variable global pin con el índice del último elemento del array (el primero es cero)
int tiempo = 100;

unsigned long instanteAnterior = 0;

void setup()
{
    // Este bucle no hay porqué cambiarlo, sólo se ejecuta una vez, al "arrancar" el Arduino
    for(int pin=0; pin < cont; pin++){ // Esta variable pin no es la misma que la variable global pin
        pinMode(matriz[pin], OUTPUT);
    }
}

void loop()
{
    unsigned long instanteActual = millis();
    if ( (instanteActual - instanteAnterior) > tiempo ) {
        instanteAnterior = instanteActual;
        digitalWrite(matriz[pin], HIGH); // Esto es lo que se hacía justo después del "delay(tiempo)"
        if (pin > 0) {      // Si pin es mayor al valor al que se quiere llegar...
            pin--;          // ... decrementamos
        }
        else {              // Si no...
            pin = cont - 1; // ... asignamos el valor del principio del bucle
        }
        digitalWrite(matriz[pin], LOW); // Esto es lo que se hacía justo antes del "delay(tiempo)"
    }
}

Nota: lo más fácil, en este caso, habría sido cambiar el orden de los elementos el array en lugar de hacer tanto cambio. Pero lo uso de ejemplo para tratar de explicar cómo hacer "otras cosas".

1 Like

Para completar "el ejercicio", vamos a hacer algo por el estilo del "Knight Rider", en que el bucle va en un sentido y luego en el otro indefinidamente. Para ello necesitamos una nueva variable global que nos dirá si se debe incrementar o decrementar el valor de pin. Esta nueva variable será paso, y tendrá el valor uno o menos uno, según se tenga que incrementar o decrementar pin. Su valor cambiará de signo cuando nos encontremos en cada uno los "extremos". Esto ocurre cuando pin vale 0 o (cont - 1), es decir, cuando pin está indicando el primer o último elemento del array. Después de decidir si paso cambia de valor o no, se suma a pin para que "pase al siguiente" que le toca.

Cuando pin vale cero cambiará de signo paso y deberá de pasar a ser positivo. Por ello, como inicialmente creamos la variable pin con el valor cero, creamos a su vez la variable paso con valor menos uno. Así, la primera vez que se evalúa el if, paso pasará a valer uno.

Se ha cambiado la inicialización de cont (mal nombre para indicar que guarda el tamaño del array). Ahora, si agregamos o quitamos algún elemento al array matriz, se calcula automáticamente el valor que ha de tener. También se ha indicado que sean constantes ambas variables, se ha puesto const en la declaración de las dos, ya que no han de cambiar los valores de ninguna de ellas.

El programa ha quedado así:

// "Simulando" el bucle for (int pin = cont - 1; pin > 0; pin--)
const int matriz[] = {8, 9, 10, 11, 12};
const int cont = sizeof(matriz) / sizeof(matriz[0]); // De esta forma se calcula automáticamente el número de elementos de matriz 
int pin = 0;    // Si empezamos por el cero...
int paso = -1;  // ... asumimos que se ha llegado al cero porque estabamos decrementando.
int tiempo = 100;

unsigned long instanteAnterior = 0;

void setup()
{
    // Este bucle no hay porqué cambiarlo, sólo se ejecuta una vez, al "arrancar" el Arduino
    for(int pin=0; pin < cont; pin++){ // Esta variable pin no es la misma que la variable global pin
        pinMode(matriz[pin], OUTPUT);
    }
}

void loop()
{
    unsigned long instanteActual = millis();
    if ( (instanteActual - instanteAnterior) > tiempo ) {
        instanteAnterior = instanteActual;
        digitalWrite(matriz[pin], HIGH); // Esto es lo que se hacía justo después del "delay(tiempo)"
        if ( (pin <= 0) || (pin >= (cont - 1)) ) {  // Si pin está en uno de los extremos...
            paso = -paso;   // ... cambiamos de sentido
        }
        pin += paso;    // Pasamos al siguiente/anterior
        digitalWrite(matriz[pin], LOW); // Esto es lo que se hacía justo antes del "delay(tiempo)"
    }
}

Gracias voy a tener que practicar para tomarle la mano, te consulto eso lo aprendiste solo con Arduino o vos venís de C++

Vengo del C++

ok gracias por tu ayuda y tu tiempo, como te dije lo voy estudiar ya que me parecio muy interesante la manera de tomar algunas cosas como la opcion de como vos usas matriz[] ;calcula automáticamente el número de elementos de matriz. :slight_smile: :slight_smile:

es aplicable a un for(int i=0;i<=255;i++);

Lo que mas me cuesta entender ya que vengo del for loop es

if ( (pin <= 0) || (pin >= (cont - 1)) ) {  // Si pin está en uno de los extremos...
            paso = -paso;   // ... cambiamos de sentido
        }
        pin += paso;

es aplicable a un for(int i=0;i<=180;i++);

Lo que se pretende hacer es que pin tome los valores 0, 1, 2, 3, 4, 3, 2, 1, 0, 1, 2, 3, 4, 3, 2... indefinidamente. Con lo cual he de sumar uno hasta que llegue a cuatro y luego restar uno hasta que llegue a cero, para luego sumar uno hasta que llegue nuevamente a cuatro para pasar a restar uno hasta que llegue a cero... así indefinidamente. Aquí tienes un pequeño ejemplo que saca por pantalla lo que digo (tener en cuenta que el valor que se muestra de la variable paso es el que se sumó por última vez a pin, no es necesariamente el mismo que se le va a sumar en el siguiente paso. También hay que tener claro que sumar -1 es lo mismo que restar uno):

void setup() {
    Serial.begin(9600);
}

int pin = 0;
int paso = -1;

void loop() {
    Serial.print(F("pin = "));
    Serial.print(pin);
    Serial.print(F("    paso = "));
    Serial.println(paso);
    delay(1000);
    if ( (pin == 0) || (pin == 4) ) {
        paso = -paso;
        Serial.println(F("Cambio de signo"));
        delay(1000);
    }
    pin += paso;
}

Si queremos hacer algo semejante con bucles for, lo más fácil sería hacerlo con dos bucles for, uno que incremente y otro que decremente. Sería algo así:

void setup() {
    Serial.begin(9600);
}

void loop() {
    Serial.println(F("Primer bucle"));
    delay(1000);
    for (int pin = 0; pin < 4; pin++) {
        Serial.print(F("pin = "));
        Serial.println(pin);
        delay(1000);
    }
    Serial.println(F("Segundo bucle"));
    delay(1000);
    for (int pin = 4; pin > 0; pin--) {
        Serial.print(F("pin = "));
        Serial.println(pin);
        delay(1000);
    }
}

Nota: el uso de los delay() y los dos programas son sólo para "visualizar" las dos diferentes soluciones.

donde tengo el error se mueve desparejo >:( >:(

#include <Servo.h>

int tiempo =25;
unsigned long actual;
int inicial=0;


Servo myservo;  

int pos = 0;    

void setup() {
  myservo.attach(9);  
  inicial=millis();
}

void loop() {
  
unsigned long actual=millis();

if(actual-inicial>=tiempo){
    
  

  for (pos = 0; pos <= 180; pos += 1) { 
    myservo.write(pos);              
  }
  for (pos = 180; pos >= 0; pos -= 1) { 
    myservo.write(pos);              
    inicial=actual;
  }
}
}

Creo que estás mezclando cosas. Se supone que si usas for es para usar delay(), pero si no quieres usar delay() has de usar millis() y si usas millis() no debes de usar for.

En los ejemplos de antes de "sin for" y "con for" puse los delay() para que se vieran las salidas por el monitor serie poco a poco. Y eran sólo unos ejercicios para ver un par de diferentes formas de implementar un bucle que podríamos llamar "de vaivén".

Fíjate que cuando se cumple la condición del tiempo y ha pasado 10 milisegundos, lo que haces en pasouno()es un bucle que va de 0 a 180 diciéndole al servo que se posicione en esos grados, uno detrás de otro, sin pausa, de sopetón. Sin darle tiempo a que se posicione el servo.

 for(int motor=0;motor<=180; motor+=1){
 miservo.write(motor);
 derecha = tiempoactual;
 }

Pero para colmo, cuando han pasado los 10 milisegundos de "la derecha" también han pasado los 10 milisegundos de "la izquierda" y en pasodos() pones el mismo servo de 180 a 0 grados con un bucle que no le da ni tiempo de respirar. Quedando finalmente en 0 grados.
Vamos, que se espera a que pasen 10 milisegundos y transcurridos los 10 milisegundos se manda al servo a ponerse de 0 a 180 y de 180 a 0, "sin respirar", y queda finalmente puesto a 0. Se inicializan los dos contadores de tiempo a la vez, para que transcurridos otros 10 milisegundos vuelva a hacerse lo mismo.

¿Tu propósito es no usar delay(), no bloquear el loop() y que el servo se mueva en pasos de un grado cada 10 milisegundos, de 0 a 180 grados y de 180 a 0 grados continuamente?

Nota: lo que he tachado ya no tiene sentido porque el comentario anterior ha sido editado y el código modificado.

Adrian_E:
donde tengo el error se mueve desparejo >:( >:(

No edites el código que ya te hayan comentado otros. Dejas los comentarios sin sentido. Añade un nuevo comentario al post con las nuevas modificaciones. El foro no está sólo para solucionarte un problema únicamente a ti. Es también para ayudar a los que vengan después. Aparte que se complica el poder ayudarte. ¿O es que voy a tener que revisar todos tus comentarios de este post por si has preguntado otra duda o hay otra modificación que quieres que te revise?

si, me equivoque, ese era el del error, el otro fue el primero

Adrian_E:
si, me equivoque, ese era el del error, el otro fue el primero

Aún así, en tu código continúas mezclando for con millis(). Si usas millis() porque no quieres que se bloquee el loop(), no uses un for para algo que no quieres que se haga lo más rápido posible.

No has dicho qué quieres hacer exactamente. Suponiendo que quieras hacer lo que te pregunté, aquí tienes el programa:

#include <Servo.h>

const unsigned long TIEMPO_INCREMENTO = 10;
const int ANGULO_MINIMO = 0;
const int ANGULO_MAXIMO = 180;
const int PIN_SERVO = 9;

unsigned long instanteAnterior = 0;
int angulo = ANGULO_MINIMO;
int incremento = -1;

Servo miServo; 

void setup() {
    miServo.attach(PIN_SERVO); 
}

void loop() {
    unsigned long instanteActual = millis();
    if( (instanteActual - instanteAnterior) >= TIEMPO_INCREMENTO ) {
        instanteAnterior = instanteActual;
        if ( (angulo <= ANGULO_MINIMO) || (angulo >= ANGULO_MAXIMO) ) {
            incremento = -incremento;
        }
        angulo += incremento;
        miServo.write(angulo);             
    }
}

Como puedes ver, no hay ningún for y ni se le espera.