Arduino tiene una "especie" de reloj interno que se pone en marcha desde punto y hora que lo encendemos hasta que lo apagamos (o hacemos un reset de la placa). Ese reloj va contando en milisegundos, asi que empieza contando 0, 1, 2, 3, 4, 5... y subiendo cada milisegundo. Cuando hacemos una llamada a millis esta nos devuelve el valor dicho reloj.
El reloj funciona de manera independiente de nuestro código. Esta montado de tal manera que funciona como una interrupción (ya lo vimos antes) que se ejecuta cada milisegundo y solo hace eso, incrementar el número. Así que aunque hagamos un delay se seguirá ejecutando.
Imaginad que tenemos un reloj de pulsera, este va a estar contando todo el rato para saber en que hora estamos. De vez en cuando miramos la hora y "¡uy! las 5, hora del café".
Tu te levantas, desayunas, te vas a trabajar/estudiar, comes, cenas, etc y haces una vida normal, pero el reloj sigue contando las horas, de vez en cuando lo miras porque tienes que hacer algo a una hora.
Pues bien, ese reloj de pulsera es el reloj de arduino y ese "mirar la hora" es llamar a la función millis.
Vamos a ver un ejemplo sencillo:
/*
* Ejemplo de millis con delay.
*/
void setup() {
Serial.begin(9600);
}
void loop() {
Serial.println(millis());
delay(1000); // Oh! Dios! Estas usando delay!
}
En este ejemplo, miro el contador millis y lo envio por el puerto serie. Luego hago un delay. Como el loop es un bucle infinito el proceso se repite eternamente. Y la salida es esta:
0
999
1999
3000
3999
5000
6000
7001
8000
9000
10001
...
...
Como veis, el reloj sigue contando aunque tu detengas el programa haciendo un delay (recordad, un delay es hacer una parada de programa). Como veis la salida no es exactamente 1000 milisegundos, casi siempre hay una diferencia de 1 ms, pero no es preocupante, tanto millis como delay no son exactos al 100%.
Veamos como hacer el parpadeo del led cada segundo. Parece lógico que tenemos que ver lo que marca millis (la "hora" que es) en este momento, guardar su valor, e ir mirando en cada pasada hasta que haya pasado un segundo, es decir millis nos da 1000 mas de lo que tenemos guardado. En ese momento cambiamos el led, volvemos a anotar el tiempo y seguimos mirando millis.
/*
* blink sin delay.
*/
// 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í la comparación mágica.
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();
}
}
et voilá! un led que parpadea sin usar delay.
Aquí el problema radica en entender el if o la comparación mágica. Recordad: loop es un bucle inficito, cuando termina vuelve a empezar y millis siempre está contando.
Al principio (setup) hemos leido el valor de millis y lo hemos guardado en t, supongamos que se ha guardado un cero. El reloj de millis sigue contando, pongamos que al entrar en el loop ya vale 10. ¿Cómo sabemos el tiempo que ha pasado? fácil, sabemos que el reloj actual vale 10, y que cuando lo miramos (lo guardamos en la variable t) valia 0. Así que 10-0 significa que han transcurrido 10 ms.
Comparamos ese tiempo transcurrido con el tiempo del parpado. Si el tiempo transcurrido fuera mayor que los 1000 ms del parpadeo deberemos cambiar el pin. De momento esa condición no se cumple, así que no hacemos nada.
Pero ¿qué ocurre cuando han pasado 1000 ms desde que iniciamos el arduino? Millis nos devolverá 1000. Volvemos a comparar, millis ahora vale 1000 y t (nuestro tiempo de inicio) vale 0, restamos y vemos el tiempo que ha transcurrido 1000-0 = 1000. Y cuando lo comparamos con 1000 resulta que es igual. Entonces cambiamos el estado del led, si estaba encendido lo apagamos y viceversa. Y ahora lo importante, si queremos volver a contar desde este punto que ya hemos cambiado el led, tendremos que guardar la "hora" actual, el valor de millis, para seguir comparando después. Hacemos t = millis() = 1000.
Nota: En este momento t = millis() podría ser 1001 o mas, el código que pongamos dentro del if tarda un tiempo en ejecutarse y no lo controlamos. En este caso el tiempo que tarda en ejecutarse el cambio del led es ridiculo, asi que vale con un valor de 1000.
A partir de aquí el proceso sigue. En la siguiente pasada millis puede valer 1001 y t vale 1000, el tiempo transcurrido será de 1001-1000=1. 1 es mayor que 1000 obvio que no, por lo tanto no hace nada. Y seguimos contando...
Llega un momento que cuando llamamos a millis nos devuelve 2000. Y al hacer la comparación 2000-1000=1000, resulta que ya han transcurrido los 1000 ms y al comparar con 1000, volvemos a cambiar el estado del led y cambiamos el valor t a 2000. Y seguimos contado...
Ocurrirá lo mismo cuando lleguemos a 3000, 4000, 5000, 6000, ..., es decir cada segundo le led cambiará de estado.