Home

Awesome

<img src="http://jean-marc.paratte.ch/wp-content/uploads/2013/01/diduino1_960x96.jpg" class="header-image" alt="jmP" height="96" width="960">

jm_Scheduler - A Cooperative Scheduler Library for Arduino

2019-01-03: v1.1.0 - Modifying start() which now fails if coroutine already started.
2018-04-30: v1.0.10 - library.properties adjustement.
2018-04-29: v1.0.9 - jm_Scheduler is now compatible with EPS32. 5 new Blink examples added.
2018-04-19: v1.0.8 - yield() function corrected.
2018-03-27: v1.0.7 - A Cooperative Scheduler Library for Arduino.
2018-02-08: v1.0.6 - Minor adjustments.
2017-10-17: v1.0.5 - Minor adjustments.
2017-05-08: v1.0.4 - Minor adjustments.
2017-05-08: v1.0.4 - Minor adjustments.
2017-05-05: v1.0.3 - Adding yield(),sleep(),rearm_async(). Removing void rearm(timestamp_t time, timestamp_t ival);
2017-04-26: v1.0.2 - Adding void rearm(timestamp_t time, timestamp_t ival);
2017-03-29: v1.0.1 - Minor adjustments.
2016-07-08: v1.0.0 - Initial commit.

Concept

jm_Scheduler schedules repeated and intervaled coroutines like the JavaScript setInterval() function does, but with some improvements:

jm_Scheduler doesn't schedule like the official Scheduler Library for Arduino DUE and ZERO does, yield() function which suspends a task is implemented, but startLoop() function which allocates a stack to the new task is not implemented.

jm_Scheduler schedules tasks sequentially on the stack processor. The rules to yield and resume are:

Basic Example

// This example schedules a coroutine every second

#include <jm_Scheduler.h>

jm_Scheduler scheduler;

void coroutine()
{
	Serial.print('.');
}

void setup(void)
{
	Serial.begin(9600);
	
	scheduler.start(coroutine, TIMESTAMP_1SEC); // Start immediately coroutine() and repeat it every second
}

void loop(void)
{
	yield();
}

Study Plan

Timestamp

The timestamp is read from the Arduino function micros(). By design, the micros() function of Arduino UNO and Leonardo running at 16MHz returns a [us] timestamp with a resolution of [4us].

micros() declaration is:

unsigned long micros();

Look at https://www.arduino.cc/en/Reference/Micros for details.

<!-- ### More about Timestamp -->

timestamp is a 32bit [us] counter and overflows about every 70 minutes (precisely 1h+11m+34s+967ms+296us).

<!-- The periodicity of 70 minutes is sometimes not enough to control slow processes. Look next section for answers and tricks. -->

Timestamp declaration and constants

typedef uint32_t timestamp_t;

#define timestamp_read() ((timestamp_t)micros())

#define TIMESTAMP_DEAD (0x01CA0000) // coroutine dead time [30s + 15ms + 488us]
#define TIMESTAMP_TMAX (0xFE35FFFF) // [1h + 11m + 4s + 951ms + 808us - 1]

#define TIMESTAMP_1US	(1UL)					// [1us]
#define TIMESTAMP_1MS	(1000*TIMESTAMP_1US)	// [1ms]
#define TIMESTAMP_1SEC	(1000*TIMESTAMP_1MS)	// [1s]
#define TIMESTAMP_1MIN	(60*TIMESTAMP_1SEC)		// [1 minute]
#define TIMESTAMP_1HOUR	(60*TIMESTAMP_1MIN)		// [1 hour]

timestamp_t defines the type of all timestamp values.

timestamp_read() returns the instantaneous timestamp. This function can also be used by interrupt coroutines to timestamp they data.

TIMESTAMP_DEAD is the maximum allowed execution time of a coroutine to guarantee right scheduling. If the coroutine doesn't end before, the scheduler could miss very long scheduling (see next).

TIMESTAMP_TMAX is the maximum allowed scheduling time of a coroutine. In practice, don't use timestamp values greater than 1 hour.

jm_Scheduler methods

// start coroutine immediately
void start(voidfuncptr_t func);

// start coroutine immediately and repeat it at fixed interval
void start(voidfuncptr_t func, timestamp_t ival);

// start coroutine on time and repeat it at fixed interval
void start(voidfuncptr_t func, timestamp_t time, timestamp_t ival);

// stop coroutine, current or scheduled, remove it from chain
void stop();

// rearm current coroutine and set or reset interval
void rearm(timestamp_t ival);

// rearm current coroutine, change coroutine function and set or reset interval
void rearm(voidfuncptr_t func, timestamp_t ival);

> `start()` initiates a scheduler variable, starts a coroutine function, immediately or on time, with or without repetitions.
`start()` is invoked once. Next `rearm()` allows changing scheduler values.

> `stop()` cancels further execution of a scheduled coroutine. 
`stop()` can be invoked from inside coroutine or elsewhere.
If invoked from inside _coroutine_, `stop()` doesn't exit the function, just cancels further execution.

> `rearm()` changes values of a scheduler variable.
The new values are evaluated on exit coroutine function.
The main usage is to change _interval_ or _function_ or both or else cancel further execution.

jm_Scheduler loop

static void cycle();

cycle() is the cornerstone of the scheduler and must be invoked as often as possible. Note that cycle() is a static method. The right place is in Arduino loop() function. Example:

void loop(void)
{
	yield();
}

cycle() can also be invoked in Arduino setup() function. Example:

void setup(void)
{
	// here, some jm_Scheduler variables initialized...
	
	Serial.begin(9600);
	while (!Serial)
	{
		// wait for USB Serial ready...
		
		yield();
	}
	
	// split long setup()...

	yield();
	
	// continue setup()...
}

cycle() can't be invoked from inside a coroutine function.

Good scheduling practices

Changing of Timestamp

Here are some hacks that can be implemented by modifying the file jm_Scheduler.h.