sys
import {
memoryUsage,
storageUsage,
deviceName,
setDeviceName,
uptime,
gc,
restart,
exit,
panic,
board,
firmware,
deviceId,
resetReason,
version,
} from 'mikro/sys'System-level functions for memory monitoring, timing, garbage collection, device info, and environment variables.
Functions
memoryUsage()
function memoryUsage(): MemoryUsageReturns current heap usage.
const mem = memoryUsage()
const free = mem.heapTotal - mem.heapUsed
console.log('Free memory: %dKB', free / 1000)storageUsage()
function storageUsage(): {total: number; used: number; free: number} | undefinedBytes on the app filesystem — the partition an over-the-air build is downloaded and staged onto. Returns undefined when the platform cannot report it.
const storage = storageUsage()
if (storage) console.log('Free storage: %dKB', storage.free / 1000)An OTA check-in can report free so the registry withholds builds that would not fit; see the over-the-air updates guide.
uptime()
function uptime(): UptimeReturns time since boot and RTC time.
const {boot, rtc} = uptime()
console.log('Up for %d ms', boot)
console.log('RTC: %d ms', rtc)| Property | Description |
|---|---|
boot | Milliseconds since last boot (resets on deep sleep) |
rtc | Milliseconds from RTC clock (survives deep sleep) |
gc()
function gc(): voidRuns the cycle collector. Not normally needed: QuickJS frees objects as soon as the last reference drops, via reference counting. The cycle collector only reclaims memory held by reference cycles (objects that refer to each other).
restart()
function restart(): neverRestarts the device immediately.
getWakeupCause()
function getWakeupCause(): stringReturns why the device booted. Useful for branching logic right after a deep sleep cycle.
const cause = getWakeupCause()
console.log('Woke up because: %s', cause)Possible values: 'timer', 'ext0', 'ext1', 'gpio', or 'undefined' (cold boot or unknown).
resetReason
const resetReason: ResetReasonWhy the device last reset, determined once at boot. Pairs with getWakeupCause(), which reports why the device woke from deep sleep.
if (resetReason === 'panic') {
console.log('Recovering from a crash')
}On ESP32 this maps the ESP-IDF reset reason: a clean restart() reports 'software', while an unhandled crash reports 'panic'. On host/Node builds there is no chip-reset concept, so it is always 'unknown'.
Possible values: 'power-on', 'software', 'panic', 'watchdog', 'interrupt-watchdog', 'task-watchdog', 'brownout', 'deep-sleep', 'external', 'sdio', 'usb', 'jtag', 'efuse', 'power-glitch', 'cpu-lockup', or 'unknown'.
'task-watchdog' means the hardware task watchdog reset the device because native code below the JavaScript layer stopped responding. The JavaScript-level watchdogs do not produce it: they restart through the normal panic path, so the next boot reports 'software', or 'deep-sleep' when onPanic deep-sleeps the device.
exit(exitCode?)
function exit(exitCode?: number): neverStops the current program. On device, this returns to the REPL.
panic(message)
function panic(message: string): neverImmediately crashes with an error message. Use for truly unrecoverable situations.
board
const board: BoardInfoBoard and hardware info, evaluated once at startup.
console.log('%s - %d core(s)', board.chip, board.cores)
console.log('Features: %s', board.features.join(', '))
console.log('Flash: %dMB', board.flash / 1024 / 1024)| Property | Description |
|---|---|
name | The board, as its firmware project names it (for example "@acme/devboard"), or "<chip>-generic" for the generic firmware (for example "esp32c6-generic"). A board that runs the generic firmware reports its own name, which mikro flash writes in |
chip | Chip target (for example "esp32c6") or "host" |
cores | Number of CPU cores |
revision | Silicon revision (major * 100 + minor on ESP32, 0 on host) |
features | Supported features (for example ["wifi", "ble"]) |
flash | Flash size in bytes (0 on host) |
psram | PSRAM size in bytes (0 if unavailable) |
wifi, ble and bt appear only when the chip supports them and the matching stack was compiled into the firmware, so features answers whether the module behind a radio will work. ieee802154 reports silicon alone.
firmware
const firmware: FirmwareInfoFirmware build identifiers. Useful for debugging and detecting firmware changes.
console.log('Build: %s', firmware.hash)
console.log('Date: %s', firmware.date)| Property | Description |
|---|---|
name | The firmware build: "<chip>-generic" for the generic firmware, or the board it was built for (for example "@acme/devboard") |
hash | ELF SHA256 hash on ESP32, "dev" on host |
date | Build date and time |
idfVersion | ESP-IDF version string, undefined on host |
bytecodeVersion | QuickJS bytecode version this firmware loads |
bytecodeVersion is what an app build has to match to run on this device: bytecode is not portable across QuickJS versions, so a build compiled for a different one cannot be loaded. Send it with version on an OTA check-in so the registry only offers builds this device can actually run. See the Over-the-air Updates guide.
version
const version: stringThe mikrojs firmware version, baked in at build time from the workspace package.json (for example "0.1.0"). Always a valid semver string. Falls back to "0.0.0-dev" if the build did not set MIK_FW_VERSION.
console.log('mikrojs %s', version)deviceId
const deviceId: stringA unique device identifier encoded as Crockford's Base32 (10 lowercase characters, no special symbols).
console.log('Device: %s', deviceId)
// e.g. "5dsgr8kwev"On ESP32, derived from the chip's base MAC address. The encoding is lossless: decoding the 10 characters yields the original 6 MAC bytes. On host/Node builds, derived from the hostname (stable across restarts on the same machine).
deviceName()
function deviceName(): {rev: number; name?: string}The name the device carries for itself, set with mikro name or at enrollment, together with the revision that orders renames. Revision 0 with no name means the device has never been named — fall back to deviceId.
const {name} = deviceName()
console.log('I am %s', name ?? deviceId)setDeviceName(value)
function setDeviceName(value: {rev: number; name?: string}): voidStores a name pair, replacing any previous one. This is an unconditional write; it does not compare revisions. Mainly for adopting a name handed down by a registry at check-in. The registry arbitrates: it compares the revision the device reports against its own, and the device stores whatever comes back. Every deliberate rename bumps rev by one, so the two sides converge without needing a clock the device may not have. Omit name to record that the name was cleared. The revision still has to move, or the old name comes back on the next sync.
setSystemTime(timestamp)
function setSystemTime(timestamp: number): void
function setSystemTime(date: Date): voidSets the system clock. Typically called after SNTP sync or with a known timestamp.
Types
MemoryUsage
interface MemoryUsage {
heapUsed: number
heapTotal: number
}Uptime
interface Uptime {
readonly boot: number // ms since boot
readonly rtc: number // ms from RTC
}BoardInfo
interface BoardInfo {
name: string
chip: string
cores: number
revision: number
features: string[]
flash: number
psram: number
}ResetReason
type ResetReason =
| 'power-on'
| 'software'
| 'panic'
| 'watchdog'
| 'interrupt-watchdog'
| 'task-watchdog'
| 'brownout'
| 'deep-sleep'
| 'external'
| 'sdio'
| 'usb'
| 'jtag'
| 'efuse'
| 'power-glitch'
| 'cpu-lockup'
| 'unknown'MonotonicTimestamp
A timestamp based on uptime that is not affected by NTP adjustments. Useful for measuring elapsed time reliably, and for logging events before the device clock is synced via NTP.
const start = MonotonicTimestamp.now()
// ... do work ...
const elapsed = MonotonicTimestamp.now().since(start)
console.log('Took %d ms', elapsed)Logging events before NTP sync:
import {MonotonicTimestamp} from 'mikro/sys'
const events: Array<{ts: MonotonicTimestamp; msg: string}> = []
// These timestamps are captured before NTP has synced
events.push({ts: MonotonicTimestamp.now(), msg: 'WiFi connected'})
events.push({ts: MonotonicTimestamp.now(), msg: 'MQTT broker reached'})
// Later, after NTP sync, convert to wall-clock times.
// toDate() returns err('ClockNotSynced') if the resolved time is
// before the firmware build date (i.e. the clock hasn't been synced).
for (const event of events) {
const result = event.ts.wallDate()
if (result.ok) {
console.log(`[${result.value.toISOString()}] ${event.msg}`)
} else {
console.log(`[uptime:${event.ts.uptimeMs}ms] ${event.msg}`)
}
}| Method | Description |
|---|---|
MonotonicTimestamp.now() | Create a timestamp at the current moment |
.since(other) | Milliseconds elapsed since another timestamp |
.wallDate() | Convert to a Date, or err 'ClockNotSynced' if clock isn't set |