https://github.com/esptoolkit/esp-worker
High-level task management for FreeRTOS so you can spawn work units, extend stacks into PSRAM and await completion with joins.
https://github.com/esptoolkit/esp-worker
async embedded esp32 iot process task thread worker
Last synced: 18 days ago
JSON representation
High-level task management for FreeRTOS so you can spawn work units, extend stacks into PSRAM and await completion with joins.
- Host: GitHub
- URL: https://github.com/esptoolkit/esp-worker
- Owner: ESPToolKit
- License: mit
- Created: 2025-09-16T07:57:35.000Z (6 months ago)
- Default Branch: main
- Last Pushed: 2026-02-20T22:00:33.000Z (24 days ago)
- Last Synced: 2026-02-21T03:57:09.311Z (24 days ago)
- Topics: async, embedded, esp32, iot, process, task, thread, worker
- Language: C++
- Homepage: https://www.esptoolkit.hu/
- Size: 67.4 KB
- Stars: 1
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- License: LICENSE.md
- Code of conduct: CODE_OF_CONDUCT.md
Awesome Lists containing this project
README
# ESPWorker
ESPWorker is a C++17 helper library for ESP32 projects that want FreeRTOS power without the boilerplate. It wraps task creation, joins, diagnostics, PSRAM stacks, and lifecycle events into a simple API that works with both Arduino-ESP32 and ESP-IDF.
## CI / Release / License
[](https://github.com/ESPToolKit/esp-worker/actions/workflows/ci.yml)
[](https://github.com/ESPToolKit/esp-worker/releases)
[](LICENSE.md)
## Features
- Works with FreeRTOS tasks while keeping `std::function`/lambda ergonomics.
- Joinable workers with runtime diagnostics (`JobDiag`) and cooperative destruction.
- Pull worker-pool metrics (`WorkerDiag`) including counts and runtime stats.
- Optional PSRAM stacks (`spawnExt`) for memory hungry jobs.
- Thread-safe event and error callbacks so firmware can log or react centrally.
- Configurable defaults and guardrails (max workers, priorities, affinities).
## Examples
Quick start:
```cpp
#include
ESPWorker worker;
void setup() {
Serial.begin(115200);
worker.init({
.maxWorkers = 4,
.stackSizeBytes = 4096,
.priority = 1,
.coreId = tskNO_AFFINITY,
.enableExternalStacks = true,
});
worker.onEvent([](WorkerEvent event) {
Serial.printf("[worker] %s\n", worker.eventToString(event));
});
worker.onError([](WorkerError error) {
Serial.printf("[worker][error] %s\n", worker.errorToString(error));
});
WorkerResult result = worker.spawn([]() {
Serial.println("[worker] running");
vTaskDelay(pdMS_TO_TICKS(250));
}, {
.stackSizeBytes = 16 * 1024,
.priority = 3,
.name = "sensor-task",
});
if (result) {
result.handler->wait(pdMS_TO_TICKS(1000)); // or destroy() for long-lived loops
}
worker.deinit();
}
```
Need deterministic cleanup? Store the handler and call `destroy()` when shutting down:
```cpp
WorkerResult job = worker.spawn([](){ /* ... */ });
if (job) {
// Stop the task cooperatively
job.handler->destroy();
}
```
Check the runnable examples under `examples/`:
- `examples/basic_worker` – spawns workers, waits for completion, prints diagnostics.
- `examples/psram_stack` – uses `spawnExt` to place heavy stacks in PSRAM.
## Gotchas
- Always call `worker.init()` once before spawning tasks. Each ESPWorker instance controls its own limits.
- Call `worker.deinit()` during shutdown/reset paths. It is safe before `init()` and safe to call repeatedly.
- `spawn` creates persistent FreeRTOS tasks; remember to end the lambda (return) or `destroy()` the handler to reclaim slots.
- Errors such as `MaxWorkersReached`, `TaskCreateFailed`, or `ExternalStackUnsupported` are reported in the returned `WorkerResult` _and_ via the error callback.
- PSRAM stack requests fail fast with `ExternalStackUnsupported` when caps-based task allocation is unavailable, PSRAM is missing, or external stacks are disabled.
## API Reference
- `void init(const ESPWorker::Config& config)` – sets defaults (max workers, default stack-bytes/priority/core, PSRAM allowance).
- `void deinit()` / `bool isInitialized() const` – explicit teardown and lifecycle state checks; `deinit()` is idempotent and safe pre-init.
- `WorkerResult spawn(TaskCallback cb, const WorkerConfig& config = {})` – create a worker. The returned handler provides `wait()` and `destroy()` helpers plus per-job diagnostics.
- `WorkerResult spawnExt(...)` – identical to `spawn` but forces PSRAM stacks using `xTaskCreatePinnedToCoreWithCaps(...)`.
- `size_t activeWorkers() const` / `void cleanupFinished()` – query or prune finished tasks.
- `WorkerDiag getDiag() const` – aggregated counts and runtime stats across the pool.
- `void onEvent(EventCallback cb)` / `void onError(ErrorCallback cb)` – receive lifecycle signals (`Created → Started → Completed/Destroyed`) and fatal issues.
- `const char* eventToString(...)` / `errorToString(...)` – convert enums to printable text for logging.
`WorkerConfig` (per job) and `ESPWorker::Config` (global defaults) expose priority, stack size bytes, core affinity, external stack usage, and an optional name that shows up in diagnostics and watchdog dumps.
Stack sizes are expressed in bytes.
## Restrictions
- Intended for ESP32-class boards where FreeRTOS and PSRAM are available; other architectures are untested.
- Requires C++17 support (`-std=gnu++17`) and should not be called from ISR context.
- Each worker consumes RAM proportional to its stack; keep `maxWorkers` and per-job stacks aligned with your heap budget.
## Tests
A native host test suite is still being assembled. For now rely on the `examples/` sketches (build with PlatformIO or Arduino IDE) to verify integration, and consider adding regression tests when contributing changes.
## License
ESPWorker is released under the [MIT License](LICENSE.md).
## ESPToolKit
- Check out other libraries:
- Hang out on Discord:
- Support the project:
- Visit the website: