Files
esp32-lua-api/LANDSCAPE.md
T
2026-08-03 17:46:14 -04:00

8.7 KiB

Current API Landscape

Snapshot taken 2026-08-03 from the generated LuaLS stubs in Slate32 and CrossPoint Reader. This inventory explains the gaps between the current firmwares and the contracts under lua/api/core/ and lua/api/features/.

Summary

LCD / Slate32 E-ink / CrossPoint
Declared functions 77 67
Names present in both 27 27
Same parameter list 21 21
Same name, different parameter list 6 6

Matching declarations do not guarantee matching behavior. Important differences remain in filesystem limits and ordering, TLS verification, network ownership, colors, and lifecycle.

Original Common Ground

These names and parameter lists matched before the clean contract was defined. They are useful implementation inventory, not constraints on lua/api/core/.

Namespace Functions Remaining decisions
http get, head, delete, post, patch, download, urlencode The contract now returns `HttpResponse
fs exists, readFile, writeFile, listFiles, listDirs Standardize the read cap, sorted listings, path validation, and failure returns.
log debug, info, error Mostly ready.
sys delay, setTickInterval The clean contract drops both; callback timers replace periodic ticks and blocking delays.
wifi disconnect, isConnected Decide whether disconnect ownership is app-specific or device-wide.

The basic gui drawing primitives are also intended for core. Colors will be opaque, platform-native integers produced by gui.color(r, g, b): RGB565 on LCD and quantized grayscale on e-ink. Apps that need to be portable use gui.color rather than hard-coded values.

Same Name, Different Signature

Function LCD E-ink
gui.clear (color) ()
gui.drawLine (x1, y1, x2, y2, color) (x1, y1, x2, y2, width, color)
gui.drawText (text, x, y, color, bg) (font, x, y, text, color, style)
gui.fillCircle (x, y, radius, color, bg) (cx, cy, radius, color)
gui.getTextWidth (text) (font, text, style)
wifi.connect (ssid, password) () using stored credentials

These GUI differences are current implementation drift, not permanent display features. Wi-Fi is also generic drift: both connection forms are useful, so the eventual shared signature can make credentials optional after both firmwares implement both behaviors.

Agreed GUI Direction

Basic geometry and text measurement belong in core with identical signatures:

gui.getWidth()
gui.getHeight()
gui.color(r, g, b)

gui.clear(color?)
gui.fillRect(x, y, w, h, color)
gui.drawRect(x, y, w, h, color)
gui.drawLine(x1, y1, x2, y2, color, width?)
gui.fillCircle(x, y, radius, color, background?)

gui.getTextWidth(font, text, style?)
gui.getFontHeight(font, style?)
gui.drawText(font, x, y, text, color?, style?, background?)

Fonts are opaque, zero-based integer IDs selected through shared semantic constants:

gui.FONT_SMALL
gui.FONT_UI
gui.FONT_BODY
gui.FONT_LARGE

Each implementation maps those roles to its available fonts or scales. Portable apps use the named constants and metric functions rather than literal IDs or assumed pixel dimensions. Physical names such as Bookerly and Noto Sans remain legacy implementation APIs, not part of the core or e-ink feature contracts.

The optional background describes the surface behind anti-aliased output; it can have no visible effect on a renderer without partial edge pixels. E-ink refresh policy and LCD live-frame behavior remain feature-specific.

Generic Features to Converge

These are not inherently LCD or e-ink concerns and should not be permanently assigned to a display feature.

Present only on E-ink

  • Filesystem mutation and paging: fileSize, mkdir, readLineAt, remove, removeTree, rename. These are intentionally under lua/api/core/; Slate32 must still implement them.
  • Independent timers: timer.after, timer.every, timer.cancel
  • sys.uptime / sys.millis naming

Present only on LCD

  • App routing: sys.launch, sys.replace, sys.back
  • Runtime state: sys.getAppName, setAppName, getMemory, isClockSynced
  • Wi-Fi management: scan, explicit credential connection, forget
  • SD-backed require and app-local modules (runtime behavior, not a binding declaration)
  • Native compact widget tree in node.*

The contract intentionally includes all of these generic capabilities. Both firmwares are under our control, so they migrate to the clean API without compatibility aliases. The compact node tree remains useful on e-ink for layout, painting, invalidation, and directional focus. Focus is tree-level state with a deliberate appearance; moving it invalidates only the old and new nodes.

Feature Contracts

  • buttons guarantees physical-button polling/callbacks and button hints.
  • touch guarantees calibrated/raw coordinates, touch callbacks, and calibration persistence.

A feature exists only where an app can call something a device may not have. Panel technology is not such a case: firmware-owned publication and waveform policy already hold for every display, and gui.setFullscreen() and gui.roundRect() are core, with a panel that has no gradient flattening it exactly as gui.color() quantizes to grayscale. So the display is never a feature, and input is never inferred from it. BLE/GATT remains required core ble; a future bt namespace is reserved for Classic Bluetooth. Generic BMP, polygon, shape, rotation, color, and text operations are core. Rotation and timezone settings are also core, while theme persistence and application belong to shared ui.lua.

Lifecycle Differences

Concern LCD E-ink
Entry required init(arg) required init()
Navigation Lua route stack exit to C++ launcher
Input callbacks touch down/move/up/tap button callback and polling
Publication drawing is immediately visible apps currently call explicit refresh
Modules app-local and shared require effectively single-file apps

The contract standardizes init(arg?), navigation, and module loading. Optional draw(deltaMs) runs once after initialization and then at most 30 FPS, best effort. The host passes monotonic elapsed milliseconds, with zero on the first frame. Callback timers replace on_tick. Input callbacks remain feature-specific. Firmware commits dirty content after callback batches and owns panel refresh policy, including e-ink waveforms. sys.getAPIVersion() identifies the integer contract version implemented by the firmware.

Contract Rules

  1. This is a clean API. Backward compatibility has no weight because all consumers are controlled and migrate with the implementations.
  2. A core function matches in name, parameters, returns, and observable behavior.
  3. GUI coordinates use a top-left origin, including text; implementations translate driver baselines and orientation details.
  4. Invalid arguments raise Lua errors. Runtime failures return nil, error; filesystem reads take an explicit maxBytes up to 64 KiB and fail rather than truncate. fs.writeFile() is atomic: failure leaves the previous contents intact.
  5. Apps live under /.lua/apps, shared modules under /.lua/lib, and persistent app data under /.lua/data/<AppId>. Launch paths name directories relative to apps; firmware appends main.lua. The first path component is the immutable app ID and determines getAppDataPath().
  6. node owns one focused ID outside its 16-byte nodes. Directional movement is geometric and non-wrapping, with a distinct focused appearance.
  7. draw(deltaMs) is the optional frame loop, capped at 30 FPS; the host supplies elapsed monotonic milliseconds and callback-based timer.after/every handle periodic work.
  8. Firmware commits dirty display content and owns panel refresh policy; no refresh API is exposed to apps.
  9. Apps are fully trusted; the contract adds no permissions or sandbox.
  10. BLE/GATT is core ble; Classic Bluetooth remains undefined rather than sharing an inaccurate namespace.
  11. This repository owns portable modules under lua/lib/, including global theme behavior and the declarative widget toolkit in ui.lua. Widgets expose only on_enter, on_exit, and on_click: touch and directional focus map onto the same active-state lifecycle.
  12. HTTPS authenticates certificates.
  13. Generated firmware stubs are checked against this repository rather than copied here as competing sources of truth.
  14. Feature contracts contain only their declared hardware capability.
  15. Runtime smoke tests cover semantics that LuaLS declarations cannot express.