QGroundControl
Ground Control Station for MAVLink Drones
Loading...
Searching...
No Matches
PowerComponent.qml
Go to the documentation of this file.
1import QtQuick
2import QtQuick.Controls
3import QtQuick.Dialogs
4import QtQuick.Layouts
5
6import QGroundControl
7import QGroundControl.FactControls
8import QGroundControl.Controls
9import QGroundControl.AutoPilotPlugins.PX4
10
11// Note: Only the _SOURCE parameter can be assumed to be always available. The remainder of the parameters
12// may or may not be available depending on the _SOURCE setting.
13SetupPage {
14 id: powerPage
15 pageComponent: pageComponent
16
17 Component {
18 id: pageComponent
19
20 Item {
21 width: Math.max(availableWidth, innerColumn.width)
22 height: innerColumn.height
23
24 readonly property string _highlightPrefix: "<font color=\"" + qgcPal.warningText + "\">"
25 readonly property string _highlightSuffix: "</font>"
26
27 property int _textEditWidth: ScreenTools.defaultFontPixelWidth * 8
28 property Fact _uavcanEnable: controller.getParameterFact(-1, "UAVCAN_ENABLE", false)
29 property int _indexedBatteryParamCount: getIndexedBatteryParamCount()
30
31 function getIndexedBatteryParamCount() {
32 var batteryIndex = 1
33 do {
34 if (!controller.parameterExists(-1, "BAT#_SOURCE".replace("#", batteryIndex))) {
35 return batteryIndex - 1
36 }
37 batteryIndex++
38 } while (true)
39 }
40
41 PowerComponentController {
42 id: controller
43 onOldFirmware: QGroundControl.showMessageDialog(powerPage, qsTr("ESC Calibration"), qsTr("%1 cannot perform ESC Calibration with this version of firmware. You will need to upgrade to a newer firmware.").arg(QGroundControl.appName))
44 onNewerFirmware: QGroundControl.showMessageDialog(powerPage, qsTr("ESC Calibration"), qsTr("%1 cannot perform ESC Calibration with this version of firmware. You will need to upgrade %1.").arg(QGroundControl.appName))
45 onDisconnectBattery: QGroundControl.showMessageDialog(powerPage, qsTr("ESC Calibration failed"), qsTr("You must disconnect the battery prior to performing ESC Calibration. Disconnect your battery and try again."))
46 onConnectBattery: escCalibrationDlgFactory.open()
47 }
48
49 ColumnLayout {
50 id: innerColumn
51 anchors.horizontalCenter: parent.horizontalCenter
52 spacing: ScreenTools.defaultFontPixelHeight
53
54 function drawArrowhead(ctx, x, y, radians)
55 {
56 ctx.save();
57 ctx.beginPath();
58 ctx.translate(x,y);
59 ctx.rotate(radians);
60 ctx.moveTo(0,0);
61 ctx.lineTo(5,10);
62 ctx.lineTo(-5,10);
63 ctx.closePath();
64 ctx.restore();
65 ctx.fill();
66 }
67
68 function drawLineWithArrow(ctx, x1, y1, x2, y2)
69 {
70 ctx.beginPath();
71 ctx.moveTo(x1, y1);
72 ctx.lineTo(x2, y2);
73 ctx.stroke();
74 var rd = Math.atan((y2 - y1) / (x2 - x1));
75 rd += ((x2 > x1) ? 90 : -90) * Math.PI/180;
76 drawArrowhead(ctx, x2, y2, rd);
77 }
78
79 Repeater {
80 id: batterySetupRepeater
81 model: _indexedBatteryParamCount
82
83 Loader {
84 sourceComponent: batterySetupComponent
85
86 property int batteryIndex: index + 1
87 property bool showBatteryIndex: batterySetupRepeater.count > 1
88 }
89 }
90
91
92 QGCGroupBox {
93 Layout.fillWidth: true
94 title: qsTr("ESC PWM Minimum and Maximum Calibration")
95
96 ColumnLayout {
97 anchors.left: parent.left
98 anchors.right: parent.right
99 spacing: ScreenTools.defaultFontPixelWidth
100
101 QGCLabel {
102 color: qgcPal.warningText
103 wrapMode: Text.WordWrap
104 text: qsTr("WARNING: Propellers must be removed from vehicle prior to performing ESC calibration.")
105 Layout.fillWidth: true
106 }
107
108 QGCLabel {
109 text: qsTr("You must use USB connection for this operation.")
110 }
111
112 QGCButton {
113 text: qsTr("Calibrate")
114 width: ScreenTools.defaultFontPixelWidth * 20
115 onClicked: controller.calibrateEsc()
116 }
117 }
118 }
119
120 QGCCheckBox {
121 id: showUAVCAN
122 text: qsTr("Show UAVCAN Settings")
123 checked: _uavcanEnable ? _uavcanEnable.rawValue !== 0 : false
124 }
125
126 QGCGroupBox {
127 Layout.fillWidth: true
128 title: qsTr("UAVCAN Bus Configuration")
129 visible: showUAVCAN.checked
130
131 Row {
132 id: uavCanConfigRow
133 spacing: ScreenTools.defaultFontPixelWidth
134
135 FactComboBox {
136 id: _uavcanEnabledCheckBox
137 width: ScreenTools.defaultFontPixelWidth * 20
138 fact: _uavcanEnable
139 indexModel: false
140 }
141
142 QGCLabel {
143 anchors.verticalCenter: parent.verticalCenter
144 text: qsTr("Change required restart")
145 }
146 }
147 }
148
149 QGCGroupBox {
150 Layout.fillWidth: true
151 title: qsTr("UAVCAN Motor Index and Direction Assignment")
152 visible: showUAVCAN.checked
153
154 ColumnLayout {
155 anchors.left: parent.left
156 anchors.right: parent.right
157 spacing: ScreenTools.defaultFontPixelWidth
158
159 QGCLabel {
160 wrapMode: Text.WordWrap
161 color: qgcPal.warningText
162 text: qsTr("WARNING: Propellers must be removed from vehicle prior to performing UAVCAN ESC configuration.")
163 Layout.fillWidth: true
164 }
165
166 QGCLabel {
167 wrapMode: Text.WordWrap
168 text: qsTr("ESC parameters will only be accessible in the editor after assignment.")
169 Layout.fillWidth: true
170 }
171
172 QGCLabel {
173 wrapMode: Text.WordWrap
174 text: qsTr("Start the process, then turn each motor into its turn direction, in the order of their motor indices.")
175 Layout.fillWidth: true
176 }
177
178 QGCButton {
179 text: qsTr("Start Assignment")
180 width: ScreenTools.defaultFontPixelWidth * 20
181 onClicked: controller.startBusConfigureActuators()
182 }
183
184 QGCButton {
185 text: qsTr("Stop Assignment")
186 width: ScreenTools.defaultFontPixelWidth * 20
187 onClicked: controller.stopBusConfigureActuators()
188 }
189 }
190 }
191
192 } // Column
193
194 Component {
195 id: batterySetupComponent
196
197 QGCGroupBox {
198 Layout.fillWidth: true
199 title: qsTr("Battery ") + (showBatteryIndex ? batteryIndex : "")
200
201 property var _controller: controller
202 property int _batteryIndex: batteryIndex
203
204 BatteryParams {
205 id: batParams
206 controller: _controller
207 batteryIndex: _batteryIndex
208 }
209
210 property bool battNumCellsAvailable: batParams.battNumCellsAvailable
211 property bool battHighVoltAvailable: batParams.battHighVoltAvailable
212 property bool battLowVoltAvailable: batParams.battLowVoltAvailable
213 property bool battVoltLoadDropAvailable: batParams.battVoltLoadDropAvailable
214 property bool battVoltageDividerAvailable: batParams.battVoltageDividerAvailable
215 property bool battAmpsPerVoltAvailable: batParams.battAmpsPerVoltAvailable
216
217 property Fact battSource: batParams.battSource
218 property Fact battNumCells: batParams.battNumCells
219 property Fact battHighVolt: batParams.battHighVolt
220 property Fact battLowVolt: batParams.battLowVolt
221 property Fact battVoltLoadDrop: batParams.battVoltLoadDrop
222 property Fact battVoltageDivider: batParams.battVoltageDivider
223 property Fact battAmpsPerVolt: batParams.battAmpsPerVolt
224
225 function getBatteryImage() {
226 switch(battNumCells.value) {
227 case 1: return "/qmlimages/PowerComponentBattery_01cell.svg";
228 case 2: return "/qmlimages/PowerComponentBattery_02cell.svg"
229 case 3: return "/qmlimages/PowerComponentBattery_03cell.svg"
230 case 4: return "/qmlimages/PowerComponentBattery_04cell.svg"
231 case 5: return "/qmlimages/PowerComponentBattery_05cell.svg"
232 case 6: return "/qmlimages/PowerComponentBattery_06cell.svg"
233 default: return "/qmlimages/PowerComponentBattery_01cell.svg";
234 }
235 }
236
237 ColumnLayout {
238
239 RowLayout {
240 spacing: ScreenTools.defaultFontPixelWidth
241 visible: battSource.rawValue == -1
242
243 QGCLabel { text: qsTr("Source") }
244 FactComboBox {
245 width: _textEditWidth
246 fact: battSource
247 indexModel: false
248 sizeToContents: true
249 }
250 }
251
252 GridLayout {
253 id: batteryGrid
254 columns: 5
255 columnSpacing: ScreenTools.defaultFontPixelWidth
256 visible: battSource.rawValue != -1
257
258 QGCLabel { text: qsTr("Source") }
259 FactComboBox {
260 width: _textEditWidth
261 fact: battSource
262 indexModel: false
263 sizeToContents: true
264 }
265
266 QGCColoredImage {
267 id: battImage
268 Layout.rowSpan: 4
269 width: height * 0.75
270 height: 100
271 sourceSize.height: height
272 fillMode: Image.PreserveAspectFit
273 smooth: true
274 color: qgcPal.text
275 cache: false
276 source: getBatteryImage(batteryIndex)
277 visible: battNumCellsAvailable && battLowVoltAvailable && battHighVoltAvailable
278 }
279
280 Item {
281 width: 1
282 height: 1
283 Layout.columnSpan: battImage.visible ? 2 : 3
284 }
285
286 QGCLabel {
287 text: qsTr("Number of Cells (in Series)")
288 visible: battNumCellsAvailable
289 }
290 FactTextField {
291 width: _textEditWidth
292 fact: battNumCells
293 showUnits: true
294 visible: battNumCellsAvailable
295 }
296 QGCLabel {
297 text: qsTr("Battery Max:")
298 visible: battImage.visible
299 }
300 QGCLabel {
301 text: visible ? (battNumCells.value * battHighVolt.value).toFixed(1) + ' V' : ""
302 visible: battImage.visible
303 }
304 Item {
305 width: 1
306 height: 1
307 Layout.columnSpan: 3
308 visible: !battImage.visible
309 }
310
311 QGCLabel {
312 text: qsTr("Empty Voltage (per cell)")
313 visible: battLowVoltAvailable
314 }
315 FactTextField {
316 width: _textEditWidth
317 fact: battLowVolt
318 showUnits: true
319 visible: battLowVoltAvailable
320 }
321 QGCLabel {
322 text: qsTr("Battery Min:")
323 visible: battImage.visible
324 }
325 QGCLabel {
326 text: visible ? (battNumCells.value * battLowVolt.value).toFixed(1) + ' V' : ""
327 visible: battImage.visible
328 }
329 Item {
330 width: 1
331 height: 1
332 Layout.columnSpan: 3
333 visible: battLowVoltAvailable && !battImage.visible
334 }
335
336 QGCLabel {
337 text: qsTr("Full Voltage (per cell)")
338 visible: battHighVoltAvailable
339 }
340 FactTextField {
341 width: _textEditWidth
342 fact: battHighVolt
343 showUnits: true
344 visible: battHighVoltAvailable
345 }
346 Item {
347 width: 1
348 height: 1
349 Layout.columnSpan: battImage.visible ? 2 : 3
350 visible: battHighVoltAvailable
351 }
352
353 QGCLabel {
354 text: qsTr("Voltage divider")
355 visible: battVoltageDividerAvailable
356 }
357 FactTextField {
358 fact: battVoltageDivider
359 visible: battVoltageDividerAvailable
360 }
361 QGCButton {
362 text: qsTr("Calculate")
363 visible: battVoltageDividerAvailable
364 onClicked: calcVoltageDividerDlgFactory.open({ batteryIndex: _batteryIndex })
365 }
366 Item { width: 1; height: 1; Layout.columnSpan: 2; visible: battVoltageDividerAvailable }
367
368 QGCLabel {
369 Layout.columnSpan: batteryGrid.columns
370 Layout.fillWidth: true
371 font.pointSize: ScreenTools.smallFontPointSize
372 wrapMode: Text.WordWrap
373 text: qsTr("If the battery voltage reported by the vehicle is largely different than the voltage read externally using a voltmeter you can adjust the voltage multiplier value to correct this. ") +
374 qsTr("Click the Calculate button for help with calculating a new value.")
375 visible: battVoltageDividerAvailable
376 }
377 QGCLabel {
378 text: qsTr("Amps per volt")
379 visible: battAmpsPerVoltAvailable
380 }
381 FactTextField {
382 fact: battAmpsPerVolt
383 visible: battAmpsPerVoltAvailable
384 }
385 QGCButton {
386 text: qsTr("Calculate")
387 visible: battAmpsPerVoltAvailable
388 onClicked: calcAmpsPerVoltDlgFactory.open({ batteryIndex: _batteryIndex })
389 }
390 Item { width: 1; height: 1; Layout.columnSpan: 2; visible: battAmpsPerVoltAvailable }
391
392 QGCLabel {
393 Layout.columnSpan: batteryGrid.columns
394 Layout.fillWidth: true
395 font.pointSize: ScreenTools.smallFontPointSize
396 wrapMode: Text.WordWrap
397 text: qsTr("If the current draw reported by the vehicle is largely different than the current read externally using a current meter you can adjust the amps per volt value to correct this. ") +
398 qsTr("Click the Calculate button for help with calculating a new value.")
399 visible: battAmpsPerVoltAvailable
400 }
401
402 QGCCheckBox {
403 id: showAdvanced
404 Layout.columnSpan: batteryGrid.columns
405 text: qsTr("Show Advanced Settings")
406 visible: battVoltLoadDropAvailable
407 }
408
409 QGCLabel {
410 text: qsTr("Voltage Drop on Full Load (per cell)")
411 visible: showAdvanced.checked
412 }
413 FactTextField {
414 id: battDropField
415 fact: battVoltLoadDrop
416 showUnits: true
417 visible: showAdvanced.checked
418 }
419 Item { width: 1; height: 1; Layout.columnSpan: 3; visible: showAdvanced.checked }
420
421 QGCLabel {
422 Layout.columnSpan: batteryGrid.columns
423 Layout.fillWidth: true
424 wrapMode: Text.WordWrap
425 font.pointSize: ScreenTools.smallFontPointSize
426 text: qsTr("Batteries show less voltage at high throttle. Enter the difference in Volts between idle throttle and full ") +
427 qsTr("throttle, divided by the number of battery cells. Leave at the default if unsure. ") +
428 _highlightPrefix + qsTr("If this value is set too high, the battery might be deep discharged and damaged.") + _highlightSuffix
429 visible: showAdvanced.checked
430 }
431
432 QGCLabel {
433 text: qsTr("Compensated Minimum Voltage:")
434 visible: showAdvanced.checked
435 }
436 QGCLabel {
437 text: visible ? ((battNumCells.value * battLowVolt.value) - (battNumCells.value * battVoltLoadDrop.value)).toFixed(1) + qsTr(" V") : ""
438 visible: showAdvanced.checked
439 }
440 Item { width: 1; height: 1; Layout.columnSpan: 3; visible: showAdvanced.checked }
441 } // Grid
442 }
443 } // QGCGroupBox - Battery settings
444 } // Component - batterySetupComponent
445
446 QGCPopupDialogFactory {
447 id: calcVoltageDividerDlgFactory
448
449 dialogComponent: calcVoltageDividerDlgComponent
450 }
451
452 Component {
453 id: calcVoltageDividerDlgComponent
454
455 QGCPopupDialog {
456 title: qsTr("Calculate Voltage Divider")
457 buttons: Dialog.Close
458
459 property alias batteryIndex: batParams.batteryIndex
460
461 property var _controller: controller
462 property FactGroup _batteryFactGroup: controller.vehicle.getFactGroup("battery" + (batteryIndex - 1))
463
464 BatteryParams {
465 id: batParams
466 controller: _controller
467 }
468
469 ColumnLayout {
470 spacing: ScreenTools.defaultFontPixelHeight
471
472 QGCLabel {
473 Layout.preferredWidth: gridLayout.width
474 wrapMode: Text.WordWrap
475 text: qsTr("Measure battery voltage using an external voltmeter and enter the value below. Click Calculate to set the new voltage multiplier.")
476 }
477
478 GridLayout {
479 id: gridLayout
480 columns: 2
481
482 QGCLabel { text: qsTr("Measured voltage:") }
483 QGCTextField { id: measuredVoltage; numericValuesOnly: true }
484
485 QGCLabel { text: qsTr("Vehicle voltage:") }
486 QGCLabel { text: _batteryFactGroup.voltage.valueString }
487
488 QGCLabel { text: qsTr("Voltage divider:") }
489 FactLabel { fact: batParams.battVoltageDivider }
490 }
491
492 QGCButton {
493 text: qsTr("Calculate")
494
495 onClicked: {
496 var measuredVoltageValue = parseFloat(measuredVoltage.text)
497 if (measuredVoltageValue === 0 || isNaN(measuredVoltageValue)) {
498 return
499 }
500 var newVoltageDivider = (measuredVoltageValue * batParams.battVoltageDivider.value) / _batteryFactGroup.voltage.value
501 if (newVoltageDivider > 0) {
502 batParams.battVoltageDivider.value = newVoltageDivider
503 }
504 }
505 }
506 }
507 }
508 }
509
510 QGCPopupDialogFactory {
511 id: calcAmpsPerVoltDlgFactory
512
513 dialogComponent: calcAmpsPerVoltDlgComponent
514 }
515
516 Component {
517 id: calcAmpsPerVoltDlgComponent
518
519 QGCPopupDialog {
520 title: qsTr("Calculate Amps per Volt")
521 buttons: Dialog.Close
522
523 property alias batteryIndex: batParams.batteryIndex
524
525 property var _controller: controller
526 property FactGroup _batteryFactGroup: controller.vehicle.getFactGroup("battery" + (batteryIndex - 1))
527
528 BatteryParams {
529 id: batParams
530 controller: _controller
531 }
532
533 ColumnLayout {
534 spacing: ScreenTools.defaultFontPixelHeight
535
536 QGCLabel {
537 Layout.preferredWidth: gridLayout.width
538 wrapMode: Text.WordWrap
539 text: qsTr("Measure current draw using an external current meter and enter the value below. Click Calculate to set the new amps per volt value.")
540 }
541
542 GridLayout {
543 id: gridLayout
544 columns: 2
545
546 QGCLabel { text: qsTr("Measured current:") }
547 QGCTextField { id: measuredCurrent; numericValuesOnly: true }
548
549 QGCLabel { text: qsTr("Vehicle current:") }
550 QGCLabel { text: _batteryFactGroup.current.valueString }
551
552 QGCLabel { text: qsTr("Amps per volt:") }
553 FactLabel { fact: batParams.battAmpsPerVolt }
554 }
555
556 QGCButton {
557 text: qsTr("Calculate")
558
559 onClicked: {
560 var measuredCurrentValue = parseFloat(measuredCurrent.text)
561 if (measuredCurrentValue === 0 || isNaN(measuredCurrentValue)) {
562 return
563 }
564 var newAmpsPerVolt = (measuredCurrentValue * batParams.battAmpsPerVolt.value) / _batteryFactGroup.current.value
565 if (newAmpsPerVolt != 0) {
566 batParams.battAmpsPerVolt.value = newAmpsPerVolt
567 }
568 }
569 }
570 }
571 }
572 }
573
574 QGCPopupDialogFactory {
575 id: escCalibrationDlgFactory
576
577 dialogComponent: escCalibrationDlgComponent
578 }
579
580 Component {
581 id: escCalibrationDlgComponent
582
583 QGCPopupDialog {
584 id: escCalibrationDlg
585 title: qsTr("ESC Calibration")
586 buttons: Dialog.Ok
587 acceptButtonEnabled: false
588
589 Connections {
590 target: controller
591
592 onBatteryConnected: textLabel.text = qsTr("Performing calibration. This will take a few seconds..")
593 onCalibrationFailed: { escCalibrationDlg.acceptButtonEnabled = true; textLabel.text = _highlightPrefix + qsTr("ESC Calibration failed. ") + _highlightSuffix + errorMessage }
594 onCalibrationSuccess: { escCalibrationDlg.acceptButtonEnabled = true; textLabel.text = qsTr("Calibration complete. You can disconnect your battery now if you like.") }
595 }
596
597 ColumnLayout {
598 QGCLabel {
599 id: textLabel
600 wrapMode: Text.WordWrap
601 text: _highlightPrefix + qsTr("WARNING: Props must be removed from vehicle prior to performing ESC calibration.") + _highlightSuffix + qsTr(" Connect the battery now and calibration will begin.")
602 Layout.fillWidth: true
603 Layout.maximumWidth: mainWindow.width / 2
604 }
605 }
606 }
607 }
608 } // Item
609 } // Component
610} // SetupPage