Re: Desbordamiento y millis()

Yo creo que al final del bucle se puede comprobar si "tiempo" anterior es mayor que el que devuelve millis(). En ese caso, habríamos dado la vuelta al contador, y comienzo la cuenta desde cero.

if(millis() < tiempo) Tiempo1_k = millis()

Hola,
En la referencia de Arduino sobre millis() dice que devuelve un unsigned long, y que a los 50 días más o menos empieza de cero. La variable unsigned long puede llegar hasta unos 4 Gigas, que son los milisegundos que hay en 50 días, más o menos.

Ahhh...... Ok, es verdad...

gracias...!

Valen, si vas a usar millis de la forma que has puesto, nunca tendrás problemas con el overflow.

Otra cosa, si quieres que dure T1 "exacto" y luego le metes una espera de 10ms, ya estás metiendo 10ms de error como poco. Ese delay lo quitaría ya que no sirve para nada.

Un saludo

Grcias, sí, quitaré el delay() del while().

aunque se necesite varios días para desbordar un long, hay máquinas que siempre están conectadas (podría ser el caso de mi módulo), y en ese caso, el "problema" se producirá.

Pero como millis() pasa ella solita a cero al desbordar, no parece problema controlar esa situación:

Cuando el valor de millis() se haga cero, durante ese ciclo, el valor almacenado en la variable "tiempo" será mayor. Testeando esta situación en cada ciclo, al cumplirse, actualizaremos la variable "tiempo" con el valor de millis(), y todo comienza de nuevo....

Bueno, esta es mi idea del asunto....

En serio, al restar se anula el posible efecto del overflow, por lo que si lo usas de la manera que has puesto no tendrás problemas con el overflow nunca jamás, ni en trescientos mil millones de años que esté el Arduino continuamente encendido.

Usando una variable 'Extra long' (cuya existencia desconocia. Muchas gracias bokeaus), no obviarás el problema de millis(), ya que es la propia función milis() la que se resetea a los 50 dias. Por tanto, no cambia nada entre usar una variable long y una long long.
Con respecto a lo que comenta el compañero Chiva, apuntarte que podrias tener problema si el overflow se produce entre la asignacion de milis() a la variable y su evaluacion:
cuando asignas, imaginate que millis() esta a punto de desbordarse, y vale una barbaridad. Antes de evaluarla, se desborda, y cuando haces la comparación, pasa a valer 3 (por decir algo) entonces, y durante los proximos 50 dias, millis()-tiempo sera menor que cero, y el while se estara ejecutando permanentemente.
Supongo que bastará con hacer un if millis-tiempo<0 dentro del while para eliminar del todo el problema.

Pofenas, ten en cuenta que millis devuelve un unsigned long, por lo que no hay números negativos.

Imaginemos que tenemos un tipo de dato sin signo que puede almacenar de 0 a 1000 y queremos saber cuando han pasado 30ms

En un instante, millis() nos devuelve 350 y a la hora de comparar devuelve 390, entonces se cumple la condición ya que 390-350 > 30

Al volver a pasar por el mismo código, millis() devuelve 980 y luego 20 (ya que hemos pillado un overflow entre las lecturas), la condición se seguirá cumpliendo ya que: 20-980 = 40 (en verdad es 41, pero por simplificar el ejemplo redondeamos a 40)

La clave esta en la linea

while(millis()-tiempo < T1) delay(10);

'Al volver a pasar por el mismo código, millis() devuelve 980 y luego 20 (ya que hemos pillado un overflow entre las lecturas),'
millis=20 y tiempo =980

el while quedara:
while(20-980<T1) delay(10)

El ejemplo no es muy bueno, ya que el overflow lo tendremos cuando millis() alcance el maximo valor para un unsigned long: 4,294,967,295
Replanteemos el ejemplo con datos mas adecuados:
-el bucle se ejecuta cuando millis() esta a punto de desbordar, y obtenemos un valor de 4,294,967,294 para la variable tiempo
-cuando llegamos al while, millis() devuelve un valor de 5
-millis()-tiempo = 5-4,294,967,294= -4,294,967,289
-como el resultado es negativo, es menor que T1, y se evalua como verdadera la condicion para el while, que ejecuta el delay(10)

  • se vuelve a evaluar la condicion del while, que da un numero aproximadamente 10 unidades mayor.
    Es sencillo ver que esta situación se va a mantener siempre, ya que nunca millis() va a devolver un valor 10 unidades mayor que tiempo.

Lo ue no acabo de ver es porque dices que no devuelve un negativo... creo que seria diferente si hicieras
unsigned numero
numero = millis()-tiempo

Me refería a que millis() funcionara con ese hipotético tipo de dato e hiciera overflow cada 1000ms, para tener números mas manejables.

No devuelve negativo por que estás trabajando con números sin signo, por lo que se aplican sus reglas.
Al igual que pasa de 4,294,967,294 a 0, al restar pasa de 0 a 4,294,967,294 y si no, no tienes más que escribir un par de líneas:

void setup(){
  Serial.begin(9600);
  unsigned long time = 50;
  Serial.print(time-4000);
}

void loop(){}

¿Y has pensado en la utilización del watchdog?

Buenas Noches.
Yo no estoy muy puesto en estas labores, pero mi solución, sin decir que es la mejor, fué:

Compruebo en cada ciclo de mákina que el contador millis() no ha pasado de cierto límite crítico.
Si pasa ese límite, y no está haciendo "nada" que le influya, (ya he puesto el cierto límite con bastante holgura, un par de dias, por ejemplo)
Resetea la makina, (por software claro). -- > problema solucionado.
Si pasa ese límite, pero está haciendo algo, pues marco un bit como que hay que resetear cuando termine. (claro para que haga lo que tieene que hacer)
Cuando ha terminado de hacer sus labores, y ya se puede, resetea la makina( por software en este caso también) ---> problema solucionado.

Es una pena, pero el code no lo tengo a mano ahora, si lo kieres o necesitas alguna aclaración más, me lo comentas y ya veremos, pero seguro que con los sesudos que hay por akí, falta no creo que te haga mucha.

Un Saludo y Gracias por motivarme todos los días a pensar en algo nuevo.

recuerdas como hacias el reseteo por software??

Yo creo que al final del bucle se puede comprobar si "tiempo" anterior es mayor que el que devuelve millis(). En ese caso, habríamos dado la vuelta al contador, y comienzo la cuenta desde cero.

if(millis() < tiempo) tiempo = millis();

while(millis()-tiempo < T1);

Cada 50 días funcionado sin parar, un ciclo de programa durará unos milisegundos más. Algo insignificante en un control de una máquina eléctrica grande, como es el caso.

Sergegsx

Para hacer el reseteo por software:

Junto a los #include introduzco un función:

void(* resetFunc)(void=0);

luego viene el resto del code

void loop()
/......
y cuando la necesito usar la invoco con su nombre

resetFunc();

...../

en este ejemplo concreto declaré una variable que comparaba si habia superado el tiempo de reset y además la salida estaba inactiva.

if ((millis() > tiempoReset) && (salida == LOW))
{
resetFunc();
}

Hay que tener en cuenta que la variable tiempoReset de este ejemplo debe ser tipo Long igual que lo es millis() .

Espero te / os sirva, no recuerdo bien de donde la saqué seguro que de este foro u otro parecido, debería de anotarme esas cosas también, el merito hay que reconocerselo al autor, y no a mi que solo soy un utilizador del recurso.

muchas gracias !

entonces la función es simplemente

void(* resetFunc)(void=0);

lo digo porque lo he incluido antes del setup()

y me da error

xx_v2_gear:102: error: default arguments are only permitted for function parameters
xx_v2_gear:102: error: '<anonymous>' has incomplete type
xx_v2_gear:102: error: invalid use of 'void'

Disculpa lo copie mal la funcion es:
void(* resetFunc)(void)=0;

es decir, el cero esta fuera del parentesis disculpa.

La solucion de Chiva es mucho mas interesante:

unsigned int ahora,tiempo;

ahora=millis();
.....
.....
tiempo=ahora-millis();
while (tiempo<10)
delay(10);