Skip to content

wk1093/CrossScheduler

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

3 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

CrossScheduler

A high-performance, ultra-lean, cross-platform cooperative event-loop framework for bare-metal systems.

Unlike the native Arduino Scheduler library, which relies on platform-specific assembly register hacks and only works on select 32-bit ARM boards, CrossScheduler is written in pure, standards-compliant C++. It runs seamlessly on everything from an 8-bit AVR micro (Uno, Nano, Mega) to a 32-bit ARM Cortex, ESP8266, ESP32, Raspberry Pi Pico, or even native desktop environments.

Features

  • Universal Compatibility: No platform-specific assembly. Runs anywhere a standard C++ compiler can reach.
  • Dual-Track API: Seamlessly accept classic parameterless callbacks (void(*)()) or context-aware handlers (void(*)(ScheduleContext)).
  • Zero-Overhead Memory Optimization: Uses anonymous C++ unions to pack multi-signature tracks into the space of a single raw pointer. No dynamic heap allocations ($0$ bytes malloc / new).
  • Zombieless Task Control: Native ScheduleContext execution tracking handles allow long-running blocking tasks to poll their own cancellation states internally and exit automatically.
  • Zero-Casting Data Passthrough: Attach generic data variables to task instances and retrieve them through an inline, type-erased template abstraction layer (self.data<T>()).
  • Precision Interval Rescheduling: Protects repeating loops against execution drift by automatically jumping timelines forward to handle missed intervals.

Installation

Via Arduino Library Manager

  1. Open the Arduino IDE.
  2. Navigate to Tools -> Manage Libraries...
  3. Search for CrossScheduler and click Install.

Manual Installation

Download this repository as a .zip file, open your Arduino IDE, navigate to Sketch -> Include Library -> Add .ZIP Library..., and select the downloaded file.

Quick Start Examples

1. Basic Non-Blocking Loops (The Clean API)

Forget nesting recursive scheduling methods inside your functions. Just define your layout once and forget it.

#include <CrossScheduler.h>

void fastBlinker() {
  digitalWrite(13, !digitalRead(13));
}

void slowLogger() {
  Serial.println("Another second has passed...");
}

void schedule() {
  Serial.begin(115200);
  pinMode(13, OUTPUT);

  // Simple, flat loops
  Schedule.loop(250, fastBlinker);
  Schedule.loop(1000, slowLogger);
}

2. Context-Aware Automated Task Control

Use ScheduleContext to build background logic that can gracefully abort itself when canceled from elsewhere in your program.

#include <CrossScheduler.h>

ScheduleContext monitoringTask;

void constantRunning(ScheduleContext self) {
  // self.poll() runs the background event loop and returns false if this task is canceled
  while (self.poll()) {
    Serial.println("Monitoring sensors...");
    self.sleep(100); // Smart, non-blocking sleep that keeps other tasks running
  }
  Serial.println("Monitoring loop safely exited!");
}

void schedule() {
  Serial.begin(115200);

  // Spin up a long-running tracking loop
  monitoringTask = Schedule.afterMS(1000, constantRunning);

  // Schedule a one-shot shutdown routine 4 seconds later
  Schedule.afterMS(4000, []() {
    Serial.println("An emergency occurred! Canceling monitoring...");
    monitoringTask.cancel(); // Instantly tears down the constantRunning loop cleanly
  });
}

3. Type-Safe Data Parameters (No Global State)

Pass persistent states or hardware profiles straight into localized tracking loops with absolutely zero pointer prose or global macros.

#include <CrossScheduler.h>

struct Motor {
  int pin;
  int speed;
};

void runMotor(ScheduleContext self) {
  // Automatically resolves the void* data back into your type safely!
  Motor* m = self.data<Motor>();
  
  analogWrite(m->pin, m->speed);
}

void schedule() {
  // Allocate static state data tracking instances safely
  static Motor driveMotor = { 5, 255 };
  static Motor intakeMotor = { 6, 128 };

  // Dispatch identical logic structures bound to distinct contexts
  Schedule.loop(500, runMotor, &driveMotor);
  Schedule.loop(500, runMotor, &intakeMotor);
}

Configuration & Tuning

You can easily override maximum structural boundaries or change logging channels prior to loading the library by defining compiler parameters:

// Expand or compress static pool limits before inclusion to dial in RAM usage
#define SCHEDULER_TASK_MAX 20
#define SCHEDULER_LISTENER_MAX 10

#include <CrossScheduler.h>

API Reference

Global Engine Methods (Schedule)

  • ScheduleContext doTask(Callback cb): Schedules a task to run immediately on the next update tick.
  • ScheduleContext afterMS(unsigned long ms, Callback cb): Runs a task once after a specified millisecond duration.
  • ScheduleContext loop(unsigned long ms, Callback cb, void* userData = nullptr): Schedules a persistent repeating interval loop.
  • ScheduleContext listen(ConditionFn cond, Callback cb, unsigned long interval = 0, WatchMode mode = ALWAYS): Attaches a conditional checker function to fire a callback on states.
  • void cancel(ScheduleContext ctx / ScheduleHandle handle): Safe explicit dynamic loop/listener tearing-down utility.
  • void abortTasks() / abortListeners() / abortAll(): Blanket flushes to immediately wipe pool elements during global state changes.

Context Operations (ScheduleContext)

  • bool poll(): Advances the tracking loop; returns false if the parent execution handle has been terminated.
  • void sleep(unsigned long ms): High-performance, non-blocking sleep delay wrapper that lets sibling tasks execute.
  • void cancel(): Self-destruct sequence to pull the running task out of rotation.
  • template <typename T> T* data(): Instantly casts associated contextual data tracking points without manual syntax casting.

License

This library is open-source software licensed under the MIT License.

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages