Skip to content

Latest commit

 

History

History
278 lines (207 loc) · 9.88 KB

File metadata and controls

278 lines (207 loc) · 9.88 KB

Lua Test Scripts

Test scripts are written in Lua 5.4 and automate simulator interaction for testing. Pass a script file with --script:

edgetx-cli dev simulator --radio "Radiomaster TX16S" --headless --script test.lua --timeout 30s --screenshot result.png

Key commands

Function Description
key.press(key) Tap: key down, 100ms pause, key up
key.longpress(key) Long press: key down, 1s pause, key up
key.down(key) Hold a key down
key.up(key) Release a key

Key arguments accept a KEY constant or a string: key.press(KEY.ENTER) or key.press("ENTER").

KEY constants: KEY.MENU, KEY.EXIT, KEY.ENTER, KEY.PAGEUP, KEY.PAGEDN, KEY.UP, KEY.DOWN, KEY.LEFT, KEY.RIGHT, KEY.PLUS, KEY.MINUS, KEY.MODEL, KEY.TELE, KEY.SYS

Touch commands

Function Description
touch.tap(x, y) Tap: touch down, 100ms pause, touch up
touch.longpress(x, y) Long press: down, 1s pause, up
touch.down(x, y) Hold touch at coordinates
touch.release() Release touch

Trim commands

Function Description
trim.press(name) Tap: down, 100ms pause, up
trim.longpress(name) Long press: down, 1s pause, up
trim.down(name) Hold trim button
trim.up(name) Release trim button
trim.get(name) Get current trim value
trim.set(name, value) Set trim to an exact value
trim.range() Get trim min/max limits (returns two values)

Trim arguments accept a TRIM constant, string name, or raw index: trim.press(TRIM.T1) or trim.press("T1").

Hardware inputs

Function Description
switch(name, state) Set switch position (-1, 0, 1)
analog(name, value) Set analog input (0-4096)
rotary(delta) Rotary encoder delta

Switch and analog accept a SWITCH/INPUT constant, string name, or raw index: switch(SWITCH.SA, -1) or switch("SA", -1).

SWITCH / INPUT / TRIM constants are radio-specific and auto-populated from the radio definition (e.g., SWITCH.SA, SWITCH.SB, INPUT.LH, INPUT.P1, TRIM.T1, TRIM.T4).

Monitor commands

Channel outputs

Function Description
channel.get(index) Get channel output value (1-based, -1024 to 1024)
channel.mixer(index) Get mixer output value (1-based)
channel.count() Number of output channels
channel.used(index) Whether channel is in use (boolean, 1-based)
channel.mix_count() Number of active mixes in the model

Logical switches

Function Description
logicalswitch.get(index) Get logical switch state (true/false, 1-based)
logicalswitch.count() Number of logical switches

Global variables

Function Description
gvar.get(gvar, flightmode) Get GVar value (both 1-based)
gvar.count() Number of global variables
gvar.flightmodes() Number of flight modes
gvar.flightmode() Current active flight mode (0-based)

Example:

-- Read all active logical switches
for i = 1, logicalswitch.count() do
    if logicalswitch.get(i) then
        print("L" .. i .. " is ON")
    end
end

-- Read channel outputs
for i = 1, channel.count() do
    if channel.used(i) then
        print("CH" .. i .. " = " .. channel.get(i))
    end
end

Utilities

Function Description
wait(seconds) Wait for a duration (float, in seconds)
screenshot(path) Save LCD framebuffer as PNG
reset() Full simulator restart — reloads all scripts, widgets, and resets screen
reload() Reload Lua scripts from SD card (mix, function, telemetry — not widgets)
exit(code) Exit with a process exit code
print(...) Debug logging (Lua standard library)

Exit codes

Scripts return exit code 0 by default. Use exit(code) to terminate early with a specific exit code. This is useful for CI pipelines where you need to signal pass/fail.

Error handling

Scripts halt immediately on any error. Error messages include file name, line number, and a description of the problem (e.g., unknown key "BOGUS" (available: MENU, EXIT, ...)). Script errors produce a non-zero exit code.

Example script

test.lua:

-- Wait for boot
wait(5)

-- Navigate to the tools menu
key.press(KEY.SYS)
wait(1)
key.press(KEY.PAGEDN)
wait(0.5)

-- Take a screenshot
screenshot("tools-menu.png")

CI example

edgetx-cli dev simulator \
  --radio "Radiomaster TX16S" \
  --headless \
  --script test.lua \
  --timeout 30s \
  --screenshot final.png

Stdin streaming

Use --script - or --script-stdin to read Lua commands from stdin. This enables AI-driven testing and interactive piped scripting. Multi-line constructs (e.g., for/end blocks) are automatically detected and buffered until complete.

# Pipe commands
echo 'print("hello")' | edgetx-cli dev simulator \
  --radio "Radiomaster TX16S" --headless --script - --timeout 10s

# Multi-line via stdin
printf 'for i=1,3 do\nprint(i)\nend\n' | edgetx-cli dev simulator \
  --radio "Radiomaster TX16S" --headless --script-stdin --timeout 10s

# Exit with a specific code
echo 'exit(42)' | edgetx-cli dev simulator \
  --radio "Radiomaster TX16S" --headless --script - --timeout 10s
echo $?  # prints 42

Interactive Lua scripting via stdin

When an AI agent (e.g. Claude Code) needs to interactively control the simulator — sending commands one at a time, observing the screen, and deciding what to do next — a simple pipe won't work because each shell invocation is a separate process. Instead, use tail -f on a regular file to keep the simulator's stdin open across multiple shell calls.

Setup — launch the simulator with tail -f

# Create a command file and log file
touch /tmp/sim-cmds
touch /tmp/sim-log

# Launch the simulator in the background, feeding commands via tail -f
tail -f /tmp/sim-cmds | edgetx-cli dev simulator \
  --radio "Radiomaster TX16S" \
  --headless \
  --script-stdin \
  --timeout 120s \
  > /tmp/sim-log 2>&1 &
SIM_PID=$!

The key insight: each echo 'cmd' >> /tmp/sim-cmds is a separate shell invocation that appends to a regular file. tail -f watches that file for new content and continuously feeds new lines into the simulator's stdin — bridging separate shell calls into one persistent stream.

Boot wait — the simulator needs ~3 seconds to fully start

# Always wait for the simulator to boot before sending navigation commands
echo 'wait(3)' >> /tmp/sim-cmds

Observe-act loop — the core interaction pattern

  1. Send Lua commands by appending to the command file (see scripting API above)
  2. Take a screenshot to observe the result
  3. Read the screenshot PNG to see what happened
  4. Decide the next action and repeat
# Send a command
echo 'key.press(KEY.SYS)' >> /tmp/sim-cmds

# Wait for the UI to update, then capture a screenshot
echo 'wait(1)' >> /tmp/sim-cmds
echo 'screenshot("/tmp/sim-screen.png")' >> /tmp/sim-cmds

# Give the screenshot time to be written, then read the PNG to observe the result
sleep 1
# (AI agent reads /tmp/sim-screen.png to see the current screen)

# Send the next command based on what was observed
echo 'touch.tap(240, 20)' >> /tmp/sim-cmds

print() output goes to stdout, which is captured in the log file (/tmp/sim-log). Read the log file to see Lua print output.

For multi-line Lua blocks (e.g. for/end), use a heredoc so all lines arrive together for the block detector:

cat >> /tmp/sim-cmds << 'CMDS'
for i = 1, 5 do
    rotary(1)
    wait(0.3)
end
CMDS

Exit code — signal pass/fail to the calling process

# Tell the simulator to exit with a specific code
echo 'exit(0)' >> /tmp/sim-cmds

# Wait for the process to finish and capture its exit code
wait $SIM_PID
echo $?  # prints 0

Cleanup — always clean up when done

# Kill the simulator if still running
kill $SIM_PID 2>/dev/null
wait $SIM_PID 2>/dev/null

# Remove temp files
rm -f /tmp/sim-cmds /tmp/sim-log /tmp/sim-screen.png

Advanced example (loops, functions)

-- Helper to navigate down N times
function nav_down(n)
    for i = 1, n do
        key.press(KEY.DOWN)
        wait(0.2)
    end
end

wait(3)
key.press(KEY.SYS)
wait(1)
nav_down(5)
screenshot("result.png")

Complete API showcase

A single script demonstrating every available Lua function and constant table: showcase.lua.