Skip to content

chore: 2.12.3 backports - #7687

Draft
pfeerick wants to merge 44 commits into
2.12from
pfeerick/backport-2.12.3
Draft

chore: 2.12.3 backports#7687
pfeerick wants to merge 44 commits into
2.12from
pfeerick/backport-2.12.3

Conversation

@pfeerick

@pfeerick pfeerick commented Aug 21, 2026

Copy link
Copy Markdown
Member

Backport series for the 2.12.3 release.

Important

Rebase merge this PR — it is a curated series of -x cherry-picks, and squashing would destroy the upstream trail.

Backported PRs

Cherry-picked in ascending PR order:

Then, out of numeric order because they merged to main after the series was assembled:

#7604 was skipped — docs-only, and 2.12 has no docs/ tree. Its backport/2.12 label can be removed.

Plus nine commits that are not backports:

Adaptations

main has diverged from 2.12 in several ways that a plain cherry-pick cannot bridge, so some commits needed hand-porting. Each carries its reasoning in the commit message; the notable ones:

Deliberate behaviour changes

Folded into #7653, correcting release-branch drift rather than originating upstream:

  • Qt 6.9.0 → 6.9.3 on macOS and Windows
  • edgetx-dev container pinned :latest:2.12 across linux_cpn.yml, build_fw.yml (×2) and nightly.yml
  • macOS Companion artifact renamed edgetx-cpn-osx-*edgetx-cpn-macos-*, matching main — anything consuming the old name needs updating
  • Windows swaps libsdl-org/setup-sdlsetup-sdl.sh and ilammy/msvc-dev-cmdsetup-msvc.ps1
  • macOS drops brew install SDL2 for source-built SDL3 + sdl2-compat — the point of fix(cpn): tie macOS bundle to its own SDL copies, and verify it #7653

Testing

Firmware builds — 16 targets, all green:

Target Family Size
zorro 128×64 518,932
x9dp2019 212×64 510,784
v12 320×240 1,381,340
tx16s 480×272 1,606,816
st16 480×320 1,370,400
nv14 320×480 1,579,396
tx16smk3 800×480 1,559,516
v14 538,600
gx15 1,443,704
t15pro 1,362,876
c14 1,345,936
x10 / x10express / x12s / v16 1,614,044 / 1,606,032 / 1,616,224 / 1,609,936

The x10 family was built specifically to exercise the regenerated yaml_datastructs_x10.cpp; c14 and v12 to exercise the lcdFlushed() additions. The v12 bootloader was built and checked separately (55,820) since #7483 changes boot_lcd.cpp too.

For #7564: gx15, tx16smk3 (the USB_CHARGE_CONTROL targets) and v12 (a colour target without it, exercising the #else branch of the bitfield) all rebuilt clean, libedgetx-tx16smk3-simulator.so links, and tools/generate-yaml.sh reproduces the committed yaml_datastructs_{tx15,tx16smk3,gx15}.cpp byte-for-byte.

For #7703: gx15 rebuilt clean after the mic pins were wired to match tx15's (PDM_CLOCK/PDM_DATA moved from GPIOD to GPIOE, matching the actual SAI1-driven PDM capture chain, replacing the two dead placeholder pins gx15's hal.h had carried since #7642). The conflict in hal.h was pure formatting drift (2.12 has column-aligned #defines; main's copy still has the original tabs) — content matches main exactly once whitespace is ignored.

For #7704: applied cleanly, no conflicts. Both gx15 and tx15 (the two targets sharing radio_mic_recorder.cpp/pdm_wav_recorder.cpp) rebuilt clean.

For #7695 and #7705: both applied cleanly (auto-merged, no manual resolution). tx16smk3 — the 800×480 target #7695's title calls out — and v12 rebuilt clean.

For #7670: applied cleanly, no conflicts. tx16smk3 rebuilt clean again after this landed.

Also verified: the simulator links (--target simu, one timersGetMsTick), and Companion configures on Linux.

Hardware smoke tests: one radio per screen-resolution family, plus v14, gx15 and t15pro. #7564 and #7703 have since been hardware-verified on gx15 (see checklist below); #7564 remains unconfirmed on tx15/tx16smk3.

Warning-fix commits (fix(cli), fix(keys), fix(lua), fix(pa01)): pl18ev, nv14, gx12 and pa01 — the specific targets each warning affects — were rebuilt clean from scratch after these commits, zero warnings, zero errors, firmware.bin produced on all four. fix(lua) and fix(pa01) have additionally been hardware-tested, on gx12 and pa01 respectively.

Still outstanding

If the Companion CI rework proves troublesome, reverting the #7653 and #7654 commits restores the current workflows without touching anything else.

@pfeerick pfeerick added bug/regression ↩️ A new version of EdgeTX broke something enhancement ✨ New feature or request color Related generally to color LCD radios B&W Related generally to black and white LCD radios companion Related to the companion software ci/cd 🔧 Related to GitHub Actions and similar issues labels Aug 21, 2026
@pfeerick pfeerick added this to the 2.12.3 milestone Aug 21, 2026
@pfeerick pfeerick added telemetry 📶 storage Related to loading, saving, converting needs: testing labels Aug 21, 2026
@pfeerick
pfeerick force-pushed the pfeerick/backport-2.12.3 branch from 89778fb to 580b282 Compare August 21, 2026 00:11
pfeerick added a commit that referenced this pull request Aug 21, 2026
actions/checkout was spread across v5, v6 and v7 in different workflows;
everything is now on one version.

  actions/checkout             v5, v6, v7  -> v7
  actions/download-artifact    v7          -> v8
  actions/setup-python         v6          -> v7
  actions/stale                v10         -> v11
  softprops/action-gh-release  v2          -> v3
  astral-sh/setup-uv           v7          -> v10.0.1
  repolevedavaj/install-nsis   v1.2.1      -> v1

Already current: actions/cache@v6, actions/upload-artifact@v7,
actions/upload-artifact/merge@v7, dorny/paths-filter@v4,
montudor/action-zip@v1, orhun/git-cliff-action@v4 and
jdpurcell/install-qt-action@v5. That last one publishes its major versions
as branches rather than tags, so v5 is the moving reference even though the
newest tag reads v4.1.1.

setup-uv is pinned to a full version rather than a major. Its bare major
tags stop at v7 - there is no v8, v9 or v10 - so a major reference cannot
follow it any further. Everything else has a moving major and uses it;
install-nsis v1 currently resolves to the same commit as v1.3.0.

The note against action-gh-release in nightly.yml pointing at its PR 670 as
the way to clear a Node.js warning goes too - v3 is what it suggested.

The equivalent sweep for 2.12 is in #7687, which is where this started:
build_fw there was warning that upload-artifact/merge@v4 targets Node.js 20
and was being forced onto Node.js 24. main was already past that one.

Worth an eye on the first run: action-gh-release crosses a major and is what
publishes the nightly release, and setup-uv crosses three.

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
@pfeerick
pfeerick force-pushed the pfeerick/backport-2.12.3 branch 2 times, most recently from 837d5ae to 92546c6 Compare August 21, 2026 10:38
richardclli and others added 14 commits August 23, 2026 04:24
Co-authored-by: xlong <984929678@qq.com>
Co-authored-by: Peter Feerick <peter.feerick@gmail.com>
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
(cherry picked from commit 256dc98)
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
(cherry picked from commit 19aa6d5)
This changes the flush contract: flushLcd() no longer calls
lv_disp_flush_ready() after invoking the driver callback, so each LCD
driver must now signal completion itself via lcdFlushed(). Upstream
updated every driver that needed it, but two on 2.12 were missed:

  - boards/helloradio-h750/lcd_driver.cpp (v12) already had the call on
    main, added there by work that never reached 2.12, so it was not part
    of the upstream diff. The call is added here, making startLcdRefresh
    byte-identical to main's.

  - targets/c14/lcd_driver.cpp is missing the call on main as well, so
    c14 is affected upstream too and needs the same fix there.

Both are synchronous busy-wait drivers that relied on flushLcd() readying
the display for them. Without the call LVGL never learns the flush
completed and the display pipeline stalls after the first frame - the
radio reaches the splash screen and stops, and because boot_lcd.cpp
carries the same contract change, the bootloader stops redrawing too and
so never appears to notice a USB connection.

Also drops trailing whitespace on one line of startLcdRefresh to match
main; the remaining whitespace drift in the commented-out init block is
left alone to keep this diff focused.

LCD_VERTICAL_INVERT is tested with defined() rather than for a non-zero
value: on 2.12 it is a bare #define on PCBX10/TX16S, so upstream's
'#if LCD_VERTICAL_INVERT' would silently take the wrong branch here.

The simulator hunk targets opentxsimulator.cpp rather than main's
simulib.cpp, which does not exist on 2.12.

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
(cherry picked from commit 50d12c7)
…ds (#7549)

Co-authored-by: Sergey Yugoman <sergey.yugoman@armenotech.com>
Co-authored-by: Peter Feerick <5500713+pfeerick@users.noreply.github.com>
(cherry picked from commit fcdf92d)
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
(cherry picked from commit 19708ce)
…ched (#7599)

Adapted for 2.12, which has no virtual Window::doKeyShortcut(). Upstream
overrides that single virtual with an empty body to neutralise the SYS/MDL/
TELE shortcuts on this page; on 2.12 Page overrides the six onPress*/
onLongPress* hooks directly, so those six are neutralised individually
instead. Without this the build fails on every colour target with
"marked 'override', but does not override".

(cherry picked from commit cffcbc4)
…ipts (#7603)

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
(cherry picked from commit 81db17e)
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
(cherry picked from commit 36a36c5)
pfeerick and others added 16 commits August 23, 2026 04:24
Adapted for 2.12, which never received the build_companion composite action
(added upstream by #6435, milestone 3.0). The action is imported here from
main and the three Companion workflows are rewired through it, keeping 2.12's
reusable-workflow layout, runners and 'ci: skip-cpn-*' guards. The docs/
hunk is dropped - 2.12 has no docs tree.

action.yml is seeded at its pre-#7654 state so that #7654 lands next as a
faithful diff; after both, the file matches origin/main except for the
setup-python input described below.

The bundle scripts needed adjusting for 2.12's macOS layout, which differs
from main's: main sets OUTPUT_NAME on the companion target and names the
bundle directory for the final product, while 2.12 leaves the executable as
'companion' and renames companion.app during install. So bundle_sdl_fixup
derives the executable name from the target's OUTPUT_NAME (falling back to
the target name), and bundle_verify checks the post-rename bundle. Both
forms are also correct on main.

.gitignore's 'build*/' and 'cmake-build-*/' rules are anchored to the
repository root here. Unanchored they matched at any depth, including the
new .github/actions/build_companion/ directory, so setup-msvc.ps1 had to be
forced in with 'git add -f' and the next script added there would have been
silently ignored. origin/main already anchors both; this brings 2.12 back in
line rather than fixing anything upstream. 'output/' is added for the same
reason - the action builds Companion into an output/ directory at the
repository root, and main already ignores it.

Also folded in, as deliberate drift corrections rather than upstream changes:
  - Qt 6.9.0 -> 6.9.3 (the action's default) on macOS and Windows
  - macOS artifact renamed edgetx-cpn-osx-* -> edgetx-cpn-macos-*
  - CMAKE_OSX_DEPLOYMENT_TARGET 10.15 -> 11.0 in tools/build-companion.sh,
    matching main and the documented macOS 12+ support floor

The CMAKE_PREFIX_PATH forwarding upstream adds to tools/build-companion.sh
has to be quoted here. main's script passes ${BUILD_OPTIONS} straight to
cmake, where a ';' arriving via a variable is just a character; 2.12's
assembles the command into a string and eval's it, so an unquoted value
splits the configure call at CMake's list separator - cmake then runs with
no source directory and the remaining options run as a command. The bare
';' Linux produces, where the action skips the Qt install step, is dropped
rather than forwarded as an empty list.
  - edgetx-dev container pinned :latest -> :2.12 across all four workflows
  - a setup-python input on the action (default true, so main's behaviour is
    unchanged) so macOS and Windows can keep the interpreter that carries the
    codegen packages; 2.12 builds the simulator plugins from source and needs
    them, whereas main's Companion jobs download prebuilt WASM modules

Co-authored-by: Peter Feerick <5500713+pfeerick@users.noreply.github.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
(cherry picked from commit c562f48)
Co-authored-by: Peter Feerick <5500713+pfeerick@users.noreply.github.com>
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
(cherry picked from commit a4299e7)
Adapted for 2.12: also restores the refreshWidgetSelectTimer() call when
entering widget select, which main already had but 2.12 was missing (it
carried a '// TODO: start timer' comment instead). Without it the new
deadline is never seeded and widget select would cancel itself on the next
GUI cycle. Same adaptation as the 2.11 backport.

(cherry picked from commit 3e1e185)
Adapted for 2.12, which has no WASM simulator - companion/src/simulation/
wasmsimulatorinterface.cpp does not exist here (it arrived upstream with
#6435, milestone 3.0). The equivalent seam is OpenTxSimulator::setSdPath().

Scope is reduced deliberately: 2.12's simulator does not crash on a bad SD
path the way the WASI runtime does - simuFatfsSetPaths() and the file ops
behind it use std::filesystem error_code overloads and simply fail. Upstream
clears the path because an empty preopen dir is safe for WASI; on 2.12 an
empty path makes simuFatfsSetPaths() fall back to the current working
directory, which would silently treat the wrong directory as the SD card.
So the guard warns and leaves the path alone.

(cherry picked from commit 80003fb)
Not an upstream cherry-pick.

#7638 replaced the stale commented-out declaration with a real
option(... OFF) for t22, tx15, tx16smk3 and v12, but missed two targets:

  gx15   - still on the old plural name in both places, so
           if(STICKS_DEAD_ZONE) was always false
  t15pro - already used the singular name in the guard, but left its
           option() declaration commented out

In both cases the option was never declared, so the guard was dead and the
sticks dead zone could not be enabled at all. Declaring it explicitly is
also what stops the value leaking in from another target's CMake cache,
which is the bug #7638 set out to fix.

No functional change: the option defaults OFF, so -DSTICK_DEAD_ZONE is
still never added and neither yaml_datastructs_gx15.cpp nor
yaml_datastructs_t15pro.cpp is affected - both already carry
YAML_PADDING(3) in that slot, matching t22.

origin/main has the same stale declarations, so it wants the same fix.
IS_STM32H7() is hand-maintained on this branch and the HelloRadioSky V12
was never added to it, so Companion treated an H7 radio as F2/F4 -
IS_STM32F2F4() is simply the negation of the H5 and H7 tests.

Three capabilities were wrong as a result, all of which the firmware gates
on defined(STM32H7):

  Gvars              9, should be 15 (gvars.h)
  Sensors            60, should be 75 (dataconstants.h)
  SportMaxBaudRate   400000, should be 0

The first two under-report what the radio supports, so models built in
Companion could not use the full range. The third is a smaller change of
behaviour rather than a plain fix: callers treat "< 400000" as the cue to
default an internal CRSF or Ghost module to 115k rather than 400k, so V12
now defaults the same way as every other H7 radio.

The V12 target sets CPU_TYPE STM32H7 in its CMakeLists.txt and runs on a
helloradio-h750 board, so H7 is not in question.

main is not affected. It has no IS_STM32H7() at all - companion there reads
the MCU from the generated hw_defs/*.json, which already marks v12 as
STM32H7, so it cannot drift from the firmware the way this list has.

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
build_fw was warning that actions/upload-artifact/merge@v4 targets Node.js
20 and is being forced onto Node.js 24, so the rest were checked at the
same time.

  actions/checkout                v6      -> v7
  actions/download-artifact       v4      -> v8
  actions/setup-python            v6      -> v7
  actions/upload-artifact/merge   v4      -> v7   (the deprecation warning)
  actions/stale                   v8      -> v11
  softprops/action-gh-release     v2      -> v3
  repolevedavaj/install-nsis      v1.2.1  -> v1

Already current, left alone: actions/cache@v6, actions/upload-artifact@v7,
maxim-lobanov/setup-xcode@v1, montudor/action-zip@v1, orhun/git-cliff-action@v4
and jdpurcell/install-qt-action@v5 - that last one publishes its major
versions as branches rather than tags, so v5 is the moving reference even
though the newest tag reads v4.1.1.

install-nsis is pinned to the major rather than a point release; v1
currently resolves to the same commit as v1.3.0.

release-drafter.yml is deliberately untouched here to avoid colliding with
the release notes work; its checkout and action-gh-release references are
updated there instead.

Two of these warrant an eye on the first run. softprops/action-gh-release
crosses a major version and is what publishes the nightly release, and the
composite action now differs from origin/main by the install-nsis pin as
well as the setup-python input, so main wants the same sweep.

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
…n compatible hardware) (#7564)

Squashed from the 2.12-targeted branch 3djc/usb-charge-control rather than
cherry-picked from main, as RadioData bitfield allocation and the Companion
general setup UI have diverged between the branches.

Equivalent to commit a51946c on main.
Regression introduced in #6998
…nly targets

The internal/external module OR-guard was added in #5703 without a
matching case for HARDWARE_EXTERNAL_MODULE-only targets (pl18/pl18ev/pl18u)
in cliSerialPassthrough's switch — the functions were compiled but never
called there, producing -Wunused-function warnings. Serial passthrough
over an external module isn't implemented yet, so narrow the guard to
match the only call site.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
s_keys_lock_combo_cnt was declared unconditionally under #if
!defined(BOOT) (from #7561), but only read/written inside the block
further down that's guarded by defined(KEYS_LOCK_KEY1) &&
defined(KEYS_LOCK_KEY2). Targets without that key combo defined in
hal.h (nb4p, nv14, el18, pa01, pl18-family, v12/v14/v16) got a
-Wunused-variable warning. Widen the declaration's guard to match,
mirroring the same fix already on main via #7565.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…g-less targets

Introduced in #6095: on targets with only a CFS LED strip and no
bling strip (BLING_LED_STRIP_LENGTH == 0, e.g. gx12/mt12/tx12mk2/boxer
sharing the RADIO_GX12 hal config), `id >= BLING_LED_STRIP_LENGTH` is
always true for the unsigned uint8_t id, triggering -Wtype-limits.
Nest the bling-specific check and offset adjustment inside their own
BLING_LED_STRIP_LENGTH > 0 guard, with an early return for the bling
case, so bling-less targets never compile the tautological comparison
at all; the CFS-handling body stays written once, shared by both
paths, matching existing runtime behavior.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…gingInfo

Introduced in #6392: pa01 defines LED_CHARGING_START as 0, so
`i >= LED_CHARGING_START` with the loop's unsigned uint8_t i is
always true, triggering -Wtype-limits. Use a plain int loop variable
so the comparisons are ordinary signed comparisons instead of testing
a constant.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
isExternalModuleAvailable()'s DSMP-disabled guard referenced
MODULE_TYPE_DSMP, which doesn't exist as an enum value (only
MODULE_TYPE_LEMON_DSMP does). Currently dead since DSMP is always
defined on STM32 targets, but would fail to compile if DSMP were
ever disabled. This restores the fix from d584237, which was
accidentally reverted by 853adfd ("chore: formatting") before
PR #7469 was squash-merged.
@pfeerick
pfeerick force-pushed the pfeerick/backport-2.12.3 branch from f22b4f2 to e5fc394 Compare August 23, 2026 07:58
@pfeerick
pfeerick force-pushed the pfeerick/backport-2.12.3 branch from e5fc394 to 9e9a999 Compare August 24, 2026 11:01
(cherry picked from commit da6f73a)
(cherry picked from commit 0c09e72adf44360772613a21bf42929399debc8b)
@pfeerick
pfeerick force-pushed the pfeerick/backport-2.12.3 branch from 9e9a999 to 33d7c10 Compare August 24, 2026 22:26
…80 LCD size (#7695)

(cherry picked from commit faa6095)
(cherry picked from commit 4ef75e2d1adbe41eb78b3f237ffc7ba1d785bc6d)
…g labels (#7705)

(cherry picked from commit 7f6bbd1)
(cherry picked from commit 92f73a207aac9d2a50a93ba81197b620159afffa)
@pfeerick
pfeerick force-pushed the pfeerick/backport-2.12.3 branch from 33d7c10 to c1e55b0 Compare August 25, 2026 02:11
philmoz and others added 2 commits August 25, 2026 02:38
… position is wrong (#7670)

(cherry picked from commit 298e6e6)
(cherry picked from commit 6f7befb5f6bec91ffb92633f57743064effad130)
(cherry picked from commit e5fc394)
(cherry picked from commit 9e9a999)
(cherry picked from commit 33d7c10)
(cherry picked from commit c1e55b0)
@pfeerick
pfeerick force-pushed the pfeerick/backport-2.12.3 branch from c1e55b0 to fc73c78 Compare August 25, 2026 02:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

B&W Related generally to black and white LCD radios bug/regression ↩️ A new version of EdgeTX broke something ci/cd 🔧 Related to GitHub Actions and similar issues color Related generally to color LCD radios companion Related to the companion software enhancement ✨ New feature or request needs: testing storage Related to loading, saving, converting telemetry 📶

Projects

None yet

Development

Successfully merging this pull request may close these issues.