LP-551 setLimits() only set limits - Added checkChannelConfig() and checkOutputConfig...
[librepilot.git] / ground / gcs / src / plugins / config / configoutputwidget.cpp
blob9b080f94ab28bd68ae6f76c1206d8b5afa065779
1 /**
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
8 * @{
9 * @addtogroup ConfigPlugin Config Plugin
10 * @{
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
22 * for more details.
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"
48 #include <QDebug>
49 #include <QStringList>
50 #include <QWidget>
51 #include <QTextEdit>
52 #include <QMessageBox>
54 // Motor settings
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
65 // Servo settings
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();
75 m_ui->setupUi(this);
77 // must be done before auto binding !
78 setWikiURL("Output+Configuration");
80 addAutoBindings();
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());
122 Q_ASSERT(mixer);
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);
130 QList<int> rates;
131 rates << 50 << 60 << 125 << 165 << 270 << 330 << 400 << 490;
132 int i = 0;
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);
167 if (!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) {
194 QMessageBox mbox;
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);
198 mbox.exec();
200 // Unfortunately must cache this since callback will reoccur
201 m_accInitialData = ActuatorCommand::GetInstance(getObjectManager())->getMetadata();
203 m_ui->channelOutTest->setChecked(false);
204 return;
207 // Confirm this is definitely what they want
208 if (state) {
209 QMessageBox mbox;
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) {
215 state = false;
216 qDebug() << "Cancelled";
217 m_ui->channelOutTest->setChecked(false);
218 return;
222 ActuatorCommand *obj = ActuatorCommand::GetInstance(getObjectManager());
223 UAVObject::Metadata mdata = obj->getMetadata();
224 if (state) {
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;
231 } else {
232 mdata = m_accInitialData; // Restore metadata
234 obj->setMetadata(mdata);
235 obj->updated();
237 // Setup the correct initial channel values when the channel testing mode is turned on.
238 if (state) {
239 sendAllChannelTests();
242 // Add info at end
243 if (!state && isDirty()) {
244 QMessageBox mbox;
245 mbox.setText(QString(tr("You may want to save your neutral settings.")));
246 mbox.setStandardButtons(QMessageBox::Ok);
247 mbox.setIcon(QMessageBox::Information);
248 mbox.exec();
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
262 return NULL;
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()) {
289 return;
292 if (index < 0 || (unsigned)index >= ActuatorCommand::CHANNEL_NUMELEM) {
293 return;
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 /********************************
316 * Output settings
317 *******************************/
320 Request the current config from the board (RC Output)
322 void ConfigOutputWidget::refreshWidgetsValuesImpl(UAVObject *obj)
324 Q_UNUSED(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();
367 Q_ASSERT(pm);
368 UAVObjectUtilManager *utilMngr = pm->getObject<UAVObjectUtilManager>();
369 Q_ASSERT(utilMngr);
370 QStringList bankLabels;
371 QList<int> channelBanks;
373 if (utilMngr) {
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) {
385 // Revolution Nano
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) {
389 // Sparky2
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) {
393 // SPRacingF3
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;
409 int i = 0;
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]);
415 if (index == -1) {
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());
423 i++;
426 // Get Channel ranges:
427 i = 0;
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());
441 i++;
443 outputChannelForm->setRange(minValue, maxValue);
444 int neutral = actuatorSettingsData.ChannelNeutral[outputChannelForm->index()];
445 outputChannelForm->setNeutral(neutral);
448 updateSpinStabilizeCheckComboBoxes();
449 checkOutputConfig();
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();
471 // Set update rates
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;
483 // Apply settings
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();
496 // Apply settings
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!."));
519 } else {
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
532 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);
537 break;
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);
550 break;
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);
558 break;
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
566 break;
568 default:
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);
575 break;
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) {
589 // Reset warning
590 warning = None;
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) {
599 // Reset warning
600 warning = None;
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
609 break;
611 if (channelForm->isReversableMotor()) {
612 // Bi-directional DShot not yet supported
613 warning = BiDirectionalDShotNotSupported;
615 break;
616 case ActuatorSettings::BANKMODE_PWMSYNC:
617 break;
618 case ActuatorSettings::BANKMODE_PWM:
619 break;
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
626 break;
628 default:
629 break;
632 return warning;
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) {
644 int bankNumber = 1;
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;
660 break;
663 bankNumber++;
667 updateChannelConfigWarning(warning_found);
670 bool ConfigOutputWidget::checkOutputConfig()
672 ChannelConfigWarning current_warning = None;
673 ChannelConfigWarning warning_found = None;
675 int bankNumber = 1;
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;
689 bankNumber++;
692 updateChannelConfigWarning(warning_found);
694 if (warning_found > None) {
695 return false;
698 return true;
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()));
718 return;
721 setBoardWarning(NULL);
724 void ConfigOutputWidget::updateChannelConfigWarning(ChannelConfigWarning warning)
726 QString warning_str;
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")));
770 Q_ASSERT(actuator);
771 UAVObjectField *field = actuator->getField("BankMode");
772 Q_ASSERT(field);
773 QStringList bankModeOptions;
775 if (field) {
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()