Arduino multitarea y la gestión del tiempo

NUEVO PROYECTO: Recibe una curiosidad diaria con nuestra Newsletter. Pequeños asombros diarios para Personas Altamente Curiosas

Prueba de Arduino para trabajar en multitarea con milis

No soy un experto en Arduino, a pesar de tener la placa desde hace tiempo no he investigado casi. Las veces que lo he usado ha sido como herramienta copiando y pegando código ya creado pero sin mucho interés por aprender realmente como funciona sino simplemente con la intención de hacer que funcione y me resulte útil. Estas Navidades tuneé un poco el Belén con unos leds y un sensor de ultrasonidos HC-SR04. Y me detuve a observar que había que hacer.

Tan sólo quería hacer cosas diferentes con dos leds a partir de una misma señal. Ups. Rápidamente tropecé con la que me parece que será una de las primeras limitaciones que te encuentras cuando empiezas a trastear con Arduino. Y no es necesario complicarlo mucho. Hablo tan sólo de unos leds te das cuenta de que no puedes hacer correctamente lo que quieres.

Vamos a dejarlo claro desde el principio en Arduino la multitarea no existe, no se pueden procesar dos tareas en paralelo. Pero existen técnicas para hacer llamadas tan rápido que parece que trabajan a la vez.

Cuento el caso con más detalle. En Navidad monté un Belén y quería  que se encendieran ciertas luces del Nacimiento cuando se acercaran mis hijas. Nada complicado. Sólo quería que dos ramas de luces led funcionaran de forma diferente ante los valores de un sensor de proximidad.

Quería que cuando alguien se acercara a menos de 10 cm

  • Una de las ramas de luces que iría a las estrellas se quedara encendida 10 segundos
  • Que la otra que iría dentro de las casas se quedara encendida 10 segundos pero desde que se separan del belén.

¿Sencillo no? pues esto puede causarte grandes problemas. Porque Arduino no es capaz trabajar la multitarea, él ejecuta una orden después de otra.

 

NUEVO PROYECTO: Recibe una curiosidad diaria con nuestra Newsletter. Pequeños asombros diarios para Personas Altamente Curiosas

circuito montaje leds parpadeando y ultrasonidos en multitarea

He hecho un montaje con el sensor de ultrasonidos HC-SR04 y 2 leds, cada uno equivaldría una rama del Belén. La primera parte no tiene mucha gracia, ya que es configurar el setup y el funcionamiento del sensor de ultrasonidos en el LOOP, pero esto lo puedes encontrar en miles de sitios. Igual otro día si indago más hago un especial, de momento aquí tienes (siento no poner créditos, pero no recuerdo de donde lo cogí)

Código HC-SR04 en Arduino

int ledPin1=8;
int ledPin2=7;
long distancia;
long tiempo;
long currenttime;
long timepast;

void setup() {
// initialize digital pin LED_BUILTIN as an output.
pinMode(ledPin1, OUTPUT);
pinMode(ledPin2, OUTPUT);
Serial.begin(9600);
pinMode(3, OUTPUT); /*activación del pin 9 como salida: para el pulso ultrasónico*/
pinMode(2, INPUT); /*activación del pin 8 como entrada: tiempo del rebote del ultrasonido*/
}

// the loop function runs over and over again forever
void loop() {

digitalWrite(3,LOW); /* Por cuestión de estabilización del sensor*/
delayMicroseconds(5);
digitalWrite(3, HIGH); /* envío del pulso ultrasónico*/
delayMicroseconds(10);
tiempo=pulseIn(2, HIGH); /* Función para medir la longitud del pulso entrante. Mide el tiempo que transcurrido entre el envío
del pulso ultrasónico y cuando el sensor recibe el rebote, es decir: desde que el pin 12 empieza a recibir el rebote, HIGH, hasta que
deja de hacerlo, LOW, la longitud del pulso entrante*/
distancia= int(0.017*tiempo); /*fórmula para calcular la distancia obteniendo un valor entero*/
/*Monitorización en centímetros por el monitor serial*/
Serial.println("Distancia ");
Serial.println(distancia);
Serial.println(" cm");
delay(1000);

Con esto se irá revisando y almacenando la distancia que mide el sensor de ultrasonido

Solución funcionamiento LEDS

Lo primero que se nos viene a la mente es empezar a meter delays. No sé por qué pero todos los principiantes pensamos en delay() y eso limita muchísimo las opciones porque mientras usas delay() la placa no sigue trabajando y por tanto ya no puedes hacer nada más en ese tiempo de suspensión. La solución es el uso de millis()

Aquí encontré una solución, sencillota a base de if y de contadores. Como decía un profesor mío, cualquier cosa se puede programar con muchos if seguidos. Pero claro no es muy elegante la verdad.

//si la distancia es menor de 10, encendemos los dos LEDS o ramales y empezamos a contar el tiempo con millis()
if (distancia < 10){ 
digitalWrite(ledPin1, HIGH);  
digitalWrite(ledPin2, HIGH);
currenttime=millis();
}

//si la distancia es mayor que 10 comprobaremos el tiempo pasado desde que se ha encendido y si es mayor que el indicado apagaremos el LED1
if (distancia > 10){
timepast=millis()-currenttime;
digitalWrite(ledPin1, LOW);

//si la distancia es mayor de 10000 apagaremos el LED2
if(timepast>10000){
digitalWrite(ledPin2,LOW);
}
}
}

Lo que pretende este código es eso que Arduino vaya ejecutando constantemente esos tres if de forma que pasará tan rápido por ellos que parece que esté haciendo varias cosas al mismo tiempo. Pero como ya sabemos sigue ejecutando sentencias de una en una.

Una vez medida la distancia y almacenada en la variable distancia, se evaluarían los if:

  • El primero comprueba si la distancia es menor de los 10 centímetro deseados. Si es así encendemos los dos LEDS y empezamos a contar el tiempo con milis()
  • y pasaríamos al segundo if  para la distancia mayor de 10 cm. Si cumple, calculamos el tiempo que ha pasado y desactivamos el led 1 que sólo dependía de la distancia.
  • Pasaríamos al tercer if donde comprobamos si ha pasado más de 10 segundos desde que se activó el contador, y en caso de ser así apaga el pin 2
  • Y así sigue el bucle. Una y otra vez.

Al final y aunque la solución funcionaba tenía claro que este problema seguro que lo había tenido mucha gente y que tenía que haber una solución más ortodoxa. Me puse a buscar y encontré una buena (que seguro que no la única) de la mano de los chicos de Adafruit y la programación orientada a objetos. Consiste en la creación de objetos con clases definidas para hacer rápidas llamadas y no tener que repetir el código cientos de veces en nuestro programa.

Que al final es «lo mismo» que mi solución con de if + contadores pero mucho más elegante y que proporciona un código mucho más legible y mucho más eficiente.

La gestión de la multitarea lleva irremediablemente a la gestión del tiempo en Arduino. En un principio esto no entraba en el artículo pero creo que es muy interesante.

Arduino y las funciones de tiempo

Como ya he comentado creo que existe una gran dependencia con delay() ,posiblemente porque la gente que empieza ve esta función en todos los ejemplos que se suelen poner desde el blink a cualquier semáforo o cualquier manipulación de encendido y apagado de LEDS.

Delay tiene un problema bien grande y es que cuando hacemos una llamada a delay() por una cantidad de tiempo todo se para. La placa no lee ningún sensor, ni sigue ejecutando sentencias, ni hace nada de nada, tan sólo esperar a que pase el tiempo que le hemos dicho y claro si queremos usar la placa para más de una cosa al mismo tiempo esto no es viable.

Tendremos que echarle un vistazo a millis() incluso a delayMicroseconds() Y también tenemos micros() que nos devuelve el número de microsegundos desde que se ha empezado a ejecutar el programa

Blink sin delay

Un ejemplo chulo para ver como funciona es ejecutar el mítico Blink pero sin el delay. La principal diferencia consiste en olvidarnos de la función delay()  usar milis() para poder contar el tiempo transcurrido entre operaciones.

3 comentarios en «Arduino multitarea y la gestión del tiempo»

Deja un comentario