Lua Scripting
POOM can run Lua scripts straight off the SD card — no rebuild, no toolchain. Drop a .lua file, launch it from the menu, and it runs in its own task with a small, curated API for the screen, buttons, buzzer and LEDs. This is the fastest way to prototype an automation or a tiny app on the device.
/sdcard. The runner is at THE MAKER → LUA.
The Runtime Model
- Each run spins up a fresh Lua VM in a dedicated FreeRTOS task; the VM lives only for that run.
- While a script runs, the menu stops redrawing so your script owns the OLED.
- Stopping is cooperative: a stop request fires a Lua hook that aborts the script with the error
"stopped". Design loops so they can be interrupted (poll a button and callApp.exit()).
The Lua API
The VM registers four global tables. This is the whole surface — deliberately small.
App — utilities
| Call | Does |
|---|---|
App.log(message) | Print to the UART console ([lua] ...) |
App.sleep(ms) | Delay for whole milliseconds |
App.beep(freq_hz, duration_ms) | Play a tone on the buzzer |
App.exit() | Stop the current script (raises "stopped") |
Oled — 128×64 display (Arduboy-style)
Oled.clear(), Oled.display(), Oled.textSize(size), Oled.setCursor(x, y), Oled.print(value), Oled.fillRect(x,y,w,h,color), Oled.drawRect(...), Oled.drawHLine(x,y,w,color), Oled.width() → 128, Oled.height() → 64. Colors: WHITE=1, BLACK=0, INVERT=2. Nothing shows until you call Oled.display().
Buttons — input events
Buttons.poll(timeout_ms) returns button, event, ts_ms for the next queued press, or nothing on timeout (0 = non-blocking). Constants: Buttons.A, Buttons.B, Buttons.LEFT, Buttons.RIGHT, Buttons.UP, Buttons.DOWN, and event Buttons.SINGLE_CLICK.
Led — WS2812 on-board strip
Led.init(brightness), Led.deinit(), Led.count(), Led.setPixel(idx, r, g, b, w) (0-based), Led.fill(r,g,b,w), Led.clear(), Led.brightness(0..255), Led.show(). The first Led.* call takes the strip over from the built-in rainbow animation and restores it when the script exits.
Experiment 1 — Hello, Lua
Save this as /sdcard/main.lua, then run THE MAKER → LUA. A beeps and lights the LED; B exits.
Oled.clear()
Oled.textSize(1)
Oled.setCursor(0, 0)
Oled.print("Lua OK")
Oled.setCursor(0, 10)
Oled.print("B=STOP")
Oled.display()
while true do
local btn, ev = Buttons.poll(50)
if btn == Buttons.B and ev == Buttons.SINGLE_CLICK then
App.exit()
end
if btn == Buttons.A and ev == Buttons.SINGLE_CLICK then
App.beep(2000, 120)
Led.init(32)
Led.setPixel(0, 0, 80, 255)
Led.show()
end
end
Observe: the loop yields with Buttons.poll(50), which keeps the script responsive and interruptible. That 50 ms poll is your event loop.
Experiment 2 — Patterns to Reuse
- Menu / counter: track a number in a Lua variable, draw it, and change it on
UP/DOWN. - Animation:
Led.fill()with values derived from a frame counter,Led.show(),App.sleep(30). - Metronome:
App.beep()on an interval usingApp.sleep(); exit onB.
Buttons.poll + App.exit() path.Where to Go Next
Scripts are the flexible sibling of the CLI — both let you drive POOM without rebuilding. The LED and buzzer your scripts control are documented in the Hardware Reference.