# How does it work?

**ptScheduler** uses the native `micros()` implementation of Arduino. The `micros()` function is a timer-based **ISR** (Interrupt Service Routine) that increments a global counter variable (32-bit unsigned integer) every microsecond. Calling the `micros()` function will return the current value of the counter. Since Arduino implements the counter as a 32-bit unsigned integer, the counter will overflow every **71.582788266667 minutes**. But ptScheduler is written with a workaround to overcome such overflow events and support up to **18446744000000000000 microseconds** (584,942.42 years).

When you create a new ptScheduler object, you can specify the **time intervals** and **execution modes**. The time interval is stored in a variable or in an array, and is compared to the **time elapsed** since the last time the task was executed. For example, if the time period for a oneshot task is 1000 milliseconds, the value is stored in a list called `intervalList` and is compared to the elapsed time every time the associated `call()` function is invoked. If the elapsed time is less than 1000 ms, `call()` will return `false`. If the elapsed time is equal or greater than 1000 ms, `call()` will return `true`. This will cause the code under the conditional clause to be executed once. The current value returned by the `micros()` will now be saved as the new entry point to calculate the elapsed time.

The next time you invoke `call()` and if the elapsed time is less than 1000 ms, the function will again return `false`. This kind of task where the code is executed only once after every time interval is called a **Oneshot** task. You might want to execute a task continuously for a specific time period and then sleep for another time period. It is possible with **Spanning** tasks. In addition to these basic types of tasks, ptScheduler allows you to alter many other behaviors of a task on the fly.

Let's look in detail how the logic behind task modes and timing work.

## Oneshot Task

This is the type of task that is executed only once every time interval. This is useful for tasks such as blinking an LED every second, or printing a message to the serial monitor, or polling a sensor etc. Let's see how a oneshot task with a single interval works.

```
ptScheduler sayHello = ptScheduler(1000000); //time in microseconds
```

1. The default task mode is `PT_MODE_ONESHOT` and the time interval is 1 second. This value is saved as the first value on an interval sequence list. An **interval sequence** is a set of repeating intervals, saved as an array.
2. When you first invoke `sayHello.call()`, the value returned by `micros()` will be saved to `entryPoint` variable. The `call()` will also immediately return `true` since it is the first time we are invoking the task. The `executionCounter` is incremented from 0 to 1. If you do not want the task to return `true` when starting the task, you can set a pre-task delay using `setSkipTime()` function. This will cause the task to wait until the skip time is elapsed, before returning `true`.
3. If you invoke `sayHello.call()`  again within one second of the last call, the value returned by `getTimeElapsed()` will be compared against the previously saved `entryPoint` and the difference is saved in `elapsedTime`. If the difference is less than 1 second, `call()` immediately returns `false`. There will be no changes to any other variables at this point.
4. If you invoke `sayHello.call()` one second after the first call, the value returned by `getTimeElapsed()` is again compared against `entryPoint`. The difference is saved in `elapsedTime` and if the value is greater than or equal to 1 second, `call()` will immediately return `true`. It will also save the new value returned by `micros()` to `entryPoint` for the next cycle, reset `elapsedTime` to 0, and increment `executionCounter` by 1.
5. If the `sequenceRepetition` value is 0, the above-explained cycle repeats indefinitely. If you want to stop the task after a finite number of cycles, you can set `sequenceRepetition` by calling `setSequenceRepetition(<value>)` function. The task will automatically **suspend** or **disable** itself (determined by `sleepMode`) after the specified number of repetitions.&#x20;

![Oneshot task](https://circuitstate.com/wp-content/uploads/2021/01/ptScheduler-Task-Types-Oneshot_1-1024x485.png)

During the Active time, the `call()` will return `true`, and will return `false` during the inactive time. **`Ti`** is the time interval of the task.

A task can also have multiple intervals in a sequence. Below shows a task with two different intervals **`T1`** and **`T2`**.

![Oneshot task with two different intervals](https://212275133-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FCLhf1N23FbWa1Jrq2EWX%2Fuploads%2Fhzl14SjarcwcBAilrRWu%2Fimage.png?alt=media\&token=6dd2feeb-18eb-43da-8f89-2f65eb3e5055)

## Spanning Task

Spanning tasks, as the name suggests, span over an interval. The output of a spanning task remains `true` for one duration and remains `false` for the next duration. Spanning tasks are useful when you want to continuously perform some operations, in a repeating cycle. Let's see how a spanning task works.

```
ptScheduler spanningTask = ptScheduler(PT_MODE_SPANNING, 1000000);
```

* The  task mode is `PT_MODE_SPANNING` and the time interval is 1 second. This value is saved as the first value on an interval sequence list. An **interval sequence** is a set of repeating intervals, saved as an array.
* When you first invoke `spanningTask.call()`, the value returned by `micros()` will be saved to `entryPoint` variable. The `call()` will also immediately return `true` since it is the first time we are invoking the task. If you do not want the task to return `true` when starting the task, you can set a pre-task delay using `setSkipTime()` function. This will cause the task to wait until the skip time is elapsed, before returning `true`.
* If you invoke `sayHello.call()`  again within one second of the last call, the value returned by `getTimeElapsed()` will be compared against the previously saved `entryPoint` and the difference is saved in `elapsedTime`. If the difference is less than 1 second, `call()` immediately returns `true`. There will be no changes to any other variables at this point.
* If you invoke `sayHello.call()` one second after the first call, the value returned by `getTimeElapsed()` is again compared against `entryPoint`. The difference is saved in `elapsedTime` and if the value is greater than or equal to 1 second, `call()` will immediately return `false`. This completes an interval cycle for spanning task. It will also save the new value returned by `micros()` to `entryPoint` for the next cycle, reset `elapsedTime` to 0, and increment `executionCounter` by 1.
* If the `sequenceRepetition` value is 0, the above-explained cycle repeats indefinitely. If you want to stop the task after a finite number of cycles, you can set `sequenceRepetition` by calling `setSequenceRepetition(<value>)` function. The task will automatically **suspend** or **disable** itself (determined by `sleepMode`) after the specified number of repetitions.&#x20;

![Spanning task with single interval Ti](https://circuitstate.com/wp-content/uploads/2021/01/ptScheduler-Task-Types-Equal-Initerval_1-1024x499.png)

![Spanning task with two different intervals T1 and T2](https://circuitstate.com/wp-content/uploads/2021/01/ptScheduler-Task-Types-Unequal-Interval_1-1024x483.png)

![Spanning task with pre-task delay (Ts)](https://circuitstate.com/wp-content/uploads/2021/01/ptScheduler-Task-Types-Skipped_1-1024x540.png)

You can have any number of intervals of any length.
