reservado para la parte 4Esta parte quizás sea un poco "técnica", tenéis dos opciones: leerla y entenderla, o quedaremos con las moralejas que dejo. Desde mi punto de vista lo lógico es leerlo todo y tratar de entederlo.
¿Por qué usas unsigned long?
El contador que usa millis es del tipo unsigned long. En Arduino este tipo de datos tiene un tamaño de 32 bits. Lo cual nos da un total de valores entre 0 y 4294967296 todo en milisegundos.
No voy a entrar en la temática de números, ya que da juego para otro tutorial. Basta decir que si millis vale 4294967000 y lo guardamos en un unsigned int que lo máximo que almacena es 65534 entonces el valor se truncará y no será el valor que deseamos.
Uno de los errores que más he visto es usar float, pensando que los float son más grandes, cosa que no es asi. En Arduino un float tiene el mismo tamaño que un unsigned long, es decir, 32 bits, y tiene que guardar el tamaño de los decimales y el signo.
Para convenceros os dejo este pequeño programa.
/*
* Demostración de como se asignan mal los números.
*/
// en N tenemos un valor de millis relativamente alto.
unsigned long n=4294967295;
float x; // un valor float.
int y; // un valor entero.
void setup() {
Serial.begin(9600);
x = n;
y = n;
Serial.println(x); // La salida es "ovf" overflow.
Serial.println(y); // La salida es -1.
// La salida no es el número grande que esperabamos.
}
void loop() {
}
Moraleja: usad siempre UNSIGNED LONG con millis.
Entonces, ¿Si el máximo valor que cuenta es 4294967296 que ocurre despues de todos esos milisegundos?
Que el contador se desborda, pasa a valer 0 y sigue contando. Más de una vez habréis oido que millis a los 49 dias se desborda. Efectivamente para ser exactos se desborda a los 49,710269629 días.
¿Y qué pasa si se desborda?
Nada. El reloj de pulsera ¿al pasar media noche se bloquea? no, sigue contando como si nada. En arduino ocurre igual.
¿Y qué pasa con mi temporizador si se desborda millis mientras tanto?
Bien. Esto es interesante.
Hasta ahora habeis visto que he usado una regla simple: siempre he calculado el intervalo de tiempo que ha transcurrido viendo el tiempo actual (millis) restando el tiempo de inicio (t) y comparando con el tiempo que queremos (duración).
Pero también podriamos calcular el valor del reloj al que queremos que haga algo. Por ejemplo cuando guardamos millis vale 10, y queremos que dentro de 30 milisegundos haga algo. Eso significa que cuando milis valga 40 (10+30) queremos que haga algo. Asi que si comparo el valor de millis con 40 y tenemos el mismo resultado.
Por ejemplo:
/*
* blink sin delay, pero sumando.
*/
// En la variable t guardamos el valor de millis para ir comprobando
// en cada pasada de loop si han pasado 1000 ms.
unsigned long t;
// En esta variable guardamos el estado del led: encendido (HIGH) o
// apagado (LOW).
int estadoLed;
void setup() {
// El led está en el pin 13, hay que ponerlo como salida.
pinMode(13,OUTPUT);
// Miramos el "reloj" por primera vez.
t = millis();
// Encendemos el led.
estadoLed = HIGH;
digitalWrite(13,estadoLed);
}
void loop() {
// Aquí sumando, hacemos la suma de cuando miramos el tiempo con el
// tiempo de nuestro intervalo y lo comparamos con millis.
if ( millis() >= t + 1000 ) {
// Cambiamos el estado del led.
if ( estadoLed==HIGH )
estadoLed=LOW;
else
estadoLed=HIGH;
digitalWrite(13,estadoLed);
// Guardamos el tiempo de nuevo para comprobar en las siguientes
// pasadas.
t = millis();
}
}
Oye! También funciona. Pero ¿qué ocurre cuando estamos a punto de desbordarnos?.
Supongamos que cuando tomamos el valor de t inicial faltan 500 ms para desbordarnos, t valdrá: 4294966796 (en hexadecimal 0xfffffe0c). Si le sumamos 1000 (0x000003e8) que es el tiempo del parpadeo, el resultado será 500 (0x000001f4).
Obviamente la suma está bien, piénsalo, faltaban 500 para desbordarse, si le sumamos 500 llegamos al 0, otros 500 y ya tenemos los 1000.
Pero que ocurre cuando comparamos, millis todavia no se ha desbordado, sigue siendo un valor alto (recordad que lo anotamos cuando valia 4294966796), y si han transcurridos unos pocos milisegundos, la condición es cierta, entramos en la condición y ya no hemos esperado los 1000 segundos,
¿Cuando restamos pasa lo mismo? No. Analicemos, la misma situación. Guardamos t cuando faltan 500ms, es decir, al igual que antes 4294966796.
Si solo han pasado dos milisegundos cuando volvemos a comparar millis nos dará 4294966798, hacemos la resta y nos da 2. Bien, por ahora bien.
Pasa el tiempo y millis se desborda, con lo vale 0. Si hacemos la resta 0-4294966796 nos da: -4294966796, pero ojo, estamos tratando enteros sin signo, asi que no hay numeros negativos, con lo que el resultado es ¡¡ 500 !!, que es el tiempo que ha transcurrido.
Vale, poca fé tenéis. Coged la calculadora de windows, en modo programador, para poder convertir en hexadecimal. Ahora teclead 0-4294966796, el resultado que aparece será -4294966796. Convertirlo en hexadecimal. El valor que os da es: FFFFFFFF000001F4, pero arduino trabaja con 32 bits, asi que teneis coger solo 8 digitos (1 digito son 4 bits). Si lo haceis queda 0x000001f4 que son 500.
¡Tachan!¡Magia!. No, simple matemática binaria. Por eso aunque millis se desborde no ocurre nada si hacemos la resta.
Moraleja: Siempre haced millis()-tiempo >= duración, restad!