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.