Changelog | Full documentation | Wiki
Delivering robust embedded systems and firmware that perform flawlessly in real-world conditions. smart solutions for smart devices
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/TaskSchedulerManual: Download or clone this repository into your Arduino libraries/ folder.
New: Scheduler::isEnabled() and Scheduler::getState(), a Scheduler destructor, and protection against stale requests in _TASK_THREAD_SAFE queues.
Behavior changes to check when upgrading:
Task::cancel()completes the task's internal StatusRequest withTASK_SR_CANCEL(it usedTASK_SR_ABORT). Tasks waiting on a canceled task now run instead of being aborted.setIntervalNodelay()options now work as documented:TASK_INTERVAL_KEEP(the default) keeps the already scheduled next run, andTASK_INTERVAL_RESETrestarts the schedule from now. A task that changes its own interval with the default option now runs once more at the old interval; useTASK_INTERVAL_RECALCto apply the new interval right away.Schedulerobjects can no longer be copied. Pass them by reference or pointer._TASK_OO_CALLBACKS: a scheduling pass counts as idle only if noCallback()returnedtrue, andgetInvokedTasks()now counts OO callbacks._TASK_THREAD_SAFE:_task_request_thas a newgenerationfield. Recompile any queue code that copies the structure.
Fixes:
- A task can delete itself inside its own
OnDisable()method. - Destroying a scheduler before its tasks no longer leaves the tasks pointing to freed memory.
- With layered priority and
_TASK_SLEEP_ON_IDLE_RUN, the base scheduler now sleeps (examples 11 and 12 never did). - Tickless
getNextRun()works with_TASK_TIMEOUTand across amillis()rollover. setIntervalNodelay(..., TASK_INTERVAL_RECALC)no longer stalls a task for about 49.7 days when the interval shrinks.- The event-only Task constructor initializes the self-destruct flag.
See the Changelog for details.
Core scheduling:
- Periodic task execution with dynamic period in
milliseconds(default) ormicroseconds - Limited or infinite number of iterations
- Execution of tasks in predefined sequence
- Dynamic change of task execution parameters (frequency, iterations, callbacks)
- Static and dynamic callback method binding
- Support for
std::functions(tested onESPxandSTM32)
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_ARDUINOand_task_millis()implementation) - Any Linux (requires
_TASK_NON_ARDUINOand_task_millis()implementation)
Note: with _TASK_NON_ARDUINO, define _task_millis() (and _task_micros() / _task_yield() where your options need them) in the same source file that includes TaskScheduler.h. The unit test tests/test-scheduler-defect-fixes.cpp shows a working setup.
- 3devo -- Commercial polymer processing and 3D printing filament production systems. TaskScheduler runs in the firmware of their entire product line: Filament Maker ONE, Filament Maker TWO, GP20 Shredder Hybrid, and Airid Polymer Dryer. Each product's license page credits TaskScheduler (BSD3, Anatoli Arkhipenko). Open-source firmware at github.com/3devo.
- Hackabot Nano by Funnyvale -- Compact plug-and-play Arduino-compatible robotic kit (Kickstarter)
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:
- Air quality monitoring networks -- Indoor/outdoor sensor mesh networks using ESP32 nodes with CO2, PM2.5, temperature, and humidity sensors, deployed across campus-scale areas (100m x 80m) and running continuously for days (Sustainability, 2022)
- Cold chain monitoring for perishable goods -- PrunusPos project evaluating ESP8266 mesh testbeds for monitoring ambient conditions inside fruit and vegetable storage containers, with sensors integrated directly into transport crates (Information, 2022)
- Mesh network performance evaluation -- Multiple peer-reviewed papers benchmarking painlessMesh delivery delay, throughput, and scalability with varying node counts and payload sizes (Yoppy et al., 2019)
Community and maker projects:
- Synchronized LED installations -- WiFi mesh-synchronized NeoPixel LED bars with coordinated animations across multiple nodes, no data wires required (Instructables)
- MeshyMcLighting -- Standalone mesh-networked NeoPixel lighting system broadcasting state across nodes without internet connectivity (MeshyMcLighting)
- MQTT-to-Mesh gateways -- Bridging painlessMesh IoT networks to MQTT brokers for cloud integration (painlessMeshMqttGateway)
- Woodshop dust collector control -- Distributed blast gate control across a workshop using mesh-networked ESP nodes
- Smart agriculture and irrigation -- Distributed soil moisture, temperature, and water flow monitoring across fields with a gateway node forwarding to cloud dashboards
- Emergency/disaster communication -- Pre-deployed ESP32 mesh networks that continue relaying status messages (water levels, power outages) when internet infrastructure is down
- Ninja Timer: Giant 7-Segment Display -- Featured on Adafruit
- NeoPixel smartBulb IoT -- DIY smart lamp with ESP8266 and motion sensing
- Arduino Bluetooth remote control + ultrasonic anti-collision car
- Houston midi clock -- Multi-master MIDI controller with shared clock, TaskScheduler at microsecond resolution (YouTube)
- Discrete Time Systems Wiki -- Educational reference
- Interactive "Do Not Disturb" Minecraft Sword sign (ESP32)
- Interactive Predator Costume with Head Tracking Plasma Cannon (Teensy, Arduino Nano)
- IoT APIS v2 -- Automated Plant Irrigation System (ESP8266)
- APIS -- Automated Plant Irrigation System (Arduino Uno)
- Party Lights LEDs music visualization (Leaf Maple Mini)
- Hexbug Scarab Robotic Spider (Arduino Nano)
- Wave your hand to control OWI Robotic Arm (Arduino Uno and Nano)
- Interactive Halloween Pumpkin (Arduino Uno)
"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.


