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
64 function _preselectIndex() {
65 var ports = controller.availablePorts
66 if (ports.length === 0) {
69 // Prefer a recognized Pixhawk
70 for (var i = 0; i < ports.length; i++) {
71 if (ports[i].boardType === _boardTypePixhawk) {
75 // Else prefer a recognized SiK radio
76 for (var j = 0; j < ports.length; j++) {
77 if (ports[j].boardType === _boardTypeSiKRadio) {
85 function _refreshSelection() {
89 var ports = controller.availablePorts
90 if (ports.length === 0) {
91 portCombo.currentIndex = -1
92 _selectedSystemLocation = ""
95 // Try to keep the current selection if its port is still present
96 if (_selectedSystemLocation !== "") {
97 for (var i = 0; i < ports.length; i++) {
98 if (ports[i].systemLocation === _selectedSystemLocation) {
99 portCombo.currentIndex = i
104 portCombo.currentIndex = _preselectIndex()
105 if (portCombo.currentIndex >= 0) {
106 _selectedSystemLocation = ports[portCombo.currentIndex].systemLocation
111 FirmwareUpgradeController {
113 progressBar: progressBar
114 statusLog: statusTextArea
116 property var activeVehicle: QGroundControl.multiVehicleManager.activeVehicle
118 onActiveVehicleChanged: {
119 if (!globals.activeVehicle && !_flashStarted) {
120 statusTextArea.append(plugInText)
124 onAvailablePortsChanged: _refreshSelection()
128 statusTextArea.append(highlightPrefix + qsTr("Device disconnected — waiting for it to reappear in bootloader mode...") + highlightSuffix)
134 statusTextArea.append(highlightPrefix + qsTr("Found device") + highlightSuffix + ": " + controller.boardType)
135 if (QGroundControl.multiVehicleManager.activeVehicle) {
136 QGroundControl.multiVehicleManager.activeVehicle.vehicleLinkManager.autoDisconnect = true
141 onShowFirmwareSelectDlg: firmwareSelectDialogFactory.open()
142 onEraseStarted: _cancellable = false
144 statusTextArea.append(flashFailText)
145 _flashStarted = false
149 _flashStarted = false
154 QGCPopupDialogFactory {
155 id: firmwareSelectDialogFactory
157 dialogComponent: firmwareSelectDialogComponent
161 id: firmwareSelectDialogComponent
164 id: firmwareSelectDialog
165 title: qsTr("Firmware Setup")
166 buttons: Dialog.Ok | Dialog.Cancel
168 property bool showFirmwareTypeSelection: _advanced.checked
171 id: customFirmwareDialog
172 title: qsTr("Select Firmware File")
173 nameFilters: [qsTr("Firmware Files (*.px4 *.apj *.bin *.ihx)"), qsTr("All Files (*)")]
174 folder: QGroundControl.settingsManager.appSettings.logSavePath
175 onAcceptedForLoad: (file) => {
176 controller.flashFirmwareUrl(file)
178 firmwareSelectDialog.close()
182 function firmwareVersionChanged(model) {
183 firmwareWarningMessageVisible = false
184 // All of this bizarre, setting model to null and index to 1 and then to 0 is to work around
185 // strangeness in the combo box implementation. This sequence of steps correctly changes the combo model
186 // without generating any warnings and correctly updates the combo text with the new selection.
187 firmwareBuildTypeCombo.model = null
188 firmwareBuildTypeCombo.model = model
189 firmwareBuildTypeCombo.currentIndex = 1
190 firmwareBuildTypeCombo.currentIndex = 0
193 function updatePX4VersionDisplay() {
194 var versionString = ""
195 if (_advanced.checked) {
196 switch (controller.selectedFirmwareBuildType) {
197 case FirmwareUpgradeController.StableFirmware:
198 versionString = controller.px4StableVersion
200 case FirmwareUpgradeController.BetaFirmware:
201 versionString = controller.px4BetaVersion
205 versionString = controller.px4StableVersion
207 px4FlightStackRadio.text = qsTr("PX4 Pro ") + versionString
208 //px4FlightStackRadio2.text = qsTr("PX4 Pro ") + versionString
211 Component.onCompleted: {
212 firmwarePage.advanced = false
213 firmwarePage.showAdvanced = false
214 updatePX4VersionDisplay()
223 if (_singleFirmwareMode) {
224 if (controller.selectedFirmwareBuildType === FirmwareUpgradeController.CustomFirmware) {
225 // User picked Custom but cancelled the auto-opened picker; reopen it
226 // and keep this dialog open until a file is chosen or Cancel is clicked.
227 customFirmwareDialog.openForLoad()
228 firmwareSelectDialog.preventClose = true
230 controller.flashSingleFirmwareMode(controller.selectedFirmwareBuildType)
233 var firmwareBuildType = firmwareBuildTypeCombo.model.get(firmwareBuildTypeCombo.currentIndex).firmwareType
234 var vehicleType = FirmwareUpgradeController.DefaultVehicleFirmware
236 var stack = apmFlightStack.checked ? FirmwareUpgradeController.AutoPilotStackAPM : FirmwareUpgradeController.AutoPilotStackPX4
237 if (apmFlightStack.checked) {
238 if (firmwareBuildType === FirmwareUpgradeController.CustomFirmware) {
239 vehicleType = apmVehicleTypeCombo.currentIndex
241 if (controller.apmFirmwareNames.length === 0) {
242 // Not ready yet, or no firmware available
243 QGroundControl.showMessageDialog(firmwarePage, firmwareSelectDialog.title, qsTr("Either firmware list is still downloading, or no firmware is available for current selection."))
244 firmwareSelectDialog.preventClose = true
247 if (ardupilotFirmwareSelectionCombo.currentIndex == -1) {
248 QGroundControl.showMessageDialog(firmwarePage, firmwareSelectDialog.title, qsTr("You must choose a board type."))
249 firmwareSelectDialog.preventClose = true
253 var firmwareUrl = controller.apmFirmwareUrls[ardupilotFirmwareSelectionCombo.currentIndex]
254 if (firmwareUrl == "") {
255 QGroundControl.showMessageDialog(firmwarePage, firmwareSelectDialog.title, qsTr("No firmware was found for the current selection."))
256 firmwareSelectDialog.preventClose = true
259 controller.flashFirmwareUrl(controller.apmFirmwareUrls[ardupilotFirmwareSelectionCombo.currentIndex])
263 //-- If custom, get file path
264 if (firmwareBuildType === FirmwareUpgradeController.CustomFirmware) {
265 customFirmwareDialog.openForLoad()
267 controller.flash(stack, firmwareBuildType, vehicleType)
273 statusTextArea.append(highlightPrefix + qsTr("Upgrade cancelled") + highlightSuffix)
279 id: firmwareBuildTypeList
282 text: qsTr("Standard Version (stable)")
283 firmwareType: FirmwareUpgradeController.StableFirmware
286 text: qsTr("Beta Testing (beta)")
287 firmwareType: FirmwareUpgradeController.BetaFirmware
290 text: qsTr("Developer Build (master)")
291 firmwareType: FirmwareUpgradeController.DeveloperFirmware
294 text: qsTr("Custom firmware file...")
295 firmwareType: FirmwareUpgradeController.CustomFirmware
300 id: singleFirmwareModeTypeList
303 text: qsTr("Standard Version")
304 firmwareType: FirmwareUpgradeController.StableFirmware
307 text: qsTr("Custom firmware file...")
308 firmwareType: FirmwareUpgradeController.CustomFirmware
313 width: Math.max(ScreenTools.defaultFontPixelWidth * 40, firmwareRadiosColumn.width)
314 spacing: globals.defaultTextHeight / 2
317 Layout.fillWidth: true
318 wrapMode: Text.WordWrap
319 text: (_singleFirmwareMode || !QGroundControl.apmFirmwareSupported) ? _singleFirmwareLabel : _pixhawkLabel
321 readonly property string _pixhawkLabel: qsTr("Detected Pixhawk board. You can select from the following flight stacks:")
322 readonly property string _singleFirmwareLabel: qsTr("Press Ok to upgrade your vehicle.")
326 id: firmwareRadiosColumn
329 visible: !_singleFirmwareMode && QGroundControl.apmFirmwareSupported
331 Component.onCompleted: {
332 if(!QGroundControl.apmFirmwareSupported) {
333 _defaultFirmwareFact.rawValue = _defaultFimwareTypePX4
334 firmwareVersionChanged(firmwareBuildTypeList)
339 id: px4FlightStackRadio
340 text: qsTr("PX4 Pro ")
341 font.bold: _defaultFirmwareIsPX4
342 checked: _defaultFirmwareIsPX4
345 _defaultFirmwareFact.rawValue = _defaultFimwareTypePX4
346 firmwareVersionChanged(firmwareBuildTypeList)
352 text: qsTr("ArduPilot")
353 font.bold: !_defaultFirmwareIsPX4
354 checked: !_defaultFirmwareIsPX4
357 _defaultFirmwareFact.rawValue = _defaultFimwareTypeAPM
358 firmwareVersionChanged(firmwareBuildTypeList)
364 Layout.fillWidth: true
365 visible: apmFlightStack.checked
366 fact: _firmwareUpgradeSettings.apmChibiOS
371 id: apmVehicleTypeCombo
372 Layout.fillWidth: true
373 visible: apmFlightStack.checked
374 fact: _firmwareUpgradeSettings.apmVehicleType
379 id: ardupilotFirmwareSelectionCombo
380 Layout.fillWidth: true
381 visible: apmFlightStack.checked && !controller.downloadingFirmwareList && controller.apmFirmwareNames.length !== 0
382 model: controller.apmFirmwareNames
383 onModelChanged: currentIndex = controller.apmFirmwareNamesBestIndex
387 Layout.fillWidth: true
388 wrapMode: Text.WordWrap
389 text: qsTr("Downloading list of available firmwares...")
390 visible: controller.downloadingFirmwareList
394 Layout.fillWidth: true
395 wrapMode: Text.WordWrap
396 text: qsTr("No Firmware Available")
397 visible: !controller.downloadingFirmwareList && (QGroundControl.apmFirmwareSupported && controller.apmFirmwareNames.length === 0)
402 text: qsTr("Advanced settings")
406 firmwareBuildTypeCombo.currentIndex = 0
407 firmwareWarningMessageVisible = false
408 updatePX4VersionDisplay()
413 Layout.fillWidth: true
414 wrapMode: Text.WordWrap
415 visible: showFirmwareTypeSelection
416 text: _singleFirmwareMode ? qsTr("Select the standard version or one from the file system (previously downloaded):") :
417 qsTr("Select which version of the above flight stack you would like to install:")
421 id: firmwareBuildTypeCombo
422 Layout.fillWidth: true
423 visible: showFirmwareTypeSelection
425 model: _singleFirmwareMode ? singleFirmwareModeTypeList : firmwareBuildTypeList
427 onActivated: (index) => {
428 var fwType = model.get(index).firmwareType
429 controller.selectedFirmwareBuildType = fwType
430 if (fwType === FirmwareUpgradeController.BetaFirmware) {
431 firmwareWarningMessageVisible = true
432 firmwareVersionWarningLabel.text = qsTr("WARNING: BETA FIRMWARE. ") +
433 qsTr("This firmware version is ONLY intended for beta testers. ") +
434 qsTr("Although it has received FLIGHT TESTING, it represents actively changed code. ") +
435 qsTr("Do NOT use for normal operation.")
436 } else if (fwType === FirmwareUpgradeController.DeveloperFirmware) {
437 firmwareWarningMessageVisible = true
438 firmwareVersionWarningLabel.text = qsTr("WARNING: CONTINUOUS BUILD FIRMWARE. ") +
439 qsTr("This firmware has NOT BEEN FLIGHT TESTED. ") +
440 qsTr("It is only intended for DEVELOPERS. ") +
441 qsTr("Run bench tests without props first. ") +
442 qsTr("Do NOT fly this without additional safety precautions. ") +
443 qsTr("Follow the forums actively when using it.")
445 firmwareWarningMessageVisible = false
447 updatePX4VersionDisplay()
448 if (fwType === FirmwareUpgradeController.CustomFirmware) {
449 customFirmwareDialog.openForLoad()
455 id: firmwareVersionWarningLabel
456 Layout.fillWidth: true
457 wrapMode: Text.WordWrap
458 visible: firmwareWarningMessageVisible
462 } // Component - firmwareSelectDialogComponent
465 Layout.fillWidth: true
466 spacing: ScreenTools.defaultFontPixelWidth
467 visible: !flashBootloaderButton.visible
471 Layout.fillWidth: true
472 visible: !_flashStarted
473 enabled: controller.availablePorts.length > 0
474 model: controller.availablePorts
475 textRole: "displayName"
476 onActivated: (index) => {
477 if (index >= 0 && index < controller.availablePorts.length) {
478 _selectedSystemLocation = controller.availablePorts[index].systemLocation
485 Layout.fillWidth: true
486 visible: _flashStarted
487 text: _selectedDisplayName
488 elide: Text.ElideRight
494 visible: !_flashStarted
495 enabled: portCombo.currentIndex >= 0 && controller.availablePorts.length > 0
497 var ports = controller.availablePorts
498 if (portCombo.currentIndex < 0 || portCombo.currentIndex >= ports.length) {
501 var entry = ports[portCombo.currentIndex]
502 _selectedSystemLocation = entry.systemLocation
503 _selectedDisplayName = entry.displayName
505 statusTextArea.append(unplugReplugText)
506 if (QGroundControl.multiVehicleManager.activeVehicle) {
507 QGroundControl.multiVehicleManager.activeVehicle.vehicleLinkManager.autoDisconnect = true
509 controller.flashPort(entry.systemLocation)
516 visible: _flashStarted
517 enabled: _cancellable
520 _flashStarted = false
522 _selectedDisplayName = ""
523 statusTextArea.append(highlightPrefix + qsTr("Cancelled. Select a port and press Flash to try again.") + highlightSuffix)
530 Layout.preferredWidth: parent.width
531 visible: !flashBootloaderButton.visible
535 id: flashBootloaderButton
536 text: qsTr("Flash ChibiOS Bootloader")
537 visible: firmwarePage.advanced
538 onClicked: globals.activeVehicle.flashBootloader()
543 Layout.preferredWidth: parent.width
544 Layout.fillHeight: true
546 font.pointSize: ScreenTools.defaultFontPointSize
547 textFormat: TextEdit.RichText
548 text: _singleFirmwareMode ? welcomeTextSingle : welcomeText
551 background: Rectangle {
552 color: qgcPal.windowShade