# 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:
- By default, **jm_Scheduler** starts immediately the coroutine and repeats it periodically.
- The first execution can be differed.
- The repeated executions can be voided.
- The interval between executions can be dynamically changed.
- The scheduled coroutine function can be dynamically changed.
- The scheduled coroutine can be stopped and later restarted.
**jm_Scheduler** doesn't schedule like the official [**Scheduler** Library for Arduino DUE and ZERO](https://www.arduino.cc/en/Reference/Scheduler) 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:
- _yield_ comes out when coroutine leaves at end of function or by an explicit `return` instruction.
- _resume_ to a next state can be done with a variable and a `switch` instruction. Or:
- _resume_ to a next state can be done by switching to another function.
- Persistent variables must be implemented _global_ or _local_ with the pragma `static`.
### Basic Example
// This example schedules a coroutine every second
#include
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
- NEW 2018-04-29: Begin with the 5 **Blink** progressive examples.
They demonstrate how to blink a led by replacing delay() Arduino function with rearm() jm_Scheduler method.
- Begin with example **Clock1.ino**. This example demonstrates the advantage to start immediately a time display coroutine and periodically repeat it.
- Follow with examples **Clock2.ino** and **Clock3.ino** which present other timing ways.
- **Clock4.ino** example presents a usefully **jm_Scheduler** technical: changing dynamically the function to execute.
- **Beat1.ino** and **Beat2.ino** examples present interaction between 2 scheduled coroutines.
- **Wakeup1.ino** example demonstrates the possible interaction between an interrupt and a scheduled coroutine, implementing a timeout.
### 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:
```C
unsigned long micros();
```
Look at https://www.arduino.cc/en/Reference/Micros for details.
_timestamp_ is a 32bit [us] counter and overflows about every 70 minutes (precisely 1h+11m+34s+967ms+296us).
### Timestamp declaration and constants
```C
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
```C
// 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
```C
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:
```C
void loop(void)
{
yield();
}
```
> `cycle()` can also be invoked in Arduino `setup()` function. Example:
```C
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
- To guarantee a good scheduling of all tasks,
the execution time of each function must be as short as possible.
- Avoid Arduino `delay()` function, use **jm_Scheduler** `rearm()` method with appropriate arguments to split the coroutine in some serialized functions.
- Use same technical for long calculations.
### Changing of Timestamp
Here are some hacks that can be implemented by modifying the file **jm_Scheduler.h**.
- Another source for the _timestamp_ could be the [ms] read from the Arduino function `millis()`.
- Gain speed during _timestamp_ comparison by shortening the size to 16bit.
- Obtain very long periodicity by implementing a 64bit _timestamp_.