Skip to content
Betaflight Lua Scripts on the Transmitter

Betaflight Lua Scripts on the Transmitter

The betaflight-tx-lua-scripts project puts PID, rate and filter adjustment on the radio itself. Change a value, fly, change it again — no laptop, no USB cable, no reconnecting between packs.

They reach the flight controller over the CRSF telemetry link, not USB.

Building

Do not copy the repository onto the SD card. The README is emphatic about this. The card needs the contents of obj/, which make generates — the repo root has a different layout and copying it produces a broken install.
make

That produces obj/ containing SCRIPTS/ and SOUNDS/ — about 75 files, 476 KB.

The build is lighter than it looks: bin/build.sh copies src/ into obj/, generates the manifest at obj/SCRIPTS/BF/COMPILE/scripts.lua, then runs luac -p over every file. -p is parse-only, so nothing is precompiled — the scripts ship as .lua and compile on the radio at first launch.

A modern system Lua (5.5) will happily syntax-check these even though EdgeTX runs Lua 5.2. The check is advisory; parse complaints from a newer Lua are version noise, not real errors.

Installing

The install merges into directories that already have content — /SCRIPTS/TOOLS/ will hold your ELRS and other tools, and /SOUNDS/en/ will already have a couple of hundred files.

Dragging a SCRIPTS folder onto the card in a file manager can replace the directory rather than merge into it, taking elrsV3.lua and everything else with it. Copy with explicit merge semantics instead, and back up first.

Put the radio in Storage mode

On EdgeTX: SYS → HARDWARE → USB Mode → Storage (SD), then replug. If USB Mode is Ask, choose Storage from the popup on connect.

The radio enumerates as 0483:5720 (Mass Storage) and a FAT partition appears.

Back up what’s there

cp -r "$SD/SCRIPTS" "$SD/SOUNDS" ~/radio-backup/

Copy with merge semantics

cp -r obj/SCRIPTS/. "$SD/SCRIPTS/"
cp -r obj/SOUNDS/.  "$SD/SOUNDS/"
sync

The trailing /. is what makes this merge into the existing directory instead of nesting inside it or replacing it.

Verify before unmounting

Confirm the new files arrived and the old ones survived:

test -f "$SD/SCRIPTS/TOOLS/bf.lua"      && echo "installed"
test -f "$SD/SCRIPTS/TOOLS/elrsV3.lua"  && echo "existing tools survived"
ls "$SD/SOUNDS/en" | wc -l              # should be old count + 4

Unmount and reboot the radio

udisksctl unmount -b /dev/sdb1

First launch looks like a failure

Opening TOOLS → Betaflight setup the first time runs a one-time compile pass and drops straight back to the TOOLS menu without showing anything. That is expected. Open it a second time and the real interface appears.

Two tools, two completely different control schemes

This is the part that wastes time. The install provides two entries, and they are navigated in entirely different ways.

Betaflight setup (bf.lua)Betaflight CMS (bfCms.lua)
What it isNative Lua UI with its own pagesA mirror of the FC’s on-screen OSD menu
Navigate withWheel / buttonsThe sticks
Use it forPIDs, rates, filtersAnything in the OSD menu

Betaflight setup handles the events you would expect — EVT_VIRTUAL_PREV/NEXT to move, ENTER to select, ENTER_LONG for menus, PREV_PAGE/NEXT_PAGE to change page. This is the one for tuning.

Betaflight CMS does not handle rotary input at all. Its entire event loop is:

if event == radio.refresh.event then      -- ENTER redraws
elseif stickMovement() then                -- ele / ail / rud past ±30
elseif event == EVT_VIRTUAL_EXIT then      -- EXIT closes

So turning the wheel in CMS correctly does nothing. Navigate it exactly as you would the OSD menu in your goggles: elevator up and down to move through items, aileron and rudder to change values, with a decent stick deflection — the threshold is ±30. ENTER only refreshes the screen.

A CMS screen that renders but ignores the wheel is not broken. It is waiting for stick input. If you want wheel navigation, you want Betaflight setup, not Betaflight CMS.

What a working CMS proves

Worth noting as a diagnostic in its own right: the CMS pulls its menu content from the flight controller over MSP-in-CRSF. If the menu draws at all, MSP request/response is working.

That is how the MSP-over-USB timeouts were narrowed down from “this firmware’s MSP is broken” to “something in the USB path is broken” — the same board answering MSP happily over the radio link rules the firmware out.

Last updated on