diff --git a/locales/en/messages.json b/locales/en/messages.json
index df32258d..aa02feff 100644
--- a/locales/en/messages.json
+++ b/locales/en/messages.json
@@ -2551,6 +2551,9 @@
"motorsEnableControl": {
"message": "I understand the risks, the propellers are removed - enable motor control and arming, and disable Runaway Takeoff Prevention."
},
+ "motorsDialogMixerReset": {
+ "message": "Invalid mixer mode selected.
The {{mixerName}} model needs {{mixerMotors}} motor resources.
If using a custom mixer mode you need to define a custom mmix before changing mixer mode. If you come back to the motor tab after setting a mmix for your mode it won't reset the mode anymore. Please see the wiki for more information how to set this up.
You are not able to test any motors or fly because firmware has {{outputs}} outputs for the selected mode.
Resetting to default mixer mode"
+ },
"motorsDialogSettingsChanged": {
"message": "Configuration changes have been detected.
Motor Test Mode is disabled until the settings have seen saved."
},
diff --git a/src/css/tabs/motors.css b/src/css/tabs/motors.css
index 5dc082c5..b635ebac 100644
--- a/src/css/tabs/motors.css
+++ b/src/css/tabs/motors.css
@@ -307,6 +307,11 @@
flex-grow: 1;
}
+.tab-motors #dialog-mixer-reset {
+ width: 400px;
+ height: fit-content;
+}
+
.tab-motors #dialog-settings-changed {
height: 120px;
}
diff --git a/src/js/tabs/motors.js b/src/js/tabs/motors.js
index ac1ad79f..b57b46f3 100644
--- a/src/js/tabs/motors.js
+++ b/src/js/tabs/motors.js
@@ -365,8 +365,38 @@ TABS.motors.initialize = function (callback) {
// select current mixer configuration
mixerListElement.val(FC.MIXER_CONFIG.mixer).change();
+ function validateMixerOutputs() {
+ MSP.promise(MSPCodes.MSP_MOTOR).then(() => {
+ const mixer = FC.MIXER_CONFIG.mixer;
+ const motors = mixerList[mixer - 1].motors;
+ // initialize for models with zero motors
+ self.numberOfValidOutputs = motors;
+
+ for (let i = 0; i < FC.MOTOR_DATA.length; i++) {
+ if (FC.MOTOR_DATA[i] === 0) {
+ self.numberOfValidOutputs = i;
+ if (motors > self.numberOfValidOutputs && motors > 0) {
+ const msg = i18n.getMessage('motorsDialogMixerReset', {
+ mixerName: mixerList[mixer - 1].name,
+ mixerMotors: motors,
+ outputs: self.numberOfValidOutputs,
+ });
+ showDialogMixerReset(msg);
+ }
+ return;
+ }
+ }
+ });
+ }
+
update_model(FC.MIXER_CONFIG.mixer);
+ // Reference: src/main/drivers/motor.h for motorPwmProtocolTypes_e;
+ const ESC_PROTOCOL_UNDEFINED = 9;
+ if (FC.PID_ADVANCED_CONFIG.fast_pwm_protocol !== ESC_PROTOCOL_UNDEFINED) {
+ validateMixerOutputs();
+ }
+
// Always start with default/empty sensor data array, clean slate all
initSensorData();
@@ -566,7 +596,6 @@ TABS.motors.initialize = function (callback) {
accelOffsetEstablished = false;
});
- self.numberOfValidOutputs = (FC.MOTOR_DATA.indexOf(0) > -1) ? FC.MOTOR_DATA.indexOf(0) : 8;
let rangeMin;
let rangeMax;
let neutral3d;
@@ -710,6 +739,7 @@ TABS.motors.initialize = function (callback) {
}
escProtocolElement.val(FC.PID_ADVANCED_CONFIG.fast_pwm_protocol + 1);
+ console.log(FC.PID_ADVANCED_CONFIG.fast_pwm_protocol);
escProtocolElement.on("change", function () {
const escProtocolValue = parseInt($(this).val()) - 1;
@@ -837,7 +867,7 @@ TABS.motors.initialize = function (callback) {
$('div.values li').eq(index).text($(this).val());
- for (let i = 0; i < 8; i++) {
+ for (let i = 0; i < self.numberOfValidOutputs; i++) {
const val = parseInt($('div.sliders input').eq(i).val());
buffer.push16(val);
}
@@ -926,14 +956,26 @@ TABS.motors.initialize = function (callback) {
}
}
+ function getMotorOutputs() {
+ const motorData = [];
+ const motorsTesting = motorsEnableTestModeElement.is(':checked');
+
+ for (let i = 0; i < self.numberOfValidOutputs; i++) {
+ motorData[i] = motorsTesting ? FC.MOTOR_DATA[i] : rangeMin;
+ }
+
+ return motorData;
+ }
+
const fullBlockScale = rangeMax - rangeMin;
function update_ui() {
const previousArmState = self.armed;
const blockHeight = $('div.m-block:first').height();
+ const motorValues = getMotorOutputs();
- for (let i = 0; i < FC.MOTOR_DATA.length; i++) {
- const motorValue = FC.MOTOR_DATA[i];
+ for (let i = 0; i < motorValues.length; i++) {
+ const motorValue = motorValues[i];
const barHeight = motorValue - rangeMin,
marginTop = blockHeight - (barHeight * (blockHeight / fullBlockScale)).clamp(0, blockHeight),
height = (barHeight * (blockHeight / fullBlockScale)).clamp(0, blockHeight),
@@ -1014,22 +1056,20 @@ TABS.motors.initialize = function (callback) {
FC.PID_ADVANCED_CONFIG.motor_pwm_rate = parseInt($('input[name="unsyncedpwmfreq"]').val());
FC.PID_ADVANCED_CONFIG.digitalIdlePercent = parseFloat($('input[name="digitalIdlePercent"]').val());
- Promise
- .resolve(true)
- .then(() => { return MSP.promise(MSPCodes.MSP_SET_FEATURE_CONFIG, mspHelper.crunch(MSPCodes.MSP_SET_FEATURE_CONFIG)); })
- .then(() => { return MSP.promise(MSPCodes.MSP_SET_MIXER_CONFIG, mspHelper.crunch(MSPCodes.MSP_SET_MIXER_CONFIG)); })
- .then(() => { return MSP.promise(MSPCodes.MSP_SET_MOTOR_CONFIG, mspHelper.crunch(MSPCodes.MSP_SET_MOTOR_CONFIG)); })
- .then(() => { return MSP.promise(MSPCodes.MSP_SET_MOTOR_3D_CONFIG, mspHelper.crunch(MSPCodes.MSP_SET_MOTOR_3D_CONFIG)); })
- .then(() => { return MSP.promise(MSPCodes.MSP_SET_ADVANCED_CONFIG, mspHelper.crunch(MSPCodes.MSP_SET_ADVANCED_CONFIG)); })
- .then(() => { return MSP.promise(MSPCodes.MSP_SET_ARMING_CONFIG, mspHelper.crunch(MSPCodes.MSP_SET_ARMING_CONFIG)); })
- .then(() => { return (semver.gte(FC.CONFIG.apiVersion, API_VERSION_1_42)) ? MSP.promise(MSPCodes.MSP_SET_FILTER_CONFIG,
- mspHelper.crunch(MSPCodes.MSP_SET_FILTER_CONFIG)) : true; })
- .then(() => { return MSP.promise(MSPCodes.MSP_EEPROM_WRITE); })
- .then(() => {
- GUI.log(i18n.getMessage('configurationEepromSaved'));
- MSP.send_message(MSPCodes.MSP_SET_REBOOT, false, false);
- reinitialiseConnection(self);
- });
+ if (semver.gte(FC.CONFIG.apiVersion, "1.25.0") && semver.lt(FC.CONFIG.apiVersion, API_VERSION_1_41)) {
+ FC.PID_ADVANCED_CONFIG.gyroUse32kHz = $('input[id="gyroUse32kHz"]').is(':checked') ? 1 : 0;
+ }
+
+ MSP.promise(MSPCodes.MSP_SET_FEATURE_CONFIG, mspHelper.crunch(MSPCodes.MSP_SET_FEATURE_CONFIG))
+ .then(() => MSP.promise(MSPCodes.MSP_SET_MIXER_CONFIG, mspHelper.crunch(MSPCodes.MSP_SET_MIXER_CONFIG)))
+ .then(() => MSP.promise(MSPCodes.MSP_SET_MOTOR_CONFIG, mspHelper.crunch(MSPCodes.MSP_SET_MOTOR_CONFIG)))
+ .then(() => MSP.promise(MSPCodes.MSP_SET_MOTOR_3D_CONFIG, mspHelper.crunch(MSPCodes.MSP_SET_MOTOR_3D_CONFIG)))
+ .then(() => MSP.promise(MSPCodes.MSP_SET_ADVANCED_CONFIG, mspHelper.crunch(MSPCodes.MSP_SET_ADVANCED_CONFIG)))
+ .then(() => MSP.promise(MSPCodes.MSP_SET_ARMING_CONFIG, mspHelper.crunch(MSPCodes.MSP_SET_ARMING_CONFIG)))
+ .then(() => semver.gte(FC.CONFIG.apiVersion, API_VERSION_1_42) ?
+ MSP.promise(MSPCodes.MSP_SET_FILTER_CONFIG, mspHelper.crunch(MSPCodes.MSP_SET_FILTER_CONFIG)) : true)
+ .then(() => MSP.promise(MSPCodes.MSP_EEPROM_WRITE))
+ .then(() => reboot());
analytics.sendChangeEvents(analytics.EVENT_CATEGORIES.FLIGHT_CONTROLLER, self.analyticsChanges);
self.analyticsChanges = {};
@@ -1058,6 +1098,30 @@ TABS.motors.initialize = function (callback) {
GUI.content_ready(callback);
}
+ function reboot() {
+ GUI.log(i18n.getMessage('configurationEepromSaved'));
+ MSP.promise(MSPCodes.MSP_SET_REBOOT, false, false).then(() => reinitialiseConnection());
+ }
+
+ function showDialogMixerReset(message) {
+ const dialogMixerReset = $('#dialog-mixer-reset')[0];
+
+ $('#dialog-mixer-reset-content').html(message);
+
+ if (!dialogMixerReset.hasAttribute('open')) {
+ dialogMixerReset.showModal();
+ $('#dialog-mixer-reset-confirmbtn').click(function() {
+ dialogMixerReset.close();
+
+ FC.MIXER_CONFIG.mixer = 3;
+
+ MSP.promise(MSPCodes.MSP_SET_MIXER_CONFIG, mspHelper.crunch(MSPCodes.MSP_SET_MIXER_CONFIG))
+ .then(() => MSP.promise(MSPCodes.MSP_EEPROM_WRITE))
+ .then(() => reboot());
+ });
+ }
+ }
+
function showDialogSettingsChanged(message) {
const dialogSettingsChanged = $('#dialog-settings-changed')[0];
diff --git a/src/tabs/motors.html b/src/tabs/motors.html
index f8c8ccbf..72946afd 100644
--- a/src/tabs/motors.html
+++ b/src/tabs/motors.html
@@ -373,6 +373,15 @@
+
+