audio bus crypto display ez global gps keyboard mesh net radio sprite storage synth system wifi

System utilities, timers, memory info, and power management

Provides access to ESP32 system functions including heap/PSRAM memory stats, time and date, timers (set_timeout/set_interval), sleep modes, timezone configuration, and garbage collection control. Timer callbacks run in the main Lua context during the scheduler update cycle.

Functions

cancel_timer() Cancel a scheduled timer
ez.system.cancel_timer(timer_id)
Stops a one-shot or repeating timer before it fires. Use the ID returned by set_timer() or set_interval(). Safe to call with an already-expired timer ID.
ParameterDescription
timer_idID returned by set_timer or set_interval
local id = ez.system.set_interval(1000, tick)
-- Later...
ez.system.cancel_timer(id)  -- Stop the interval
chip_model() → string Get ESP32 chip model name
ez.system.chip_model() -> string
Returns the specific ESP32 chip model. T-Deck Plus uses ESP32-S3.
Returns: Chip model string (e.g., "ESP32-S3")
print("Running on:", ez.system.chip_model())
cpu_freq() → integer Get CPU frequency
ez.system.cpu_freq() -> integer
Returns the current CPU clock frequency. ESP32-S3 typically runs at 240MHz but can be reduced for power saving.
Returns: Frequency in MHz
print("CPU:", ez.system.cpu_freq(), "MHz")
deep_sleep() Enter deep sleep mode, device will reboot on wake
ez.system.deep_sleep(seconds)
Deep sleep is the lowest power mode (~10µA). The CPU and most RAM are powered off, so all program state is lost. When the device wakes (via timer or GPIO), it performs a full reboot and starts from setup(). Use this for long idle periods (hours/days) where you want maximum battery life. For shorter pauses where you need to preserve state, use light_sleep() instead.
ParameterDescription
secondsSleep duration (0 = indefinite, wake on GPIO only)
delay() Blocking delay execution
ez.system.delay(ms)
Pauses execution for the specified duration. Blocks all Lua code but allows C++ background tasks to run. Use sparingly as it freezes the UI. Prefer set_timer() or set_interval() for non-blocking delays.
ParameterDescription
msDelay duration in milliseconds (max 60000)
ez.audio.beep()
ez.system.delay(500)  -- Wait half second
ez.audio.beep()
gc() Force full garbage collection
ez.system.gc()
Runs a complete garbage collection cycle immediately. Use after unloading screens or when memory is low. The main loop runs incremental GC automatically, so manual calls are rarely needed.
unload_module("large_module")
ez.system.gc()  -- Reclaim memory immediately
gc_step() → integer Perform incremental garbage collection
ez.system.gc_step(steps) -> integer
Runs a limited number of GC steps without completing a full cycle. Used by the main loop to spread GC work across frames, preventing frame drops.
ParameterDescription
stepsNumber of GC steps (default 10)
Returns: 1 if collection finished, 0 if more work needed
ez.system.gc_step(5)  -- Do a little GC work
get_battery_percent() → integer Get battery charge level
ez.system.get_battery_percent() -> integer
Returns an estimated battery percentage based on ADC voltage reading. The value is approximate and may need calibration for your specific battery. Use for status display or low-battery warnings.
Returns: Battery percentage (0-100)
local pct = ez.system.get_battery_percent()
if pct < 20 then
print("Low battery!")
end
get_battery_voltage() → number Get battery voltage
ez.system.get_battery_voltage() -> number
Returns the estimated battery voltage. LiPo batteries are nominally 3.7V, fully charged ~4.2V, and depleted at ~3.0V. The reading assumes a 2:1 voltage divider and may need calibration for accuracy.
Returns: Estimated battery voltage in volts
local v = ez.system.get_battery_voltage()
print(string.format("Battery: %.2fV", v))
get_firmware_info() → table Get firmware partition info
ez.system.get_firmware_info() -> table
Returns information about the running firmware partition including size, app binary size, and available space for OTA updates.
Returns: Table with partition_size, app_size, free_bytes, partition_label, flash_chip_size
local info = ez.system.get_firmware_info()
print("Partition:", info.partition_label)
print("App size:", info.app_size / 1024, "KB")
get_free_heap() → integer Get free internal RAM
ez.system.get_free_heap() -> integer
Returns free internal SRAM. Critical for Lua operations and core system functions. If this drops below 32KB, the system may become unstable. Use get_free_psram() for larger allocations.
Returns: Free heap memory in bytes
local heap = ez.system.get_free_heap()
print(string.format("Free heap: %d KB", heap / 1024))
get_free_psram() → integer Get free PSRAM
ez.system.get_free_psram() -> integer
Returns free external PSRAM. The ESP32-S3 on T-Deck has 8MB PSRAM for large allocations like framebuffers and caches. PSRAM is slower than internal SRAM but much more abundant.
Returns: Free PSRAM in bytes
local psram = ez.system.get_free_psram()
print(string.format("Free PSRAM: %.1f MB", psram / (1024 * 1024)))
get_last_error() → string Get last Lua error message
ez.system.get_last_error() -> string
Returns the most recent error message from a failed Lua operation. Useful for debugging when pcall returns false or a module fails to load.
Returns: Error message string, or nil if no error
local err = ez.system.get_last_error()
if err then print("Last error:", err) end
get_loop_delay() → integer Get the current main loop delay in milliseconds
ez.system.get_loop_delay() -> integer
Returns the delay added at the end of each main loop iteration.
Returns: Current loop delay in milliseconds
print("Loop delay:", ez.system.get_loop_delay(), "ms")
get_lua_memory() → integer Get memory used by Lua runtime
ez.system.get_lua_memory() -> integer
Returns memory currently allocated by the Lua VM for scripts, tables, and strings. Use to monitor memory growth and detect leaks.
Returns: Memory usage in bytes
print("Lua using:", ez.system.get_lua_memory() / 1024, "KB")
get_mac_address() → string Get the device MAC address
ez.system.get_mac_address() -> string
Returns: MAC address as hex string (e.g., "AA:BB:CC:DD:EE:FF")
get_time() → table|nil Get current wall clock time
ez.system.get_time() -> table|nil
Returns: Table with hour, minute, second, or nil if time not set
local t = ez.system.get_time()
if t then print(t.hour .. ":" .. t.minute) end
get_time_unix() → integer Get current Unix timestamp
ez.system.get_time_unix() -> integer
Returns the current time as a Unix timestamp (seconds since 1970-01-01 00:00:00 UTC). Returns 0 if system time has not been set from GPS or manual input.
Returns: Unix timestamp (seconds since 1970-01-01), or 0 if time not set
local ts = ez.system.get_time_unix()
if ts > 0 then
print("Current timestamp:", ts)
end
get_timezone() → integer Get current timezone UTC offset in hours
ez.system.get_timezone() -> integer
Returns the difference between local time and UTC in hours. Positive values are east of UTC, negative are west. Accounts for DST if the timezone string includes daylight saving rules.
Returns: UTC offset in hours
local offset = ez.system.get_timezone()
print("Timezone: UTC" .. (offset >= 0 and "+" or "") .. offset)
get_total_heap() → integer Get total heap size
ez.system.get_total_heap() -> integer
Returns total internal SRAM available for heap allocations. Use with get_free_heap() to calculate usage percentage.
Returns: Total heap memory in bytes
local total = ez.system.get_total_heap()
local free = ez.system.get_free_heap()
print(string.format("Heap: %d%% used", 100 - (free * 100 / total)))
get_total_psram() → integer Get total PSRAM size
ez.system.get_total_psram() -> integer
Returns total external PSRAM size. T-Deck has 8MB (8388608 bytes).
Returns: Total PSRAM in bytes
print("Total PSRAM:", ez.system.get_total_psram() / (1024*1024), "MB")
get_wake_reason() → string Get the reason the device woke from sleep
ez.system.get_wake_reason() -> string
Returns: Wake reason: "timer", "gpio", "touch", "ulp", "reset"
is_low_memory() → boolean Check if memory is critically low
ez.system.is_low_memory() -> boolean
Returns true when free heap drops below 32KB. At this level, the system may become unstable. Consider unloading unused modules or triggering GC.
Returns: true if less than 32KB heap available
if ez.system.is_low_memory() then
ez.system.gc()
print("Warning: Low memory")
end
is_sd_available() → boolean Check if SD card is available
ez.system.is_sd_available() -> boolean
Checks if an SD card is inserted and accessible. Returns false if no card is present, card is damaged, or USB MSC mode is active.
Returns: true if SD card is present and accessible
if not ez.system.is_sd_available() then
print("Please insert SD card")
end
is_usb_msc_active() → boolean Check if USB MSC mode is active
ez.system.is_usb_msc_active() -> boolean
Returns true if the device is currently in USB Mass Storage mode. SD card operations will fail while MSC is active.
Returns: true if MSC mode is active
if ez.system.is_usb_msc_active() then
print("USB mode - SD not available to Lua")
end
light_sleep() → string Enter light sleep mode, execution continues on wake
ez.system.light_sleep(seconds) -> string
ParameterDescription
secondsSleep duration (0 = indefinite, wake on GPIO only)
Returns: Wake reason: "timer", "gpio", or "unknown"
millis() → integer Returns milliseconds since boot
ez.system.millis() -> integer
Returns the number of milliseconds since the device started. Wraps around approximately every 49 days. Use for timing, animations, and measuring durations between events.
Returns: Milliseconds elapsed since device started
local start = ez.system.millis()
-- do some work
local elapsed = ez.system.millis() - start
print("Took", elapsed, "ms")
reload_scripts() → boolean Reload all Lua scripts (hot reload)
ez.system.reload_scripts() -> boolean
Reinitializes the Lua runtime and reloads all scripts. Useful during development to test changes without rebooting. Screen stack and state are reset.
Returns: true if reload successful
if ez.system.reload_scripts() then
print("Scripts reloaded")
end
restart() Restart the device
ez.system.restart()
Performs a full system reboot. All state is lost. Use for applying settings that require restart or recovering from error conditions.
print("Restarting in 3 seconds...")
ez.system.delay(3000)
ez.system.restart()
set_interval() → integer Schedule a repeating callback
ez.system.set_interval(ms, callback) -> integer
ParameterDescription
msInterval between calls (minimum 10ms)
callbackFunction to call repeatedly
Returns: Timer ID for cancellation
local id = ez.system.set_interval(1000, function() print("tick") end)
set_loop_delay() Set the main loop delay in milliseconds
ez.system.set_loop_delay(ms)
Adds a delay at the end of each main loop iteration. Can reduce CPU usage and heat when fast updates aren't needed. Default 0 for maximum responsiveness.
ParameterDescription
msDelay in milliseconds (0-100, default 0)
ez.system.set_loop_delay(10)  -- ~100 FPS max
set_time() → boolean Set system clock time
ez.system.set_time(year, month, day, hour, minute, second) -> boolean
ParameterDescription
yearFull year (e.g., 2024)
monthMonth (1-12)
dayDay of month (1-31)
hourHour (0-23)
minuteMinute (0-59)
secondSecond (0-59)
Returns: true if time was set successfully
set_time_unix() → boolean Set system clock from Unix timestamp
ez.system.set_time_unix(timestamp) -> boolean
ParameterDescription
timestampUnix timestamp (seconds since 1970-01-01)
Returns: true if time was set successfully
set_timer() → integer Schedule a one-shot callback
ez.system.set_timer(ms, callback) -> integer
ParameterDescription
msDelay before callback fires
callbackFunction to call
Returns: Timer ID for cancellation
ez.system.set_timer(1000, function() print("Done!") end)
set_timezone() → boolean Set timezone using POSIX TZ string
ez.system.set_timezone(tz_string) -> boolean
ParameterDescription
tz_stringPOSIX timezone string (e.g., "CET-1CEST,M3.5.0,M10.5.0/3")
Returns: true if timezone was set successfully
ez.system.set_timezone("CET-1CEST,M3.5.0,M10.5.0/3")  -- Amsterdam/Berlin
ez.system.set_timezone("EST5EDT,M3.2.0,M11.1.0")      -- New York
ez.system.set_timezone("GMT0BST,M3.5.0/1,M10.5.0")    -- London
start_usb_msc() → boolean Start USB Mass Storage mode to access SD card from PC
ez.system.start_usb_msc() -> boolean
Presents the SD card as a USB drive to the connected computer. The device appears as a removable drive for file transfer. While MSC mode is active, ezOS cannot access the SD card. Call stop_usb_msc() to resume normal operation.
Returns: true if MSC mode started successfully
if ez.system.start_usb_msc() then
print("SD card now accessible from PC")
end
stop_usb_msc() Stop USB Mass Storage mode
ez.system.stop_usb_msc()
Exits USB Mass Storage mode and returns SD card control to ezOS. Always call this after file transfers are complete to restore normal operation.
ez.system.stop_usb_msc()
print("SD card returned to ezOS")
uptime() → integer Get device uptime
ez.system.uptime() -> integer
Returns how long the device has been running since last boot. Useful for status displays and monitoring.
Returns: Seconds since boot
local up = ez.system.uptime()
print(string.format("Uptime: %d:%02d:%02d",
up / 3600, (up % 3600) / 60, up % 60))
yield() Yield execution to allow C++ background tasks to run
ez.system.yield(ms)
Temporarily yields execution to allow background C++ tasks (radio, GPS, timers) to run. Also processes Lua timers. Essential in custom main loops to prevent watchdog timeouts and keep mesh networking responsive.
ParameterDescription
msOptional sleep time in milliseconds (default 1, max 100)
while game_running do
update_game()
render_game()
ez.system.yield(10)  -- Give background tasks time to run
end

Bus Messages

screen/popped payload: string Screen title that was removed Posted when a screen is removed from the navigation stack
Fired after ScreenManager.pop() completes. The previous screen is now active.
Payload: string Screen title that was removed
ez.bus.subscribe("screen/popped", function(title)
print("Left screen: " .. title)
end)
screen/pushed payload: string Screen title Posted when a screen is pushed onto the navigation stack
Fired after ScreenManager.push() completes. Useful for analytics, logging navigation paths, or updating UI elements that depend on the current screen.
Payload: string Screen title
ez.bus.subscribe("screen/pushed", function(title)
print("Navigated to: " .. title)
end)
screen/replaced payload: string Format: "old_title>new_title" Posted when the current screen is replaced with another
Fired when ScreenManager.replace() swaps the current screen without pushing to the stack. Useful for tracking screen transitions.
Payload: string Format: "old_title>new_title"
ez.bus.subscribe("screen/replaced", function(data)
local old, new = data:match("(.+)>(.+)")
print("Replaced " .. old .. " with " .. new)
end)
settings/changed payload: string Format: "setting_name=value" Posted when any setting value is modified
Fired when user changes a setting in the Settings screen. Can be used to react to configuration changes in real-time.
Payload: string Format: "setting_name=value"
ez.bus.subscribe("settings/changed", function(data)
local name, value = data:match("(.+)=(.+)")
if name == "brightness" then
print("Brightness changed to: " .. value)
end
end)