SystemStatusModule¶
Exposes runtime system metrics as live display controls in the frontend. Introduced in Release 2, Sprint 1.
What it does¶
SystemStatusModule counts hot-path ticks in loop() and samples system metrics in loop1s() (called by the Scheduler at ~1 Hz). Each sample queries heap usage and computes a live fps value from the tick counter delta since the previous sample, then updates read-only display controls that the frontend renders as green text.
No state is persisted — all values are runtime-only metrics.
Controls¶
All controls are display type — rendered as read-only green text in the frontend, live-updated via WebSocket, never POST'd.
Dynamic — updated every ~1 s (via loop1s())¶
| Key | Description | PC |
|---|---|---|
fps |
Tick rate over the last ~1 s sample window | ✓ |
uptime_s |
Seconds elapsed since setup() |
✓ |
heap_free_kb |
Current free internal heap (KB) | ✓ (via statvfs) |
heap_min_kb |
Minimum free internal heap since boot (KB) | 0 (not available) |
max_alloc_kb |
Largest contiguous free block (KB); fragmentation indicator | 0 |
core_temp |
CPU core temperature (°C) | 0 |
Static — set once in setup()¶
| Key | Description | PC |
|---|---|---|
cpu_freq_mhz |
CPU clock frequency (MHz) | 0 |
cpu_cores |
Number of CPU cores | 0 |
total_heap_kb |
Total internal heap size (KB) | 0 |
flash_size_mb |
Flash chip total size (MB) | 0 |
flash_speed_mhz |
Flash chip clock speed (MHz) | 0 |
sketch_kb |
Firmware size in flash (KB) | 0 |
free_sketch_kb |
Remaining flash available for OTA (KB) | 0 |
reset_reason |
Last reset cause code (esp_reset_reason_t int) |
0 |
Identity — string, set once in setup()¶
| Key | Description | PC |
|---|---|---|
firmware_version |
Firmware version string; set via FIRMWARE_VERSION build flag (default "dev") |
"dev" |
firmware_date |
Build date as YYYYMMDD string; derived from __DATE__ at compile time |
✓ |
sdk_version |
ESP-IDF version string from ESP.getSdkVersion() |
"" |
chip_model |
Chip model and silicon revision, e.g. "ESP32-S3 Rev 2" |
"" |
Platform notes¶
ESP32: heap_min_kb uses heap_caps_get_minimum_free_size(MALLOC_CAP_INTERNAL) — this is an ESP-IDF heap scan that walks the free-block list. It takes ~10 ms on the very first call (during startup), settling to ~0.3 ms avg thereafter. This causes a max_ms spike of ~10 ms for SystemStatusModule in the first timing report, which is expected and not a concern.
PC: heap_min_kb is always 0.0 — reliable minimum-free tracking is not available cross-platform.
What cannot be matched from MoonLight's SystemStatusModule¶
The following fields from MoonLight/lib/framework/SystemStatusModule.cpp are not included because they are framework-specific or require data not available at runtime:
| Field | Reason |
|---|---|
arduino_version |
No standard runtime API; ARDUINO macro is an integer, not a version string |
flash_chip_mode |
String — QIO / DIO / QOUT; not exposed by Arduino ESP32 |
cpu_reset_reason |
Verbose string version of reset_reason; would require a lookup table |
psram_* / psram_mode |
Conditionally present; mode is a string (OPI/QSPI) |
heap_info_app / heap_info_dma |
Multi-field struct with emoji health string |
lps_all / lps_effects / lps_drivers |
MoonLight-specific loop-per-second counters |
Footprint (ESP32)¶
| Sprint 1a (4 controls) | Sprint 1b (14 controls) | Sprint 1c (18 controls) | |
|---|---|---|---|
Static (classSize()) |
272 B | 536 B | 224 B |
controls_ heap |
0 B (embedded in struct) | 0 B (embedded 16 × 28 B) | 560 B (lazy-allocated, 20-slot buffer) |
| RAM total | 11.3% (37 KB / 320 KB) | 11.3% (37 KB / 320 KB) | 11.3% (37 KB / 320 KB) |
| Flash total | 51.9% (680 KB / 1280 KB) | 52.1% (683 KB / 1280 KB) | 52.2% (684 KB / 1280 KB) |
Sprint 1b embedded controls_[16] in the struct (+448 B vs Sprint 1a). Sprint 1c switches to lazy heap allocation: classSize() drops from 536 B to 224 B (array removed from struct); the 20-slot buffer (560 B) moves to heap. The struct gains 130 B of char[] string buffers (sdkVersion_[72], chipModel_[32], etc.), which is why Sprint 1c classSize is larger than Sprint 1a's 272 B.
Build and run results¶
ESP32 (Sprint 1c, 16×16, 18 controls): avg 0.011 ms, max 0.41 ms (500 ms sample gate), ~88 K fps own. Tick rate with all modules: ~145 fps at 1 000 ticks, ~133 fps at 2 000 ticks.
PC (Sprint 1c, 16×16, --count 5000, 18 controls): avg 0.0001 ms, ~14 M fps own. Adds negligible overhead.
See Release 2 — Observable, Hierarchical, and Network-Ready for sprint-by-sprint results and comparisons.
Source¶
- src/modules/system/SystemStatusModule.h
- src/core/MemoryStats.h
- src/core/Logger.h
- tests/test_system_info.cpp
Test coverage¶
→ System Status — LogLevel parsing, heapSize calculations, lifecycle, control count, healthReport keys after 1-second sampling.
→ Stateful Module — progress/button control types, sensitive reboot button, string controls.
Live: test0 — Infrastructure Modules — SystemStatusModule present on every device before each live test run.