Arduino Task Manager and IO Abstraction library

TL;DR: This library provides support for writing non trivial Arduino applications. It contains a
very simple task management facility, IO abstraction (serial, 8754 i2c), interrupt management,
switch de-bouncing and rotary encoder support. This library is built for AtMega328 (Uno) and up,
but it can probably be used carefully with ATTiny devices too. There are examples packaged with it.

Using TaskManager in your sketches

TaskManager is a very simple executor framework that allows work to be scheduled in the future. Instead of writing code
using delays, one simply adds jobs to be done at some point in the future. In addition to this, interrupt handling is
also supported, such that the interrupt is “marshalled” and handled like a very high priority task to be processed
immediately.

When using this library you should never use any method that will delay for more than a few
microseconds, failing to follow that guideline will cause problems with interrupt handling and
timing.

Getting started with TaskManager

Firstly include it

#include <TaskManager.h>

Secondly, create a taskManager in the global scope at the top of the file - outside of setup(). There can be only one
task manager within an application.

TaskManager taskManager;

Advanced: If you need more or less task slots, you can provide the number of slots too, it defaults to 6 and each slot
uses about 8 bytes of memory:

TaskManager taskManager(numberOfSlots);

Scheduling things to be done

For every scheduled task that we create, we provide a call back function that will be called at that time. These are
declared as below. You can also use C11’s shortened lambda syntax if you wish, see the examples in the
taskManagement.ino sketch.

void onTimer() {
// do something here
}

To schedule something once in the future - very similar to setTimeout in javascript:

Setting up loop()

In the Arduino loop method, just put one call to the task manager.

void loop() {
taskManager.runLoop();
}

Interrupt handling

Interrupt handling is generally an advanced topic, but this library provides a very simple way to handle a few common
interrupts. When you tell the library to handle an interrupt, the library registers the interrupt handler on your
behalf, then when the condition is met the internal handler is triggered, it sets a flag to tell the library an interrupt
has been raised. The task library treats this as the highest priority, and as soon as the current task is completed
the interrupt code runs. This may not be real time enough for all uses, but will be fine for most cases.

You must provide a function that will be called back when the interrupt triggers. You can call any Arduino
functions as normal during the callback, as you are not in the interrupt handler, the task library has ‘marshalled’ it.

To register the interrupt callback, and add an interrupt on a pin whenever the pin changes:

taskManager.setInterruptCallback (onInterrupt); // <--- always do this first
taskManager.addInterrupt(2, CHANGE);

Using IO Abstraction in your sketches

BasicIoAbstraction makes it easy for both libraries and code to abstract away where the pins are located.
You can treat both shift registers and i2c based IO expander devices very much like Arduino pins. This is exceptionally
useful for library writers, who can then provide one library that works for most cases; but it is also useful in
code, as it provides flexibility and simplicity. Also see This blog post about using IO Abstraction

There are also several sketches in the examples folder showing how to use this abstraction, one for Arduino pins, one
for shift registers and one for i2c based IO using an expander device. In each case the circuit and code have been kept
as simple as possible.

There is also a fork of the LiquidCrystal library that works with this abstraction, and therefore can be used with
pins, i2c or a shift register without too much complexity.

Creating an Io Abstraction

To include it:

#include <BasicIoAbstraction.h>

To use it with an 8754 i2c expander:

With the IO expander version, the i2c communication bus is used to read and write values. You will need to know the
address of the i2c device on the bus. If you are not sure what address it’s on, use this i2c address scanner.

To use with Arduino pins directly

You can use the IO abstraction even for direct use of Arduino pins, especially if you are considering changing to use
a different type of IO later.

IoAbstactionRef arduinoPins = ioUsingArduino();

Want a different IO device?

Either submit a patch on github or get in touch via one of the means at the bottom of this page. Longer term I’m
considering adding another abstraction for [https://github.com/mikaelpatel/Arduino-GPIO] as I probably will have a
need for it.

Setting pin direction, reading and writing.

Now that you’ve got an instance of the IoAbstraction, you need to be able to set up the pin directions, just like
you would in a regular Arduino sketch:

Reading from and writing to pins.

Reading and writing from pins works slightly differently with the library, this is because the IO may well be going
over a serial bus. Due to this inefficiency, the serial implementations use a buffer to reduce reads and writes;
but Arduino direct programs pins directly without buffering. However, even when writing for Arduino, include the
synchronisation or it won’t work later when you use it with another IO device.

It’s up to you how and when you call ioDeviceSync(ioExpander); but using the sync is most optimal when you first write,
then sync, then read. This is demonstrated below:

Using SwitchInput in your sketches

Switch input provides support for event based programming, similar to how web and UI applications are written. It supports
switches and rotary encoders. It also de-bounces any switch presses to give greater accuracy and avoid flickering around
presses. This library is designed for use with TaskManager, and it is trivial to set it up that way.

To use it, you register switches and optionally a rotary encoder with the switchInput instance, you also register a call back
that is used by the library to tell you when there has been a change (for example a switch pressed or rotary encoder change).