New LUA scripts & widgets - #3
Open
jurgelenas wants to merge 200 commits into
Open
Conversation
…ns for mk3 and 1 column for small screen radios
Commit f04caf2 replaced the per-type save wrappers with the codec's sendWriteInt, but the B&W edit path kept calling Protocol.fieldIntSave, so committing any edited value crashed the tool with "attempt to call a nil value". Both numeric and text-selection edits go over sendWriteInt, matching the LVGL UI.
poll() hands one data table to every handler registered for a frame type, so the decoders must not write into it. The string reader now collects chars into its own buffer instead of converting frame bytes in place, which lets any handler decode the same frame independently of registration order.
crsf_params.lua is a pure codec: reassembly runs over an explicit
caller-owned rx table ({ chunk, data, dataId, expect, done }) with the
device address passed in, and the frame builders return
(frameType, payload) for the caller to push instead of pushing
themselves. Callers no longer lend the codec a slice of their own
table -- the six flat reassembly keys and the addressing reads are
gone, and every send site is crsf.push(encode*(...)).
crsf_session.lua is a stateful CRSF parameter client (CRSFSession.new, multi-instance): it owns the field store, the load queue, the paced write queue, the command state machine, and optionally device discovery, link status and ELRS 1.x detection. The overloaded fieldTimeout becomes named per-purpose deadlines, and tick() documents the send ladder it always implied: at most one parameter frame per tick, command keep-alive > write drain > link-status > reads. protocol.lua dissolves: the session absorbs its machinery, main.lua's App keeps the policy residue (device switching, folder-ready edges, the synthetic "Other Devices" row types), and both UIs talk to the session surface -- writes go through writeField, which encodes by field type and re-reads the related fields itself, so the codec no longer appears in any UI. Two deliberate hardenings over the old engine: the load-queue head pops only when the decoded entry answers it, and CMD_IDLE dismisses the popup only for the active command.
Each widget instance owns a CRSFSession in passive fan-out mode (acceptUnsolicited): discovery re-derives from the session's field store through the onFieldUpdate callback, writes go through the paced write queue (STATE_SENDING exits when isWriting() clears), and the bounded folder read-back is the session's refresh slot. The widget keeps only its state machine, statusText and write policy. Two session rules surfaced by running many instances on one bus: a fan-out session must decode folder names fresh (no staleness flag can cover a sibling's write rewriting the name -- non-folder names stay cached, they are static), and the root-children auto-queue must only trigger off the session's own read, or every sibling's root answer multiplies the load traffic by the instance count.
Merge the widget's VTX and Protocol tables into a single VTXAdmin component: the client of the ELRS "VTX Administrator" service, owning the discovery state machine, current/desired VTX state, write policy, and the 6POS quick-change and push-trigger automation behind one tick(). The machine state is now "phase" -- VTXAdmin.state is the parsed VTX state. Around it, loadable.lua wires focused components: presets_storage.lua is a pure settings store (preset slots, sources, flags, and their key=value persistence), ui/display.lua carries the VTXDisplay read-model and WidgetLayout builders, ui/fullscreen.lua the full-screen editor, and the schema-free key=value file I/O joins the shared ELRS library as file_storage.lua. Dependencies run one way: FileStorage <- PresetsStorage <- VTXAdmin <- UI. Behavior is unchanged: log lines, the hold-until-ready 6POS latch, the push-trigger first-sample adoption and the presets.txt format all survive verbatim, verified in the simulator against a pre-refactor baseline (discovery, 6POS sweep, trigger edges, save/load roundtrip, no-module, 11 instances). The CRSF simulator mock rides along: it now builds folder-name summaries only for the TX module and mimics the firmware's Fan Thresh visibility rule (hidden while Dynamic power is off).
Six collections of six band/channel presets, stored as one line per collection in presets.txt and selected from a dropdown in the full-screen editor. The dropdown drives both what the 6POS switch applies and what the preset rows below edit; picking a collection re-applies the parked switch position without touching the switch. The preset store becomes a per-radio singleton shared by every widget instance, and the 6POS and push-trigger state moves onto it: an edge -- a switch move, a collection switch, a push trigger -- is consumed by the first instance whose session can act, so it produces exactly one write per radio instead of one per instance. The push trigger re-latches when its source is reassigned, adopting the new source's level without a phantom push. The full-screen page is built once on entry and never rebuilt; values that change under it reach the screen through per-frame get() callbacks, so focus and scroll survive a collection switch. numberEdit re-polls get() only on EdgeTX 2.12.0+, so on older firmware the channel and power digits catch up when the page is next entered.
A bare .lua install ran out of memory opening the tool on the GX12: EdgeTX compiles each file to bytecode on first load, no GC runs between loadScript calls, and the seven-file chain stacks its parse peaks until the heap is gone. Later launches worked because every attempt caches a few more .luac files. main.lua now loads through loadPart(), which collects garbage before each load, and the parse path itself gets smaller. Frames are now consumed pull-style everywhere. The firmware replicates every incoming frame into each widget instance's private queue and pop() reads the queue of the instance whose code is running, so each consumer drains its own replica through crsf.drain(consumer, onFrame) -- the tool and VTX Admin through their session, the telemetry widget through elrsinfo. The handler registry, poll() and attachBus() go away, and with them the tick guard under which only one instance's queue was popped per tick while the others accumulated stale backlog. hasTelemetry derives inside pop() as a drain empties, so frames always land before a consumer can observe the flip. Decoding sheds its allocation churn: the link-layer readString converts bytes to chars in place (each frame type has exactly one string-decoding consumer per instance), and the parameter codec refills a text selection's caller-owned values table in place, bumping field.valuesRev for the LVGL UI to watch instead of table identity. Folder-name suffix stripping moves into ui/lvgl.lua, its only consumer. The generic telemetry reader moves to sensors.lua; crsf.lua loads it and exposes crsf.getSensorValue, the seam setMock() patches exactly like pop and push. shim.lua slims to the tableConcat polyfill, the simulator carries its own shim, and reloading at the tool's root goes through session:discoverDevices() instead of raw pings. The CRSF mock now mirrors the firmware's replication: frames append to a shared log and every consumer advances its own cursor, receiving a fresh copy per delivery. Verified in the simulator on TX16S (both VTX Admin instances, telemetry widget, choice edits with sibling reload bursts) and GX12 (full load chain, edits, root rediscovery).
The title flash ran free: it was never re-anchored when the warning flags changed, so a fresh warning could wait a full flash period for its visible half (and start on the hidden one), and a cleared warning could linger to finish its cycle. Latch the flags byte and restart the flash on any change -- a new warning now starts visible on the frame its status answer lands, and a cleared one drops immediately. This restores the re-anchor the rewrite dropped from elrsV3.lua. In the CRSF simulator, drive the armed flag from CH5 (AUX1, the ELRS arm channel) the way the firmware samples handset->IsArmed(): every connected scenario ORs in the armed bit while CH5 is high, and the warning message now derives from the flags byte with the firmware's highest-bit-wins priority. Arming becomes testable mid-session by flipping a switch mapped to CH5.
Conflicts resolved by taking the unified-lua-lsp side wholesale; the only PR-side content carried forward is SCRIPTS/TOOLS/elrs-bind/main.lua, which still speaks the old crsf.lua API (registerHandler/poll/isConnected) and fails typecheck until its rearchitecture lands in a later commit.
The PR's LVGL-only elrs-bind script becomes a folder tool mirroring the config tool's shape: main.lua owns the App state and pumps the pull-model transport (sole drainer, MSP_RESP routed through the msp codec), and one UI chunk loads at runtime. The UID retry is now bounded -- six silent attempts end in a "needs ELRS 4.1+" notice instead of wire spam forever. History persists through FileStorage as indexed h1..h5 keys, and the manifest gains the tool with the data file excluded, so pkg install finally ships it.
The CRSF mock mirrors the receiver's parameter list, which now reads "Antenna Mode" with "Antenna 1;Antenna 2;Diversity".
One string carried both transient status and the UID bytes, and it lived in the page subtitle -- so the header changed meaning as you used it, and the only value the tool read back had no home of its own. Split it into a phrase form, an identity block, and feedback attached to the control that caused it. Drop "Request UID". Every event it existed to cover -- the target selector moving, a first read falling silent -- is one the App can observe, so the probe runs on tool open, after each write and on a target change, keeping the bounded six-attempt retry that reports a pre-4.1 module. Stop reading the receiver at all. It answers only over a link, and a link only exists between devices already sharing a UID, so its answer could never differ from the transmitter's -- while the link's presence says the same thing for free and costs no over-air traffic. The row now reports Connected / Not connected, and the simulator no longer seeds the two devices with different UIDs, a state the radios cannot be in. Bind and Unbind confirm before acting, which is where the advice belongs: the firmware reaches EnterBindingModeSafely at either address, so "unbind" puts the receiver into bind mode rather than erasing its binding, and the other half of the pairing is still the user's to do. The Set button carries the write sequence's progress instead of a floating status line, which frees the row that restores [EXIT] on 128x64. Set also moves to its own row with fixed text, the phrase field stays editable regardless of what is reachable, and a Both write no longer flips the selector to Transmitter behind the user.
This reverts commit e7b1680.
|
Just tested the B&W bind phrase script on RM Pocket, worked great. Using it to switch between trainer bindphrase and plane/quad bindphrase. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.