Add USB hot-plug AutoFlasher with bootloader interception + portable mode#3720
Open
slinax wants to merge 1 commit into
Open
Add USB hot-plug AutoFlasher with bootloader interception + portable mode#3720slinax wants to merge 1 commit into
slinax wants to merge 1 commit into
Conversation
Adds a QGroundControl-style firmware flasher that intercepts the STM32
bootloader on USB plug instead of requiring a pre-selected COM port.
The user picks the firmware first; on plug, a WM_DEVICECHANGE listener
(plus a 250ms WMI safety poll) triggers a race to open the virtual COM
port and run px4uploader.Uploader.identify() before user firmware boots.
Also forces portable mode at startup so this build cannot touch the
regular Mission Planner install (Documents\Mission Planner, AppData,
ProgramData) — all writes go to <exe>\PortableData.
New files (auto-included by the SDK-style csproj):
GCSViews/ConfigurationView/AutoFlasher/BootloaderTargets.cs
GCSViews/ConfigurationView/AutoFlasher/UsbPlugListener.cs
GCSViews/ConfigurationView/AutoFlasher/AutoFlasherService.cs
GCSViews/ConfigurationView/AutoFlasher/AutoFlasherForm.cs
Modified:
Program.cs - Settings.CustomUserDataDirectory + APPDATA
override at the very top of Start()
ConfigFirmware.cs - LinkLabel "Auto-Flash (USB hot-plug)" added
via constructor (no Designer/.resx churn)
Defensive coding choices:
- FirstOrDefault everywhere on VID/PID matching (avoids the recurring
"Sequence contains no elements" exception).
- TryIdentify swallows UnauthorizedAccessException/IOException and
retries other ports during the 8s bootloader window.
- WMI scans run on Task.Run, never on the message pump.
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.
Summary
Adds a QGroundControl-style firmware flasher that intercepts the STM32 bootloader on USB plug instead of requiring the user to pre-select a COM port. The user picks the firmware first; on plug, a
WM_DEVICECHANGElistener (plus a 250 ms WMI safety poll) races to open the virtual COM port and runpx4uploader.Uploader.identify()before the user firmware boots and grabs the link.The motivating use case is the recurring "comms timeout" failure mode on Cube Black / Pixhawk 2.1 where the existing flow can't catch the bootloader window reliably.
Also adds a portable mode that redirects all user-writable state to
<exe>\PortableData\so this build can coexist with a regular Mission Planner install without pollutingDocuments\Mission Planner,%APPDATA%, or%PROGRAMDATA%.What changed
New files (auto-included by the SDK-style csproj, no .csproj edit needed):
GCSViews/ConfigurationView/AutoFlasher/BootloaderTargets.cs— VID/PID table (3DR PX4, Cube Black/Orange/Orange+, ArduPilot generic, STM32 DFU)GCSViews/ConfigurationView/AutoFlasher/UsbPlugListener.cs— nativeWM_DEVICECHANGEvia hiddenNativeWindow+ 250 ms WMI safety poll, callbacks dispatched on the capturedSynchronizationContextGCSViews/ConfigurationView/AutoFlasher/AutoFlasherService.cs— orchestrator: on plug → 8-second bootloader window with port race +Uploader.identify()+Uploader.upload(fw)GCSViews/ConfigurationView/AutoFlasher/AutoFlasherForm.cs— code-only WinForms UI (no Designer.cs / .resx churn)Modified:
Program.cs— setsSettings.CustomUserDataDirectoryand overridesAPPDATA/LOCALAPPDATAat the very top ofStart(), before anySettings.*callGCSViews/ConfigurationView/ConfigFirmware.cs— adds anAuto-Flash (USB hot-plug)LinkLabelvia the constructorWhy
The current
Firmware.UploadPX4path depends onAttemptRebootToBootloader()succeeding, which fails when MAVLink is already broken. By inverting the flow (firmware first, then watch for the plug event), we can catch the bootloader during the natural fresh-boot window after a physical replug — the same approach QGC uses.Reviewer notes (please read)
MSBuild MissionPlanner.csproj /p:Configuration=Release, but I do not have a Pixhawk on hand to validate the timing of the bootloader race. Consider this an RFC/draft until someone with hardware can verify the 8-second window is enough for Cube Black specifically.MissionPlanner.slnbuild still depends on themonosubmodule; this PR builds the mainMissionPlanner.csprojdirectly, which is sufficient for the produced exe.LinkLabelis added at(8, 8)in code; on some screen sizes it may overlap an existingImageLabel. Trivial to reposition once the layout is reviewed.Sequence contains no elementsissues:FirstOrDefaulton every VID/PID match,TryIdentifyswallowsUnauthorizedAccessException/IOExceptionand retries other ports, WMI scans run onTask.Run(never on the message pump).portable.txtin startup dir) before merging.Test plan
MSBuild MissionPlanner.csproj /p:Configuration=Release(verified locally, exit 0)PortableData\Mission Planner\is created next to the exeAuto-Flash (USB hot-plug)link is visible.apj, click the link, replug a Cube Black, verify bootloader sync + flash within 8 s🤖 Generated with Claude Code