QMan (Queue Manager) — Ultra-lightweight cooperative task queue manager for Arduino. Features async-style DSL macros, zero-stack coroutines, and LIFO/FIFO priority management. Includes static safety checks, overflow protection via WDT/Error callbacks, and deterministic tick-based timing.
// Quick glimpse
QMAN_TASK(myTask) {
QMAN_INIT { /* Setup */ }
QMAN_LOOP {
doSomething();
QMAN_SLEEP_MS(500);
}
}QMan is an ultra-lightweight, high-performance cooperative task queue manager for Arduino and AVR-based microcontrollers. It allows you to write non-blocking, asynchronous-style code using simple DSL macros without the overhead of a full RTOS.
Status: v1.0.0-beta
License: MIT
Unlike simple timers, QMan is a disciplined Queue Manager. It doesn't just run functions; it manages their lifecycle, execution order, and system safety.
- Async-style Syntax: Write complex logic with
QMAN_SLEEPandQMAN_LOOPthat looks likeasync/await. - Zero-Stack Coroutines: Tasks don't need dedicated stack space, saving precious RAM.
- Smart Queue: Optimized LIFO/FIFO priority management.
- Static Safety: Built-in counter warns you if the number of tasks in code exceeds the pool size.
- Deterministic: Time is injected from outside, making it perfect for unit testing and stress testing.
- Download this repository as a
.zipfile. - In Arduino IDE, go to Sketch -> Include Library -> Add .ZIP Library.
#include <QMan.h>
// 1. Define a task using DSL macros
QMAN_TASK(blinkTask) {
QMAN_INIT {
pinMode(LED_BUILTIN, OUTPUT);
}
QMAN_LOOP {
digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
QMAN_SLEEP_MS(500); // Non-blocking sleep
}
}
void setup() {
// 2. Start the task immediately
QMAN_GO(blinkTask, 0);
}
void loop() {
// 3. Drive the manager with system ticks (32us per tick)
QMAN_TICK();
}QMan is built for reliability in embedded systems:
- OnError Callback: Triggered on dynamic pool overflow before a hard Watchdog reset.
- OnWarning Callback: Triggered if your code defines more tasks than the allocated memory pool.
- Recursion Protection: Prevents stack crashes if a task recursively calls the dispatcher.
| Code | Enum | Description |
|---|---|---|
| 1 | QMAN_ERR_POOL_OVERFLOW | Dynamic queue is full. System will reboot. |
| 2 | QMAN_WARN_STATIC_LIMIT | Total defined tasks exceed pool size. Check your memory! |
| Feature | QMan | AceRoutine | GyverOS | TaskScheduler | Protothreads | FreeRTOS |
|---|---|---|---|---|---|---|
| Programming Style | DSL (Async-like) | C++ Classes | Polling | Callbacks | Macros/Switch | Task Functions |
| RAM Usage | Extremely Low | Medium | Low | Medium | Low | Very High |
| Context Saving | Yes (Zero-stack) | Yes | No | No | Yes | Yes (Full stack) |
| Safety Guards | WDT / OnError | None | None | None | None | HardFault |
| Time Rollover | Immune (38h) | millis() based | millis() | millis() drift | Manual check | Tick-based |
Linear code, no global flags needed. Best for 8-bit MCUs.
QMAN_TASK(blink) {
QMAN_INIT { pinMode(13, OUTPUT); }
QMAN_LOOP {
digitalWrite(13, !digitalRead(13));
QMAN_SLEEP_MS(500);
}
}Uses C++ classes. Good, but higher RAM overhead due to object-oriented design.
COROUTINE(blink) {
COROUTINE_LOOP() {
digitalWrite(13, !digitalRead(13));
COROUTINE_DELAY(500);
}
}Lightweight but requires manual state management (global flags) for complex logic.
void task() {
static bool state;
digitalWrite(13, state = !state);
}
// Logic: OS.attach(0, task, 500);No context saving. Tasks must be broken down into many separate callback functions.
void blinkCb() { digitalWrite(13, !digitalRead(13)); }
Task t1(500, TASK_FOREVER, &blinkCb);Low-level macros. Lacks a built-in scheduler; you must run each thread manually in loop().
PT_THREAD(blink(struct pt *p)) {
PT_BEGIN(p);
while(1) {
digitalWrite(13, !digitalRead(13));
PT_WAIT_UNTIL(p, millis() - last > 500);
last = millis();
}
PT_END(p);
}Preemptive multitasking. Powerful, but consumes ~150 bytes of RAM per task.
void vBlink(void *pv) {
for(;;) {
digitalWrite(13, !digitalRead(13));
vTaskDelay(500 / portTICK_PERIOD_MS);
}
}Created by developers for the Arduino community.