Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,10 @@ SCRIPTS/
edgetx_version.lua -- EdgeTX minimum-version gate (both tools)
ui/
lcd/
dialogs.lua -- BW dialogs (both tools)
text_edit.lua -- BW text editor (bind tool)
alert.lua -- BW alert screen (both tools)
lvgl/
dialogs.lua -- color startup dialogs (both tools)
dialogs.lua -- color dialogs (both tools, widgets)
sensors.lua -- telemetry sensor reader
file_storage.lua -- key=value file persistence
shim.lua -- BW compatibility shim
Expand Down
6 changes: 3 additions & 3 deletions docs/development.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,10 +93,10 @@ The tools build on the shared `SCRIPTS/ELRS/` library, which the widgets use too
| `SCRIPTS/ELRS/msp.lua` | Opt-in MSP-over-CRSF codec: stateless encoders returning `(frameType, payload)` for `MSP_REQ`/`MSP_WRITE` and decoders for single-frame v1 `MSP_RESP`, plus the ELRS `RXTX_CONFIG` UID/phrase helpers. Loaded only by the bind tool |
| `SCRIPTS/ELRS/defer.lua` | Single-slot `setTimeout`/`poll` timer; scheduling replaces the pending callback, which is what cancels a stale retry when a new action starts. Loaded only by the bind tool |
| `SCRIPTS/ELRS/ui/lcd/text_edit.lua` | BW text editor replicating the firmware's `editName()` model-name semantics (rotary cycles the char, ENTER advances, long ENTER toggles case or commits on a space). Loaded only by the bind tool's BW UI. `ui/<display>/` is the library's home for shared UI components, mirroring the tools' own `ui/` split |
| `SCRIPTS/ELRS/ui/lcd/alert.lua` | BW full-screen alert (MIDSIZE title, body lines, optional bottom action labels). Loaded by both tools' BW UIs |
| `SCRIPTS/ELRS/ui/lvgl/dialogs.lua` | The color-LCD startup dialogs a tool can raise before it has a page -- the version gate and the missing-module notice. Both are terminal, so each takes the caller's `onExit` for the close box and the Exit button. Loaded by both tools' LVGL UIs |
| `SCRIPTS/ELRS/ui/lcd/dialogs.lua` | BW full-screen dialogs (MIDSIZE title, body lines, optional bottom action labels), redrawn every frame: `draw` plus the version gate and the missing-module notice. Loaded by both tools' BW UIs |
| `SCRIPTS/ELRS/ui/lvgl/dialogs.lua` | Color-LCD dialogs: `showConfirm`/`showMessage` over the firmware popups, plus the version gate and the missing-module notice a tool raises before it has a page. Those two are terminal, so each takes the caller's `onExit` for the close box and the Exit button. Loaded by both tools' LVGL UIs; the widgets' full-screen pages reuse `noModuleChecklist` |
| `SCRIPTS/ELRS/loader.lua` | The tools' GC-guarded script loader: a full collection before each `loadScript` keeps fresh-install compile peaks from stacking. The one part consumers bootstrap with a bare `loadScript` |
| `SCRIPTS/ELRS/edgetx_version.lua` | The one home of the minimum EdgeTX requirement (2.11.6 / 2.12.1 / 3.0). Each tool's `main.lua` checks it once and hands `deps.versionOk` to its UI chunk, whose `preCheck` owns the presentation. Keep `min_edgetx_version` in `edgetx.yml` in step |
| `SCRIPTS/ELRS/edgetx_version.lua` | The one home of the minimum EdgeTX requirement (2.11.6 / 2.12.1 / 3.0). Each tool's `main.lua` checks it once and hands `deps.versionOk` and the `REQUIRED_VERSIONS` dialog lines (`deps.requiredVersions`) to its UI chunk, whose `preCheck` owns the presentation. Keep `min_edgetx_version` in `edgetx.yml` in step |
| `SCRIPTS/ELRS/sensors.lua` | Generic EdgeTX telemetry reader (`getSensorValue` with a cached name-to-ID lookup), not CRSF-specific. Loaded by `crsf.lua`, which exposes it to every consumer as `crsf.getSensorValue`. A cached ID addresses a slot in the model that was loaded when it was resolved, so a consumer that survives a model change must call `crsf.resetSensorCache()` on that edge |
| `SCRIPTS/ELRS/file_storage.lua` | Generic key=value file persistence (`read`/`write`), schema-free. Loaded by the VTX Admin widget and the bind tool |
| `SCRIPTS/ELRS/shim.lua` | `table.concat` polyfill for BW radios |
Expand Down
11 changes: 9 additions & 2 deletions src/SCRIPTS/ELRS/edgetx_version.lua
Original file line number Diff line number Diff line change
@@ -1,14 +1,21 @@
---------------------------------------------------------------------------
-- EdgeTX Version Gate --
-- Loaded via loadScript() with no arguments; returns the isSupported --
-- function. --
-- function and the REQUIRED_VERSIONS dialog lines. --
-- --
-- The one home of the package's minimum EdgeTX requirement. The tools --
-- check it once at load and hand the result to their UI chunk, whose --
-- preCheck owns the presentation. Keep the manifest's --
-- min_edgetx_version (edgetx.yml) in step with this ladder. --
---------------------------------------------------------------------------

-- Keep in step with isSupported
local REQUIRED_VERSIONS = {
"- 2.11.6 or later",
"- 2.12.1 or later",
"- 3.0 or later",
}

--- True when the running firmware meets the minimum: 2.11.6, 2.12.1
-- or 3.0.
local function isSupported()
Expand All @@ -25,4 +32,4 @@ local function isSupported()
return false
end

return isSupported
return isSupported, REQUIRED_VERSIONS
37 changes: 0 additions & 37 deletions src/SCRIPTS/ELRS/ui/lcd/alert.lua

This file was deleted.

62 changes: 62 additions & 0 deletions src/SCRIPTS/ELRS/ui/lcd/dialogs.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
---------------------------------------------------------------------------
-- B&W Dialogs --
-- Loaded via loadScript() with no arguments; returns the Dialogs table. --
-- Shared by every tool's B&W UI. --
-- --
-- Full-screen dialog: MIDSIZE title, body lines, and an optional row of --
-- action labels along the bottom. Redrawn every frame; the caller --
-- handles the keys. --
---------------------------------------------------------------------------

local Dialogs = {}

-- B&W text row height
local TEXT_H = 8

--- Clear the screen and draw a dialog.
-- @param title heading, drawn MIDSIZE
-- @param lines array of body lines
-- @param actions optional { left, right } action labels for the bottom row
function Dialogs.draw(title, lines, actions)
lcd.clear()
local y = 0
lcd.drawText(2, y, title, MIDSIZE)
y = y + (TEXT_H * 2) - 2
for _, line in ipairs(lines) do
lcd.drawText(2, y, line)
y = y + TEXT_H
end
if actions then
y = y + TEXT_H
if actions.left then
lcd.drawText(2, y, actions.left, 0)
end
if actions.right then
lcd.drawText(LCD_W - 2, y, actions.right, RIGHT)
end
end
end

--- The EdgeTX version gate.
-- @param versions REQUIRED_VERSIONS from SCRIPTS/ELRS/edgetx_version.lua
function Dialogs.drawVersionRequired(versions)
local lines = { "Requires EdgeTX:" }
for i, version in ipairs(versions) do
lines[i + 1] = version
end
Dialogs.draw("Unsupported", lines)
end

--- No CRSF module configured on the model.
function Dialogs.drawNoModule()
Dialogs.draw(" No ExpressLRS", {
"Enable a CRSF Internal",
" or External module in",
" Model settings",
" If module is internal",
"also set Internal RF to",
"CRSF in SYS->Hardware",
})
end

return Dialogs
72 changes: 48 additions & 24 deletions src/SCRIPTS/ELRS/ui/lvgl/dialogs.lua
Original file line number Diff line number Diff line change
@@ -1,17 +1,32 @@
---------------------------------------------------------------------------
-- Color LCD Startup Dialogs --
-- Color LCD Dialogs --
-- Loaded via loadScript() with no arguments; returns the Dialogs table. --
-- Shared by every tool's LVGL UI. --
-- Shared by every tool's LVGL UI; the widgets' full-screen pages reuse --
-- the no-module checklist. --
-- --
-- The two dialogs a tool can raise before it has a page: the EdgeTX --
-- version gate and the missing-module notice. Both are terminal -- the --
-- only way out is exiting the tool -- so each takes the caller's --
-- onExit and wires it to both the dialog's close box and its Exit --
-- button. --
-- The version gate and the missing-module notice are terminal -- the --
-- only way out is exiting the tool -- so each takes the caller's onExit --
-- and wires it to both the dialog's close box and its Exit button. --
---------------------------------------------------------------------------

local Dialogs = {}

function Dialogs.showConfirm(options)
return lvgl.confirm({
title = options.title,
message = options.message,
confirm = options.onConfirm,
cancel = options.onCancel,
})
end

function Dialogs.showMessage(options)
return lvgl.message({
title = options.title,
message = options.message,
})
end

--- Build a full-screen dialog: a column of text lines over a single Exit
-- button. lines are label descriptors ({ text = ..., font = ... }), used
-- verbatim as children.
Expand Down Expand Up @@ -55,27 +70,36 @@ local function buildExitDialog(title, lines, onExit)
return dg
end

--- The EdgeTX version gate. Keep the versions in step with the ladder in
-- SCRIPTS/ELRS/edgetx_version.lua, which decides when this is shown.
function Dialogs.showVersionRequired(onExit)
return buildExitDialog("EdgeTX Version Not Supported", {
{ type = lvgl.LABEL, text = "Requires EdgeTX:" },
{ type = lvgl.LABEL, text = "- 2.11.6 or later" },
{ type = lvgl.LABEL, text = "- 2.12.1 or later" },
{ type = lvgl.LABEL, text = "- 3.0 or later" },
}, onExit)
--- The EdgeTX version gate.
-- @param versions REQUIRED_VERSIONS from SCRIPTS/ELRS/edgetx_version.lua
function Dialogs.showVersionRequired(versions, onExit)
local lines = { { type = lvgl.LABEL, text = "Requires EdgeTX:" } }
for i, version in ipairs(versions) do
lines[i + 1] = { type = lvgl.LABEL, text = version }
end
return buildExitDialog("EdgeTX Version Not Supported", lines, onExit)
end

--- Label rows listing what to check when no CRSF module is found.
-- @param color optional text color
function Dialogs.noModuleChecklist(color)
return {
{ type = lvgl.LABEL, color = color, text = "- Internal/External module enabled" },
{
type = lvgl.LABEL,
color = color,
font = SMLSIZE,
text = " Internal: set Internal RF type to CRSF in SYS > Hardware",
},
{ type = lvgl.LABEL, color = color, text = "- Protocol set to CRSF" },
{ type = lvgl.LABEL, color = color, text = "- Suggested baud rate (depends on packet rate):" },
{ type = lvgl.LABEL, color = color, font = SMLSIZE, text = " 400k for 250Hz, 921k for 500Hz, 1.87M for 1000Hz" },
}
end

--- No CRSF module configured on the model.
function Dialogs.showNoModule(onExit)
return buildExitDialog("No Module Found: Check Model Settings", {
{ type = lvgl.LABEL, text = "- Internal/External module enabled" },
{ type = lvgl.LABEL, text = "- Protocol set to CRSF" },
{ type = lvgl.LABEL, text = "- Minimum Baud rate (depends on packet rate):" },
{ type = lvgl.LABEL, font = SMLSIZE, text = " 400k for 250Hz" },
{ type = lvgl.LABEL, font = SMLSIZE, text = " 921k for 500Hz" },
{ type = lvgl.LABEL, font = SMLSIZE, text = " 1.87M for F1000" },
}, onExit)
return buildExitDialog("No Module Found: Check Model Settings", Dialogs.noModuleChecklist(), onExit)
end

return Dialogs
14 changes: 4 additions & 10 deletions src/SCRIPTS/TOOLS/ExpressLRS/main.lua
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ local crsf = loader("/SCRIPTS/ELRS/crsf.lua")
local params = loader("/SCRIPTS/ELRS/crsf_params.lua", crsf)
local CRSFSession = loader("/SCRIPTS/ELRS/crsf_session.lua", crsf, params)
local Navigation = loader("/SCRIPTS/TOOLS/ExpressLRS/navigation.lua")
local versionOk = loader("/SCRIPTS/ELRS/edgetx_version.lua")()
local isVersionSupported, requiredVersions = loader("/SCRIPTS/ELRS/edgetx_version.lua")
local versionOk = isVersionSupported()

-- ============================================================================
-- App Module: business logic between the session and the UI
Expand Down Expand Up @@ -138,6 +139,7 @@ local function init()
crsf = crsf,
VERSION = VERSION,
versionOk = versionOk,
requiredVersions = requiredVersions,
}
if useLvgl then
UI = loader("/SCRIPTS/TOOLS/ExpressLRS/ui/lvgl.lua", deps)
Expand All @@ -159,22 +161,14 @@ local function run(event, touchState)
return 2
end

-- UI-specific pre-checks (version check on both LVGL and BW paths)
-- UI-specific pre-checks (version and module gates on both LVGL and BW paths)
if UI.preCheck then
local result = UI.preCheck(event)
if result ~= nil then
return result
end
end

if not App.checkCrsfModule() then
UI.handleNoModule()
if App.shouldExit then
return 2
end
return 0
end

session:drain()
session:tick()

Expand Down
41 changes: 13 additions & 28 deletions src/SCRIPTS/TOOLS/ExpressLRS/ui/lcd.lua
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ local session = deps.session
local crsf = deps.crsf
local VERSION = deps.VERSION

local drawAlert = loadScript("/SCRIPTS/ELRS/ui/lcd/alert.lua")()
local Dialogs = loadScript("/SCRIPTS/ELRS/ui/lcd/dialogs.lua")()

-- ============================================================================
-- UI state
Expand Down Expand Up @@ -69,23 +69,22 @@ function UI.init()
end

-- ============================================================================
-- Interface: preCheck (version gate)
-- Interface: preCheck (version and module gates)
-- ============================================================================

function UI.preCheck(event)
if not deps.versionOk then
drawAlert("Unsupported", {
"Requires EdgeTX:",
"- 2.11.6 or later",
"- 2.12.1 or later",
"- 3.0 or later",
})
Dialogs.drawVersionRequired(deps.requiredVersions)
if event == EVT_VIRTUAL_EXIT then
App.shouldExit = true
return 2
end
return 0
end
if not App.checkCrsfModule() then
Dialogs.drawNoModule()
return 0
end
return nil
end

Expand Down Expand Up @@ -116,29 +115,15 @@ function UI.onNewDevice()
UI.invalidate()
end

-- ============================================================================
-- Interface: handleNoModule
-- ============================================================================

function UI.handleNoModule()
drawAlert(" No ExpressLRS", {
"Enable a CRSF Internal",
" or External module in",
" Model settings",
" If module is internal",
"also set Internal RF to",
"CRSF in SYS->Hardware",
})
end

-- ============================================================================
-- Interface: handleUnsupported
-- ============================================================================

function UI.handleUnsupported()
drawAlert("Unsupported Firmware", {
"ELRS 1.x firmware detected.",
"Please update to 3.x.",
Dialogs.draw("Unsupported", {
"ELRS 1.x firmware",
"detected. Update to",
"3.5.4 or later.",
})
end

Expand Down Expand Up @@ -167,7 +152,7 @@ function UI.render(event, _touchState)
UI.warningDismissedAt = nil
end

-- Model mismatch alert (full-screen, blocks normal rendering)
-- Model mismatch dialog (full-screen, blocks normal rendering)
if session.status.modelMismatch and not UI.warningDismissedAt then
if event == EVT_VIRTUAL_ENTER then
UI.warningDismissedAt = getTime()
Expand All @@ -177,7 +162,7 @@ function UI.render(event, _touchState)
App.shouldExit = true
return
end
drawAlert("Model Mismatch", {
Dialogs.draw("Model Mismatch", {
"RX connected but",
"Model ID doesn't match.",
"Toggle Model Match",
Expand Down
Loading