lua_runtime now loads the running script from a new LittleFS-backed drivers::storage (joltwallet/littlefs, the project's fifth external managed component -- mounts the "storage" partition already reserved in partitions.csv), falling back to the compiled-in default script only on first boot. transport exposes GET/POST /api/script -- raw Lua text, not JSON, since json_helpers.h can't round-trip scripts safely -- relayed to lua_runtime via the same callback-registration pattern already used for LedColorHandler, keeping transport free of a dependency on lua_runtime. lua_runtime_reload() persists and hot-swaps to a fresh Lua state immediately, no reboot. Verified on real ESP32-C6 hardware: script loads via kvida-sdk, an edited script hot-reloads within one tick, and the edit survives a power cycle (proving LittleFS persistence, not just an in-RAM change). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
3.8 KiB
3.8 KiB
lua_runtime
Executes the sandboxed Lua program (compiled from Blockly) that defines device behaviour. Exposes the Kvida API (e.g. publish("temperature", 21.3)) to Lua scripts; Lua never touches ESP-IDF or transport directly. REQUIRES drivers profiles transport.
Built on espressif/lua (v5.5.0, pinned in idf_component.yml) -- Espressif's own official IDF Component Registry package, MIT licensed, built specifically for embedding Lua in ESP-IDF apps. No JS/exotic bindings: standard lua_State/luaL_newstate/lua_pushcfunction C API. The project's fourth external managed component (after mdns, mqtt, led_strip).
What's here
lua_runtime_init()mounts storage (drivers::storage_init()) and tries to read/storage/script.lua; if none is stored yet (first boot, or the read fails), it falls back to the compiled-in default script (src/default_script.h). Either way it then builds the Lua state: opens onlybase/table/string/math(deliberately notluaL_openlibs(), which would also exposeio,os,package/require,debug-- see AGENTS.md's "Lua should never access ESP-IDF directly"), registers thekvidaAPI table, then runs the script, which just defineson_tick().lua_runtime_tick()callson_tick()-- one "wake, execute Lua, publish changes, sleep" cycle. A Lua runtime error is logged, not fatal.lua_runtime_reload(script, len)persists a new script to storage and hot-reloads it into a fresh Lua state immediately -- no reboot. Wired totransport::set_script_change_handler()(POST /api/script) bymain.cpp.lua_runtime_current_script(buf, cap)is the read side, wired totransport::set_script_provider()(GET /api/script).- The
kvidatable exposed to scripts:kvida.chip_temperature()-- reads the ESP32-C6's internal die temperature viaprofiles::ChipTemperatureSensor(the sameSensorimplementationmain.cppused directly before this component existed).kvida.set_led(r, g, b)-- sets the onboard WS2812 viadrivers::rgb_led_set_color().kvida.publish(name, value)-- callstransport::publish_value(), AGENTS.md's semantic publish API, built for the first time here.
- The default script reads chip temperature, maps it to a blue(cool)-to-red(hot) LED color (linear interpolation, 20-60C, clamped), and publishes the reading to Home Assistant. Verified end-to-end on real hardware.
Known limitations, accepted for now
- The onboard LED is also controllable manually from Home Assistant (an MQTT light entity, see
components/transport/src/mqtt.cpp). The two aren't reconciled:on_tick()overwrites a manually-chosen HA color every 30s. Accepted as a conflict between two "quick proof" demos rather than solved now -- a real answer (e.g. a Lua-settable "mode" toggle, or the MQTT light entity deferring to Lua) needs product input, not a guess. lua_runtime_reload()doesn't roll back to the last-good script if the new one fails to compile/run -- it just leaveson_tickundefined until the next successful reload (same behaviorlua_runtime_init()already has for a bad default script). Storage andlua_runtime_current_script()always reflect the most recently POSTed script either way, even a broken one, so a user editing fromkvida-sdkcan see (and fix) exactly what they submitted.
Not yet done
- No Blockly-to-Lua compiler exists yet (that's
kvida-sdk's job per AGENTS.md's "Level 2") -- for now scripts are hand-written Lua, edited as raw text inkvida-sdk. - No memory/CPU/runtime limits on the Lua state beyond the restricted library set -- a script with an infinite loop would hang
lua_runtime_tick()(and, since it currently runs on the same timer callback, block otheresp_timercallbacks too). Was a non-issue for a compiled-in, developer-authored script; now that scripts are user-editable and persisted, this is a real gap, just not one this pass solves.