Skip to content

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/, esp32/ and teensy/ 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.

Source: platform.h

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.

Source: platform.h

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.

Source: platform.h

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.

Source: platform.h

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.

Source: platform.h

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.

Source: platform.h

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.

Source: platform.h

Video output

NDI, HLS, RTSP and HDMI. Present where the target can carry them and absent elsewhere, which a module discovers by asking rather than by target. HDMI hands a frame to the display and reads back the monitor: its hot-plug, its EDID and the mode it runs. An ESP32-P4 drives an LT8912B bridge; a computer drives its own display.

Source: platform.h

Three implementations, one contract

src/platform/desktop/, src/platform/esp32/ and src/platform/teensy/ 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_thumb2 Teensy 4.1
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 target's own platform_config: desktop, ESP32, which carries the per-chip capability table, and Teensy 4.1. Two headers serve every target: the Ethernet wiring each config's default is written in, and the loopback bit check every LED self-test ends in.

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 the code check counts it, at zero.