Platform¶
The one interface every module reaches hardware through. Core and the light domain call these names and never a vendor SDK, so the same source drives an ESP32, a Teensy, a Raspberry Pi and a desktop.
Why the boundary is drawn here, and what is allowed to cross it, is MoonCore's platform abstraction. This page is the reference half: what the interface offers, and where each target implements it.
platform.h declares the interface; desktop/ and esp32/ implement it. A module that needs something the interface does not offer gets a new function here rather than an #ifdef at the call site, which is what keeps the domains target-blind.
Time¶
millis, micros, delayMs, delayUs. The clock every animation is written against, and the only sleep a module may take.
Detail: technical
Memory¶
alloc, free, allocInternal, freeHeap, freeInternalHeap, plus the accounting (allocatedBytes, allocatedCount, allocatedPeak) that MoonStats reports. Internal versus PSRAM is a platform question, so a module asks for what it needs and the layer decides where it lands.
Detail: technical
Executable memory¶
allocExec, writeExec, freeExec. Memory a CPU will fetch instructions from, which is IRAM on an ESP32 and an mmap page on desktop. MoonLive compiles into it; nothing else does.
Detail: technical
Filesystem¶
fsMount, fsRead, fsWrite, fsList, fsExists, fsMkdir. LittleFS on a device and std::filesystem on desktop, behind one set of names, so a config file and a script are read the same way on both.
Detail: technical
GPIO and ADC¶
gpioRead, gpioWrite, gpioInputBegin, adcRead, adcReadMv, adcMaxCount, and the capability introspection PinsModule renders. A pin's existence is a per-chip fact, so the layer answers it rather than each module guessing.
Detail: technical
Networking¶
Sockets, plus the raw-L2 ethernet path (ethInit, ethSendRaw, ethLinkUp, ethGetIPv4). Art-Net and sACN reach the wire through this, which is why a driver never opens a socket itself.
Detail: technical
Tasks and workers¶
RTOS task introspection for TasksModule, and the pinned worker with wake notification that carries the render and encode split across cores. On desktop these are threads; the contract is identical.
Detail: technical
Video output¶
NDI and HLS. Present where the target can carry them and absent elsewhere, which a module discovers by asking rather than by target.
Detail: technical
Two implementations, one contract¶
src/platform/desktop/ and src/platform/esp32/ implement the same declarations. The desktop one exists so the whole system runs, is tested and is developed without hardware, which is what makes the desktop-first rule possible.
The MoonLive assemblers live here too, one per instruction set, because emitting machine code is the most target-specific thing the system does. The engine and its lowering stay in core; only the encoding is per target.
| Assembler | Target |
|---|---|
| moonlive_asm_xtensa | classic ESP32 and S3 |
| moonlive_asm_riscv | ESP32-P4 |
| moonlive_asm_host | desktop arm64 and x86-64 |
Each satisfies the assembler contract the lowering names, so adding an instruction set is a new file behind an unchanged IR. Per-target build settings are in each half's own platform_config: desktop and ESP32, the latter carrying the per-chip capability table.
What does not belong here¶
A vendor SDK call in core or the light domain. A #ifdef ESP32 outside this folder. A module that reads a chip register directly. Each is the same mistake: a target fact escaping the layer that exists to hold it, and check_platform_boundary fails the build on it.