chore: 2.12.3 backports - #7687
Draft
pfeerick wants to merge 44 commits into
Draft
Conversation
pfeerick
force-pushed
the
pfeerick/backport-2.12.3
branch
from
August 21, 2026 00:11
89778fb to
580b282
Compare
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
force-pushed
the
pfeerick/backport-2.12.3
branch
2 times, most recently
from
August 21, 2026 10:38
837d5ae to
92546c6
Compare
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)
(cherry picked from commit 1b12ee9)
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com> (cherry picked from commit 19708ce)
(cherry picked from commit a82f44a)
…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)
Co-authored-by: Claude Fable 5 <noreply@anthropic.com> (cherry picked from commit 36a36c5)
(cherry picked from commit 245da45)
(cherry picked from commit 1d42488)
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)
(cherry picked from commit a0c1aa7)
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)
(cherry picked from commit 7461bfc)
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>
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>
pfeerick
force-pushed
the
pfeerick/backport-2.12.3
branch
from
August 23, 2026 06:08
92546c6 to
f22b4f2
Compare
4 tasks
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
force-pushed
the
pfeerick/backport-2.12.3
branch
from
August 23, 2026 07:58
f22b4f2 to
e5fc394
Compare
(cherry picked from commit 5d69da6)
pfeerick
force-pushed
the
pfeerick/backport-2.12.3
branch
from
August 24, 2026 11:01
e5fc394 to
9e9a999
Compare
(cherry picked from commit da6f73a) (cherry picked from commit 0c09e72adf44360772613a21bf42929399debc8b)
pfeerick
force-pushed
the
pfeerick/backport-2.12.3
branch
from
August 24, 2026 22:26
9e9a999 to
33d7c10
Compare
pfeerick
force-pushed
the
pfeerick/backport-2.12.3
branch
from
August 25, 2026 02:11
33d7c10 to
c1e55b0
Compare
pfeerick
force-pushed
the
pfeerick/backport-2.12.3
branch
from
August 25, 2026 02:40
c1e55b0 to
fc73c78
Compare
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.
Backport series for the 2.12.3 release.
Important
Rebase merge this PR — it is a curated series of
-xcherry-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
mainafter the series was assembled:#7604 was skipped — docs-only, and 2.12 has no
docs/tree. Itsbackport/2.12label can be removed.Plus nine commits that are not backports:
fix(build): use the renamed STICK_DEAD_ZONE option on gx15 and t15pro— see fix(tools): YAML generation leaking build options between targets #7638;maingot the same fix via fix(c14): LCD flush never acknowledged; tidy STICK_DEAD_ZONE on gx15 and t15pro #7688fix(cpn): V12 missing from the STM32H7 board list— the V12 was never added to the hand-maintainedIS_STM32H7(), so Companion under-reported gvars (9 vs 15) and sensors (60 vs 75) and mis-defaultedSportMaxBaudRate.mainis not affected — it reads the MCU from the generatedhw_defs/*.jsonand has no such list to driftchore(ci): update actions to their current major versions— clears the Node.js 20 deprecation warnings; mirrors chore(ci): update actions to their current major versions #7691 onmainfix(color): sign-compare warning— regression in fix(color): subtrim value on Outputs page not updated when copying all trims to sub trims #6998fix(cli): drop dead spModuleInit/DeInit compiled on EXTERNAL_MODULE-only targets— regression in feat: support for RadioMaster GX12 #5703; only pl18ev lacks an internal module (the rest of the pl18 family defines one), so only pl18ev hit the warning. Also unfixed onmainfix(keys): guard key-lock combo state behind KEYS_LOCK_KEY1/2— regression in this branch's own feat(radio): optional key lock feature (2.12 backport) #7561 backport; mirrors the guardmainalready carries via feat(firmware): add a special function to trigger key lock #7565fix(lua): avoid always-true BLING_LED_STRIP_LENGTH comparison on bling-less targets— regression in feat(radio): refactor of switch handling to better support customisable switches #6095, on targets sharing the bling-lessRADIO_GX12hal config (gx12/mt12/tx12mk2/boxer here; just gx12 onmain, where the others have since moved to their own hal configs — also unfixed there). Hardware-tested on gx12 with the communityRGBLEDLua scripts, which sweep every LED index every framefix(pa01): avoid always-true LED_CHARGING_START comparison in ledChargingInfo— regression in feat: Flysky PA01 support #6392. Also unfixed onmain. Hardware-tested on pa01chore: bump version— 2.12.2 → 2.12.3Adaptations
mainhas 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:flushLcd()no longer callslv_disp_flush_ready(), so each driver must calllcdFlushed()itself. Upstream updated every driver that needed it, butboards/helloradio-h750/lcd_driver.cpp(v12) already had the call onmainfrom work that never reached 2.12, so it was not in the diff — andtargets/c14/lcd_driver.cppis missing it onmaintoo. Both are added here. Without them LVGL spins forever inlv_refron the second frame, taking the radio and the bootloader with it.stm32_ws2812.{cpp,h}, this branch's pre-rename name forstm32_rgbleds.{cpp,h}.hal.hmacros rather than the checked-inhw_defs/*.jsonthatmainuses, so the two JSON files were re-expressed as macros intargets/taranis/hal.h. Verified by running the generator and diffing its output against upstream'sv14.json.yaml_datastructs_x10.cpp. Upstream regenerated t15pro and tx15, the descriptors the option leak had corrupted onmain; on 2.12 a different file was hit.ModuleData.antennaModehad been replaced by padding on feat(hrs-v12): Support for HelloRadioSky V12 - v2.12 #7113, so per-module antenna selection was silently neither saved to nor loaded from model files on x10, x10express, tx16s, v16 and x12s. Regenerating all 25 targets leaves the other 24 byte-identical.build_companioncomposite action frommain(added upstream by feat: WASM modules based simulation in Companion #6435, milestone 3.0) and routes all three Companion workflows through it, keeping 2.12's reusable-workflow layout, runners andci: skip-cpn-*guards.opentxsimulator.cppinstead ofwasmsimulatorinterface.cpp.3djc/usb-charge-controlrather than cherry-picked frommain, sinceRadioDatabitfield allocation and the Companion general setup UI differ between the branches. Same 38-file set as themaincommit;usbChargeDisabledtakes a bit fromspare:3here rather thanspare:7as onmain, since 2.12 carries the model/radio tab-control flags andkeyLockEnabledin that position.Deliberate behaviour changes
Folded into #7653, correcting release-branch drift rather than originating upstream:
edgetx-devcontainer pinned:latest→:2.12acrosslinux_cpn.yml,build_fw.yml(×2) andnightly.ymledgetx-cpn-osx-*→edgetx-cpn-macos-*, matchingmain— anything consuming the old name needs updatinglibsdl-org/setup-sdl→setup-sdl.shandilammy/msvc-dev-cmd→setup-msvc.ps1brew install SDL2for source-built SDL3 + sdl2-compat — the point of fix(cpn): tie macOS bundle to its own SDL copies, and verify it #7653Testing
Firmware builds — 16 targets, all green:
The x10 family was built specifically to exercise the regenerated
yaml_datastructs_x10.cpp; c14 and v12 to exercise thelcdFlushed()additions. The v12 bootloader was built and checked separately (55,820) since #7483 changesboot_lcd.cpptoo.For #7564: gx15, tx16smk3 (the
USB_CHARGE_CONTROLtargets) and v12 (a colour target without it, exercising the#elsebranch of the bitfield) all rebuilt clean,libedgetx-tx16smk3-simulator.solinks, andtools/generate-yaml.shreproduces the committedyaml_datastructs_{tx15,tx16smk3,gx15}.cppbyte-for-byte.For #7703: gx15 rebuilt clean after the mic pins were wired to match tx15's (
PDM_CLOCK/PDM_DATAmoved fromGPIODtoGPIOE, matching the actual SAI1-driven PDM capture chain, replacing the two dead placeholder pins gx15'shal.hhad carried since #7642). The conflict inhal.hwas pure formatting drift (2.12 has column-aligned#defines;main's copy still has the original tabs) — content matchesmainexactly 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, onetimersGetMsTick), 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,gx12andpa01— the specific targets each warning affects — were rebuilt clean from scratch after these commits, zero warnings, zero errors,firmware.binproduced on all four.fix(lua)andfix(pa01)have additionally been hardware-tested, on gx12 and pa01 respectively.Still outstanding
ghcr.io/edgetx/edgetx-dev:2.12resolves, since four workflows now depend on that tagbackport/2.12label — please confirm it belongsIf the Companion CI rework proves troublesome, reverting the #7653 and #7654 commits restores the current workflows without touching anything else.