6f2d618b8d
The http table copies crosspoint-reader's signatures exactly -- get/head/delete/post/ patch returning (body|nil, status), download taking maxBytes/expectedSize/sha256, the same 50000 byte body cap and the same -1 for a request that never left the device -- so a script that talks to a server runs on either firmware. docs/lua-api-parity.md records that, and every other place the two APIs agree, differ for a reason, or differ because nobody noticed. Two crosspoint behaviours are deliberately not copied. It reinterprets a string in argument 2 of a GET as a request body, which turns a mistyped headers table into a silent protocol error. More seriously it calls setInsecure() on every request, so TLS is encrypted but unauthenticated on the very path a firmware update would use; this verifies against the root bundle already sitting in the framework, and the emulator confirms expired.badssl.com is refused while a wrong sha256 deletes the file. Downloading exposed two failures worth naming. A 2KB read buffer on the stack tripped the loop task's canary because a TLS handshake had already spent it, and the hand-rolled read loop spun forever on a stream that stopped producing -- HTTPClient's own writeToStream handles both, so the loop is gone and the loop task gets 16KB. scripts/gen_lua_stubs.py generates stubs/esp32lcd.lua in the same LuaLS format crosspoint uses, reading annotations off the luaL_Reg tables so a module's docs sit with its registration. make test runs --check, which crosspoint's copy never wired up.
96 lines
6.3 KiB
Markdown
96 lines
6.3 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.width`, `gui.height`, `gui.fillRect`, `gui.drawRect`, `gui.drawLine`,
|
|
`sys.millis`, `sys.delay`, `sys.exit`, `log.debug/info/error`, the whole `http` table,
|
|
and the `draw()` / `on_tick()` callbacks.
|
|
|
|
## Deliberate differences
|
|
|
|
| Area | crosspoint-reader | esp32-lcd | 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)`, `textWidth(text)` | crosspoint ships several fonts; this firmware has one built-in font, 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 | `sys.getTheme/setTheme`, `/lib/theme.lua` | Colour panel. |
|
|
| Rotation | `gui.setOrientation("portrait")` | `sys.setRotation(degrees)` persisted, `gui.setRotation(0-3)` for one frame | This device stores rotation in settings and remaps touch to match. |
|
|
| Clock | nothing exposed; UTC offset is a C++ setting | `sys.clockSynced`, `sys.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)`, launcher is a Lua app | The launcher is just another app here. |
|
|
| 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 | esp32-lcd | Suggested resolution |
|
|
|---|---|---|---|
|
|
| Entry callback | `init()`, required | `setup()`, optional | Pick one name. Neither is better. |
|
|
| Tick interval | `app.setTickInterval(ms)` | global `TICK_MS` | Pick one mechanism. |
|
|
| `wifi.status()` | returns a **string** | returns a **table** of state/ssid/ip/rssi | Same name, incompatible types — the sharpest edge here. This repo added `isConnected()` and `localIP()` so the crosspoint idioms work either way. |
|
|
| `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/esp32lcd.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.
|