Scheduler
Source:
Scheduler.h
Scheduler¶
src/core/Scheduler.h:47Domain-neutral orchestrator that owns the top-level module list, runs the 4-phase boot, drives the per-tick loop callbacks, and provides tree-walk utilities (delete, name-uniquify).
Boot phases: see the setup() comment in Scheduler.cpp.
Tick: gates each top-level module by enabled() / respectsEnabled() and dispatches tick / tick20ms / tick1s. A per-second window averages the tick time and publishes each module's loop time.
Loop rates: three cadences cover every module. tick() is the hot path for effects and drivers, called every iteration — the Scheduler handles pacing (yielding to other tasks between iterations via taskYIELD() on ESP32, an optional sleep on desktop). tick20ms() runs every ~20 ms for UI updates, control reads, and network polling. tick1s() runs every ~1 second for diagnostics, reconnects, and housekeeping. Not every module needs tick(): system modules (HTTP, WiFi) use tick20ms() or tick1s() only.
Timing: effects animate off a synchronized clock (millis from the platform), so the visual speed is frame-rate independent — the same at 30 fps and 60 fps. For multi-device sync a leader synchronizes this clock across devices; no frame counter is needed.
Execution model:tick() runs every top-level module inline, in one loop, in declared order; each module then drives its own children. There is no per-module task and no core-affinity field — a single render loop, not a task-per-module fan-out.
Module ordering: child modules run in their declared order within the parent, and top-level modules also run in declared order. The UI supports reordering, backed by the scheduler. Relationships are parent/child only — there is no arbitrary dependency graph.
Prior art: MoonLight's effectTask / svelteTask — two FreeRTOS tasks (effects on core 1, system/drivers on core 0) with a per-node tick() every frame and a tick20ms() for slow updates.
This is the .h interface; bodies live in Scheduler.cpp.
Public Methods¶
inline void setLoadAllHook(LoadAllFn fn)
inline void setNoteDirtyHook(NoteDirtyFn fn)
void addModule(MoonModule * mod)
void setup()
void tick()
void release()
uint32_t elapsed() const
void prepareTree()
inline uint32_t tickTimeUs() const
inline uint32_t fps() const
inline uint8_t moduleCount() const
inline MoonModule * module(uint8_t i) const
void ensureUniqueName(MoonModule * mod)
: Ensure mod's name is tree-globally unique.
void deduplicateNamesInTree()
: Walk the whole tree and disambiguate every duplicated name.
MoonModule * firstByName(constchar * name)
: First module in tree-walk order with this name, or nullptr if none.
SetControlResult setControl(constchar * moduleName, constchar * controlName, constchar * valueJson)
: Set one control by (module name, control name) to a value, applying the full control-change reaction: parse+validate, rebuild the module's control list, fire onControlChanged, mark dirty for persistence, and prepareTree() when the control reshapes dims/mapping.
Public Static Methods¶
static void deleteTree(MoonModule * mod)
static inline Scheduler * instance()
: The single live Scheduler, or nullptr before setup() / after release().
Public Types¶
enum SetControlResult
: Outcome of setControl — the generic control-set primitive's result.
| Value | Description |
|---|---|
Ok |
|
ModuleNotFound |
no module with that name in the tree |
ControlNotFound |
module exists but has no such control |
OutOfRange |
numeric value outside the control's bounds |
Malformed |
value didn't parse |
ReadOnly |
tried to write a display-only control |
using LoadAllFn = void()(Scheduler )
: Function-pointer hook invoked between phase 1 (defineControls) and phase 3 (setup).
using NoteDirtyFn = void(*)()
: Hook invoked after a control mutation so the persistence layer can schedule a debounced save (FilesystemModule::noteDirty).