ptScheduler - Library Documentation

ptScheduler is a non-preemptive task scheduler and timing library for Arduino. It helps you write non-blocking periodic tasks easily without using an RTOS.

Pretty tiny Scheduler or ptScheduler is a non-preemptive task scheduler and timing library for Arduino. It helps you write non-blocking, periodic tasks easily without using ordinary delay routines or using millis() or micros() functions. Delay routines can prevent other parts of your code from running until the delay is exhausted. Using millis() function in your code can make it harder to understand and manage. ptScheduler solves these problems.

ptScheduler acts as a wrapper for millis() or micros() based logic used for timing of tasks. Under the hood, ptScheduler uses the native micros() implementation of Arduino. The micros() function is a hardware timer-based ISR that increments a global counter variable (unsigned integer) every microsecond. This function is available on all microcontrollers that support the Arduino framework. ptScheduler was using millis() function previously, but the new micros() provides granular timing in microseconds compared to milliseconds.

When you create a new ptScheduler task, you can specify the time intervals and execution modes. There are two execution modes - ONESHOT and SPANNING. Oneshot task is executed only once every time an interval is elapsed. Spanning task on the other hand remains active during the span of an interval.

All class member variables and functions are public and therefore give you full control over your tasks, allowing dynamically changing the behavior of the task.

To run a task, just enclose the call() function inside any conditional statements, either inside your infinite loop or inside a function. Every time you invoke the call() function, it checks if the elapsed time is larger than the preset interval. If yes, it will return true and cause the code under the conditional block to be executed once.

ptScheduler is good mainly for control applications that require periodic polling of sensors, GPIOs, and other IO devices. ptScheduler tasks can coexist with preemptive tasks such as FreeRTOS tasks.

Github

Basic Usage

Let's look at a Hello World example. sayHello is a ptScheduler task initialized with 1000000 microseconds (1 second) time period. The default execution mode is ONESHOT and therefore sayHello is executed only once after every second passed. The corresponding call() function for sayHello is enclosed inside an if statement. The code inside the clause will be executed once every 1 second, printing "Hello World" to the serial monitor. You can change this time period on the fly, or get the number of times the code block was executed. After the if block, you can add other ptScheduler tasks or non-blocking code.

include "ptScheduler.h"

//create tasks
ptScheduler sayHello = ptScheduler(1000000); //time in microseconds

//setup function runs once
void setup() {
  Serial.begin(9600);
}

//infinite loop
void loop() {
  if (sayHello.call()) {  //executed every second
    Serial.println("Hello World");
  }

  //add other tasks and non-blocking code here
}

Here is a blink example. The blinkLed is a ONESHOT task set to run every 500 milliseconds. The associated if code block will toggle the LED state every 500 milliseconds. On line 23 is a statement that will be executed continuously.

include "ptScheduler.h"

//create tasks
ptScheduler sayHello = ptScheduler(PT_TIME_1S);  //1s
ptScheduler blinkLed = ptScheduler(PT_TIME_500MS);  //500ms

//setup function runs once
void setup() {
  Serial.begin(9600);
  pinMode(LED_BUILTIN, OUTPUT);
}

//infinite loop
void loop() {
  if (sayHello.call()) {  //executed every second
    Serial.println("Hello World");
  }
  
  if (blinkLED.call()) {
    digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));  //toggle LED
  }
  
  Serial.println(analogRead(A0));  //continuous task
}

Tutorial

A complete tutorial on ptScheduler is available at CIRCUITSTATE.

Known Limitations

  1. ptScheduler is not a replacement for an RTOS. While an RTOS can preempt (temporarily interrupting and stopping a task/process to run other tasks, and later resuming the interrupted task) tasks, ptScheduler tasks have to wait for the current task to finish. This is called non-preemptive task scheduling. If you have time-critical tasks to run, you must use an RTOS that can execute tasks near-real-time.

  2. The interval you set for a ptScheduler task is not the exact time the task would be executed. For example, if the interval is 1000ms, you should not expect the task would run exactly at every 1000ms. Instead, the task will be executed if the elapsed time (the time elapsed since the last execution) is equal to or greater than 1000ms. If the time taken to execute every other code is less than 1000ms, then it is guaranteed that your task will run at roughly every 1000ms, give or take a few milliseconds.

  3. The variables used for storing time intervals are 64-bit wide unsigned numbers (uint64t). 8-bit microcontrollers such as AVR can take longer to finish computations on these large numbers. If you don't need 64-bit width, you can change it to 32-bit by modifying the time_us_t type definition. 64-bit numbers are less of a concern for 32-bit processors.

Last updated