Add Sync Backpack Time button (real timestamps on goggle DVR) + lua memory optimisations#18
Add Sync Backpack Time button (real timestamps on goggle DVR) + lua memory optimisations#18bob9 wants to merge 2 commits into
Conversation
|
We will NOT be adding this much code to the existing Lua script. It already has problems running on low memory radios. |
- Hoist the CHAR_UP/CHAR_DOWN substitution table out of fieldGetStrOrOpts:
it was rebuilt for every character parsed (2-3 heap allocations per byte
of every name/option/unit string). Worst-case heap growth within a single
string parse drops from ~7.4KB to ~1.9KB (-74%); parse CPU -40%.
- Share one table between the four identical INT entries and the two
identical STRING entries in the functions dispatch table.
- Drop explicit nil fields from table constructors (each costs a VM
instruction and hash-slot padding).
- Replace seven duplicated crossfireTelemetryPush(cmd, {deviceId,
handsetId, x, y}) sites with a shared crsfPush helper.
- Inline single-caller fieldUnsignedToSigned into fieldSignedLoad.
- setLCDvar: one getVersion() call and one lcd.RGB branch instead of two;
drop the unused textWidth global.
- Hoist repeated subexpressions in the draw loop and popup handler.
Net vs master: -384B stripped bytecode (13,958 vs 14,342), -28 VM
instructions, ~-1.3KB resident heap (64-bit desktop measure; ~half on the
radio's 32-bit Lua). Behavior verified byte-identical to master across a
scripted session covering chunked parameters, folders, device switching,
editing with clamping, command popups, warning flags, and touch events on
both color and B&W paths.
…handset Adds a Sync Backpack Time entry at the bottom of the root menu, shown only when the selected device is an ELRS TX module. Pressing it sends the handset date/time as a parameter write to fieldId 0x3C (handled by the module firmware to set the backpack RTC) and plays a confirmation tone. Combined with the preceding perf commit, the script remains smaller than master with the feature included: 14,338 vs 14,342 bytes stripped bytecode, 2,596 vs 2,624 VM instructions, and lower resident heap at every measured checkpoint.
|
Thanks for taking a look - that's a fair concern, so I spent some time measuring where the script's memory actually goes. (For context on why the button's worth having: it gets real timestamps onto HDZero DVR files via the backpack, instead of every recording claiming to be from 1970.) Reworked into two commits: optimisations to the existing script first, then the button on top. Net result is the script is now slightly smaller than master with the feature included (14338 vs 14342 bytes compiled), and heap use is lower everywhere I measured. Details and numbers in the updated description. The more interesting find along the way: the string parser allocates 2-3 temporary objects per character for everything it reads, which I suspect feeds the "not enough memory" panics on the small radios. Fixing that cut the parser's worst-case allocation burst by about 74%. Verified the optimisations against master with a stubbed-radio harness - output is byte-identical across a full session apart from the new row. The two commits are independent, so if you want the optimisations without the button that works too. Or if you'd rather have no menu item at all I can make the sync fire automatically on connect, which is even smaller. |
Adds a "Sync Backpack Time" item at the bottom of the root menu (only shown for an ELRS TX module). It sends the handset date/time to the module as a param write on fieldId 0x3C, which the firmware uses to set the backpack RTC. Beeps so you know it fired.
The point of it: the backpack can pass time on to HDZero goggles, so DVR recordings get real timestamps instead of defaulting to epoch. Handy for finding the right clip after a session, and for anyone using DVR footage to match up logs. Radios don't have GPS but they do have a clock the pilot already sets, so the handset is the natural source.
After the review feedback about script size I went and measured things properly, and ended up splitting this into two commits. The first is a set of optimisations to the existing script, the second is the button. The optimisations save more than the button costs, so the script comes out slightly smaller than master overall:
The big one is the first row. fieldGetStrOrOpts builds the CHAR_UP/CHAR_DOWN substitution table from scratch for every single character it parses, so every field name / option list / unit costs 2-3 heap allocations per byte, hundreds of strings per load. Hoisting that table out to a local kills it completely. I suspect this burst pattern is behind some of the "Script panic: not enough memory" reports on the B&W radios (ExpressLRS/ExpressLRS#2052) - a radio whose free headroom sits between those two numbers should stop panicking, though I can't promise that for every setup.
The rest is smaller stuff: the four INT entries (and two STRING entries) in the functions table were identical tables so they're shared now, the seven copies of
crossfireTelemetryPush(0x2D, {deviceId, handsetId, x, y})became one helper, removed the explicit nils from table constructors, folded fieldUnsignedToSigned into its only caller, deduped the getVersion/lcd.RGB checks in setLCDvar, and hoisted a couple of repeated expressions out of the draw loop.On correctness: I built a small harness that stubs the radio API and drives both versions through the same scripted session - loading with chunked params, folders, editing with clamping, command popups (confirm and cancel), switching to another device and back, warning flags, the works, on both colour and B&W paths - and diffs everything observable (every push, every drawText with coordinates, popups, tones, return values). Output is byte-identical to master except for the new menu row. Can attach the harness if anyone wants to rerun it. Heap numbers above are from desktop lua with stripped chunks, so on the radio's 32-bit lua the absolute values are roughly half, but the deltas scale the same way.