Duda acerca de la función millis()

Buenas tardes y saludos a todos.

Tengo una duda con el funcionamiento de la función millis() y el ejemplo Blink Without Delay.

En el ejemplo, el led parpadea cierto tiempo usando la función millis(). Hace una resta del tiempo actual y el tiempo anterior guardados y lo compara con "x" valor.

En la descripción de la función millis() dice que te regresa un valor en milisegundos cuando lo mandas a llamar y que este valor regresa a cero después de aproximadamente 50 días.

Mi duda es: Si tengo un sistema que está conectado las 24 horas y cuando pasen los 50 días se regresa a cero, ¿ya no funcionaría el programa y lo tendría que reiniciar?

Porque usando como ejemplo el Blink Without Delay, el valor actual sería 0 y el último valor serían los 50 días en milisegundos y la resta daría un valor negativo y nunca sería mayor o igual al tiempo "x" determinado.

Espero me haya dado a entender y que me puedan ayudar. Gracias!

Si funciona el programa. Es algo ya debatido. Te puedo asegurar que puedes estar cerca del limite de millis() pero las cuentas se seguirán haciendo correctamente.

Pero la pregunta es interesante para conocer y entender el desarrollo del programa.

Si preguntamos si millis es superior al dato de millisanterior que hemos guardado previamente mas el intervalo que queramos para hacer algo, lo correcto seria pensar que cuando millis pase a ser 0 nunca pueda cumplir la condicion.

A partir de ahi me surgen dos reflexiones.

  1. Tendria facil solucion puesto que seria tan simple como poner un concidion que se pregunte si millisanterior es mayor que millis y en ese caso volver a cargar millisanterior.

  2. Me gustaria conocer la realidad del problema sin tener que montar fisicamente un ejemplo, puesto que hacer pruebas con algo que sucede cada 50 dias es un poco lento. Si es algo debatido, donde hay documentacion de esto?

Busca millis() respondido por Noter, hasta de eso me acuerdo.
Bueno, apareció duda millis()

Noter da un ejemplo donde un valor anterior se carga casi al momento del desbordamiento (overflow)

El cálculo resultante sería correcto incluso aunque hubiera ocurrido un desbordamiento entre la primera y segunda toma de millis. Es una peculiaridad que ocurre cuando trabajamos con datos unsigned. Puedes hacer esta prueba:
Code: [Select]

unsigned long a = 0xfffffff0; // Faltan 16 cuentas para el desbordamiento

unsigned long b = 5; // Han transcurrido 5 cuentas desde el desbordamiento
unsigned long c = b - a; // Debería resultar 21, que es el número total transcurrido, ¿no?





Por ello es el cálculo que suelo recomendar, en lugar de otros como sumar el tiempo buscado al tiempo inicial y luego comparar con millis actual, que sí pueden verse afectados por el desbordamiento.
Si lo que quieres controlar es, por ejemplo, días transcurridos, puedes hacerlo de forma similar:
Code: [Select]




const unsigned long millisTieneUnDia = 10006060*24;
unsigned long diasTranscurridos; // Este contador desbordará en 11 millones de años. ¿Suficiente?

tiempoTranscurrido = millisActual - millisAnterior; //Cálculo de millis transcurridos desde la última marca

if (tiempoTranscurrido >= millisTieneUnDia) { // si ha transcurrido un día desde la última marca
    diasTranscurridos ++; // aumentamos el contador de días
    millisAnterior += millisTieneUnDia; // establecemos la nueva marca de inicio
}

A mi me gusta tratar de entender el porque de las cosas porque asi ese trabajo ya me sirve para la siguiente vez. Es por eso que hice el comentario porque si bien se le puede contestar que no, que no afecta, creo que la respuesta no es del todo correcta.

En primer lugar como dice Noter hay que tene cuidado porque:

Por ello es el cálculo que suelo recomendar, en lugar de otros como sumar el tiempo buscado al tiempo inicial y luego comparar con millis actual, que sí pueden verse afectados por el desbordamiento.

Es decir, depende como hagamos el calculo el desbordamiento si nos puede afectar.

Ahora bien, en este caso por que no nos afecta? Pues porque usamos variables UNSIGNED (sin signo) Para que se entienda lo explicare con variables INT que son mas "manejables".

UNSIGNED INT abarca desde 0 hasta 65535, ahora bien si sumamos 1 ocurre el desbordamiento, pero que es en realidad? pues nada mas que volver a empezar en forma infinta el ciclo desde 0, por lo tanto la secuencia +1 en esta variable seria:

65533, 65534, 65535, 0, 1, 2, 3, 4

Por eso si restamos 2-65534 no obtenemos un numero negativo sino 4, ya que 2 esta 4 posiciones por delante de 65534. A los efectos de restar el "minuendo" (el numero que va a ser restado) es el mayor pero no necesariamente en valor sino en orden.

Pero esto solo sucede en los unsigned porque en el caso de solo INT que va desde -32768 a +32767 si que el desbordamiento da problemas porque la secuencia es:

+32765, +32766, +32767, -32768, -32767, -32766

En sintisis el desbordamiento afecta a un codigo como Blink whitout delay? Depende. Si usamos variables sin signos y hacemos la comparacion de una determinada manera NO, pero en otro caso SI y mucho.

No hace falta explicar algo con SIGNO porque millis() es unsigned long y lo que sigue luego del ultimo valor
son 4 bytes o 32 bits (4x8)
0xFFFFFFFD
0xFFFFFFFE
0xFFFFFFFF

0x0000000
0x0000001

En sintisis el desbordamiento afecta a un codigo como Blink whitout delay? Depende. Si usamos variables sin signos y hacemos la comparacion de una determinada manera NO, pero en otro caso SI y mucho.

Esto es un error de quien use valores con SIGNO.
millis() devuelve un unsigned long
si usas una variable como long es TU error.
Si compilas una comparación de millis() con una variable long el IDE te arroja un WARNING indicando que hay una excepción que puede generar problemas.
EL 99% de las personas no le da importancia.
He visto incluso librerías mal hechas que he modificado justamente porque cargan millis() a un long, pero esta mal.

CONCLUSION: Simpre usar unsigned long con millis()

La definicion de "esta mal" desde mi punto de vista no es correcta.

Even signed long may encounter errors as its maximum value is half that of its unsigned counterpart.

Si yo conozco esta limitacion, que un long con signo me desbordara en la mitad de tiempo que el unsigned long, y a pesar de eso lo uso porque interesa al desarrollo de mi programa no veo que nadie me pueda decir que esta mal.

Por eso amplie la respuesta a JoCar10 porque creo que ademas de decirle que se le puede asegurar que las cuentas se haran bien me parece mas importante explicar por que.

Por eso explique lo del signo, porque es justamente la ausencia de signo lo que hace que las cuentas se hagan bien en cualquier caso. La duda de JoCar10 proviene de no incorporar a su razonamiento que el desbordamiento no es un reset de la variable sino que despues de 65535 viene el 0 en los INT sin signo o despues del 0xFFFFFFFF viene el 0x00000000 en un LONG sin signo.

Millis() devuelve un unsigned long, si, pero muchas variables que se deriben de esa podrian no serlo, incluso por interes del codigo, y no pasa nada, si lo sabemos y lo tenemos en cuenta.

Yo tengo un codigo en que tomo unos tiempos en que ocurren cosas (pongamos A y B) y los comparo. A veces A pasa antes que B y a veces B pasa antes que A, me interesa tener variables con signo para poder comparar esos tiempos ya que algunos son positivos y otros negativos. Mas que me interesa deberia decir TENGO QUE porque si tuviera unsigned una resta me la haria bien pero la otra mal.

Si bien en mi codigo estoy muy lejos de tenerlo 50 dias andando me surgio la duda de pensar que pasaria si asi fuera y ahi fue cuando llegue a la conclusion que puse en mi primer intervencion.

  1. Tendria facil solucion puesto que seria tan simple como poner un concidion que se pregunte si millisanterior es mayor que millis y en ese caso volver a cargar millisanterior.

cas6678 tienes y no tienes razón.
Porque no vas a la fuente millis() y verás que dice

millis()

Description

Returns the number of milliseconds since the Arduino board began running the current program. This number will overflow (go back to zero), after approximately 50 days.

Parameters

None

Returns

Number of milliseconds since the program started (unsigned long)

Note:

Please note that the return value for millis() is an unsigned long, logic errors may occur if a programmer tries to do arithmetic with smaller data types such as int's. Even signed long may encounter errors as its maximum value is half that of its unsigned counterpart.

Dime donde dice long?
Si la información no usa long para que usarlo y entonces para que explicarlo?
He resaltado el párrafo final.
Aún long con signo pueden provocar errores ya que su máximo valor es la mitad de su contraparte sin signo.

cas6678:
Ahora bien, en este caso por que no nos afecta? Pues porque usamos variables UNSIGNED (sin signo) Para que se entienda lo explicare con variables INT que son mas "manejables".

Primeramente, gracias por sus respuestas.

Leyendo sus opiniones concluyo que, y espero estar en lo correcto, que como es una variable UNSIGNED no importa si el resultado de currentMillis - previousMillis es positivo o negativo, siempre lo va a tomar como positivo, no?

Digamos que 10000 es el valor máximo de millis(), cuando millis() se desborde va a regresar a 0, entonces:

  • currentMillis = 0
  • previousMillis = 10000

currentMillis - previousMillis = -10000 pero como es unsigned lo va a tomar como 10000 positivo y si tengo la condición:

if(currentMillis - previousMillis >= 1000){
previousMillis = currentMillis;
}

entonces previousMillis volverá a ser 0 y se reiniciaría la condición después de los 50 días.

Espero estar en los correcto. Gracias!

No, no lo has entendido bien. Por eso me gusta ser mas didactico, te has quedado con la idea de que funcionara (que es correcta) pero aun no lo has entendido.

En el ejemplo que pones: Si currentMillis es 0 y previusMillis es 10000 el resultado no sera -10000 ni tomara +10000.

El resultado de esa cuenta sera igual a: lo que quede desde 10000 hasta el desbordamiento mas 1, por el valor de la posicion que ocupa el 0.

Por eso puse el ejemplo de INT en vez de LONG ya que son mas "manejables". Imaginate que son INT (UNSIGNED INT) y previusMillis es 65000., Entonces cuanto es currentMillis-previusMillis (0-65000)? pues 536. Como? 535 que es que lo que va desde 65000 hasta 65535 (limite) mas uno de la posicion 0.

Con UNSIGNED LONG pasa lo mismo solo que el limite es mucho mas alto y como lo solemos expresar en hexadecimal es mas dificill de visualizar.

En las variables sin signos las restas son mas complicadas de enteder. 4-2 = 2, esta claro, pero ojo, si piensas que 2-4=-2 y por eso toma 2 te equivocas. En las variables sin signo 2-4 sera igual al limite del desbordamiento menos dos.

Para que lo entiendad bien te sugiero que crees dos variables unsigned int le des un valor y las restes (primero a-b y despues b-a) y veas el resultado en monitor serial y asi saldras de dudas. Luego puedes hacer lo mismo con variables con signo y veras como el resultado es diferente. Te propongo que lo hagas con INT por que es mas facil de ver.

Surbyte. No se por que me mandas a la referencia cuando lo que he puesto lo he extraido de ahi. Lo pones en rojo como si fuera una novedad cuando justamente fue lo que yo puse en mi post.

Tus preguntas:

Dime donde dice long?

En ningun lado, en todos dice que Millis devuelve un unsigned long y yo no he dicho lo contrario.

Si la información no usa long para que usarlo?

Otra cosa, que si he dicho, es que en el desarrollo de nuestro codigo pueden aparecer otras variables con signo que deriben de Millis o de alguna otra en donde Millis se haya cargado. Y no solo eso, puse un ejemplo puntual de porque podria ser necesario tener variables de este tipo (con signo).

y entonces para que explicarlo?

Es un problema de gustos. Hay quien dice las cosas son asi y no las discuto y hay quien quiere entender el porque ya que si lo hace posiblemente el desarrollo de ese entendimiento sera de aplicacion en otras futuras dudas.

En este caso se da la circunstancia que JoCar10 con su ultimo mensaje me da la razon sobre "para que explicarlo". Al parecer lo que le has dicho le sirvio. Ya se ha convencido de que no tendra problemas. Ahora bien, todavia no entendio por que. Dice que:

currentMillis - previousMillis = -10000 pero como es unsigned lo va a tomar como 10000 positivo y si tengo la condición:

Evidentemento lo anterior es un error. Yo creo que si le explico el error y lo entiende posiblemente mi ayuda le sirva ademas para el futuro en temas similares.

Hay dos interpretaciones para este problema:

  1. La que afirma que eso no va a a generar problemas si usas los tipos de variables apropiadas.
    Es decir, usando unsigned long y estando próximo al maximo de 4 bytes o 32 bits-1 de un unsigned long no va a provocar que tu código falle. Seguirá comportándose bien. Esta respuesta debería bastarle a la mayoría.

  2. La segunda respuesta requiere conocimiento.
    Si quieres explicar algo que se justifica con número SIN SIGNO usando números CON SIGNO estamos equivocados desde mi punto de vista.
    Explica lo que desees pero hazlo desde las restricciones del conjunto de números utilizados con las reglas correspondientes a ese conjunto numérico.
    Cuando un contador con signo llega al máximo y se incrementa uno que ocurre? pues pasa a 0 y sigue con 1.

Si estamos con unsigned char y venimos contando 0xFE 0xFF 0x00 0x01
Si estamos con unsigned int y venimos contando 0xFFFE 0xFFFF 0x0000 0x0001
si estamos con unsigned long y venimos contando 0xFFFFFFFE 0xFFFFFFFF 0x00000000 0x00000001

Nada cambia. no hay negativos.
Entonces una resta de números SIN SIGNO no sigue los patrones signados. Solo cambia el modo en que se debe visualizar. Si restamos 0-1 = la respuesta no es -1 y como no hay negativos lo hago positivo. Será un juego de palabras pero no lo es.

Si tu restas como (unsigned int) 0 - (unsigned int) 1 lo que en realidad haces es esto

// Veamos el mismo caso poro con unsigned int para no abrumar con tanto número.
Le pido 1 a una unidad mayor porque en el campo unsigned el máximo es 0xffff
0x1 0000 - 0x0001 = esa cuenta da 0xFFFF! y 0xFFFF no es 0x8001 que es en realidad -1 (con SIGNO) .

0xFFFF es el verdadero resultado de la resta anterior.
Ya dije que no existe -1 en los unsigned int o unsigned long la cuenta da 0xFFFF pero si quieren veanlo como la reperesentación en unsigned int de un -1

Otro ejemplo, mas simple, o basicamente lo mismo.
Si tenemos a= 5 y b = 6. La representación binaria de los dos será

a = 0101
b = 0110

Hagamos la resta de esto ( o tomemos el complemento a dos de b, sumo a y le sumo 1)

0101
1001
+  1
------
1111

Entonces este es el valor de la resta de 5-6 sin signo o 0xF

Tengo verdaderas dificultades para seguirte en lo que has expuesto. Pero lo mas importante, no entiendo que es lo que te parece mal de lo expuesto por mi mas arriba. Si consideras que hay un error en algo te ruego que me digas en que porque yo no lo veo.

Cuando decis que la segunda respuesta requiere conocimiento que me estas queriendo decir?

Tengo la costumbre de participar poco, porque solo lo hago en las cosas que domino y si estoy equivocado seria el primero en querer saberlo, de momento sigo pensando que no lo estoy.

Yo en general no ofendo a nadie cuando me expreso y menos desde mi actual condición de moderador.

Solo dije conocimiento y me faltó tal vez profundizar al decir : de aritmetica binaria y números binarios.
Cuando estudiaba matemática y/o algebra en ingeniería cada campo numérico tiene tus restricciones.
Eso es conocimiento o estoy equivocado?
Por eso separé las respuestas.
Cada cual que tome la respuesta que quiera.

No leí bien tu ultimo post cas6678. Dices muchas cosas similares a las mias o yo a las tuyas. No importa el orden.
Listo el tema para mi.

1 Like

Lamento que hayas dicho mi nombre, preferia mantenerme en el anonimato solo conocido como Cas6678.

Ahora fuera de broma quisiera decir que llevo casi dos años en contacto con Surbyte por diferentes proyectos y creo que con resultados positivos para ambos. Todo esto a pesar de no conocernos personalmente y separarnos mas de 12.000 km. Normalmente nos entendemos con mayor armonia y rapidez que lo que en este hilo pudiera parecer.

Lo digo porque un simple lector podria pensar lo contrario despues de leer estos ultimos mensajes.

Saludos.

1 Like