2 ******************************************************************************
4 * @file configoutputwidget.cpp
5 * @author The LibrePilot Project, http://www.librepilot.org Copyright (C) 2015.
6 * E. Lafargue & The OpenPilot Team, http://www.openpilot.org Copyright (C) 2010.
7 * @addtogroup GCSPlugins GCS Plugins
9 * @addtogroup ConfigPlugin Config Plugin
11 * @brief Servo output configuration panel for the config gadget
12 *****************************************************************************/
14 * This program is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License as published by
16 * the Free Software Foundation; either version 3 of the License, or
17 * (at your option) any later version.
19 * This program is distributed in the hope that it will be useful, but
20 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
21 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
24 * You should have received a copy of the GNU General Public License along
25 * with this program; if not, write to the Free Software Foundation, Inc.,
26 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
29 #include "configoutputwidget.h"
31 #include "ui_output.h"
32 #include "ui_outputchannelform.h"
34 #include "outputchannelform.h"
35 #include "configvehicletypewidget.h"
37 #include "uavsettingsimportexport/uavsettingsimportexportfactory.h"
38 #include <extensionsystem/pluginmanager.h>
39 #include <uavobjecthelper.h>
41 #include "mixersettings.h"
42 #include "actuatorcommand.h"
43 #include "actuatorsettings.h"
44 #include "flightmodesettings.h"
45 #include "flightstatus.h"
46 #include "systemsettings.h"
49 #include <QStringList>
52 #include <QMessageBox>
55 #define DSHOT_MAXOUTPUT_RANGE 2000
56 #define DSHOT_MINTOUTPUT_RANGE 0
57 #define PWMSYNC_MAXOUTPUT_RANGE 1900
58 #define DEFAULT_MAXOUTPUT_RANGE 2000
59 #define DEFAULT_MINOUTPUT_RANGE 900
60 #define DEFAULT_MINOUTPUT_VALUE 1000
61 #define REVMOTOR_NEUTRAL_TARGET_VALUE 1500
62 #define REVMOTOR_NEUTRAL_DIFF_VALUE 150
63 #define MOTOR_NEUTRAL_DIFF_VALUE 200
66 #define SERVO_MAXOUTPUT_RANGE 2500
67 #define SERVO_MINOUTPUT_RANGE 500
68 #define SERVO_MAXOUTPUT_VALUE 2000
69 #define SERVO_MINOUTPUT_VALUE 1000
70 #define SERVO_NEUTRAL_VALUE 1500
72 ConfigOutputWidget::ConfigOutputWidget(QWidget
*parent
) : ConfigTaskWidget(parent
)
74 m_ui
= new Ui_OutputWidget();
77 // must be done before auto binding !
78 setWikiURL("Output+Configuration");
82 m_ui
->boardWarningFrame
->setVisible(false);
83 m_ui
->configWarningFrame
->setVisible(false);
85 ExtensionSystem::PluginManager
*pm
= ExtensionSystem::PluginManager::instance();
86 UAVSettingsImportExportFactory
*importexportplugin
= pm
->getObject
<UAVSettingsImportExportFactory
>();
87 connect(importexportplugin
, SIGNAL(importAboutToBegin()), this, SLOT(stopTests()));
89 connect(m_ui
->channelOutTest
, SIGNAL(clicked(bool)), this, SLOT(runChannelTests(bool)));
91 // Configure the task widget
93 // Track the ActuatorSettings object
94 addUAVObject("ActuatorSettings");
96 // NOTE: we have channel indices from 0 to 9, but the convention for OP is Channel 1 to Channel 10.
97 // Register for ActuatorSettings changes:
98 for (unsigned int i
= 0; i
< ActuatorCommand::CHANNEL_NUMELEM
; i
++) {
99 OutputChannelForm
*form
= new OutputChannelForm(i
, this);
100 form
->moveTo(*(m_ui
->channelLayout
));
102 connect(m_ui
->channelOutTest
, SIGNAL(toggled(bool)), form
, SLOT(enableChannelTest(bool)));
103 connect(form
, SIGNAL(channelChanged(int, int)), this, SLOT(sendChannelTest(int, int)));
105 addWidget(form
->ui
->actuatorMin
);
106 addWidget(form
->ui
->actuatorNeutral
);
107 addWidget(form
->ui
->actuatorMax
);
108 addWidget(form
->ui
->actuatorRev
);
109 addWidget(form
->ui
->actuatorLink
);
112 // Associate the buttons with their UAVO fields
113 addWidget(m_ui
->spinningArmed
);
114 connect(m_ui
->spinningArmed
, SIGNAL(clicked(bool)), this, SLOT(updateSpinStabilizeCheckComboBoxes()));
116 addUAVObject("FlightModeSettings");
117 addWidgetBinding("FlightModeSettings", "AlwaysStabilizeWhenArmedSwitch", m_ui
->alwaysStabilizedSwitch
);
119 connect(FlightStatus::GetInstance(getObjectManager()), SIGNAL(objectUpdated(UAVObject
*)), this, SLOT(updateAlwaysStabilizeStatus()));
121 MixerSettings
*mixer
= MixerSettings::GetInstance(getObjectManager());
123 m_banks
<< OutputBankControls(mixer
, m_ui
->chBank1
, QColor("#C6ECAE"), m_ui
->cb_outputRate1
, m_ui
->cb_outputMode1
);
124 m_banks
<< OutputBankControls(mixer
, m_ui
->chBank2
, QColor("#91E5D3"), m_ui
->cb_outputRate2
, m_ui
->cb_outputMode2
);
125 m_banks
<< OutputBankControls(mixer
, m_ui
->chBank3
, QColor("#FCEC52"), m_ui
->cb_outputRate3
, m_ui
->cb_outputMode3
);
126 m_banks
<< OutputBankControls(mixer
, m_ui
->chBank4
, QColor("#C3A8FF"), m_ui
->cb_outputRate4
, m_ui
->cb_outputMode4
);
127 m_banks
<< OutputBankControls(mixer
, m_ui
->chBank5
, QColor("#F7F7F2"), m_ui
->cb_outputRate5
, m_ui
->cb_outputMode5
);
128 m_banks
<< OutputBankControls(mixer
, m_ui
->chBank6
, QColor("#FF9F51"), m_ui
->cb_outputRate6
, m_ui
->cb_outputMode6
);
131 rates
<< 50 << 60 << 125 << 165 << 270 << 330 << 400 << 490;
133 foreach(OutputBankControls controls
, m_banks
) {
134 addWidget(controls
.rateCombo());
136 controls
.rateCombo()->addItem(tr("-"), QVariant(0));
137 controls
.rateCombo()->model()->setData(controls
.rateCombo()->model()->index(0, 0), QVariant(0), Qt::UserRole
- 1);
138 foreach(int rate
, rates
) {
139 controls
.rateCombo()->addItem(tr("%1 Hz").arg(rate
), rate
);
142 addWidgetBinding("ActuatorSettings", "BankMode", controls
.modeCombo(), i
++, 0, true);
143 connect(controls
.modeCombo(), SIGNAL(currentIndexChanged(int)), this, SLOT(onBankTypeChange()));
146 SystemAlarms
*systemAlarmsObj
= SystemAlarms::GetInstance(getObjectManager());
147 connect(systemAlarmsObj
, SIGNAL(objectUpdated(UAVObject
*)), this, SLOT(updateBoardWarnings(UAVObject
*)));
149 // TODO why do we do that ?
150 disconnect(this, SLOT(refreshWidgetsValues(UAVObject
*)));
153 ConfigOutputWidget::~ConfigOutputWidget()
155 SystemAlarms
*systemAlarmsObj
= SystemAlarms::GetInstance(getObjectManager());
157 disconnect(systemAlarmsObj
, SIGNAL(objectUpdated(UAVObject
*)), this, SLOT(updateBoardWarnings(UAVObject
*)));
158 foreach(OutputBankControls controls
, m_banks
) {
159 disconnect(controls
.modeCombo(), SIGNAL(currentIndexChanged(int)), this, SLOT(onBankTypeChange()));
163 void ConfigOutputWidget::enableControls(bool enable
)
165 ConfigTaskWidget::enableControls(enable
);
168 m_ui
->channelOutTest
->setChecked(false);
170 m_ui
->channelOutTest
->setEnabled(enable
);
174 Force update all channels with the values in the OutputChannelForms.
176 void ConfigOutputWidget::sendAllChannelTests()
178 for (unsigned int i
= 0; i
< ActuatorCommand::CHANNEL_NUMELEM
; i
++) {
179 OutputChannelForm
*form
= getOutputChannelForm(i
);
180 sendChannelTest(i
, form
->neutral());
185 Toggles the channel testing mode by making the GCS take over
186 the ActuatorCommand objects
188 void ConfigOutputWidget::runChannelTests(bool state
)
190 SystemAlarms
*systemAlarmsObj
= SystemAlarms::GetInstance(getObjectManager());
191 SystemAlarms::DataFields systemAlarms
= systemAlarmsObj
->getData();
193 if (state
&& systemAlarms
.Alarm
[SystemAlarms::ALARM_ACTUATOR
] != SystemAlarms::ALARM_OK
) {
195 mbox
.setText(QString(tr("The actuator module is in an error state. This can also occur because there are no inputs. "
196 "Please fix these before testing outputs.")));
197 mbox
.setStandardButtons(QMessageBox::Ok
);
200 // Unfortunately must cache this since callback will reoccur
201 m_accInitialData
= ActuatorCommand::GetInstance(getObjectManager())->getMetadata();
203 m_ui
->channelOutTest
->setChecked(false);
207 // Confirm this is definitely what they want
210 mbox
.setText(QString(tr("This option will start your motors by the amount selected on the sliders regardless of transmitter."
211 "It is recommended to remove any blades from motors. Are you sure you want to do this?")));
212 mbox
.setStandardButtons(QMessageBox::Yes
| QMessageBox::No
);
213 int retval
= mbox
.exec();
214 if (retval
!= QMessageBox::Yes
) {
216 qDebug() << "Cancelled";
217 m_ui
->channelOutTest
->setChecked(false);
222 ActuatorCommand
*obj
= ActuatorCommand::GetInstance(getObjectManager());
223 UAVObject::Metadata mdata
= obj
->getMetadata();
225 m_accInitialData
= mdata
;
226 UAVObject::SetFlightAccess(mdata
, UAVObject::ACCESS_READONLY
);
227 UAVObject::SetFlightTelemetryUpdateMode(mdata
, UAVObject::UPDATEMODE_ONCHANGE
);
228 UAVObject::SetGcsTelemetryAcked(mdata
, false);
229 UAVObject::SetGcsTelemetryUpdateMode(mdata
, UAVObject::UPDATEMODE_ONCHANGE
);
230 mdata
.gcsTelemetryUpdatePeriod
= 100;
232 mdata
= m_accInitialData
; // Restore metadata
234 obj
->setMetadata(mdata
);
237 // Setup the correct initial channel values when the channel testing mode is turned on.
239 sendAllChannelTests();
243 if (!state
&& isDirty()) {
245 mbox
.setText(QString(tr("You may want to save your neutral settings.")));
246 mbox
.setStandardButtons(QMessageBox::Ok
);
247 mbox
.setIcon(QMessageBox::Information
);
252 OutputChannelForm
*ConfigOutputWidget::getOutputChannelForm(const int index
) const
254 QList
<OutputChannelForm
*> outputChannelForms
= findChildren
<OutputChannelForm
*>();
255 foreach(OutputChannelForm
* outputChannelForm
, outputChannelForms
) {
256 if (outputChannelForm
->index() == index
) {
257 return outputChannelForm
;
261 // no OutputChannelForm found with given index
266 * Set the label for a channel output assignement
268 void ConfigOutputWidget::assignOutputChannel(UAVDataObject
*obj
, QString
&str
)
270 // FIXME: use signal/ slot approach
271 UAVObjectField
*field
= obj
->getField(str
);
272 QStringList options
= field
->getOptions();
273 int index
= options
.indexOf(field
->getValue().toString());
275 OutputChannelForm
*outputChannelForm
= getOutputChannelForm(index
);
277 if (outputChannelForm
) {
278 outputChannelForm
->setName(str
);
283 Sends the channel value to the UAV to move the servo.
284 Returns immediately if we are not in testing mode
286 void ConfigOutputWidget::sendChannelTest(int index
, int value
)
288 if (!m_ui
->channelOutTest
->isChecked()) {
292 if (index
< 0 || (unsigned)index
>= ActuatorCommand::CHANNEL_NUMELEM
) {
296 ActuatorCommand
*actuatorCommand
= ActuatorCommand::GetInstance(getObjectManager());
297 Q_ASSERT(actuatorCommand
);
298 ActuatorCommand::DataFields actuatorCommandFields
= actuatorCommand
->getData();
299 actuatorCommandFields
.Channel
[index
] = value
;
300 actuatorCommand
->setData(actuatorCommandFields
);
303 void ConfigOutputWidget::setColor(QWidget
*widget
, const QColor color
)
305 QPalette
p(palette());
307 p
.setColor(QPalette::Background
, color
);
308 p
.setColor(QPalette::Base
, color
);
309 p
.setColor(QPalette::Active
, QPalette::Button
, color
);
310 p
.setColor(QPalette::Inactive
, QPalette::Button
, color
);
311 widget
->setAutoFillBackground(true);
312 widget
->setPalette(p
);
315 /********************************
317 *******************************/
320 Request the current config from the board (RC Output)
322 void ConfigOutputWidget::refreshWidgetsValuesImpl(UAVObject
*obj
)
326 // Get Actuator Settings
327 ActuatorSettings
*actuatorSettings
= ActuatorSettings::GetInstance(getObjectManager());
329 Q_ASSERT(actuatorSettings
);
330 ActuatorSettings::DataFields actuatorSettingsData
= actuatorSettings
->getData();
332 // Get channel descriptions
333 QStringList channelDesc
= ConfigVehicleTypeWidget::getChannelDescriptions();
335 // Initialize output forms
336 QList
<OutputChannelForm
*> outputChannelForms
= findChildren
<OutputChannelForm
*>();
337 foreach(OutputChannelForm
* outputChannelForm
, outputChannelForms
) {
338 outputChannelForm
->setName(channelDesc
[outputChannelForm
->index()]);
340 // init min,max,neutral
341 int minValue
= actuatorSettingsData
.ChannelMin
[outputChannelForm
->index()];
342 int maxValue
= actuatorSettingsData
.ChannelMax
[outputChannelForm
->index()];
343 outputChannelForm
->setRange(minValue
, maxValue
);
345 int neutral
= actuatorSettingsData
.ChannelNeutral
[outputChannelForm
->index()];
346 outputChannelForm
->setNeutral(neutral
);
349 // Get the SpinWhileArmed setting
350 m_ui
->spinningArmed
->setChecked(actuatorSettingsData
.MotorsSpinWhileArmed
== ActuatorSettings::MOTORSSPINWHILEARMED_TRUE
);
352 for (int i
= 0; i
< m_banks
.count(); i
++) {
353 OutputBankControls controls
= m_banks
.at(i
);
354 // Reset to all disabled
355 controls
.label()->setText("-");
357 controls
.rateCombo()->setEnabled(false);
358 setColor(controls
.rateCombo(), palette().color(QPalette::Background
));
359 controls
.rateCombo()->setCurrentIndex(0);
361 controls
.modeCombo()->setEnabled(false);
362 setColor(controls
.modeCombo(), palette().color(QPalette::Background
));
365 // Get connected board model
366 ExtensionSystem::PluginManager
*pm
= ExtensionSystem::PluginManager::instance();
368 UAVObjectUtilManager
*utilMngr
= pm
->getObject
<UAVObjectUtilManager
>();
370 QStringList bankLabels
;
371 QList
<int> channelBanks
;
374 int board
= utilMngr
->getBoardModel();
375 // Setup labels and combos for banks according to board type
376 if ((board
& 0xff00) == 0x0400) {
377 // Coptercontrol family of boards 4 timer banks
378 bankLabels
<< "1 (1-3)" << "2 (4)" << "3 (5,7-8)" << "4 (6,9-10)";
379 channelBanks
<< 1 << 1 << 1 << 2 << 3 << 4 << 3 << 3 << 4 << 4;
380 } else if (board
== 0x0903) {
381 // Revolution family of boards 6 timer banks
382 bankLabels
<< "1 (1-2)" << "2 (3)" << "3 (4)" << "4 (5-6)" << "5 (7,12)" << "6 (8-11)";
383 channelBanks
<< 1 << 1 << 2 << 3 << 4 << 4 << 5 << 6 << 6 << 6 << 6 << 5;
384 } else if (board
== 0x0905) {
386 bankLabels
<< "1 (1)" << "2 (2,7,11)" << "3 (3)" << "4 (4)" << "5 (5-6)" << "6 (8-10,12)";
387 channelBanks
<< 1 << 2 << 3 << 4 << 5 << 5 << 2 << 6 << 6 << 6 << 2 << 6;
388 } else if (board
== 0x9201) {
390 bankLabels
<< "1 (1-2)" << "2 (3)" << "3 (4)" << "4 (5-6)" << "5 (7-8)" << "6 (9-10)";
391 channelBanks
<< 1 << 1 << 2 << 3 << 4 << 4 << 5 << 5 << 6 << 6;
392 } else if (board
== 0x1001) {
394 bankLabels
<< "1 (1-3,7)" << "2 (4,8)" << "3 (5)" << "4 (6)";
395 channelBanks
<< 1 << 1 << 1 << 2 << 3 << 4 << 1 << 2;
396 } else if (board
== 0x1002 || board
== 0x1003) {
397 // SPRacingF3_EVO, NucleoF303RE
398 bankLabels
<< "1 (1-3)" << "2 (4)" << "3 (5-8)";
399 channelBanks
<< 1 << 1 << 1 << 2 << 3 << 3 << 3 << 3;
400 } else if (board
== 0x1005) { // PikoBLX
401 bankLabels
<< "1 (1-4)" << "2 (5-6)" << "3 (7)" << "4 (8)";
402 channelBanks
<< 1 << 1 << 1 << 1 << 2 << 2 << 3 << 4;
403 } else if (board
== 0x1006) { // tinyFISH
404 bankLabels
<< "1 (1)" << "2 (2)" << "3 (3-4)" << "4 (5-6)";
405 channelBanks
<< 1 << 2 << 3 << 3 << 4 << 4;
410 foreach(QString banklabel
, bankLabels
) {
411 OutputBankControls controls
= m_banks
.at(i
);
413 controls
.label()->setText(banklabel
);
414 int index
= controls
.rateCombo()->findData(actuatorSettingsData
.BankUpdateFreq
[i
]);
416 controls
.rateCombo()->addItem(tr("%1 Hz").arg(actuatorSettingsData
.BankUpdateFreq
[i
]), actuatorSettingsData
.BankUpdateFreq
[i
]);
418 controls
.rateCombo()->setCurrentIndex(index
);
419 controls
.rateCombo()->setEnabled(controls
.modeCombo()->currentIndex() == ActuatorSettings::BANKMODE_PWM
);
420 setColor(controls
.rateCombo(), controls
.color());
421 controls
.modeCombo()->setEnabled(true);
422 setColor(controls
.modeCombo(), controls
.color());
426 // Get Channel ranges:
428 foreach(OutputChannelForm
* outputChannelForm
, outputChannelForms
) {
429 int minValue
= actuatorSettingsData
.ChannelMin
[outputChannelForm
->index()];
430 int maxValue
= actuatorSettingsData
.ChannelMax
[outputChannelForm
->index()];
432 if (channelBanks
.count() > i
) {
433 int bankNumber
= channelBanks
.at(i
);
434 OutputBankControls bankControls
= m_banks
.at(bankNumber
- 1);
436 setChannelLimits(outputChannelForm
, &bankControls
);
438 outputChannelForm
->setBank(QString::number(bankNumber
));
439 outputChannelForm
->setColor(bankControls
.color());
443 outputChannelForm
->setRange(minValue
, maxValue
);
444 int neutral
= actuatorSettingsData
.ChannelNeutral
[outputChannelForm
->index()];
445 outputChannelForm
->setNeutral(neutral
);
448 updateSpinStabilizeCheckComboBoxes();
453 * Sends the config to the board, without saving to the SD card (RC Output)
455 void ConfigOutputWidget::updateObjectsFromWidgetsImpl()
457 ActuatorSettings
*actuatorSettings
= ActuatorSettings::GetInstance(getObjectManager());
459 Q_ASSERT(actuatorSettings
);
460 if (actuatorSettings
) {
461 ActuatorSettings::DataFields actuatorSettingsData
= actuatorSettings
->getData();
463 // Set channel ranges
464 QList
<OutputChannelForm
*> outputChannelForms
= findChildren
<OutputChannelForm
*>();
465 foreach(OutputChannelForm
* outputChannelForm
, outputChannelForms
) {
466 actuatorSettingsData
.ChannelMax
[outputChannelForm
->index()] = outputChannelForm
->max();
467 actuatorSettingsData
.ChannelMin
[outputChannelForm
->index()] = outputChannelForm
->min();
468 actuatorSettingsData
.ChannelNeutral
[outputChannelForm
->index()] = outputChannelForm
->neutral();
472 actuatorSettingsData
.BankUpdateFreq
[0] = m_ui
->cb_outputRate1
->currentData().toUInt();
473 actuatorSettingsData
.BankUpdateFreq
[1] = m_ui
->cb_outputRate2
->currentData().toUInt();
474 actuatorSettingsData
.BankUpdateFreq
[2] = m_ui
->cb_outputRate3
->currentData().toUInt();
475 actuatorSettingsData
.BankUpdateFreq
[3] = m_ui
->cb_outputRate4
->currentData().toUInt();
476 actuatorSettingsData
.BankUpdateFreq
[4] = m_ui
->cb_outputRate5
->currentData().toUInt();
477 actuatorSettingsData
.BankUpdateFreq
[5] = m_ui
->cb_outputRate6
->currentData().toUInt();
479 actuatorSettingsData
.MotorsSpinWhileArmed
= m_ui
->spinningArmed
->isChecked() ?
480 ActuatorSettings::MOTORSSPINWHILEARMED_TRUE
:
481 ActuatorSettings::MOTORSSPINWHILEARMED_FALSE
;
484 UAVObjectUpdaterHelper updateHelper
;
485 actuatorSettings
->setData(actuatorSettingsData
, false);
486 updateHelper
.doObjectAndWait(actuatorSettings
);
489 FlightModeSettings
*flightModeSettings
= FlightModeSettings::GetInstance(getObjectManager());
490 Q_ASSERT(flightModeSettings
);
492 if (flightModeSettings
) {
493 FlightModeSettings::DataFields flightModeSettingsData
= flightModeSettings
->getData();
494 flightModeSettingsData
.AlwaysStabilizeWhenArmedSwitch
= m_ui
->alwaysStabilizedSwitch
->currentIndex();
497 flightModeSettings
->setData(flightModeSettingsData
);
501 void ConfigOutputWidget::updateSpinStabilizeCheckComboBoxes()
503 m_ui
->alwayStabilizedLabel1
->setEnabled(m_ui
->spinningArmed
->isChecked());
504 m_ui
->alwayStabilizedLabel2
->setEnabled(m_ui
->spinningArmed
->isChecked());
505 m_ui
->alwaysStabilizedSwitch
->setEnabled(m_ui
->spinningArmed
->isChecked());
507 if (!m_ui
->spinningArmed
->isChecked()) {
508 m_ui
->alwaysStabilizedSwitch
->setCurrentIndex(FlightModeSettings::ALWAYSSTABILIZEWHENARMEDSWITCH_DISABLED
);
512 void ConfigOutputWidget::updateAlwaysStabilizeStatus()
514 FlightStatus
*flightStatusObj
= FlightStatus::GetInstance(getObjectManager());
515 FlightStatus::DataFields flightStatus
= flightStatusObj
->getData();
517 if (flightStatus
.AlwaysStabilizeWhenArmed
== FlightStatus::ALWAYSSTABILIZEWHENARMED_TRUE
) {
518 m_ui
->alwayStabilizedLabel2
->setText(tr("AlwaysStabilizeWhenArmed is <b>ACTIVE</b>. This prevents arming!."));
520 m_ui
->alwayStabilizedLabel2
->setText(tr("(Really be careful!)."));
524 void ConfigOutputWidget::setChannelLimits(OutputChannelForm
*channelForm
, OutputBankControls
*bankControls
)
526 // Set UI limits according to the bankmode and destination
527 switch (bankControls
->modeCombo()->currentIndex()) {
528 case ActuatorSettings::BANKMODE_DSHOT
:
529 // 0 - 2000 UI limits, DShot min value is fixed to zero
530 if (channelForm
->isServoOutput()) {
531 // Driving a servo using DShot doest not make sense so break
534 channelForm
->setLimits(DSHOT_MINTOUTPUT_RANGE
, DSHOT_MINTOUTPUT_RANGE
, DSHOT_MINTOUTPUT_RANGE
, DSHOT_MAXOUTPUT_RANGE
);
535 channelForm
->setRange(DSHOT_MINTOUTPUT_RANGE
, DSHOT_MAXOUTPUT_RANGE
);
536 channelForm
->setNeutral(DSHOT_MINTOUTPUT_RANGE
);
538 case ActuatorSettings::BANKMODE_PWMSYNC
:
539 // 900 - 1900 UI limits
540 // Default values 1000 - 1900
541 channelForm
->setLimits(DEFAULT_MINOUTPUT_RANGE
, PWMSYNC_MAXOUTPUT_RANGE
, DEFAULT_MINOUTPUT_RANGE
, PWMSYNC_MAXOUTPUT_RANGE
);
542 channelForm
->setRange(DEFAULT_MINOUTPUT_VALUE
, PWMSYNC_MAXOUTPUT_RANGE
);
543 channelForm
->setNeutral(DEFAULT_MINOUTPUT_VALUE
);
544 if (channelForm
->isServoOutput()) {
545 // Servo: Some of them can handle PWMSync, 500 - 1900 UI limits
546 // Default values 1000 - 1900 + neutral 1500
547 channelForm
->setRange(SERVO_MINOUTPUT_VALUE
, PWMSYNC_MAXOUTPUT_RANGE
);
548 channelForm
->setNeutral(SERVO_NEUTRAL_VALUE
);
551 case ActuatorSettings::BANKMODE_PWM
:
552 if (channelForm
->isServoOutput()) {
553 // Servo: 500 - 2500 UI limits
554 // Default values 1000 - 2000 + neutral 1500
555 channelForm
->setLimits(SERVO_MINOUTPUT_RANGE
, SERVO_MAXOUTPUT_RANGE
, SERVO_MINOUTPUT_RANGE
, SERVO_MAXOUTPUT_RANGE
);
556 channelForm
->setRange(SERVO_MINOUTPUT_VALUE
, SERVO_MAXOUTPUT_VALUE
);
557 channelForm
->setNeutral(SERVO_NEUTRAL_VALUE
);
560 // PWM motor outputs fall to default
561 case ActuatorSettings::BANKMODE_ONESHOT125
:
562 case ActuatorSettings::BANKMODE_ONESHOT42
:
563 case ActuatorSettings::BANKMODE_MULTISHOT
:
564 if (channelForm
->isServoOutput()) {
565 // Driving a servo using this mode does not make sense so break
569 // Motors 900 - 2000 UI limits
570 // Default values 1000 - 2000, neutral set to min
571 // This settings are used for PWM, OneShot125, OneShot42 and MultiShot
572 channelForm
->setLimits(DEFAULT_MINOUTPUT_RANGE
, DEFAULT_MAXOUTPUT_RANGE
, DEFAULT_MINOUTPUT_RANGE
, DEFAULT_MAXOUTPUT_RANGE
);
573 channelForm
->setRange(DEFAULT_MINOUTPUT_VALUE
, DEFAULT_MAXOUTPUT_RANGE
);
574 channelForm
->setNeutral(DEFAULT_MINOUTPUT_VALUE
);
579 ConfigOutputWidget::ChannelConfigWarning
ConfigOutputWidget::checkChannelConfig(OutputChannelForm
*channelForm
, OutputBankControls
*bankControls
)
581 ChannelConfigWarning warning
= None
;
582 int currentNeutralValue
= channelForm
->getNeutralValue();
584 // Check if RevMotor has neutral value around center
585 if (channelForm
->isReversableMotor()) {
586 warning
= IsReversibleMotorCheckNeutral
;
587 int neutralDiff
= qAbs(REVMOTOR_NEUTRAL_TARGET_VALUE
- currentNeutralValue
);
588 if (neutralDiff
< REVMOTOR_NEUTRAL_DIFF_VALUE
) {
594 // Check if NormalMotor neutral is not too high
595 if (channelForm
->isNormalMotor()) {
596 warning
= IsNormalMotorCheckNeutral
;
597 int neutralDiff
= currentNeutralValue
- DEFAULT_MINOUTPUT_VALUE
;
598 if (neutralDiff
< MOTOR_NEUTRAL_DIFF_VALUE
) {
604 switch (bankControls
->modeCombo()->currentIndex()) {
605 case ActuatorSettings::BANKMODE_DSHOT
:
606 if (channelForm
->isServoOutput()) {
607 warning
= CannotDriveServo
;
608 // Driving a servo using DShot doest not make sense so break
611 if (channelForm
->isReversableMotor()) {
612 // Bi-directional DShot not yet supported
613 warning
= BiDirectionalDShotNotSupported
;
616 case ActuatorSettings::BANKMODE_PWMSYNC
:
618 case ActuatorSettings::BANKMODE_PWM
:
620 case ActuatorSettings::BANKMODE_ONESHOT125
:
621 case ActuatorSettings::BANKMODE_ONESHOT42
:
622 case ActuatorSettings::BANKMODE_MULTISHOT
:
623 if (channelForm
->isServoOutput()) {
624 warning
= CannotDriveServo
;
625 // Driving a servo using this mode does not make sense so break
636 void ConfigOutputWidget::onBankTypeChange()
638 QComboBox
*bankModeCombo
= qobject_cast
<QComboBox
*>(sender());
640 ChannelConfigWarning current_warning
= None
;
641 ChannelConfigWarning warning_found
= None
;
643 if (bankModeCombo
!= NULL
) {
645 QList
<OutputChannelForm
*> outputChannelForms
= findChildren
<OutputChannelForm
*>();
646 foreach(OutputBankControls controls
, m_banks
) {
647 if (controls
.modeCombo() == bankModeCombo
) {
648 bool enabled
= bankModeCombo
->currentIndex() == ActuatorSettings::BANKMODE_PWM
;
649 controls
.rateCombo()->setEnabled(enabled
);
650 controls
.rateCombo()->setCurrentIndex(enabled
? 1 : 0);
651 foreach(OutputChannelForm
* outputChannelForm
, outputChannelForms
) {
652 if (outputChannelForm
->bank().toInt() == bankNumber
) {
653 setChannelLimits(outputChannelForm
, &controls
);
654 current_warning
= checkChannelConfig(outputChannelForm
, &controls
);
655 if (current_warning
> None
) {
656 warning_found
= current_warning
;
667 updateChannelConfigWarning(warning_found
);
670 bool ConfigOutputWidget::checkOutputConfig()
672 ChannelConfigWarning current_warning
= None
;
673 ChannelConfigWarning warning_found
= None
;
677 QList
<OutputChannelForm
*> outputChannelForms
= findChildren
<OutputChannelForm
*>();
679 foreach(OutputBankControls controls
, m_banks
) {
680 foreach(OutputChannelForm
* outputChannelForm
, outputChannelForms
) {
681 if (!outputChannelForm
->isDisabledOutput() && (outputChannelForm
->bank().toInt() == bankNumber
)) {
682 current_warning
= checkChannelConfig(outputChannelForm
, &controls
);
683 if (current_warning
> None
) {
684 warning_found
= current_warning
;
692 updateChannelConfigWarning(warning_found
);
694 if (warning_found
> None
) {
701 void ConfigOutputWidget::stopTests()
703 m_ui
->channelOutTest
->setChecked(false);
706 void ConfigOutputWidget::updateBoardWarnings(UAVObject
*)
708 SystemAlarms
*systemAlarmsObj
= SystemAlarms::GetInstance(getObjectManager());
709 SystemAlarms::DataFields systemAlarms
= systemAlarmsObj
->getData();
711 if (systemAlarms
.Alarm
[SystemAlarms::ALARM_SYSTEMCONFIGURATION
] > SystemAlarms::ALARM_WARNING
) {
712 switch (systemAlarms
.ExtendedAlarmStatus
[SystemAlarms::EXTENDEDALARMSTATUS_SYSTEMCONFIGURATION
]) {
713 case SystemAlarms::EXTENDEDALARMSTATUS_UNSUPPORTEDCONFIG_ONESHOT
:
714 setBoardWarning(tr("OneShot and PWMSync output only works with Receiver Port settings marked with '+OneShot'<br>"
715 "When using Receiver Port setting 'PPM_PIN8+OneShot' "
716 "<b><font color='%1'>Bank %2</font></b> must be set to PWM")
717 .arg(m_banks
.at(3).color().name()).arg(m_banks
.at(3).label()->text()));
721 setBoardWarning(NULL
);
724 void ConfigOutputWidget::updateChannelConfigWarning(ChannelConfigWarning warning
)
728 if (warning
== BiDirectionalDShotNotSupported
) {
729 // TODO: Implement bi-directional DShot
730 warning_str
= "There is at least <b>one reversable motor using DShot</b> in your configuration.<br>"
731 "Bi-directional DShot is not currently supported, you should use PWM, OneShotXXX or MultiShot.";
734 if (warning
== IsNormalMotorCheckNeutral
) {
735 warning_str
= "Seems there is at least one pretty <b>high neutral value</b> set in your configuration.<br>"
736 "Be sure all Esc are calibrated and no mechanical stress in all motors.";
739 if (warning
== IsReversibleMotorCheckNeutral
) {
740 warning_str
= "There is at least one <b>reversable motor</b> in your configuration.<br>"
741 "Be sure you set a appropriate neutral value before saving and applying power to the vehicule.";
744 if (warning
== CannotDriveServo
) {
745 warning_str
= "One Bank cannot drive a <b>servo output!</b><br>"
746 "You must use PWM for this bank or move the servo output to another compatible bank.";
749 setConfigWarning(warning_str
);
752 void ConfigOutputWidget::setBoardWarning(QString message
)
754 m_ui
->boardWarningFrame
->setVisible(!message
.isNull());
755 m_ui
->boardWarningPic
->setPixmap(message
.isNull() ? QPixmap() : QPixmap(":/configgadget/images/error.svg"));
756 m_ui
->boardWarningTxt
->setText(message
);
759 void ConfigOutputWidget::setConfigWarning(QString message
)
761 m_ui
->configWarningFrame
->setVisible(!message
.isNull());
762 m_ui
->configWarningPic
->setPixmap(message
.isNull() ? QPixmap() : QPixmap(":/configgadget/images/error.svg"));
763 m_ui
->configWarningTxt
->setText(message
);
766 QString
ConfigOutputWidget::bankModeName(int index
)
768 UAVDataObject
*actuator
= dynamic_cast<UAVDataObject
*>(getObjectManager()->getObject(QString("ActuatorSettings")));
771 UAVObjectField
*field
= actuator
->getField("BankMode");
773 QStringList bankModeOptions
;
776 bankModeOptions
= field
->getOptions();
779 return bankModeOptions
.at(index
);
782 OutputBankControls::OutputBankControls(MixerSettings
*mixer
, QLabel
*label
, QColor color
, QComboBox
*rateCombo
, QComboBox
*modeCombo
) :
783 m_mixer(mixer
), m_label(label
), m_color(color
), m_rateCombo(rateCombo
), m_modeCombo(modeCombo
)
786 OutputBankControls::~OutputBankControls()