The bar repainted itself whole every second. It now compares each field against what it last painted, adds seconds and a memory percentage, and keys the cache on gui.getRotation() and ui.themeName so rotation and theme changes still repaint it. Invalidation lives entirely in Lua; the firmware's push flag and gfx/statusbar.h are gone. Bindings follow getName/setName/isName, persisted preferences move from sys to a settings table, and gui.setRotation takes degrees like settings does. A bar that dies mid-run now keeps its rows reserved rather than silently resizing the running app.
6.6 KiB
Lua API parity with crosspoint-reader
Both firmwares expose a Lua API to apps on an SD card, and both are ESP32 devices, so a script that only touches files and the network should behave the same on either. This records where they agree, where they differ for a reason, and where they differ because nobody noticed. The last group is a bug list, not a design.
Compared against crosspoint-reader at src/util/lua/LuaBindings*.cpp.
Identical
fs.listDirs, fs.listFiles, fs.exists, fs.readFile, fs.writeFile,
gui.getWidth, gui.getHeight, gui.fillRect, gui.drawRect, gui.drawLine,
sys.getMillis, sys.delay, sys.exit, log.debug/info/error, the whole http table,
sys.setTickInterval, wifi.getStatus, wifi.isConnected, wifi.getLocalIP, and the
init() / draw() / on_tick() callbacks. init() is required in both, so a
misspelled entry point is an error rather than an app that quietly draws nothing.
wifi.getStatus() returns {state, ssid, ip, rssi} in both. crosspoint returned a bare
string until this was reconciled; a string had nowhere to put the address and signal
strength a status screen wants. Its state vocabulary is a subset: crosspoint reports
disconnected, connecting, connected and failed, while this firmware adds
not_found, because it joins one named network rather than walking a credential list.
Deliberate differences
| Area | crosspoint-reader | slate32 | Why |
|---|---|---|---|
| Input | input.wasPressed(button) and friends, 8 named buttons |
input.getTouch, getRawTouch, touched |
Different hardware. A touch panel has no button names and a button device has no coordinates. |
| Drawing | gui.drawText(font, x, y, text, color, style), getTextWidth(font, text) |
gui.drawText(text, x, y, color, bg), gui.getTextWidth(text) |
crosspoint ships several fonts; this firmware has one built-in font scaled by gui.setTextSize(n), and needs an opaque background colour because the panel is not e-ink. |
| Refresh | gui.refresh(mode), REFRESH_FULL/HALF/FAST |
none | An LCD has no waveform modes. |
| Colour | COLOR_* constants, 4 grey levels |
gui.color(r, g, b) returning RGB565 |
16-bit colour has too many values to enumerate. |
| Shapes | drawRoundedRect + fillRoundedRect |
one gui.roundRect(...) with gradient and border |
Fill and border derive from a single distance field, so their edges cannot disagree. |
| Themes | none | settings.getTheme/setTheme, /lib/theme.lua |
Colour panel. |
| Rotation | gui.setOrientation("portrait") |
settings.setRotation(degrees) persisted, gui.setRotation(degrees) for one frame |
This device stores rotation in settings and remaps touch to match. |
| Clock | nothing exposed; UTC offset is a C++ setting | sys.isClockSynced, settings.getTimezone/setTimezone with POSIX TZ rules |
Timezone here is a stored rule, so os.date() returns local time with DST handled by libc. |
| Launching | launcher is C++ | sys.launch(path), home is a Lua app |
The launcher is just another app here, named home. |
| Modules | single-file apps; require unusable |
require works, package.searchers reads the SD card, /lib on the path |
Shared code such as ui.lua needs it. crosspoint should adopt this. |
| BLE | ble.* |
none | No BLE use case here yet. |
| TLS memory | TlsScratchLoan lends the framebuffer to wolfSSL |
none | crosspoint is heap-starved; this device has ~280KB free. |
Accidental differences — drift, not design
Each of these is the same concept spelled two ways. Fixing them means changing one repo.
| Concern | crosspoint-reader | slate32 | Suggested resolution |
|---|---|---|---|
wifi.connect() |
no arguments, uses stored credentials | (ssid, password), saves them |
Both are wanted: a no-argument reconnect and an explicit join. |
fs.readFile cap |
50000 bytes | 65536 bytes | Arbitrary in both. |
fs mutation |
mkdir, rename, remove, removeTree, plus a path-safety check rejecting .. |
absent | This repo is missing them, including the traversal guard. |
fs.fileSize, fs.readLineAt |
present | absent | Worth porting; readLineAt exists for paging large files. |
| Timers | timer.after/every/cancel + on_timer(id) |
absent | Worth porting. |
The http table
Signatures match crosspoint exactly, so scripts port unchanged:
http.get(url, headers?) -> body|nil, status
http.head(url, headers?) -> body|nil, status
http.delete(url, headers?) -> body|nil, status
http.post(url, body?, headers?) -> body|nil, status
http.patch(url, body?, headers?) -> body|nil, status
http.download(url, dest, options) -> bytesWritten | nil, error
http.urlencode(input) -> string
status is -1 when the request never left the device. Bodies are capped at 50000
bytes, matching crosspoint, and a larger response yields nil with the real status.
http.download requires HTTPS, requires maxBytes, accepts expectedSize and
sha256, rejects unknown option keys, and deletes the file if any check fails.
Two behaviours are deliberately not copied:
- No argument shifting. crosspoint (
LuaBindingsNet.cpp:209) treats a string in argument 2 ofget/head/deleteas a request body, so a mistyped headers table becomes a silent protocol error. Here that raises. - Certificates are verified. crosspoint calls
setInsecure()for every Lua request and forhttp.download, so traffic is encrypted but unauthenticated — including the path a firmware update would use. This firmware verifies against the root bundle already embedded in the framework (_binary_x509_crt_bundle_start, ~62KB, linked only when referenced). Verified in the emulator:https://expired.badssl.comreturns status-1, and a matchingsha256accepts while a wrong one deletes the file.
The cost of matching the signatures is that there is nowhere to put a per-request CA or an insecure escape hatch, so TLS policy is device-wide and scripts cannot weaken it. That is the right trade for a device that flashes itself.
Stub generation
scripts/gen_lua_stubs.py emits stubs/slate32.lua in the same LuaLS ---@meta
format crosspoint uses for data/lua/crosspoint.lua, so one editor setup covers both.
The parser differs: crosspoint annotates each C function and reads addFunction(...)
calls, while this repo annotates the luaL_Reg table so a module's documentation stays
contiguous with its registration.
make test runs --check. crosspoint's generator has no such wiring, so its stub can
drift from its bindings silently; that is worth copying back.