QGroundControl
Ground Control Station for MAVLink Drones
Loading...
Searching...
No Matches
FirmwareUpgrade.qml
Go to the documentation of this file.
1import QtQuick
2import QtQuick.Controls
3import QtQuick.Dialogs
4import QtQuick.Layouts
5
6import QGroundControl
7import QGroundControl.Controls
8import QGroundControl.FactControls
9
10SetupPage {
11 id: firmwarePage
12 pageComponent: firmwarePageComponent
13 pageName: qsTr("Firmware")
14 showAdvanced: globals.activeVehicle && globals.activeVehicle.apmFirmware
15
16 Component {
17 id: firmwarePageComponent
18
19 ColumnLayout {
20 width: availableWidth
21 height: availableHeight
22 spacing: ScreenTools.defaultFontPixelHeight
23
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.
27
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 + "."
38
39 readonly property int _defaultFimwareTypePX4: 12
40 readonly property int _defaultFimwareTypeAPM: 3
41
42 readonly property int _boardTypePixhawk: 0
43 readonly property int _boardTypeSiKRadio: 1
44
45 property var _firmwareUpgradeSettings: QGroundControl.settingsManager.firmwareUpgradeSettings
46 property var _defaultFirmwareFact: _firmwareUpgradeSettings.defaultFirmwareType
47 property bool _defaultFirmwareIsPX4: true
48
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
56
57 property bool _singleFirmwareMode: QGroundControl.corePlugin.options.firmwareUpgradeSingleURL.length != 0 ///< true: running in special single firmware download mode
58
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
62 }
63
64 function _preselectIndex() {
65 var ports = controller.availablePorts
66 if (ports.length === 0) {
67 return -1
68 }
69 // Prefer a recognized Pixhawk
70 for (var i = 0; i < ports.length; i++) {
71 if (ports[i].boardType === _boardTypePixhawk) {
72 return i
73 }
74 }
75 // Else prefer a recognized SiK radio
76 for (var j = 0; j < ports.length; j++) {
77 if (ports[j].boardType === _boardTypeSiKRadio) {
78 return j
79 }
80 }
81 // Else first item
82 return 0
83 }
84
85 function _refreshSelection() {
86 if (_flashStarted) {
87 return
88 }
89 var ports = controller.availablePorts
90 if (ports.length === 0) {
91 portCombo.currentIndex = -1
92 _selectedSystemLocation = ""
93 return
94 }
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
100 return
101 }
102 }
103 }
104 portCombo.currentIndex = _preselectIndex()
105 if (portCombo.currentIndex >= 0) {
106 _selectedSystemLocation = ports[portCombo.currentIndex].systemLocation
107 }
108 }
109
110
111 FirmwareUpgradeController {
112 id: controller
113 progressBar: progressBar
114 statusLog: statusTextArea
115
116 property var activeVehicle: QGroundControl.multiVehicleManager.activeVehicle
117
118 onActiveVehicleChanged: {
119 if (!globals.activeVehicle && !_flashStarted) {
120 statusTextArea.append(plugInText)
121 }
122 }
123
124 onAvailablePortsChanged: _refreshSelection()
125
126 onBoardGone: {
127 if (_flashStarted) {
128 statusTextArea.append(highlightPrefix + qsTr("Device disconnected — waiting for it to reappear in bootloader mode...") + highlightSuffix)
129 }
130 }
131
132 onBoardFound: {
133 if (_flashStarted) {
134 statusTextArea.append(highlightPrefix + qsTr("Found device") + highlightSuffix + ": " + controller.boardType)
135 if (QGroundControl.multiVehicleManager.activeVehicle) {
136 QGroundControl.multiVehicleManager.activeVehicle.vehicleLinkManager.autoDisconnect = true
137 }
138 }
139 }
140
141 onShowFirmwareSelectDlg: firmwareSelectDialogFactory.open()
142 onEraseStarted: _cancellable = false
143 onError: {
144 statusTextArea.append(flashFailText)
145 _flashStarted = false
146 _cancellable = true
147 }
148 onFlashComplete: {
149 _flashStarted = false
150 _cancellable = true
151 }
152 }
153
154 QGCPopupDialogFactory {
155 id: firmwareSelectDialogFactory
156
157 dialogComponent: firmwareSelectDialogComponent
158 }
159
160 Component {
161 id: firmwareSelectDialogComponent
162
163 QGCPopupDialog {
164 id: firmwareSelectDialog
165 title: qsTr("Firmware Setup")
166 buttons: Dialog.Ok | Dialog.Cancel
167
168 property bool showFirmwareTypeSelection: _advanced.checked
169
170 QGCFileDialog {
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)
177 close()
178 firmwareSelectDialog.close()
179 }
180 }
181
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
191 }
192
193 function updatePX4VersionDisplay() {
194 var versionString = ""
195 if (_advanced.checked) {
196 switch (controller.selectedFirmwareBuildType) {
197 case FirmwareUpgradeController.StableFirmware:
198 versionString = controller.px4StableVersion
199 break
200 case FirmwareUpgradeController.BetaFirmware:
201 versionString = controller.px4BetaVersion
202 break
203 }
204 } else {
205 versionString = controller.px4StableVersion
206 }
207 px4FlightStackRadio.text = qsTr("PX4 Pro ") + versionString
208 //px4FlightStackRadio2.text = qsTr("PX4 Pro ") + versionString
209 }
210
211 Component.onCompleted: {
212 firmwarePage.advanced = false
213 firmwarePage.showAdvanced = false
214 updatePX4VersionDisplay()
215 }
216
217 Connections {
218 target: controller
219 onError: reject()
220 }
221
222 onAccepted: {
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
229 } else {
230 controller.flashSingleFirmwareMode(controller.selectedFirmwareBuildType)
231 }
232 } else {
233 var firmwareBuildType = firmwareBuildTypeCombo.model.get(firmwareBuildTypeCombo.currentIndex).firmwareType
234 var vehicleType = FirmwareUpgradeController.DefaultVehicleFirmware
235
236 var stack = apmFlightStack.checked ? FirmwareUpgradeController.AutoPilotStackAPM : FirmwareUpgradeController.AutoPilotStackPX4
237 if (apmFlightStack.checked) {
238 if (firmwareBuildType === FirmwareUpgradeController.CustomFirmware) {
239 vehicleType = apmVehicleTypeCombo.currentIndex
240 } else {
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
245 return
246 }
247 if (ardupilotFirmwareSelectionCombo.currentIndex == -1) {
248 QGroundControl.showMessageDialog(firmwarePage, firmwareSelectDialog.title, qsTr("You must choose a board type."))
249 firmwareSelectDialog.preventClose = true
250 return
251 }
252
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
257 return
258 }
259 controller.flashFirmwareUrl(controller.apmFirmwareUrls[ardupilotFirmwareSelectionCombo.currentIndex])
260 return
261 }
262 }
263 //-- If custom, get file path
264 if (firmwareBuildType === FirmwareUpgradeController.CustomFirmware) {
265 customFirmwareDialog.openForLoad()
266 } else {
267 controller.flash(stack, firmwareBuildType, vehicleType)
268 }
269 }
270 }
271
272 function reject() {
273 statusTextArea.append(highlightPrefix + qsTr("Upgrade cancelled") + highlightSuffix)
274 controller.cancel()
275 close()
276 }
277
278 ListModel {
279 id: firmwareBuildTypeList
280
281 ListElement {
282 text: qsTr("Standard Version (stable)")
283 firmwareType: FirmwareUpgradeController.StableFirmware
284 }
285 ListElement {
286 text: qsTr("Beta Testing (beta)")
287 firmwareType: FirmwareUpgradeController.BetaFirmware
288 }
289 ListElement {
290 text: qsTr("Developer Build (master)")
291 firmwareType: FirmwareUpgradeController.DeveloperFirmware
292 }
293 ListElement {
294 text: qsTr("Custom firmware file...")
295 firmwareType: FirmwareUpgradeController.CustomFirmware
296 }
297 }
298
299 ListModel {
300 id: singleFirmwareModeTypeList
301
302 ListElement {
303 text: qsTr("Standard Version")
304 firmwareType: FirmwareUpgradeController.StableFirmware
305 }
306 ListElement {
307 text: qsTr("Custom firmware file...")
308 firmwareType: FirmwareUpgradeController.CustomFirmware
309 }
310 }
311
312 ColumnLayout {
313 width: Math.max(ScreenTools.defaultFontPixelWidth * 40, firmwareRadiosColumn.width)
314 spacing: globals.defaultTextHeight / 2
315
316 QGCLabel {
317 Layout.fillWidth: true
318 wrapMode: Text.WordWrap
319 text: (_singleFirmwareMode || !QGroundControl.apmFirmwareSupported) ? _singleFirmwareLabel : _pixhawkLabel
320
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.")
323 }
324
325 Column {
326 id: firmwareRadiosColumn
327 spacing: 0
328
329 visible: !_singleFirmwareMode && QGroundControl.apmFirmwareSupported
330
331 Component.onCompleted: {
332 if(!QGroundControl.apmFirmwareSupported) {
333 _defaultFirmwareFact.rawValue = _defaultFimwareTypePX4
334 firmwareVersionChanged(firmwareBuildTypeList)
335 }
336 }
337
338 QGCRadioButton {
339 id: px4FlightStackRadio
340 text: qsTr("PX4 Pro ")
341 font.bold: _defaultFirmwareIsPX4
342 checked: _defaultFirmwareIsPX4
343
344 onClicked: {
345 _defaultFirmwareFact.rawValue = _defaultFimwareTypePX4
346 firmwareVersionChanged(firmwareBuildTypeList)
347 }
348 }
349
350 QGCRadioButton {
351 id: apmFlightStack
352 text: qsTr("ArduPilot")
353 font.bold: !_defaultFirmwareIsPX4
354 checked: !_defaultFirmwareIsPX4
355
356 onClicked: {
357 _defaultFirmwareFact.rawValue = _defaultFimwareTypeAPM
358 firmwareVersionChanged(firmwareBuildTypeList)
359 }
360 }
361 }
362
363 FactComboBox {
364 Layout.fillWidth: true
365 visible: apmFlightStack.checked
366 fact: _firmwareUpgradeSettings.apmChibiOS
367 indexModel: false
368 }
369
370 FactComboBox {
371 id: apmVehicleTypeCombo
372 Layout.fillWidth: true
373 visible: apmFlightStack.checked
374 fact: _firmwareUpgradeSettings.apmVehicleType
375 indexModel: false
376 }
377
378 QGCComboBox {
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
384 }
385
386 QGCLabel {
387 Layout.fillWidth: true
388 wrapMode: Text.WordWrap
389 text: qsTr("Downloading list of available firmwares...")
390 visible: controller.downloadingFirmwareList
391 }
392
393 QGCLabel {
394 Layout.fillWidth: true
395 wrapMode: Text.WordWrap
396 text: qsTr("No Firmware Available")
397 visible: !controller.downloadingFirmwareList && (QGroundControl.apmFirmwareSupported && controller.apmFirmwareNames.length === 0)
398 }
399
400 QGCCheckBox {
401 id: _advanced
402 text: qsTr("Advanced settings")
403 checked: false
404
405 onClicked: {
406 firmwareBuildTypeCombo.currentIndex = 0
407 firmwareWarningMessageVisible = false
408 updatePX4VersionDisplay()
409 }
410 }
411
412 QGCLabel {
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:")
418 }
419
420 QGCComboBox {
421 id: firmwareBuildTypeCombo
422 Layout.fillWidth: true
423 visible: showFirmwareTypeSelection
424 textRole: "text"
425 model: _singleFirmwareMode ? singleFirmwareModeTypeList : firmwareBuildTypeList
426
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.")
444 } else {
445 firmwareWarningMessageVisible = false
446 }
447 updatePX4VersionDisplay()
448 if (fwType === FirmwareUpgradeController.CustomFirmware) {
449 customFirmwareDialog.openForLoad()
450 }
451 }
452 }
453
454 QGCLabel {
455 id: firmwareVersionWarningLabel
456 Layout.fillWidth: true
457 wrapMode: Text.WordWrap
458 visible: firmwareWarningMessageVisible
459 }
460 } // ColumnLayout
461 } // QGCPopupDialog
462 } // Component - firmwareSelectDialogComponent
463
464 RowLayout {
465 Layout.fillWidth: true
466 spacing: ScreenTools.defaultFontPixelWidth
467 visible: !flashBootloaderButton.visible
468
469 QGCComboBox {
470 id: portCombo
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
479 }
480 }
481 }
482
483 QGCLabel {
484 id: portLabel
485 Layout.fillWidth: true
486 visible: _flashStarted
487 text: _selectedDisplayName
488 elide: Text.ElideRight
489 }
490
491 QGCButton {
492 id: flashButton
493 text: qsTr("Flash")
494 visible: !_flashStarted
495 enabled: portCombo.currentIndex >= 0 && controller.availablePorts.length > 0
496 onClicked: {
497 var ports = controller.availablePorts
498 if (portCombo.currentIndex < 0 || portCombo.currentIndex >= ports.length) {
499 return
500 }
501 var entry = ports[portCombo.currentIndex]
502 _selectedSystemLocation = entry.systemLocation
503 _selectedDisplayName = entry.displayName
504 _flashStarted = true
505 statusTextArea.append(unplugReplugText)
506 if (QGroundControl.multiVehicleManager.activeVehicle) {
507 QGroundControl.multiVehicleManager.activeVehicle.vehicleLinkManager.autoDisconnect = true
508 }
509 controller.flashPort(entry.systemLocation)
510 }
511 }
512
513 QGCButton {
514 id: cancelButton
515 text: qsTr("Cancel")
516 visible: _flashStarted
517 enabled: _cancellable
518 onClicked: {
519 controller.cancel()
520 _flashStarted = false
521 _cancellable = true
522 _selectedDisplayName = ""
523 statusTextArea.append(highlightPrefix + qsTr("Cancelled. Select a port and press Flash to try again.") + highlightSuffix)
524 }
525 }
526 }
527
528 ProgressBar {
529 id: progressBar
530 Layout.preferredWidth: parent.width
531 visible: !flashBootloaderButton.visible
532 }
533
534 QGCButton {
535 id: flashBootloaderButton
536 text: qsTr("Flash ChibiOS Bootloader")
537 visible: firmwarePage.advanced
538 onClicked: globals.activeVehicle.flashBootloader()
539 }
540
541 TextArea {
542 id: statusTextArea
543 Layout.preferredWidth: parent.width
544 Layout.fillHeight: true
545 readOnly: true
546 font.pointSize: ScreenTools.defaultFontPointSize
547 textFormat: TextEdit.RichText
548 text: _singleFirmwareMode ? welcomeTextSingle : welcomeText
549 color: qgcPal.text
550
551 background: Rectangle {
552 color: qgcPal.windowShade
553 }
554 }
555
556 } // ColumnLayout
557 } // Component
558} // SetupPage