A lightweight event-driven library for Arduino. It provides a simple way to register callbacks and emit events, manage timers and async operations in Arduino sketches with a simple flow.
The goal is to avoid blocking code and keep your sketch loop() clean when multiple activities run in parallel.
This library is useful when you want to:
- react to internal events (states, sensors, inputs)
- run periodic or delayed actions (
every,after) - start an async task and receive the result later (
runAsync) - handle timeouts and cancellation
Avoid using it if:
- you only need simple timing (millis())
- you are dealing directly with hardware interrupts
Unlike task schedulers or input-specific libraries, ArduinoEvents is a general-purpose event system.
- Supported architecture:
zephyr(seelibrary.properties) - Main include:
#include <ArduinoEvents.h>
- In
setup(), initialize the library witharduino_events::begin(...) - Register listeners, timers, or async tasks
- In
loop(), always callarduino_events::update()
Minimal example:
#include <ArduinoEvents.h>
using namespace arduino_events;
struct LedToggleEvent {};
void onLedToggle(const LedToggleEvent&) {
Serial.println("Toggle LED");
}
void emitToggle() {
Events.post(LedToggleEvent{});
}
void setup() {
Serial.begin(115200);
arduino_events::begin();
Events.listen<LedToggleEvent>(onLedToggle);
Events.every(1000, emitToggle);
}
void loop() {
arduino_events::update();
}Events.listen<EventT>(callback)registers a listenerEvents.listenOnce<EventT>(callback)runs once, then auto-removesEvents.post(EventT{...})publishes the eventEvents.unlisten(subscription)removes a listener
When you publish an event, callbacks run in the sketch context through update().
Events.after(ms, callback)runs once aftermsEvents.every(ms, callback)runs periodicallyEvents.cancelTimer(id)cancels a timer
Both APIs return a timerId (uint32_t). If it returns 0, the timer was not created.
With runAsync, you start background work and get a Future:
- success:
future.onDone(...) - error:
future.onError(...) - always at the end:
future.onFinish(...) - timeout:
future.withTimeout(ms) - cancellation:
future.cancel()
Inside the async job you receive a Deferred<T>:
deferred.resolve(value)for successdeferred.reject(error)for failuredeferred.isCancelled()to stop work that is no longer needed
You can start with defaults:
arduino_events::begin();Or customize:
Config cfg;
cfg.eventQueueCapacity = 32;
cfg.workerQueueCapacity = 16;
cfg.workerThreadCount = 1;
cfg.defaultTimeoutMs = 30000;
arduino_events::begin(cfg);Practical meaning of each field:
eventQueueCapacity: how many callbacks can be queuedworkerQueueCapacity: internal capacity used for jobs/timersworkerThreadCount: number of parallel workers (max 4)defaultTimeoutMs: available in config (usewithTimeout(...)on futures)
Errors are reported as Error:
err.code(ErrorCode)err.messageerr.nativeCode(optional native code)
Main error codes:
TimeoutCancelledQueueFullInvalidStateNetworkInternal
examples/EventBasics/EventBasics.ino- basic events,
listen,listenOnce,unlisten
- basic events,
examples/TimersBasics/TimersBasics.ino- one-shot and periodic timers, cancellation
examples/RunAsyncBasics/RunAsyncBasics.inorunAsyncwith result and error handling
examples/RunAsyncTimeoutCancel/RunAsyncTimeoutCancel.ino- timeout (
withTimeout) and cancellation (cancel)
- timeout (
examples/StateMachineWithEvents/StateMachineWithEvents.ino- event-driven state machine
examples/AsyncHttpConnect/AsyncHttpConnect.ino- WiFi workflow + async HTTP request (simulated)
- Call
arduino_events::update()in everyloop(). - Avoid long callbacks in the main context.
- In async tasks, check
deferred.isCancelled()before resolving. - Use
listenOncefor events that should be consumed only once. - Explicitly remove listeners and timers you no longer need.
- Init:
bool arduino_events::begin(const Config& = {})void arduino_events::update(uint32_t budgetMs = 0)
- Events:
Events.listen<EventT>(handler)Events.listenOnce<EventT>(handler)Events.post(event)Events.unlisten(subscription)
- Timer:
Events.after(delayMs, callback)Events.every(periodMs, callback)Events.cancelTimer(timerId)
- Async:
Events.runAsync<T>(start)Future<T>.onDone(...).onError(...).onFinish(...)Future<T>.withTimeout(ms)Future<T>.cancel()
If needed, you can stop everything with:
Events.end();In many sketches this is not required, but it can help in controlled restart/stop flows.