Skip to content

Repository files navigation

TaskScheduler

Cooperative multitasking for Arduino, ESPx, STM32 and other microcontrollers

Version 4.1.0: 2026-09-26

arduino-library-badge Unit Tests

Changelog | Full documentation | Wiki

Delivering robust embedded systems and firmware that perform flawlessly in real-world conditions. smart solutions for smart devices

github


Overview

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.


Quick Start

#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.


Installation

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.


What's new in 4.1.0

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 with TASK_SR_CANCEL (it used TASK_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, and TASK_INTERVAL_RESET restarts the schedule from now. A task that changes its own interval with the default option now runs once more at the old interval; use TASK_INTERVAL_RECALC to apply the new interval right away.
  • Scheduler objects can no longer be copied. Pass them by reference or pointer.
  • _TASK_OO_CALLBACKS: a scheduling pass counts as idle only if no Callback() returned true, and getInvokedTasks() now counts OO callbacks.
  • _TASK_THREAD_SAFE: _task_request_t has a new generation field. 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_TIMEOUT and across a millis() 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.


Features

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)

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.


TaskScheduler process diagram

In the Wild

Commercial products

  • 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)

painlessMesh and ESP32 mesh networking

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

Community projects

My projects

Video tutorial


User Feedback

"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


Contributing

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!


License

BSD 3-Clause. See LICENSE.txt.


github

About

Cooperative multitasking for Arduino, ESPx, STM32, nRF and other microcontrollers

Topics

Resources

Stars

1.7k stars

Watchers

78 watching

Forks

Releases

Sponsor this project

Packages

Used by

Contributors

Languages