Read this in other languages: English · Português · Español · 中文
Lightweight cooperative task scheduler for Arduino and ESP32.
- Two execution modes — background (cooperative, runs the earliest-due task per
loop()call) and critical (runs all due tasks; pair with the optional FreeRTOS runner on ESP32). - Portable core — no
String, nostd::vector, nostd::function; works on AVR, SAMD, RP2040, ESP8266, ESP32, etc. - Per-task stats — runs, last/avg/max execution time, total time, next-run time.
- Pluggable time source — inject a fake clock for unit tests; default is
millis(). - Optional FreeRTOS critical thread — auto-enabled on ESP32, RP2040, and nRF52 (FreeRTOS bundled in core). STM32 and Teensy 4.x require manual opt-in (
-D CRITICALTASKSCHEDULER_HAS_FREERTOS=1+ adding a FreeRTOS library tolib_deps). Any other FreeRTOS platform can opt in the same way.
Battle-tested on a real ESP32-S3 robot in production.
- Open Tools → Manage Libraries…
- Search for CriticalTaskScheduler and click Install.
Add to your platformio.ini:
lib_deps = andrenepomuceno/CriticalTaskScheduler@^1.0.0Clone or download into your libraries/ folder:
git clone https://github.com/andrenepomuceno/CriticalTaskScheduler.git CriticalTaskSchedulergraph TD
USER["Your sketch"]
subgraph SCHED["Scheduler"]
BG["Background bucket\nTask* [MAX_TASKS]"]
CR["Critical bucket\nTask* [MAX_TASKS]"]
end
subgraph TASK["Task"]
CB["TaskCallback\nvoid (*)()" ]
STATS["Stats\nruns · last · avg · max · total"]
NRT["nextRunTime"]
end
subgraph ESP32["ESP32 only"]
FRT["FreeRTOSCriticalRunner\nvTaskDelayUntil @ tickMs"]
end
USER -- "addTask()" --> BG
USER -- "addTask() + critical=true" --> CR
USER -- "execute()\nin loop()" --> BG
FRT -- "executeCritical()\nhigh-priority thread" --> CR
BG -- "runs earliest-due\none per call" --> TASK
CR -- "runs all due\nper call" --> TASK
TASK --> CB
TASK --> STATS
TASK --> NRT
#include <CriticalTaskScheduler.h>
TSScheduler sched;
void blink() { digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN)); }
void status() { Serial.println("alive"); }
TSTask blinkTask("blink", 500, blink);
TSTask statusTask("status", 1000, status);
void setup() {
Serial.begin(115200);
pinMode(LED_BUILTIN, OUTPUT);
sched.addTask(&blinkTask);
sched.addTask(&statusTask);
sched.enableAll();
}
void loop() {
sched.execute(); // runs the earliest-due background task; never delay()
}See examples/ for more — including critical-vs-background timing, delayed start, and stats.
| Feature | This library | arkhipenko/TaskScheduler |
|---|---|---|
| Critical (FreeRTOS-thread) tasks | Built-in (ESP32) | No |
Earliest-due single-shot in execute() |
Yes (anti-starvation) | No (runs all due) |
Per-task runs/avg/max/total stats |
Built-in | Optional |
| Portable AVR↔ESP32 core | Yes | Yes |
| Static allocation only | Yes (no heap) | Optional |
- Quick Start
- API Reference
- Timing Semantics — critical vs background, reschedule rules, jitter
- Troubleshooting
MIT — see LICENSE.