TaskManager: Simple Multitasking for Arduino with RF networking

I am releasing the first public release of TaskManager, a multitasking manager for Arduino. It
has been undergoing alpha testing for a number of months, and is ready for additional users.

TaskManager offers the following:

  • Any number of tasks.
  • Extends the Arduino “setup/loop” paradigm – the programmer creates several
    “loop” routines (tasks) instead of one. So programming is simple and
    straightforward.
  • Tasks can communicate through signals or messages. A signal is an information-free
    “poke” sent to whatever task is waiting for the poke. A message has information
    (string or data), and is passed to a particular task.
  • TaskManager programs can use RF24 2.4GHz radios to communicate between nodes.
    So tasks running on different nodes can communicate through signals and messages
    in the same manner as if they were on the same node.

TaskManager is available at https://github.com/drsteveplatt/Arduino-TaskManager.

A simple program that flashes two lights at different rates looks like this:

//
// Blink two LEDs at different rates
// Respond to a switch on the third LED
//

#include <SPI.h>
#include <RF24.h>
#include <TaskManager.h>

#define LED_1_PORT  2
bool led_1_state;

#define LED_2_PORT  3
bool led_2_state;

void setup() {
  pinMode(LED_1_PORT, OUTPUT);
  digitalWrite(LED_1_PORT, LOW);
  led_1_state = LOW;
  
  pinMode(LED_2_PORT, OUTPUT);
  digitalWrite(LED_2_PORT, LOW);
  led_2_state = LOW;
  
  TaskMgr.add(1, loop_led_1);
  TaskMgr.add(2, loop_led_2);
}

void loop_led_1() {
    led_1_state = (led_1_state==LOW) ? HIGH : LOW;
    digitalWrite(LED_1_PORT, led_1_state);
    TaskMgr.yieldDelay(500);  
}

void loop_led_2() {
    led_2_state = (led_2_state==LOW) ? HIGH : LOW;
    digitalWrite(LED_2_PORT, led_2_state);
    TaskMgr.yieldDelay(100);  
}

Some quick notes:

  • There is no loop(). TaskManager has its own loop().
  • There are two routines that look like loop() routines. Each does one task.
  • We tell TaskMgr (the TaskManager object) about all of the loop-like things we
    want to run. It takes care of scheduling and running them.
  • We don’t use delay() (which stops everything). We use TaskManager’s delay, which
    stops one loop from running while allowing others to run

The TaskManager release includes a brief book with examples as well as full API documentation.

Enjoy, and let me know how things go.

I’m sure I’m just an old fuddy-duddy but there seems to be a great deal more code in your library than in the Blink Without Delay example or in Several Things at a Time

And, in spite of all the code your library does not actually have the code to do anything useful. (Like spin-doctors surrounding politicians ?)

What does this do ?

void TaskManager::yield() {
    longjmp(taskJmpBuf, YtYield);
}

It looks like a call to a function called longjmp() but I could not find it.
Maybe I am just poor at figuring out C/C++

…R

Hi Robin2, thanks for reviewing and reading the code!

Yes, the approach you use does work. Some of the sample code in the TaskManager library uses a similar process as a parallel (TaskManager-free) approach. In fact, an early example in the book included in the release compares the two exactly! The objective of TaskManager was to take the scheduling maintenance out of the programmer's space and to let the programmer focus on stimuli and activities.

I'm not sure what you mean by "...your library does not actually have the code to do anything useful." Yes, for simple examples (two blinking lights, etc.), TaskManager is probably overkill...

TaskManager provides a framework for complex interactions between multiple tasks, including tasks that may be operating on different devices and communicating using RF24 modules. The tasks may each have their own states, can be suspended until signaled, can pass messages, and so on. The test setup I used for this involved three devices each with three switches and two LEDs. Buttons passed information between devices as well as controlled LEDs on different devices. I'm not sure if it made this edition of the book, but will certainly include it in the next one.

As for the longjmp() call... that's me showing my C/C++ age. longjmp() is a simple method of transfer of control ("return") across several stack frames. So if your task t() called procedure p(), you can return control from p() by yielding within p() instead of having to return to t and yielding within t. longjmp() is paired with setjmp(); google should dig up some examples.

Think of it as an early form of try/catch. I should probably recode it using try/catch! Some day I'll check the efficiency of the generated code, and if try/catch is efficient then I'll switch over.

I hope you keep reading through the code and try it out. Please stay in contact,

-Steve

drsteve: I'm not sure what you mean by "...your library does not actually have the code to do anything useful." Yes, for simple examples (two blinking lights, etc.), TaskManager is probably overkill...

I just meant that the user must write some "action code" in addition to including your library whereas the Blink Without Delay example includes both the timing code and the action code so that, if you exclude the action code, the timing code is even shorter.

I apologise for my ignorance of longjmp(). I only use C/C++ when there is no alternative.

Are you satisfied that the use of longjmp() won't cause memory problems in the small memory of an Arduino.

...R

Ah, I understand now what you mean! Yes, TaskManager is a baseline package used to interleave multiple activities. Make it easy to write small tasks, each with a single focus. Run many on the same system.

My alpha testers have been using it to monitor/manage beer brewing (temperature) and RC cars. My (personal) long term plans involve interactive art projects -- proximity/color/light sensors, LEDs, perhaps sound, and communications between nodes in several sculptures in a single room. And for next halloween, a lot of little sensors communicating with each other and actuators that speak, move things, flash lights, etc. But that may take a while..

Yes, longjmp() is old-style C from back in the 70s. I used it on Z80 processors, 64K of RAM for everything, program, data, boot ROM, but that was a looong time ago. It is one of those obscure corners of C that they tell you not to use any more!

Some of my side-tests for TaskManager test memory leakage and longer run times (hours/overnight). No problems so far!

I've been trying to use this library and am having trouble locating dependencies. I've found RF24, but can't seem to locate the necessary library for Streaming.h. Is TaskManager still meant to be used today, or should I be looking elsewhere for simple task management (I don't think I even need message passing)? Thanks for your work on this!

Beuller: or should I be looking elsewhere for simple task management

See my link in Reply #1 and this Simple nRF24L01+ Tutorial

...R