Skip to content

API System and Config

System-level functions and persistent configuration storage.

Returns milliseconds since boot.

  • Parameters: None
  • Returns: (number) Milliseconds since system startup
local start = picocalc.sys.getTimeMs()
-- do work
local elapsed = picocalc.sys.getTimeMs() - start

Sleeps for the specified number of milliseconds. Does not consume input events.

sys.sleep() and input.update() both service the OS while they run: HTTP, TCP and sound callbacks, the system menu and dev commands. Callbacks can therefore fire inside input.update().

  • Parameters:
    • ms (number): Milliseconds to sleep
  • Returns: None
picocalc.sys.sleep(100) -- Sleep for 100ms

Returns the battery charge level. Cached for 5 seconds to avoid slow I²C reads.

  • Parameters: None
  • Returns: (number) Battery percentage (0-100), or -1 if unknown/USB powered
local battery = picocalc.sys.getBattery()
if battery >= 0 then
print("Battery: " .. battery .. "%")
end

Checks if the device is powered via USB (GP24 VBUS sense).

  • Parameters: None
  • Returns: (boolean) true when USB power is connected

Resets the idle screen-dim timer. Call on user activity to keep the display from dimming (see the dim_timeout_s key in API Sysconfig).

  • Parameters: None
  • Returns: None
if pressed ~= 0 then
picocalc.sys.resetIdleTimer()
end

Logs a message to the USB serial debug output (115200 baud).

  • Parameters:
    • message (string): Message to log
  • Returns: None
picocalc.sys.log("Debug info: x=" .. tostring(x))

Exits the current app cleanly and returns to the launcher. Works from any call depth.

  • Parameters: None
  • Returns: Never returns
picocalc.sys.exit()

Triggers a system reboot via the watchdog timer.

  • Parameters: None
  • Returns: Never returns
picocalc.sys.reboot()

Returns the current time as a table. Time is synchronized via NTP when WiFi is connected.

  • Parameters: None
  • Returns: (table) Clock data with fields:
    • synced (boolean): true if time has been synchronized via NTP
    • hour (number): Current hour (0-23, adjusted for timezone)
    • min (number): Current minute (0-59)
    • sec (number): Current second (0-59)
    • epoch (number): UTC Unix timestamp in seconds
local clock = picocalc.sys.getClock()
if clock.synced then
local time_str = string.format("%02d:%02d:%02d", clock.hour, clock.min, clock.sec)
picocalc.display.drawText(10, 10, time_str, picocalc.display.WHITE)
end

Returns a snapshot of heap memory usage.

  • Parameters: None
  • Returns: (table) with fields:
    • psram_free (number): Free bytes in the Lua PSRAM heap
    • psram_used (number): Used bytes in the Lua PSRAM heap
    • psram_total (number): Total bytes in the Lua PSRAM heap
    • psram_largest_block (number): Largest single free block — what one big allocation can get (compare min_psram_kb in app.json)
    • psram_fragmentation (number): 0-100
    • small_pool_slabs, small_pool_bytes (number): Lua small-object pool slabs (4 KB each, carved from the PSRAM heap) and their bytes
    • small_pool_objects, small_pool_object_bytes (number): live objects of up to 128 B in those pools and their bytes
    • sram_free (number): Free bytes in the system SRAM heap (via mallinfo; the SRAM heap is only ~2.6 KB)
    • sram_used (number): Used bytes in the system SRAM heap
    • pio_psram_available (boolean), pio_psram_size (number): mainboard PIO PSRAM
local mem = picocalc.sys.getMemInfo()
picocalc.sys.log(string.format("PSRAM: %dKB free / %dKB total",
mem.psram_free // 1024, mem.psram_total // 1024))

Adds a custom item to the system menu overlay (Menu key). Maximum 4 items per app.

  • Parameters:
    • label (string): Menu item text
    • callback (function): Function to call when the item is selected
  • Returns: None
picocalc.sys.addMenuItem("Restart Level", function()
level = 1
end)

Removes all app-registered menu items. Called automatically on app exit.

  • Parameters: None
  • Returns: None

Deliberately triggers a HardFault by writing to address 0. For crash-handler testing only — never call in production code.

The fault handler will display register state on screen, save crash data via watchdog scratch registers, then reboot. On the next boot the crash data is written to /system/crashlog.txt.

  • Parameters: None
  • Returns: Never returns
-- Test that crash logging works
picocalc.sys.triggerFault()

Get the PicoDeck firmware version string.

  • Parameters: None
  • Returns: (string) Version string (e.g. “1.2.0”)
local version = picocalc.sys.getVersion()
picocalc.sys.log("PicoDeck version: " .. version)

Get detailed power status.

  • Parameters: None
  • Returns: (table) With fields:
    • charging (boolean): Whether the device is currently charging
    • percent (number): Battery percentage (0-100)
local power = picocalc.sys.getPowerStatus()
if power.charging then
print("Charging: " .. power.percent .. "%")
end

Flash a firmware image (picodeck.bin) from the SD card. Only present for apps that declare "system-update" AND are OS apps (id net.picodeck.updater / net.picodeck.store, or installed under /system/).

Before anything is flashed:

  • /system/update.sha256 (exactly 64 hex digits + optional newline) must match the image, and /system/update.sig (DER ECDSA P-256 over the SHA-256 of the image) must verify against the key built into the running firmware;
  • a confirmation dialog names the file; declining returns false, "cancelled" (keys pressed before the dialog appears are ignored).

The image is copied to /system/update.bin and the device reboots; the boot checks the checksum and signature again before writing flash. An image the boot refuses is renamed update.bin.rejected and logged (OTA REJECTED in /system/error.log); it is never retried. A /system/update.bin found at boot without an update request is renamed update.bin.stale.

  • Parameters:
    • path (string): Path to the .bin image on the SD card
  • Returns: does not return on success; false, err on failure
local ok, err = picocalc.sys.applyUpdate("/system/update.bin")

(Release assets: picodeck.bin, picodeck.sha256, picodeck.sig. Signing: tools/sign_update.py. Local builds embed a TEST key whose private half is in the repo; release builds use the UPDATE_SIGNING_KEY CI secret.)


Pause Core 1 background tasks (WiFi polling, audio decode, HTTP). Useful before intensive SD card operations.

  • Parameters: None
  • Returns: (boolean) true if Core 1 successfully paused
picocalc.sys.pauseBackground()
-- perform intensive SD card operations
picocalc.sys.resumeBackground()

Resume Core 1 background tasks after pauseBackground().

  • Parameters: None
  • Returns: None
picocalc.sys.resumeBackground()

Load a shared Lua library from /system/lib/<name>.lua and return its result. Libraries are standard Lua files that return a table of functions.

  • Parameters:
    • name (string): Library name (without .lua extension)
  • Returns: Whatever the library script returns (typically a table)
local json = picocalc.sys.loadlib("json")
local data = json.decode(raw)

Read bytes from PIO PSRAM (mainboard 8MB).

Apps may use addresses from 0x48000 to the end of the chip; anything below (the OS’s MP3 ring and reserved video region), negative, or past the end raises an error.

  • Parameters:
    • addr (number): Byte address
    • len (number): Number of bytes to read
  • Returns: (string) Data, or nil if PIO PSRAM not available
local data = picocalc.sys.pioPsramRead(0x48000, 256)

Write bytes to PIO PSRAM.

Apps may use addresses from 0x48000 to the end of the chip; anything below (the OS’s MP3 ring and reserved video region), negative, or past the end raises an error.

  • Parameters:
    • addr (number): Byte address
    • data (string): Bytes to write
  • Returns: (number) Bytes written (0 if unavailable)
local written = picocalc.sys.pioPsramWrite(0x48000, myData)

Get PIO PSRAM size.

  • Parameters: None
  • Returns: (number) Size in bytes (0 if not available)
local size = picocalc.sys.pioPsramSize()
if size > 0 then
picocalc.sys.log("PIO PSRAM: " .. (size // 1024) .. " KB")
end

Allocate a buffer in QMI PSRAM (Lua heap). Low-level; prefer standard Lua tables for most uses.

Returns a bounds-checked buffer handle (userdata), not a pointer. It is freed by qmiPsramFree (idempotent) or when garbage-collected; qmiPsramRead/Write raise on an out-of-range offset/length or a freed handle. The handle can be passed to picocalc.graphics.image.loadFromBuffer(handle [, len]).

  • Parameters:
    • size (number): Bytes to allocate
  • Returns: (userdata) Handle, or nil on failure
local buf = picocalc.sys.qmiPsramAlloc(4096)

Free a QMI PSRAM allocation.

  • Parameters:
    • handle (userdata): Handle from qmiPsramAlloc
  • Returns: None
picocalc.sys.qmiPsramFree(buf)

picocalc.sys.qmiPsramWrite(handle, offset, data)

Section titled “picocalc.sys.qmiPsramWrite(handle, offset, data)”

Write to a QMI PSRAM buffer.

  • Parameters:
    • handle (userdata): Handle from qmiPsramAlloc
    • offset (number): Byte offset within the buffer
    • data (string): Bytes to write
  • Returns: (number) Bytes written
picocalc.sys.qmiPsramWrite(buf, 0, "Hello PSRAM")

picocalc.sys.qmiPsramRead(handle, offset, len)

Section titled “picocalc.sys.qmiPsramRead(handle, offset, len)”

Read from a QMI PSRAM buffer.

  • Parameters:
    • handle (userdata): Handle from qmiPsramAlloc
    • offset (number): Byte offset within the buffer
    • len (number): Number of bytes to read
  • Returns: (string) Data
local data = picocalc.sys.qmiPsramRead(buf, 0, 11)

Persistent per-app key-value configuration storage, stored at /data/<APP_ID>/config.json. Each app gets its own isolated store. The same store is also available under the alias picocalc.appconfig — same data, two names.

For the system-wide store shared by all apps (/system/config.json), use picocalc.sysconfig — see API Sysconfig.

picocalc.sysconfig exists only for apps whose app.json declares the "sysconfig" requirement (otherwise it is nil).

Retrieves a configuration value.

  • Parameters:
    • key (string): Configuration key
  • Returns: (string or nil) Value, or nil if key does not exist
local highscore = picocalc.config.get("highscore")

Sets a configuration value. Pass nil as value to delete the key.

  • Parameters:
    • key (string): Configuration key
    • value (string or nil): Value to store, or nil to delete
  • Returns: None
picocalc.config.set("highscore", "1000")
picocalc.config.set("old_key", nil) -- Delete key

Saves the current configuration to /data/<APP_ID>/config.json.

  • Parameters: None
  • Returns: (boolean) true on success, false on error
if picocalc.config.save() then
print("Config saved")
end

Loads configuration from /data/<APP_ID>/config.json.

  • Parameters: None
  • Returns: (boolean) true on success, false on error
picocalc.config.load()

Clears all keys from the current app’s configuration in memory. Does not delete the config file on disk; call save() afterwards to persist the change.

  • Parameters: None
  • Returns: None
picocalc.config.clear()
picocalc.config.save() -- persist the empty config

Resets the app configuration by deleting the config file from the SD card and clearing the in-memory state.

  • Parameters: None
  • Returns: (boolean) true if the file was deleted successfully, false on error
if picocalc.config.reset() then
picocalc.sys.log("App config reset to defaults")
end