7import QGroundControl.Controls
8import QGroundControl.FactControls
12 pageComponent: firmwarePageComponent
13 pageName: qsTr("Firmware")
14 showAdvanced: globals.activeVehicle && globals.activeVehicle.apmFirmware
17 id: firmwarePageComponent
21 height: availableHeight
22 spacing: ScreenTools.defaultFontPixelHeight
24 // Those user visible strings are hard to translate because we can't send the
25 // HTML strings to translation as this can create a security risk. we need to find
26 // a better way to highlight them, or use less highlights.
28 // User visible strings
29 readonly property string title: qsTr("Firmware Setup") // Popup dialog title
30 readonly property string highlightPrefix: "<font color=\"" + qgcPal.warningText + "\">"
31 readonly property string highlightSuffix: "</font>"
32 readonly property string welcomeText: qsTr("%1 can upgrade the firmware on Pixhawk devices and SiK Radios.").arg(QGroundControl.appName)
33 readonly property string welcomeTextSingle: qsTr("Update the autopilot firmware to the latest version")
34 readonly property string plugInText: highlightPrefix + qsTr("Plug in your device") + highlightSuffix + qsTr(" via USB, then select it below and press ") + highlightPrefix + qsTr("Flash") + highlightSuffix + "."
35 readonly property string unplugReplugText: highlightPrefix + qsTr("Now unplug your device and plug it back in to enter bootloader mode.") + highlightSuffix
36 readonly property string flashFailText: qsTr("If upgrade failed, make sure to connect ") + highlightPrefix + qsTr("directly") + highlightSuffix + qsTr(" to a powered USB port on your computer, not through a USB hub. ") +
37 qsTr("Also make sure you are only powered via USB ") + highlightPrefix + qsTr("not battery") + highlightSuffix + "."
39 readonly property int _defaultFimwareTypePX4: 12
40 readonly property int _defaultFimwareTypeAPM: 3
42 readonly property int _boardTypePixhawk: 0
43 readonly property int _boardTypeSiKRadio: 1
45 property var _firmwareUpgradeSettings: QGroundControl.settingsManager.firmwareUpgradeSettings
46 property var _defaultFirmwareFact: _firmwareUpgradeSettings.defaultFirmwareType
47 property bool _defaultFirmwareIsPX4: true
49 property string firmwareWarningMessage
50 property bool firmwareWarningMessageVisible: false
51 property string firmwareName
52 property bool _flashStarted: false ///< true: user has clicked Flash, suppress further preselection
53 property bool _cancellable: true ///< false once erase has started — past the point of clean cancellation
54 property string _selectedSystemLocation
55 property string _selectedDisplayName ///< snapshot of chosen port's label, used while flashing
57 property bool _singleFirmwareMode: QGroundControl.corePlugin.options.firmwareUpgradeSingleURL.length != 0 ///< true: running in special single firmware download mode
59 function setupPageCompleted() {
60 controller.startBoardSearch()
61 _defaultFirmwareIsPX4 = _defaultFirmwareFact.rawValue === _defaultFimwareTypePX4 // we don't want this to be bound and change as radios are selected
65 property int _lastKnownCount: 0
67 function _recognizedBoardCount() {
68 var ports = controller.availablePorts
70 for (var i = 0; i < ports.length; i++) {
71 if (ports[i].boardType === _boardTypePixhawk || ports[i].boardType === _boardTypeSiKRadio) {
78 function _preselectIndex() {
79 var ports = controller.availablePorts
80 if (ports.length === 0) {
83 for (var i = 0; i < ports.length; i++) {
84 if (ports[i].boardType === _boardTypePixhawk) {
88 for (var j = 0; j < ports.length; j++) {
89 if (ports[j].boardType === _boardTypeSiKRadio) {
93 // No recognized device found
97 function _refreshSelection() {
98 if (_flashStarted || portCombo.popup.visible) {
101 var knownCount = _recognizedBoardCount()
102 if (knownCount > 1 && _lastKnownCount <= 1) {
103 statusTextArea.append(highlightPrefix + qsTr("Multiple devices detected. Make sure to select the correct one from the list.") + highlightSuffix)
105 _lastKnownCount = knownCount
106 var ports = controller.availablePorts
107 if (ports.length === 0) {
108 portCombo.currentIndex = -1
109 _selectedSystemLocation = ""
112 if (_selectedSystemLocation !== "") {
113 // Check if the current selection is already a recognized device
114 var currentIsRecognized = false
115 for (var i = 0; i < ports.length; i++) {
116 if (ports[i].systemLocation === _selectedSystemLocation) {
117 if (ports[i].boardType === _boardTypePixhawk || ports[i].boardType === _boardTypeSiKRadio) {
118 currentIsRecognized = true
123 // Only keep the current selection if it is a recognized device;
124 // otherwise fall through to preselect the first recognized device
125 if (currentIsRecognized) {
126 for (var j = 0; j < ports.length; j++) {
127 if (ports[j].systemLocation === _selectedSystemLocation) {
128 portCombo.currentIndex = j
134 portCombo.currentIndex = _preselectIndex()
135 if (portCombo.currentIndex >= 0) {
136 _selectedSystemLocation = ports[portCombo.currentIndex].systemLocation
141 FirmwareUpgradeController {
143 progressBar: progressBar
144 statusLog: statusTextArea
146 property var activeVehicle: QGroundControl.multiVehicleManager.activeVehicle
148 onActiveVehicleChanged: {
149 if (!globals.activeVehicle && !_flashStarted) {
150 statusTextArea.append(plugInText)
154 onAvailablePortsChanged: _refreshSelection()
158 statusTextArea.append(highlightPrefix + qsTr("Device disconnected — waiting for it to reappear in bootloader mode...") + highlightSuffix)
164 statusTextArea.append(highlightPrefix + qsTr("Found device") + highlightSuffix + ": " + controller.boardType + " (" + controller.boardPort + ")")
165 if (QGroundControl.multiVehicleManager.activeVehicle) {
166 QGroundControl.multiVehicleManager.activeVehicle.vehicleLinkManager.autoDisconnect = true
171 onShowFirmwareSelectDlg: firmwareSelectDialogFactory.open()
172 onEraseStarted: _cancellable = false
174 statusTextArea.append(flashFailText)
175 _flashStarted = false
179 _flashStarted = false
184 QGCPopupDialogFactory {
185 id: firmwareSelectDialogFactory
187 dialogComponent: firmwareSelectDialogComponent
191 id: firmwareSelectDialogComponent
194 id: firmwareSelectDialog
195 title: qsTr("Firmware Setup")
196 buttons: Dialog.Ok | Dialog.Cancel
198 property bool showFirmwareTypeSelection: _advanced.checked
201 id: customFirmwareDialog
202 title: qsTr("Select Firmware File")
203 nameFilters: [qsTr("Firmware Files (*.px4 *.apj *.bin *.ihx)"), qsTr("All Files (*)")]
204 folder: QGroundControl.settingsManager.appSettings.logSavePath
205 onAcceptedForLoad: (file) => {
206 controller.flashFirmwareUrl(file)
208 firmwareSelectDialog.close()
212 function firmwareVersionChanged(model) {
213 firmwareWarningMessageVisible = false
214 // All of this bizarre, setting model to null and index to 1 and then to 0 is to work around
215 // strangeness in the combo box implementation. This sequence of steps correctly changes the combo model
216 // without generating any warnings and correctly updates the combo text with the new selection.
217 firmwareBuildTypeCombo.model = null
218 firmwareBuildTypeCombo.model = model
219 firmwareBuildTypeCombo.currentIndex = 1
220 firmwareBuildTypeCombo.currentIndex = 0
223 function updatePX4VersionDisplay() {
224 var versionString = ""
225 if (_advanced.checked) {
226 switch (controller.selectedFirmwareBuildType) {
227 case FirmwareUpgradeController.StableFirmware:
228 versionString = controller.px4StableVersion
230 case FirmwareUpgradeController.BetaFirmware:
231 versionString = controller.px4BetaVersion
235 versionString = controller.px4StableVersion
237 px4FlightStackRadio.text = qsTr("PX4 Pro ") + versionString
238 //px4FlightStackRadio2.text = qsTr("PX4 Pro ") + versionString
241 Component.onCompleted: {
242 firmwarePage.advanced = false
243 firmwarePage.showAdvanced = false
244 updatePX4VersionDisplay()
253 if (_singleFirmwareMode) {
254 if (controller.selectedFirmwareBuildType === FirmwareUpgradeController.CustomFirmware) {
255 // User picked Custom but cancelled the auto-opened picker; reopen it
256 // and keep this dialog open until a file is chosen or Cancel is clicked.
257 customFirmwareDialog.openForLoad()
258 firmwareSelectDialog.preventClose = true
260 controller.flashSingleFirmwareMode(controller.selectedFirmwareBuildType)
263 var firmwareBuildType = firmwareBuildTypeCombo.model.get(firmwareBuildTypeCombo.currentIndex).firmwareType
264 var vehicleType = FirmwareUpgradeController.DefaultVehicleFirmware
266 var stack = apmFlightStack.checked ? FirmwareUpgradeController.AutoPilotStackAPM : FirmwareUpgradeController.AutoPilotStackPX4
267 if (apmFlightStack.checked) {
268 if (firmwareBuildType === FirmwareUpgradeController.CustomFirmware) {
269 vehicleType = apmVehicleTypeCombo.currentIndex
271 if (controller.apmFirmwareNames.length === 0) {
272 // Not ready yet, or no firmware available
273 QGroundControl.showMessageDialog(firmwarePage, firmwareSelectDialog.title, qsTr("Either firmware list is still downloading, or no firmware is available for current selection."))
274 firmwareSelectDialog.preventClose = true
277 if (ardupilotFirmwareSelectionCombo.currentIndex == -1) {
278 QGroundControl.showMessageDialog(firmwarePage, firmwareSelectDialog.title, qsTr("You must choose a board type."))
279 firmwareSelectDialog.preventClose = true
283 var firmwareUrl = controller.apmFirmwareUrls[ardupilotFirmwareSelectionCombo.currentIndex]
284 if (firmwareUrl == "") {
285 QGroundControl.showMessageDialog(firmwarePage, firmwareSelectDialog.title, qsTr("No firmware was found for the current selection."))
286 firmwareSelectDialog.preventClose = true
289 controller.flashFirmwareUrl(controller.apmFirmwareUrls[ardupilotFirmwareSelectionCombo.currentIndex])
293 //-- If custom, get file path
294 if (firmwareBuildType === FirmwareUpgradeController.CustomFirmware) {
295 customFirmwareDialog.openForLoad()
297 controller.flash(stack, firmwareBuildType, vehicleType)
303 statusTextArea.append(highlightPrefix + qsTr("Upgrade cancelled") + highlightSuffix)
309 id: firmwareBuildTypeList
312 text: qsTr("Standard Version (stable)")
313 firmwareType: FirmwareUpgradeController.StableFirmware
316 text: qsTr("Beta Testing (beta)")
317 firmwareType: FirmwareUpgradeController.BetaFirmware
320 text: qsTr("Developer Build (master)")
321 firmwareType: FirmwareUpgradeController.DeveloperFirmware
324 text: qsTr("Custom firmware file...")
325 firmwareType: FirmwareUpgradeController.CustomFirmware
330 id: singleFirmwareModeTypeList
333 text: qsTr("Standard Version")
334 firmwareType: FirmwareUpgradeController.StableFirmware
337 text: qsTr("Custom firmware file...")
338 firmwareType: FirmwareUpgradeController.CustomFirmware
343 width: Math.max(ScreenTools.defaultFontPixelWidth * 40, firmwareRadiosColumn.width)
344 spacing: globals.defaultTextHeight / 2
347 Layout.fillWidth: true
348 wrapMode: Text.WordWrap
349 text: (_singleFirmwareMode || !QGroundControl.apmFirmwareSupported) ? _singleFirmwareLabel : _pixhawkLabel
351 readonly property string _pixhawkLabel: qsTr("Detected Pixhawk board. You can select from the following flight stacks:")
352 readonly property string _singleFirmwareLabel: qsTr("Press Ok to upgrade your vehicle.")
356 id: firmwareRadiosColumn
359 visible: !_singleFirmwareMode && QGroundControl.apmFirmwareSupported
361 Component.onCompleted: {
362 if(!QGroundControl.apmFirmwareSupported) {
363 _defaultFirmwareFact.rawValue = _defaultFimwareTypePX4
364 firmwareVersionChanged(firmwareBuildTypeList)
369 id: px4FlightStackRadio
370 text: qsTr("PX4 Pro ")
371 font.bold: _defaultFirmwareIsPX4
372 checked: _defaultFirmwareIsPX4
375 _defaultFirmwareFact.rawValue = _defaultFimwareTypePX4
376 firmwareVersionChanged(firmwareBuildTypeList)
382 text: qsTr("ArduPilot")
383 font.bold: !_defaultFirmwareIsPX4
384 checked: !_defaultFirmwareIsPX4
387 _defaultFirmwareFact.rawValue = _defaultFimwareTypeAPM
388 firmwareVersionChanged(firmwareBuildTypeList)
394 Layout.fillWidth: true
395 visible: apmFlightStack.checked
396 fact: _firmwareUpgradeSettings.apmChibiOS
401 id: apmVehicleTypeCombo
402 Layout.fillWidth: true
403 visible: apmFlightStack.checked
404 fact: _firmwareUpgradeSettings.apmVehicleType
409 id: ardupilotFirmwareSelectionCombo
410 Layout.fillWidth: true
411 visible: apmFlightStack.checked && !controller.downloadingFirmwareList && controller.apmFirmwareNames.length !== 0
412 model: controller.apmFirmwareNames
413 onModelChanged: currentIndex = controller.apmFirmwareNamesBestIndex
417 Layout.fillWidth: true
418 wrapMode: Text.WordWrap
419 text: qsTr("Downloading list of available firmwares...")
420 visible: controller.downloadingFirmwareList
424 Layout.fillWidth: true
425 wrapMode: Text.WordWrap
426 text: qsTr("No Firmware Available")
427 visible: !controller.downloadingFirmwareList && (QGroundControl.apmFirmwareSupported && controller.apmFirmwareNames.length === 0)
432 text: qsTr("Advanced settings")
436 firmwareBuildTypeCombo.currentIndex = 0
437 firmwareWarningMessageVisible = false
438 updatePX4VersionDisplay()
443 Layout.fillWidth: true
444 wrapMode: Text.WordWrap
445 visible: showFirmwareTypeSelection
446 text: _singleFirmwareMode ? qsTr("Select the standard version or one from the file system (previously downloaded):") :
447 qsTr("Select which version of the above flight stack you would like to install:")
451 id: firmwareBuildTypeCombo
452 Layout.fillWidth: true
453 visible: showFirmwareTypeSelection
455 model: _singleFirmwareMode ? singleFirmwareModeTypeList : firmwareBuildTypeList
457 onActivated: (index) => {
458 var fwType = model.get(index).firmwareType
459 controller.selectedFirmwareBuildType = fwType
460 if (fwType === FirmwareUpgradeController.BetaFirmware) {
461 firmwareWarningMessageVisible = true
462 firmwareVersionWarningLabel.text = qsTr("WARNING: BETA FIRMWARE. ") +
463 qsTr("This firmware version is ONLY intended for beta testers. ") +
464 qsTr("Although it has received FLIGHT TESTING, it represents actively changed code. ") +
465 qsTr("Do NOT use for normal operation.")
466 } else if (fwType === FirmwareUpgradeController.DeveloperFirmware) {
467 firmwareWarningMessageVisible = true
468 firmwareVersionWarningLabel.text = qsTr("WARNING: CONTINUOUS BUILD FIRMWARE. ") +
469 qsTr("This firmware has NOT BEEN FLIGHT TESTED. ") +
470 qsTr("It is only intended for DEVELOPERS. ") +
471 qsTr("Run bench tests without props first. ") +
472 qsTr("Do NOT fly this without additional safety precautions. ") +
473 qsTr("Follow the forums actively when using it.")
475 firmwareWarningMessageVisible = false
477 updatePX4VersionDisplay()
478 if (fwType === FirmwareUpgradeController.CustomFirmware) {
479 customFirmwareDialog.openForLoad()
485 id: firmwareVersionWarningLabel
486 Layout.fillWidth: true
487 wrapMode: Text.WordWrap
488 visible: firmwareWarningMessageVisible
492 } // Component - firmwareSelectDialogComponent
495 spacing: ScreenTools.defaultFontPixelWidth
496 visible: !flashBootloaderButton.visible
501 visible: !_flashStarted
502 enabled: controller.availablePorts.length > 0
503 model: controller.availablePorts
504 textRole: "displayName"
507 if (_lastKnownCount !== 1) {
510 var idx = portCombo.currentIndex
511 var ports = controller.availablePorts
512 if (idx < 0 || idx >= ports.length) {
516 if (p.boardName && p.description) {
517 return p.description + " [" + p.boardName + "]"
519 return p.boardName || p.description || p.displayName
522 onActivated: (index) => {
523 if (index >= 0 && index < controller.availablePorts.length) {
524 _selectedSystemLocation = controller.availablePorts[index].systemLocation
531 visible: _flashStarted
532 text: qsTr("Flashing - %1").arg(_selectedDisplayName)
533 elide: Text.ElideRight
539 visible: !_flashStarted
540 enabled: portCombo.currentIndex >= 0 && controller.availablePorts.length > 0
542 var ports = controller.availablePorts
543 if (portCombo.currentIndex < 0 || portCombo.currentIndex >= ports.length) {
546 var entry = ports[portCombo.currentIndex]
547 _selectedSystemLocation = entry.systemLocation
548 _selectedDisplayName = portCombo.alternateText !== "" ? portCombo.alternateText : entry.displayName
550 statusTextArea.append(unplugReplugText)
551 if (QGroundControl.multiVehicleManager.activeVehicle) {
552 QGroundControl.multiVehicleManager.activeVehicle.vehicleLinkManager.autoDisconnect = true
554 controller.flashPort(entry.systemLocation)
561 visible: _flashStarted
562 enabled: _cancellable
565 _flashStarted = false
567 _selectedDisplayName = ""
568 statusTextArea.append(highlightPrefix + qsTr("Cancelled. Select a port and press Flash to try again.") + highlightSuffix)
575 Layout.preferredWidth: parent.width
576 visible: !flashBootloaderButton.visible
580 id: flashBootloaderButton
581 text: qsTr("Flash ChibiOS Bootloader")
582 visible: firmwarePage.advanced
583 onClicked: globals.activeVehicle.flashBootloader()
588 Layout.preferredWidth: parent.width
589 Layout.fillHeight: true
591 font.pointSize: ScreenTools.defaultFontPointSize
592 textFormat: TextEdit.RichText
593 text: _singleFirmwareMode ? welcomeTextSingle : welcomeText
596 background: Rectangle {
597 color: qgcPal.windowShade