Correo: [email protected] - Teléfono: +56979962128

Después de aprender a hacer parpadear un solo LED en tu Arduino, probablemente estés buscando una manera de hacer códigos geniales, pero te sientes limitado por el uso de delay(). Si preguntas en los foros, te dicen que mires el ejemplo de “Parpadeo sin retardo”. Este ejemplo presenta la idea de reemplazar delay() con una máquina de estado. Si estás confundido sobre cómo usarlo, este tutorial está pensado para llevarte desde parpadear dos LED con retardo, hasta usar un método alternativo, hasta cómo puedes usar millis().

La función millis() es una de las funciones más potentes de la biblioteca Arduino. Esta función devuelve el número de milisegundos que se ha estado ejecutando el boceto actual desde el último reinicio. Al principio, podrías estar pensando, ¡bueno, eso no es tan útil! Pero ten en cuenta cómo sabes la hora durante el día. Efectivamente, se observa cuántos minutos han transcurrido desde la medianoche. ¡Esa es la idea detrás de millis()! 

En lugar de “esperar una cierta cantidad de tiempo” como lo haces con delay(), puedes usar millis() para

preguntar

“¿cuánto tiempo ha pasado”? Comencemos por ver un par de formas en que puede usar delay() para parpadear LED.

 

Ejemplo #1: Retardo básico 

Probablemente ya estés familiarizado con este primer ejemplo de código: El IDE de Arduino incluye este ejemplo como “Blink”.

void setup() { 
pinMode(13, OUTPUT); 
} 

void loop() { 
digitalWrite(13, HIGH); // Enciende el LED
delay(1000); // Espera un segundo 

digitalWrite(13, LOW); // apaga el LED 

delay(1000); // Espera un segundo 
} 

Leyendo cada línea de loop() en la secuencia del boceto:

  1. Enciende el LED del pin 13, 
  2. Espera 1 segundo (o 1000 milisegundos), 
  3. Apaga el LED del pin 13 y, 
  4. Espera 1 segundo. 
  5. A continuación, toda la secuencia se repite. 

El problema potencial es que mientras estás en el delay(), tu código no puede estar haciendo nada más. Así que veamos un ejemplo en el que no estás “bloqueando” durante esos 1000 milisegundos completos.

Ejemplo #2: Retardo básico con bucles for() 

Para nuestro segundo ejemplo, solo vamos a retrasar 1 ms, pero lo hacemos dentro de un bucle for().

void setup() { 
pinMode(13, OUTPUT); 
} 

void loop() { 
digitalWrite(13, HIGH); // Enciende el LED

for (int x=0; x < 1000; x++) { // Espera un segundo
delay(1); 
} 

digitalWrite(13, LOW); // Apaga el LED
for (int x=0; x < 1000; x++) { // Espera un segundo
delay(1); 
} 
} 

Este nuevo sketch cumplirá la misma secuencia que el Ejemplo #1. La diferencia es que el Arduino solo se “retrasa” un milisegundo a la vez. Un truco inteligente sería llamar a otras funciones dentro de ese bucle for(). Sin embargo, su tiempo estará fuera de lugar porque esas instrucciones agregarán un retraso adicional.

 

Ejemplo #3: bucles for() con 2 LEDs 

En este ejemplo, hemos agregado un segundo LED en el pin 12 (¡con una resistencia limitadora de corriente!). Ahora veamos cómo podríamos escribir el código del Ejemplo #2 para que parpadee el 2º LED.

void setup() { 
pinMode(13, OUTPUT); 
pinMode(12, OUTPUT); 

} 

void loop() { 
digitalWrite(13, HIGH); // Enciende el primer LED

for (int x=0; x < 1000; x++) { // Contador de un segundo
delay(1); 

if (x==500) { //Cuando el contador llegue a 500, enciende el segundo LED
digitalWrite(12, HIGH); 
} 
} 

digitalWrite(13, LOW); // Apaga el primer LED

for (int x=0; x < 1000; x++) { // Contador de un segundo
delay(1); 

if (x==500) { //Cuando el contador llegue a 500, apaga el segundo LED
digitalWrite(12, LOW); 
} 
} 
} 

Comenzando con el primer bucle for(), mientras el pin 13 está alto, el pin 12 se activará después de 500 veces a través del bucle for().  Parecería que el pin 13 y el pin 12 estaban parpadeando en secuencia.  El pin 12 se encendería 1/2 segundo después de que se encienda el pin 13.  1/2 segundo después, el pin 13 se apaga, seguido por el pin 12 otro 1/2 segundo.

Este código es bastante complicado, ¿no?

Si desea agregar otros LED o cambiar la secuencia, debe comenzar a ser ingenioso con todas las declaraciones if. Al igual que en el ejemplo # 2, la sincronización de estos LED estará apagada. La declaración if() y la función digitalWrite() toman tiempo, lo que se suma al “delay()”.

Ahora veamos cómo millis() soluciona este problema.

La función millis() 

“Volviendo a la definición de la función millis(): cuenta el número de milisegundos que se ha estado ejecutando el boceto”.

Da un paso atrás y piensa en eso por un segundo. En el Ejemplo #3, estamos tratando de hacer parpadear los LED en función de una cierta cantidad de tiempo. Cada 500 ms queremos que uno de los LEDs haga algo diferente. Entonces, ¿qué pasa si escribimos el código

para ver cuánto tiempo pasó,

en lugar de esperar a que pase el tiempo?

Ejemplo #4: Parpadear con millis() 

Este código es el mismo ejemplo de “Blink” del #1 reescrito para hacer uso de millis(). Un diseño un poco más complicado, porque hay que incluir un par de variables más. Uno para saber cuánto tiempo esperar y otro para conocer el estado del LED en el pin 13.

unsigned long intervalo=1000; // El tiempo que queremos esperar
unsigned long millisAnterior=0; // millis() devuelve una variable de tipo unsigned long
bool estadoLed = false; // Estado de la variable del LED

void setup() { 
     pinMode(13, OUTPUT); 
     digitalWrite(13, estadoLed); 
} 

void loop() { 
unsigned long millisActual = millis(); // Registra el tiempo transcurrido

// Revisa si ha pasado el tiempo del intervalo
if ((unsigned long)(millisActual - millisAnterior) >= intervalo) { 

estadoLed = !estadoLed; // Alterna el estado
digitalWrite(13, estadoLed); // Enciende o apaga el LED según el estado

millisAnterior = millis(); // Registra el tiempo del término del ciclo
} 
} 

Echemos un vistazo al código desde el principio.

unsigned long intervalo=1000; // El tiempo que queremos esperar
unsigned long millisAnterior=0; // millis() devuelve una variable de tipo unsigned long

millis() Devuelve un valor unsigned (solo devuelve números positivos).

Al usar variables asociadas con millis() o micros(), SIEMPRE declárelas como unsigned long  

La variable intervalo es la cantidad de tiempo que vamos a esperar. La variable anterior Millis se utiliza para que podamos ver cuánto tiempo ha pasado desde que sucedió algo.

bool estadoLed = false; // Estado de la variable del LED

Este código utiliza una variable para el estado del LED. En lugar de escribir directamente un “HIGH” o “LOW” con digitalWrite(), escribiremos el valor de esta variable.

La función setup() es bastante estándar, así que saltemos directamente al loop(): 

void loop() { 
unsigned long millisActual = millis(); // Registra el tiempo transcurrido

Cada bucle de tiempo () se repite, lo primero que hacemos es tomar el valor actual de millis(). En lugar de llamadas repetidas a millis(), usaremos este tiempo como una marca de tiempo.

void loop() { 
if ((unsigned long)(millisActual - millisAnterior) >= intervalo) { 

¡Eso parece complicado! Pero realmente no lo es, así que no tengas miedo de simplemente “copiar y pegar”. En principio , lleva una declaración if(). En segundo, el (unsigned long) pero no es necesario ( no voy esperar 49 días para asegurarme). La tercera “(currentMillis – anteriorMillis) >= intervalo)” .

A lo que se reduce, este código solo se convertirá en VERDADERO después de que millis() sea al menos un “intervalo” más grande que el valor almacenado anteriormente de millis, en Millis anterior. En otras palabras, nada en la instrucción if() se ejecutará hasta que millis() sea 1000 milisegundos más grande que el Millis anterior.

La razón por la que usamos esta resta es que se encargará de la reinversión de milis. No necesitas hacer nada más.

estadoLed = !estadoLed; // Alterna el estado

Esta línea de código establecerá el valor de ledState en el valor correspondiente. Si es verdadero, se convierte en falso. Si es falso, se convierte en verdadero.

digitalWrite(13, estadoLed); // Enciende o apaga el LED según el estado

En lugar de escribir directamente un valor ALTO o BAJO, utilizamos una variable de estado para el LED. En el siguiente ejemplo comprenderás por qué.

millisAnterior = millis(); // Registra el tiempo del término del ciclo

Lo último que se hace dentro de la instrucción if es establecer previousMillis en la marca de tiempo actual. Este valor permite que la instrucción if realice un seguimiento de cuándo ha transcurrido al menos un intervalo (o 1000 ms).

Ejemplo n.° 5: Agregar un segundo LED con millis() 

Agregar un segundo LED intermitente es sencillo con el código millis(). Duplique la instrucción if con una segunda variable waitUntil y LEDstate.

// Cada "evento" (LED) tendrá su propia variable
unsigned long millisAnteriorLED12=0; 
unsigned long millisAnteriorLED13=0; 

// Diferentes intervalos para cada LED
int intervaloLED12 = 500; 
int intervaloLED13 = 1000; 

// Cada LED tiene su propia variable de estado
boolean estadoLed12 = false; 
boolean estadoLed13 = false; 

void setup() { 
pinMode(13, OUTPUT); 
pinMode(12, OUTPUT); 
} 

void loop() { 
// Registrar el tiempo actual
// Solo se necesita uno para los dos condicionales If
unsigned long millisActual = millis(); 

// Revisar si ha pasado el tiempo para cambiar el estado del LED 12
if ((unsigned long)(millisActual - millisAnteriorLED12) >= intervaloLED12) { 

estadoLed12 = !estadoLed12; 
digitalWrite(12, estadoLed12); 

// Guardar el tiempo para el fin del ciclo de parpadeo del LED 12
millisAnteriorLED12 = millisActual; 
} 

// Revisar si ha pasado el tiempo para cambiar el estado del LED 13
if ((unsigned long)(millisActual - millisAnteriorLED13) >= intervaloLED13) { 

estadoLed13 = !estadoLed13; 
digitalWrite(13, estadoLed13); 

// Guardar el tiempo para el fin del ciclo de parpadeo del LED 12
millisAnteriorLED12 = millisActual; 
} 
} 

Este código tiene algunos cambios menores con respecto al ejemplo anterior. Hay variables de estado y de milís anteriores independientes para los pines 12 y 13.

Para tener una idea de lo que sucede con este código, cambie los valores de intervalo de cada uno. Ambos LED parpadearán de forma independiente.

Otros ejemplos 

Consulte esta lista de ejemplos de millis(): 

https://www.baldengineer.com/millis-cookbook.html

Hay ejemplos para crear un efecto estroboscópico de luz policial, cronometrar su código, cambiar el tiempo de encendido y apagado de un LED, y otros.

Conclusión 

En un principio, parece que el uso de la función millis() hará que el boceto sea más complicado que el uso de delay(). En cierto modo, esto es cierto. Sin embargo, la contrapartida es que el boceto se vuelve significativamente más flexible. Las animaciones se vuelven mucho más sencillas. Hablar con las pantallas LCD mientras se leen los botones ya no supone ningún problema. Prácticamente, puedes hacer que tu Arduino sea multitarea.

Después de realizar estos ejercicios, consulte este artículo sobre 

cómo evitar el vuelco o el reinicio de mills()

 .

Aprende esto en un curso

Si prefieres verlo en vivo y con alguien que resuelva tus dudas, estos cursos presenciales de MCI Educación cubren el tema de este tutorial: