Browse by type
Changelog | Full documentation | Wiki
A lightweight implementation of cooperative multitasking (task scheduling). An easier alternative to preemptive programming and frameworks like FreeRTOS.
Why cooperative?
You mostly do not need to worry about pitfalls of concurrent processing (races, deadlocks, livelocks, resource sharing, etc.). The fact of cooperative processing takes care of such issues by design.
"Everybody who learns concurrency and thinks they understand it, ends up finding mysterious races they thought weren't possible, and discovers that they didn't actually understand it yet after all." Herb Sutter, chair of the ISO C++ standards committee, Microsoft.
#include <TaskScheduler.h>
Scheduler runner;
// Blink LED every 500ms, read sensor every 2 seconds
Task taskBlink(500, TASK_FOREVER, &blinkLED);
Task taskSensor(2000, TASK_FOREVER, &readSensor);
void blinkLED() {
digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
}
void readSensor() {
Serial.println(analogRead(A0));
}
void setup() {
Serial.begin(115200);
pinMode(LED_BUILTIN, OUTPUT);
runner.addTask(taskBlink);
runner.addTask(taskSensor);
taskBlink.enable();
taskSensor.enable();
}
void loop() {
runner.execute();
}
Try it live on Wokwi -- no hardware required.
Arduino IDE: Sketch -> Include Library -> Manage Libraries -> search for "TaskScheduler" -> Install
PlatformIO: Add to platformio.ini:
lib_deps = arkhipenko/TaskScheduler
Manual: Download or clone this repository into your Arduino libraries/ folder.
Core scheduling:
1. Periodic task execution with dynamic period in milliseconds (default) or microseconds
2. Limited or infinite number of iterations
3. Execution of tasks in predefined sequence
4. Dynamic change of task execution parameters (frequency, iterations, callbacks)
5. Static and dynamic callback method binding
6. Support for std::functions (tested on ESPx and STM32)
Power and timing: 7. Power saving via IDLE sleep mode when tasks are not scheduled to run 8. Overall task timeout 9. CPU load / idle statistics for time-critical applications 10. Scheduling options: priority for original schedule (with and without catchup) and interval 11. Support for "tickless" execution under FreeRTOS (continuous sleep until next scheduled task invocation)
Advanced: 12. Event-driven task invocation via Status Request object 13. Task IDs and Control Points for error handling and watchdog timer 14. Local Task Storage pointer (allowing use of same callback code for multiple tasks) 15. Layered task prioritization 16. Ability to pause/resume and enable/disable scheduling 17. Thread-safe scheduling while running under preemptive scheduler (e.g., FreeRTOS) 18. Optional self-destruction of dynamically created tasks upon disable
Platform support: 19. Arduino IDE style (headers only) and PlatformIO style (header + CPP files) 20. Non-Arduino platforms
Scheduling overhead: between 15 and 18 microseconds per scheduling pass (Arduino UNO rev 3 @ 16MHz clock, single scheduler w/o prioritization)
Tested platforms:
* Arduino Uno R3, Nano, Micro
* ATtiny85
* ESP8266, ESP32
* Teensy (tested on Teensy 3.5)
* nRF52832, nRF52 Adafruit Core (tested on nRF52840 with v3.6.2 workaround)
* STM32 (tested on Mini USB STM32F103RCBT6 ARM Cortex-M3 leaflabs Leaf maple mini module F)
* MSP430 and MSP432 boards
* Raspberry Pi (requires _TASK_NON_ARDUINO and _task_millis() implementation)
* Any Linux (requires _TASK_NON_ARDUINO and _task_millis() implementation -- that's how unit tests are done)

TaskScheduler is a core dependency of painlessMesh -- the widely adopted ESP8266/ESP32 mesh networking library. painlessMesh uses TaskScheduler internally to manage node discovery, connection maintenance, message routing, and mesh self-healing, all running cooperatively within a single loop() call.
This pairing of TaskScheduler + painlessMesh has been deployed across a range of real-world applications:
Academic and industrial research:
Community and maker projects:
"I've used TaskScheduler with great success. Running LED patterns, monitoring button presses, reading data from an accelerometer, auto advancing to the next pattern, reading data from Serial. All at the same time." -- Reddit
"You basically queue up a list of task callbacks and a schedule in your setup() and then do a call to tasks.execute() in loop(), which pops off the next task that is due in a queue or sleeps otherwise. It's simple, but much more straightforward than manually using if millis() - last > delta1... else sleep()" -- Hacker News
"I encourage you to use it in the Arduino environment, it allows you to save a lot of time (and code lines) wherever you need to schedule, i.e. run many tasks that should perform at different frequencies and when we want to have the greatest control over the performance of these tasks and we want good diagnostic of errors." -- elektroda.pl
Code: As of version 4.0.0 TaskScheduler has a comprehensive set of compilation and unit tests. Please submit a PR with your changes and make sure that your code passes all the tests.
Tests: There is no such thing as enough testing. If you come up with another test scenario -- please contribute!
BSD 3-Clause. See LICENSE.txt.
$ claude mcp add TaskScheduler \
-- python -m otcore.mcp_server <graph>