b5146b8642
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.
101 lines
6.6 KiB
Markdown
101 lines
6.6 KiB
Markdown
# 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:
|
|
|
|
```lua
|
|
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:
|
|
|
|
1. **No argument shifting.** crosspoint (`LuaBindingsNet.cpp:209`) treats a string in
|
|
argument 2 of `get`/`head`/`delete` as a request *body*, so a mistyped headers table
|
|
becomes a silent protocol error. Here that raises.
|
|
2. **Certificates are verified.** crosspoint calls `setInsecure()` for every Lua request
|
|
and for `http.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.com`
|
|
returns status `-1`, and a matching `sha256` accepts 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.
|