Arduino: TaskScheduler, no más millis ni delay

En esta entrada os voy a hablar de los TaskScheduler. Los TaskScheduler son como su nombre indica, programadores de tareas que nos permitirán crear tareas periódicas sin tener que usar millis o condicionales.

¿Cómo funcionan?

Los TaskSchedulers son objetos a los que les puedes añadir tareas y ellos se encargan de ejecutarlas cada cierto tiempo. Además, estos programadores son dinámicos y permiten variar el tiempo de espera entre tareas, e incluso activarlas y desactivarlas.

En esta entrada os hablaré de uno que he usado y que se llama TaskScheduler, el cual me ha parecido simple y funcional, además de aportar ciertas funcionalidades útiles.

Instalación

Para instalar el TaskScheduler, sólo tendremos que abrir el Arduino IDE, entrar en el gestor de librerías, buscar TaskScheduler e instalar el nos saldrá:

Funciones

Entre las funciones de las que dispone, se pueden destacar las siguientes:

  • Ejecución programada cada x milisegundos e incluso microsegundos.
  • Cambio a modo «En Espera», cuando no está ejecutando ninguna tarea, lo cual nos permitirá ahorrar energía.
  • Ejecución de X veces o por siempre.
  • Activación y desactivación dinámica de las tareas.
  • Cambio de configuraciones como el número de ejecuciones o el tiempo entre ejecuciones de forma dinámica.

¿Qué le diferencia de millis?

La diferencia con milis es básicamente que no tienes que andar usando lógicas para gestionar las ejecuciones, sino que se encarga de hacerlo el propio módulo. Esto nos permitirá hacer código más legible y más fácil de mantener. Además, hay que tener en cuenta las funciones extra que nos proporciona, como el ahorro de energía cuando no se está usando, o el cambio de configuraciones de forma dinámica.

¿La ejecución es multitarea?

Depende… personalmente, me niego a llamar multitarea a algo que no lo es realmente, y tanto la función millis como el TaskScheduler no son multitarea en Arduino Uno por mucho que la gente por internet clame que si. ¿Por qué opino esto?, porque en ninguno de los dos casos se ejecutan dos tareas a la vez, sino que se hace una, luego otra, y otra… pero jamás dos al mismo tiempo. Si tu creas una tarea que tarda 10 segundos en ejecutarse, y luego otra que se ejecute cada segundo, la que se ejecuta cada segundo tendrá que esperar a que finalice la ejecución de la otra antes de poder continuar.

Por supuesto te estaría engañando si te dijera que esto es así siempre. Si tu microcontrolador es Single Core como por ejemplo el Arduino Uno que comento arriba, Nano o todos los basados en el ATMega328, no será multitarea. Sin embargo si tu microcontrolador es Dual Core o superior como por ejemplo el Arduino Due, entonces si podrás ejecutar funciones en modo multitarea.

Objetos de los que disponemos

  • Task: Este objeto te permitirá configurar la ejecución de la función, así como su número de ejecuciones y cada cuanto tiempo.
  • Scheduler: Este objeto es el programador encargado de la ejecución de las tareas, el cual se tendrá que ejecutar en cada loop.

Ejemplo simple

En este ejemplo veremos la función simple de encender y apagar el LED interno de Arduino, pero usando el TaskScheduler para dicho propósito.

#include <TaskScheduler.h>

// Declaramos la función que vamos a usar
void led_blink();

// Creamos el Scheduler que se encargará de gestionar las tareas
Scheduler runner;

// Creamos la tarea indicando que se ejecute cada 500 milisegundos, para siempre, y llame a la función led_blink
Task TareaLED(500, TASK_FOREVER, &led_blink);

bool statusLED = false;

// the setup function runs once when you press reset or power the board
void setup() {
  Serial.begin(9600);

  // Configuramos el pin del led interno como salida
  pinMode(LED_BUILTIN, OUTPUT);

  // Añadimos la tarea al programador de tareas
  runner.addTask(TareaLED);

  // Activamos la tarea
  TareaLED.enable();
}

// the loop function runs over and over again forever
void loop() {
  // Es necesario ejecutar el runner en cada loop
  runner.execute();
}

void led_blink() {
  statusLED = !statusLED;
  digitalWrite(LED_BUILTIN, statusLED);
  Serial.println((String)millis() + " - Led: " + (String)statusLED);
}

Como podemos observar, se crea el Scheduler para gestionar las tareas, luego se crea la tarea que ejecutaremos cada 500ms y por último añadimos dicha tarea al Scheduler, activándola después (en caso contrario no se ejecutaría).

Ahorro de energía

Una de las razones por las que me ha gustado esta librería y por la que me parece mucho mejor que usar millis o delay, es por la gestión de energía que hace. Tan sólo diciendo que queremos que el microcontrolador se ponga en modo bajo consumo mientras no haya tareas, ella se encarga de gestionarlo.

En una prueba que hice con un ATMega328p con la configuración básica, ha sido capaz de bajar el consumo de 8.4ma durante el funcionamiento del microcontrolador, a tan sólo 3.4ma en espera. Esto hace que sin hacer nada especial consuma menos de la mitad. En un proyecto donde por ejemplo, se lea un sensor cada minuto, es un gran ahorro de batería.

Sin más os dejo el ejemplo que usé para medir la corriente utilizada, el cual no es más que encender y apagar un led cada cinco segundos. Por supuesto, cuando el led está encendido consume bastante más que lo que he mencionado arriba.

#include <TaskScheduler.h>

void ledBlink();

Scheduler runner;
Task tBlink(5000, TASK_FOREVER, &ledBlink);

bool ledStatus = false;

void ledBlink(){
  ledStatus = !ledStatus;
  digitalWrite(LED_BUILTIN, ledStatus);
}

void setup(){
  runner.addTask(tBlink);
  tBlink.enable();
  pinMode(LED_BUILTIN, OUTPUT);
}

void loop(){
  runner.execute();
}

Como se puede observar en el código, con tan sólo añadir una línea, se ha añadido ahorro de energía al Scheduler:

#define _TASK_SLEEP_ON_IDLE_RUN

Más información

Para más información acerca del módulo, puedes pasarte por su página de GitHub:
https://github.com/arkhipenko/TaskScheduler

Recomiendo utilizar el método descrito al principio para instalarlo, en lugar de descargarlo de Github. El funcionamiento será el mismo, pero instalándolo desde el gestor de librerías de Arduino nos será más fácil actualizarlo.

Deja un comentario

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.