2 * This file is part of Cleanflight and Betaflight.
4 * Cleanflight and Betaflight are free software. You can redistribute
5 * this software and/or modify this software under the terms of the
6 * GNU General Public License as published by the Free Software
7 * Foundation, either version 3 of the License, or (at your option)
10 * Cleanflight and Betaflight are distributed in the hope that they
11 * will be useful, but WITHOUT ANY WARRANTY; without even the implied
12 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 * See the GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this software.
18 * If not, see <http://www.gnu.org/licenses/>.
27 #include "build/build_config.h"
28 #include "build/debug.h"
30 #include "common/maths.h"
32 #include "config/config.h"
33 #include "config/feature.h"
35 #include "drivers/dshot.h"
37 #include "fc/controlrate_profile.h"
38 #include "fc/runtime_config.h"
41 #include "flight/mixer_tricopter.h"
42 #include "flight/pid.h"
46 #include "sensors/battery.h"
48 #include "mixer_init.h"
50 PG_REGISTER_WITH_RESET_FN(mixerConfig_t
, mixerConfig
, PG_MIXER_CONFIG
, 1);
52 void pgResetFn_mixerConfig(mixerConfig_t
*mixerConfig
)
54 mixerConfig
->mixerMode
= DEFAULT_MIXER
;
55 mixerConfig
->yaw_motors_reversed
= false;
56 mixerConfig
->crashflip_motor_percent
= 0;
58 mixerConfig
->crashflip_rate
= 30;
60 mixerConfig
->crashflip_rate
= 0;
62 mixerConfig
->mixer_type
= MIXER_LEGACY
;
64 mixerConfig
->rpm_limit
= false;
65 mixerConfig
->rpm_limit_p
= 25;
66 mixerConfig
->rpm_limit_i
= 10;
67 mixerConfig
->rpm_limit_d
= 8;
68 mixerConfig
->rpm_limit_value
= 18000;
72 PG_REGISTER_ARRAY(motorMixer_t
, MAX_SUPPORTED_MOTORS
, customMotorMixer
, PG_MOTOR_MIXER
, 0);
74 mixerMode_e currentMixerMode
;
76 static const motorMixer_t mixerQuadX
[] = {
77 { 1.0f
, -1.0f
, 1.0f
, -1.0f
}, // REAR_R
78 { 1.0f
, -1.0f
, -1.0f
, 1.0f
}, // FRONT_R
79 { 1.0f
, 1.0f
, 1.0f
, 1.0f
}, // REAR_L
80 { 1.0f
, 1.0f
, -1.0f
, -1.0f
}, // FRONT_L
82 #ifndef USE_QUAD_MIXER_ONLY
83 static const motorMixer_t mixerTricopter
[] = {
84 { 1.0f
, 0.0f
, 1.333333f
, 0.0f
}, // REAR
85 { 1.0f
, -1.0f
, -0.666667f
, 0.0f
}, // RIGHT
86 { 1.0f
, 1.0f
, -0.666667f
, 0.0f
}, // LEFT
89 static const motorMixer_t mixerQuadP
[] = {
90 { 1.0f
, 0.0f
, 1.0f
, -1.0f
}, // REAR
91 { 1.0f
, -1.0f
, 0.0f
, 1.0f
}, // RIGHT
92 { 1.0f
, 1.0f
, 0.0f
, 1.0f
}, // LEFT
93 { 1.0f
, 0.0f
, -1.0f
, -1.0f
}, // FRONT
96 #if defined(USE_UNCOMMON_MIXERS)
97 static const motorMixer_t mixerBicopter
[] = {
98 { 1.0f
, 1.0f
, 0.0f
, 0.0f
}, // LEFT
99 { 1.0f
, -1.0f
, 0.0f
, 0.0f
}, // RIGHT
102 #define mixerBicopter NULL
105 static const motorMixer_t mixerY4
[] = {
106 { 1.0f
, 0.0f
, 1.0f
, -1.0f
}, // REAR_TOP CW
107 { 1.0f
, -1.0f
, -1.0f
, 0.0f
}, // FRONT_R CCW
108 { 1.0f
, 0.0f
, 1.0f
, 1.0f
}, // REAR_BOTTOM CCW
109 { 1.0f
, 1.0f
, -1.0f
, 0.0f
}, // FRONT_L CW
112 #if (MAX_SUPPORTED_MOTORS >= 6)
113 static const motorMixer_t mixerHex6X
[] = {
114 { 1.0f
, -0.5f
, 0.866025f
, 1.0f
}, // REAR_R
115 { 1.0f
, -0.5f
, -0.866025f
, 1.0f
}, // FRONT_R
116 { 1.0f
, 0.5f
, 0.866025f
, -1.0f
}, // REAR_L
117 { 1.0f
, 0.5f
, -0.866025f
, -1.0f
}, // FRONT_L
118 { 1.0f
, -1.0f
, 0.0f
, -1.0f
}, // RIGHT
119 { 1.0f
, 1.0f
, 0.0f
, 1.0f
}, // LEFT
122 #if defined(USE_UNCOMMON_MIXERS)
123 static const motorMixer_t mixerHex6H
[] = {
124 { 1.0f
, -1.0f
, 1.0f
, -1.0f
}, // REAR_R
125 { 1.0f
, -1.0f
, -1.0f
, 1.0f
}, // FRONT_R
126 { 1.0f
, 1.0f
, 1.0f
, 1.0f
}, // REAR_L
127 { 1.0f
, 1.0f
, -1.0f
, -1.0f
}, // FRONT_L
128 { 1.0f
, 0.0f
, 0.0f
, 0.0f
}, // RIGHT
129 { 1.0f
, 0.0f
, 0.0f
, 0.0f
}, // LEFT
132 static const motorMixer_t mixerHex6P
[] = {
133 { 1.0f
, -0.866025f
, 0.5f
, 1.0f
}, // REAR_R
134 { 1.0f
, -0.866025f
, -0.5f
, -1.0f
}, // FRONT_R
135 { 1.0f
, 0.866025f
, 0.5f
, 1.0f
}, // REAR_L
136 { 1.0f
, 0.866025f
, -0.5f
, -1.0f
}, // FRONT_L
137 { 1.0f
, 0.0f
, -1.0f
, 1.0f
}, // FRONT
138 { 1.0f
, 0.0f
, 1.0f
, -1.0f
}, // REAR
140 static const motorMixer_t mixerY6
[] = {
141 { 1.0f
, 0.0f
, 1.333333f
, 1.0f
}, // REAR
142 { 1.0f
, -1.0f
, -0.666667f
, -1.0f
}, // RIGHT
143 { 1.0f
, 1.0f
, -0.666667f
, -1.0f
}, // LEFT
144 { 1.0f
, 0.0f
, 1.333333f
, -1.0f
}, // UNDER_REAR
145 { 1.0f
, -1.0f
, -0.666667f
, 1.0f
}, // UNDER_RIGHT
146 { 1.0f
, 1.0f
, -0.666667f
, 1.0f
}, // UNDER_LEFT
149 #define mixerHex6H NULL
150 #define mixerHex6P NULL
152 #endif // USE_UNCOMMON_MIXERS
154 #define mixerHex6X NULL
155 #endif // MAX_SUPPORTED_MOTORS >= 6
157 #if defined(USE_UNCOMMON_MIXERS) && (MAX_SUPPORTED_MOTORS >= 8)
158 static const motorMixer_t mixerOctoX8
[] = {
159 { 1.0f
, -1.0f
, 1.0f
, -1.0f
}, // REAR_R
160 { 1.0f
, -1.0f
, -1.0f
, 1.0f
}, // FRONT_R
161 { 1.0f
, 1.0f
, 1.0f
, 1.0f
}, // REAR_L
162 { 1.0f
, 1.0f
, -1.0f
, -1.0f
}, // FRONT_L
163 { 1.0f
, -1.0f
, 1.0f
, 1.0f
}, // UNDER_REAR_R
164 { 1.0f
, -1.0f
, -1.0f
, -1.0f
}, // UNDER_FRONT_R
165 { 1.0f
, 1.0f
, 1.0f
, -1.0f
}, // UNDER_REAR_L
166 { 1.0f
, 1.0f
, -1.0f
, 1.0f
}, // UNDER_FRONT_L
169 static const motorMixer_t mixerOctoX8P
[] = {
170 { 1.0f
, 0.0f
, 1.0f
, -1.0f
}, // REAR
171 { 1.0f
, -1.0f
, 0.0f
, 1.0f
}, // RIGHT
172 { 1.0f
, 1.0f
, 0.0f
, 1.0f
}, // LEFT
173 { 1.0f
, 0.0f
, -1.0f
, -1.0f
}, // FRONT
174 { 1.0f
, 0.0f
, 1.0f
, 1.0f
}, // UNDER_REAR
175 { 1.0f
, -1.0f
, 0.0f
, -1.0f
}, // UNDER_RIGHT
176 { 1.0f
, 1.0f
, 0.0f
, -1.0f
}, // UNDER_LEFT
177 { 1.0f
, 0.0f
, -1.0f
, 1.0f
}, // UNDER_FRONT
180 static const motorMixer_t mixerOctoFlatP
[] = {
181 { 1.0f
, 0.707107f
, -0.707107f
, 1.0f
}, // FRONT_L
182 { 1.0f
, -0.707107f
, -0.707107f
, 1.0f
}, // FRONT_R
183 { 1.0f
, -0.707107f
, 0.707107f
, 1.0f
}, // REAR_R
184 { 1.0f
, 0.707107f
, 0.707107f
, 1.0f
}, // REAR_L
185 { 1.0f
, 0.0f
, -1.0f
, -1.0f
}, // FRONT
186 { 1.0f
, -1.0f
, 0.0f
, -1.0f
}, // RIGHT
187 { 1.0f
, 0.0f
, 1.0f
, -1.0f
}, // REAR
188 { 1.0f
, 1.0f
, 0.0f
, -1.0f
}, // LEFT
191 static const motorMixer_t mixerOctoFlatX
[] = {
192 { 1.0f
, 1.0f
, -0.414178f
, 1.0f
}, // MIDFRONT_L
193 { 1.0f
, -0.414178f
, -1.0f
, 1.0f
}, // FRONT_R
194 { 1.0f
, -1.0f
, 0.414178f
, 1.0f
}, // MIDREAR_R
195 { 1.0f
, 0.414178f
, 1.0f
, 1.0f
}, // REAR_L
196 { 1.0f
, 0.414178f
, -1.0f
, -1.0f
}, // FRONT_L
197 { 1.0f
, -1.0f
, -0.414178f
, -1.0f
}, // MIDFRONT_R
198 { 1.0f
, -0.414178f
, 1.0f
, -1.0f
}, // REAR_R
199 { 1.0f
, 1.0f
, 0.414178f
, -1.0f
}, // MIDREAR_L
202 #define mixerOctoX8 NULL
203 #define mixerOctoX8P NULL
204 #define mixerOctoFlatP NULL
205 #define mixerOctoFlatX NULL
208 static const motorMixer_t mixerVtail4
[] = {
209 { 1.0f
, -0.58f
, 0.58f
, 1.0f
}, // REAR_R
210 { 1.0f
, -0.46f
, -0.39f
, -0.5f
}, // FRONT_R
211 { 1.0f
, 0.58f
, 0.58f
, -1.0f
}, // REAR_L
212 { 1.0f
, 0.46f
, -0.39f
, 0.5f
}, // FRONT_L
215 static const motorMixer_t mixerAtail4
[] = {
216 { 1.0f
, -0.58f
, 0.58f
, -1.0f
}, // REAR_R
217 { 1.0f
, -0.46f
, -0.39f
, 0.5f
}, // FRONT_R
218 { 1.0f
, 0.58f
, 0.58f
, 1.0f
}, // REAR_L
219 { 1.0f
, 0.46f
, -0.39f
, -0.5f
}, // FRONT_L
222 #if defined(USE_UNCOMMON_MIXERS)
223 static const motorMixer_t mixerDualcopter
[] = {
224 { 1.0f
, 0.0f
, 0.0f
, -1.0f
}, // LEFT
225 { 1.0f
, 0.0f
, 0.0f
, 1.0f
}, // RIGHT
228 #define mixerDualcopter NULL
231 static const motorMixer_t mixerSingleProp
[] = {
232 { 1.0f
, 0.0f
, 0.0f
, 0.0f
},
235 static const motorMixer_t mixerQuadX1234
[] = {
236 { 1.0f
, 1.0f
, -1.0f
, -1.0f
}, // FRONT_L
237 { 1.0f
, -1.0f
, -1.0f
, 1.0f
}, // FRONT_R
238 { 1.0f
, -1.0f
, 1.0f
, -1.0f
}, // REAR_R
239 { 1.0f
, 1.0f
, 1.0f
, 1.0f
}, // REAR_L
242 // Keep synced with mixerMode_e
243 // Some of these entries are bogus when servos (USE_SERVOS) are not configured,
244 // but left untouched to keep ordinals synced with mixerMode_e (and configurator).
245 const mixer_t mixers
[] = {
246 // motors, use servo, motor mixer
247 { 0, false, NULL
}, // entry 0
248 { 3, true, mixerTricopter
}, // MIXER_TRI
249 { 4, false, mixerQuadP
}, // MIXER_QUADP
250 { 4, false, mixerQuadX
}, // MIXER_QUADX
251 { 2, true, mixerBicopter
}, // MIXER_BICOPTER
252 { 0, true, NULL
}, // * MIXER_GIMBAL
253 { 6, false, mixerY6
}, // MIXER_Y6
254 { 6, false, mixerHex6P
}, // MIXER_HEX6
255 { 1, true, mixerSingleProp
}, // * MIXER_FLYING_WING
256 { 4, false, mixerY4
}, // MIXER_Y4
257 { 6, false, mixerHex6X
}, // MIXER_HEX6X
258 { 8, false, mixerOctoX8
}, // MIXER_OCTOX8
259 { 8, false, mixerOctoFlatP
}, // MIXER_OCTOFLATP
260 { 8, false, mixerOctoFlatX
}, // MIXER_OCTOFLATX
261 { 1, true, mixerSingleProp
}, // * MIXER_AIRPLANE
262 { 1, true, mixerSingleProp
}, // * MIXER_HELI_120_CCPM
263 { 0, true, NULL
}, // * MIXER_HELI_90_DEG
264 { 4, false, mixerVtail4
}, // MIXER_VTAIL4
265 { 6, false, mixerHex6H
}, // MIXER_HEX6H
266 { 0, true, NULL
}, // * MIXER_PPM_TO_SERVO
267 { 2, true, mixerDualcopter
}, // MIXER_DUALCOPTER
268 { 1, true, NULL
}, // MIXER_SINGLECOPTER
269 { 4, false, mixerAtail4
}, // MIXER_ATAIL4
270 { 0, false, NULL
}, // MIXER_CUSTOM
271 { 2, true, NULL
}, // MIXER_CUSTOM_AIRPLANE
272 { 3, true, NULL
}, // MIXER_CUSTOM_TRI
273 { 4, false, mixerQuadX1234
}, // MIXER_QUADX_1234
274 { 8, false, mixerOctoX8P
}, // MIXER_OCTOX8P
276 #endif // !USE_QUAD_MIXER_ONLY
278 FAST_DATA_ZERO_INIT mixerRuntime_t mixerRuntime
;
282 return mixers
[currentMixerMode
].useServo
;
285 uint8_t getMotorCount(void)
287 return mixerRuntime
.motorCount
;
290 bool areMotorsRunning(void)
292 bool motorsRunning
= false;
293 if (ARMING_FLAG(ARMED
)) {
294 motorsRunning
= true;
296 for (int i
= 0; i
< mixerRuntime
.motorCount
; i
++) {
297 if (motor_disarmed
[i
] != mixerRuntime
.disarmMotorOutput
) {
298 motorsRunning
= true;
303 return motorsRunning
;
306 bool areMotorsSaturated(void)
308 for (int i
= 0; i
< getMotorCount(); i
++) {
309 if (motor
[i
] >= motorConfig()->maxthrottle
) {
317 bool mixerIsTricopter(void)
319 return (currentMixerMode
== MIXER_TRI
|| currentMixerMode
== MIXER_CUSTOM_TRI
);
323 // All PWM motor scaling is done to standard PWM range of 1000-2000 for easier tick conversion with legacy code / configurator
324 // DSHOT scaling is done to the actual dshot range
325 void initEscEndpoints(void)
327 float motorOutputLimit
= 1.0f
;
328 if (currentPidProfile
->motor_output_limit
< 100) {
329 motorOutputLimit
= currentPidProfile
->motor_output_limit
/ 100.0f
;
331 motorInitEndpoints(motorConfig(), motorOutputLimit
, &mixerRuntime
.motorOutputLow
, &mixerRuntime
.motorOutputHigh
, &mixerRuntime
.disarmMotorOutput
, &mixerRuntime
.deadbandMotor3dHigh
, &mixerRuntime
.deadbandMotor3dLow
);
334 // Initialize pidProfile related mixer settings
336 void mixerInitProfile(void)
339 if (useDshotTelemetry
) {
340 mixerRuntime
.dynIdleMinRps
= currentPidProfile
->dyn_idle_min_rpm
* 100.0f
/ 60.0f
;
342 mixerRuntime
.dynIdleMinRps
= 0.0f
;
344 mixerRuntime
.dynIdlePGain
= currentPidProfile
->dyn_idle_p_gain
* 0.00015f
;
345 mixerRuntime
.dynIdleIGain
= currentPidProfile
->dyn_idle_i_gain
* 0.01f
* pidGetDT();
346 mixerRuntime
.dynIdleDGain
= currentPidProfile
->dyn_idle_d_gain
* 0.0000003f
* pidGetPidFrequency();
347 mixerRuntime
.dynIdleMaxIncrease
= currentPidProfile
->dyn_idle_max_increase
* 0.001f
;
348 // before takeoff, use the static idle value as the dynamic idle limit.
349 // whoop users should first adjust static idle to ensure reliable motor start before enabling dynamic idle
350 mixerRuntime
.dynIdleStartIncrease
= motorConfig()->motorIdle
* 0.0001f
;
351 mixerRuntime
.minRpsDelayK
= 800 * pidGetDT() / 20.0f
; //approx 20ms D delay, arbitrarily suits many motors
352 if (!mixerRuntime
.feature3dEnabled
&& mixerRuntime
.dynIdleMinRps
) {
353 mixerRuntime
.motorOutputLow
= DSHOT_MIN_THROTTLE
; // Override value set by initEscEndpoints to allow zero motor drive
357 #if defined(USE_BATTERY_VOLTAGE_SAG_COMPENSATION)
358 mixerRuntime
.vbatSagCompensationFactor
= 0.0f
;
359 if (currentPidProfile
->vbat_sag_compensation
> 0 && !RPM_LIMIT_ACTIVE
) {
360 //TODO: Make this voltage user configurable
361 mixerRuntime
.vbatFull
= CELL_VOLTAGE_FULL_CV
;
362 mixerRuntime
.vbatRangeToCompensate
= mixerRuntime
.vbatFull
- batteryConfig()->vbatwarningcellvoltage
;
363 if (mixerRuntime
.vbatRangeToCompensate
> 0) {
364 mixerRuntime
.vbatSagCompensationFactor
= ((float)currentPidProfile
->vbat_sag_compensation
) / 100.0f
;
370 mixerRuntime
.rpmLimiterRpmLimit
= mixerConfig()->rpm_limit_value
;
371 mixerRuntime
.rpmLimiterPGain
= mixerConfig()->rpm_limit_p
* 15e-6f
;
372 mixerRuntime
.rpmLimiterIGain
= mixerConfig()->rpm_limit_i
* 1e-3f
* pidGetDT();
373 mixerRuntime
.rpmLimiterDGain
= mixerConfig()->rpm_limit_d
* 3e-7f
* pidGetPidFrequency();
374 mixerRuntime
.rpmLimiterI
= 0.0;
375 pt1FilterInit(&mixerRuntime
.rpmLimiterAverageRpmFilter
, pt1FilterGain(6.0f
, pidGetDT()));
376 pt1FilterInit(&mixerRuntime
.rpmLimiterThrottleScaleOffsetFilter
, pt1FilterGain(2.0f
, pidGetDT()));
377 mixerResetRpmLimiter();
380 mixerRuntime
.ezLandingThreshold
= 2.0f
* currentPidProfile
->ez_landing_threshold
/ 100.0f
;
381 mixerRuntime
.ezLandingLimit
= currentPidProfile
->ez_landing_limit
/ 100.0f
;
382 mixerRuntime
.ezLandingSpeed
= 2.0f
* currentPidProfile
->ez_landing_speed
/ 10.0f
;
386 void mixerResetRpmLimiter(void)
388 mixerRuntime
.rpmLimiterI
= 0.0;
389 mixerRuntime
.rpmLimiterThrottleScale
= constrainf(mixerRuntime
.rpmLimiterRpmLimit
/ motorEstimateMaxRpm(), 0.0f
, 1.0f
);
390 mixerRuntime
.rpmLimiterInitialThrottleScale
= mixerRuntime
.rpmLimiterThrottleScale
;
393 #endif // USE_RPM_LIMIT
395 #ifdef USE_LAUNCH_CONTROL
396 // Create a custom mixer for launch control based on the current settings
397 // but disable the front motors. We don't care about roll or yaw because they
398 // are limited in the PID controller.
399 void loadLaunchControlMixer(void)
401 for (int i
= 0; i
< MAX_SUPPORTED_MOTORS
; i
++) {
402 mixerRuntime
.launchControlMixer
[i
] = mixerRuntime
.currentMixer
[i
];
403 // limit the front motors to minimum output
404 if (mixerRuntime
.launchControlMixer
[i
].pitch
< 0.0f
) {
405 mixerRuntime
.launchControlMixer
[i
].pitch
= 0.0f
;
406 mixerRuntime
.launchControlMixer
[i
].throttle
= 0.0f
;
412 #ifndef USE_QUAD_MIXER_ONLY
414 static void mixerConfigureOutput(void)
416 mixerRuntime
.motorCount
= 0;
418 if (currentMixerMode
== MIXER_CUSTOM
|| currentMixerMode
== MIXER_CUSTOM_TRI
|| currentMixerMode
== MIXER_CUSTOM_AIRPLANE
) {
419 // load custom mixer into currentMixer
420 for (int i
= 0; i
< MAX_SUPPORTED_MOTORS
; i
++) {
422 if (customMotorMixer(i
)->throttle
== 0.0f
) {
425 mixerRuntime
.currentMixer
[i
] = *customMotorMixer(i
);
426 mixerRuntime
.motorCount
++;
429 mixerRuntime
.motorCount
= mixers
[currentMixerMode
].motorCount
;
430 if (mixerRuntime
.motorCount
> MAX_SUPPORTED_MOTORS
) {
431 mixerRuntime
.motorCount
= MAX_SUPPORTED_MOTORS
;
433 // copy motor-based mixers
434 if (mixers
[currentMixerMode
].motor
) {
435 for (int i
= 0; i
< mixerRuntime
.motorCount
; i
++)
436 mixerRuntime
.currentMixer
[i
] = mixers
[currentMixerMode
].motor
[i
];
439 #ifdef USE_LAUNCH_CONTROL
440 loadLaunchControlMixer();
442 mixerResetDisarmedMotors();
445 void mixerLoadMix(int index
, motorMixer_t
*customMixers
)
450 for (int i
= 0; i
< MAX_SUPPORTED_MOTORS
; i
++) {
451 customMixers
[i
].throttle
= 0.0f
;
453 // do we have anything here to begin with?
454 if (mixers
[index
].motor
!= NULL
) {
455 for (int i
= 0; i
< mixers
[index
].motorCount
; i
++) {
456 customMixers
[i
] = mixers
[index
].motor
[i
];
461 static void mixerConfigureOutput(void)
463 mixerRuntime
.motorCount
= QUAD_MOTOR_COUNT
;
464 for (int i
= 0; i
< mixerRuntime
.motorCount
; i
++) {
465 mixerRuntime
.currentMixer
[i
] = mixerQuadX
[i
];
467 #ifdef USE_LAUNCH_CONTROL
468 loadLaunchControlMixer();
470 mixerResetDisarmedMotors();
472 #endif // USE_QUAD_MIXER_ONLY
474 void mixerInit(mixerMode_e mixerMode
)
476 currentMixerMode
= mixerMode
;
478 mixerRuntime
.feature3dEnabled
= featureIsEnabled(FEATURE_3D
);
482 if (mixerIsTricopter()) {
483 mixerTricopterInit();
488 mixerRuntime
.dynIdleI
= 0.0f
;
489 mixerRuntime
.prevMinRps
= 0.0f
;
492 mixerConfigureOutput();
495 void mixerResetDisarmedMotors(void)
497 // set disarmed motor values
498 for (int i
= 0; i
< MAX_SUPPORTED_MOTORS
; i
++) {
499 motor_disarmed
[i
] = mixerRuntime
.disarmMotorOutput
;
503 mixerMode_e
getMixerMode(void)
505 return currentMixerMode
;
508 bool mixerModeIsFixedWing(mixerMode_e mixerMode
)
511 case MIXER_FLYING_WING
:
513 case MIXER_CUSTOM_AIRPLANE
:
522 bool isFixedWing(void)
524 return mixerModeIsFixedWing(currentMixerMode
);
527 float getMotorOutputLow(void)
529 return mixerRuntime
.motorOutputLow
;
532 float getMotorOutputHigh(void)
534 return mixerRuntime
.motorOutputHigh
;