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
113 #if (MAX_SUPPORTED_MOTORS >= 6)
114 static const motorMixer_t mixerHex6X
[] = {
115 { 1.0f
, -0.5f
, 0.866025f
, 1.0f
}, // REAR_R
116 { 1.0f
, -0.5f
, -0.866025f
, 1.0f
}, // FRONT_R
117 { 1.0f
, 0.5f
, 0.866025f
, -1.0f
}, // REAR_L
118 { 1.0f
, 0.5f
, -0.866025f
, -1.0f
}, // FRONT_L
119 { 1.0f
, -1.0f
, 0.0f
, -1.0f
}, // RIGHT
120 { 1.0f
, 1.0f
, 0.0f
, 1.0f
}, // LEFT
123 #if defined(USE_UNCOMMON_MIXERS)
124 static const motorMixer_t mixerHex6H
[] = {
125 { 1.0f
, -1.0f
, 1.0f
, -1.0f
}, // REAR_R
126 { 1.0f
, -1.0f
, -1.0f
, 1.0f
}, // FRONT_R
127 { 1.0f
, 1.0f
, 1.0f
, 1.0f
}, // REAR_L
128 { 1.0f
, 1.0f
, -1.0f
, -1.0f
}, // FRONT_L
129 { 1.0f
, 0.0f
, 0.0f
, 0.0f
}, // RIGHT
130 { 1.0f
, 0.0f
, 0.0f
, 0.0f
}, // LEFT
133 static const motorMixer_t mixerHex6P
[] = {
134 { 1.0f
, -0.866025f
, 0.5f
, 1.0f
}, // REAR_R
135 { 1.0f
, -0.866025f
, -0.5f
, -1.0f
}, // FRONT_R
136 { 1.0f
, 0.866025f
, 0.5f
, 1.0f
}, // REAR_L
137 { 1.0f
, 0.866025f
, -0.5f
, -1.0f
}, // FRONT_L
138 { 1.0f
, 0.0f
, -1.0f
, 1.0f
}, // FRONT
139 { 1.0f
, 0.0f
, 1.0f
, -1.0f
}, // REAR
141 static const motorMixer_t mixerY6
[] = {
142 { 1.0f
, 0.0f
, 1.333333f
, 1.0f
}, // REAR
143 { 1.0f
, -1.0f
, -0.666667f
, -1.0f
}, // RIGHT
144 { 1.0f
, 1.0f
, -0.666667f
, -1.0f
}, // LEFT
145 { 1.0f
, 0.0f
, 1.333333f
, -1.0f
}, // UNDER_REAR
146 { 1.0f
, -1.0f
, -0.666667f
, 1.0f
}, // UNDER_RIGHT
147 { 1.0f
, 1.0f
, -0.666667f
, 1.0f
}, // UNDER_LEFT
150 #define mixerHex6H NULL
151 #define mixerHex6P NULL
153 #endif // USE_UNCOMMON_MIXERS
155 #define mixerHex6X NULL
156 #endif // MAX_SUPPORTED_MOTORS >= 6
158 #if defined(USE_UNCOMMON_MIXERS) && (MAX_SUPPORTED_MOTORS >= 8)
159 static const motorMixer_t mixerOctoX8
[] = {
160 { 1.0f
, -1.0f
, 1.0f
, -1.0f
}, // REAR_R
161 { 1.0f
, -1.0f
, -1.0f
, 1.0f
}, // FRONT_R
162 { 1.0f
, 1.0f
, 1.0f
, 1.0f
}, // REAR_L
163 { 1.0f
, 1.0f
, -1.0f
, -1.0f
}, // FRONT_L
164 { 1.0f
, -1.0f
, 1.0f
, 1.0f
}, // UNDER_REAR_R
165 { 1.0f
, -1.0f
, -1.0f
, -1.0f
}, // UNDER_FRONT_R
166 { 1.0f
, 1.0f
, 1.0f
, -1.0f
}, // UNDER_REAR_L
167 { 1.0f
, 1.0f
, -1.0f
, 1.0f
}, // UNDER_FRONT_L
170 static const motorMixer_t mixerOctoX8P
[] = {
171 { 1.0f
, 0.0f
, 1.0f
, -1.0f
}, // REAR
172 { 1.0f
, -1.0f
, 0.0f
, 1.0f
}, // RIGHT
173 { 1.0f
, 1.0f
, 0.0f
, 1.0f
}, // LEFT
174 { 1.0f
, 0.0f
, -1.0f
, -1.0f
}, // FRONT
175 { 1.0f
, 0.0f
, 1.0f
, 1.0f
}, // UNDER_REAR
176 { 1.0f
, -1.0f
, 0.0f
, -1.0f
}, // UNDER_RIGHT
177 { 1.0f
, 1.0f
, 0.0f
, -1.0f
}, // UNDER_LEFT
178 { 1.0f
, 0.0f
, -1.0f
, 1.0f
}, // UNDER_FRONT
181 static const motorMixer_t mixerOctoFlatP
[] = {
182 { 1.0f
, 0.707107f
, -0.707107f
, 1.0f
}, // FRONT_L
183 { 1.0f
, -0.707107f
, -0.707107f
, 1.0f
}, // FRONT_R
184 { 1.0f
, -0.707107f
, 0.707107f
, 1.0f
}, // REAR_R
185 { 1.0f
, 0.707107f
, 0.707107f
, 1.0f
}, // REAR_L
186 { 1.0f
, 0.0f
, -1.0f
, -1.0f
}, // FRONT
187 { 1.0f
, -1.0f
, 0.0f
, -1.0f
}, // RIGHT
188 { 1.0f
, 0.0f
, 1.0f
, -1.0f
}, // REAR
189 { 1.0f
, 1.0f
, 0.0f
, -1.0f
}, // LEFT
192 static const motorMixer_t mixerOctoFlatX
[] = {
193 { 1.0f
, 1.0f
, -0.414178f
, 1.0f
}, // MIDFRONT_L
194 { 1.0f
, -0.414178f
, -1.0f
, 1.0f
}, // FRONT_R
195 { 1.0f
, -1.0f
, 0.414178f
, 1.0f
}, // MIDREAR_R
196 { 1.0f
, 0.414178f
, 1.0f
, 1.0f
}, // REAR_L
197 { 1.0f
, 0.414178f
, -1.0f
, -1.0f
}, // FRONT_L
198 { 1.0f
, -1.0f
, -0.414178f
, -1.0f
}, // MIDFRONT_R
199 { 1.0f
, -0.414178f
, 1.0f
, -1.0f
}, // REAR_R
200 { 1.0f
, 1.0f
, 0.414178f
, -1.0f
}, // MIDREAR_L
203 #define mixerOctoX8 NULL
204 #define mixerOctoX8P NULL
205 #define mixerOctoFlatP NULL
206 #define mixerOctoFlatX NULL
209 static const motorMixer_t mixerVtail4
[] = {
210 { 1.0f
, -0.58f
, 0.58f
, 1.0f
}, // REAR_R
211 { 1.0f
, -0.46f
, -0.39f
, -0.5f
}, // FRONT_R
212 { 1.0f
, 0.58f
, 0.58f
, -1.0f
}, // REAR_L
213 { 1.0f
, 0.46f
, -0.39f
, 0.5f
}, // FRONT_L
216 static const motorMixer_t mixerAtail4
[] = {
217 { 1.0f
, -0.58f
, 0.58f
, -1.0f
}, // REAR_R
218 { 1.0f
, -0.46f
, -0.39f
, 0.5f
}, // FRONT_R
219 { 1.0f
, 0.58f
, 0.58f
, 1.0f
}, // REAR_L
220 { 1.0f
, 0.46f
, -0.39f
, -0.5f
}, // FRONT_L
223 #if defined(USE_UNCOMMON_MIXERS)
224 static const motorMixer_t mixerDualcopter
[] = {
225 { 1.0f
, 0.0f
, 0.0f
, -1.0f
}, // LEFT
226 { 1.0f
, 0.0f
, 0.0f
, 1.0f
}, // RIGHT
229 #define mixerDualcopter NULL
232 static const motorMixer_t mixerSingleProp
[] = {
233 { 1.0f
, 0.0f
, 0.0f
, 0.0f
},
236 static const motorMixer_t mixerQuadX1234
[] = {
237 { 1.0f
, 1.0f
, -1.0f
, -1.0f
}, // FRONT_L
238 { 1.0f
, -1.0f
, -1.0f
, 1.0f
}, // FRONT_R
239 { 1.0f
, -1.0f
, 1.0f
, -1.0f
}, // REAR_R
240 { 1.0f
, 1.0f
, 1.0f
, 1.0f
}, // REAR_L
243 // Keep synced with mixerMode_e
244 // Some of these entries are bogus when servos (USE_SERVOS) are not configured,
245 // but left untouched to keep ordinals synced with mixerMode_e (and configurator).
246 const mixer_t mixers
[] = {
247 // motors, use servo, motor mixer
248 { 0, false, NULL
}, // entry 0
249 { 3, true, mixerTricopter
}, // MIXER_TRI
250 { 4, false, mixerQuadP
}, // MIXER_QUADP
251 { 4, false, mixerQuadX
}, // MIXER_QUADX
252 { 2, true, mixerBicopter
}, // MIXER_BICOPTER
253 { 0, true, NULL
}, // * MIXER_GIMBAL
254 { 6, false, mixerY6
}, // MIXER_Y6
255 { 6, false, mixerHex6P
}, // MIXER_HEX6
256 { 1, true, mixerSingleProp
}, // * MIXER_FLYING_WING
257 { 4, false, mixerY4
}, // MIXER_Y4
258 { 6, false, mixerHex6X
}, // MIXER_HEX6X
259 { 8, false, mixerOctoX8
}, // MIXER_OCTOX8
260 { 8, false, mixerOctoFlatP
}, // MIXER_OCTOFLATP
261 { 8, false, mixerOctoFlatX
}, // MIXER_OCTOFLATX
262 { 1, true, mixerSingleProp
}, // * MIXER_AIRPLANE
263 { 1, true, mixerSingleProp
}, // * MIXER_HELI_120_CCPM
264 { 0, true, NULL
}, // * MIXER_HELI_90_DEG
265 { 4, false, mixerVtail4
}, // MIXER_VTAIL4
266 { 6, false, mixerHex6H
}, // MIXER_HEX6H
267 { 0, true, NULL
}, // * MIXER_PPM_TO_SERVO
268 { 2, true, mixerDualcopter
}, // MIXER_DUALCOPTER
269 { 1, true, NULL
}, // MIXER_SINGLECOPTER
270 { 4, false, mixerAtail4
}, // MIXER_ATAIL4
271 { 0, false, NULL
}, // MIXER_CUSTOM
272 { 2, true, NULL
}, // MIXER_CUSTOM_AIRPLANE
273 { 3, true, NULL
}, // MIXER_CUSTOM_TRI
274 { 4, false, mixerQuadX1234
}, // MIXER_QUADX_1234
275 { 8, false, mixerOctoX8P
}, // MIXER_OCTOX8P
277 #endif // !USE_QUAD_MIXER_ONLY
279 FAST_DATA_ZERO_INIT mixerRuntime_t mixerRuntime
;
283 return mixers
[currentMixerMode
].useServo
;
286 uint8_t getMotorCount(void)
288 return mixerRuntime
.motorCount
;
291 bool areMotorsRunning(void)
293 bool motorsRunning
= false;
294 if (ARMING_FLAG(ARMED
)) {
295 motorsRunning
= true;
297 for (int i
= 0; i
< mixerRuntime
.motorCount
; i
++) {
298 if (motor_disarmed
[i
] != mixerRuntime
.disarmMotorOutput
) {
299 motorsRunning
= true;
304 return motorsRunning
;
307 bool areMotorsSaturated(void)
309 for (int i
= 0; i
< getMotorCount(); i
++) {
310 if (motor
[i
] >= motorConfig()->maxthrottle
) {
318 bool mixerIsTricopter(void)
320 return (currentMixerMode
== MIXER_TRI
|| currentMixerMode
== MIXER_CUSTOM_TRI
);
324 // All PWM motor scaling is done to standard PWM range of 1000-2000 for easier tick conversion with legacy code / configurator
325 // DSHOT scaling is done to the actual dshot range
326 void initEscEndpoints(void)
328 float motorOutputLimit
= 1.0f
;
329 if (currentPidProfile
->motor_output_limit
< 100) {
330 motorOutputLimit
= currentPidProfile
->motor_output_limit
/ 100.0f
;
332 motorInitEndpoints(motorConfig(), motorOutputLimit
, &mixerRuntime
.motorOutputLow
, &mixerRuntime
.motorOutputHigh
, &mixerRuntime
.disarmMotorOutput
, &mixerRuntime
.deadbandMotor3dHigh
, &mixerRuntime
.deadbandMotor3dLow
);
335 // Initialize pidProfile related mixer settings
337 void mixerInitProfile(void)
340 if (useDshotTelemetry
) {
341 mixerRuntime
.dynIdleMinRps
= currentPidProfile
->dyn_idle_min_rpm
* 100.0f
/ 60.0f
;
343 mixerRuntime
.dynIdleMinRps
= 0.0f
;
345 mixerRuntime
.dynIdlePGain
= currentPidProfile
->dyn_idle_p_gain
* 0.00015f
;
346 mixerRuntime
.dynIdleIGain
= currentPidProfile
->dyn_idle_i_gain
* 0.01f
* pidGetDT();
347 mixerRuntime
.dynIdleDGain
= currentPidProfile
->dyn_idle_d_gain
* 0.0000003f
* pidGetPidFrequency();
348 mixerRuntime
.dynIdleMaxIncrease
= currentPidProfile
->dyn_idle_max_increase
* 0.001f
;
349 // before takeoff, use the static idle value as the dynamic idle limit.
350 // whoop users should first adjust static idle to ensure reliable motor start before enabling dynamic idle
351 mixerRuntime
.dynIdleStartIncrease
= motorConfig()->motorIdle
* 0.0001f
;
352 mixerRuntime
.minRpsDelayK
= 800 * pidGetDT() / 20.0f
; //approx 20ms D delay, arbitrarily suits many motors
353 if (!mixerRuntime
.feature3dEnabled
&& mixerRuntime
.dynIdleMinRps
) {
354 mixerRuntime
.motorOutputLow
= DSHOT_MIN_THROTTLE
; // Override value set by initEscEndpoints to allow zero motor drive
358 #if defined(USE_BATTERY_VOLTAGE_SAG_COMPENSATION)
359 mixerRuntime
.vbatSagCompensationFactor
= 0.0f
;
360 if (currentPidProfile
->vbat_sag_compensation
> 0 && !RPM_LIMIT_ACTIVE
) {
361 //TODO: Make this voltage user configurable
362 mixerRuntime
.vbatFull
= CELL_VOLTAGE_FULL_CV
;
363 mixerRuntime
.vbatRangeToCompensate
= mixerRuntime
.vbatFull
- batteryConfig()->vbatwarningcellvoltage
;
364 if (mixerRuntime
.vbatRangeToCompensate
> 0) {
365 mixerRuntime
.vbatSagCompensationFactor
= ((float)currentPidProfile
->vbat_sag_compensation
) / 100.0f
;
371 mixerRuntime
.rpmLimiterRpmLimit
= mixerConfig()->rpm_limit_value
;
372 mixerRuntime
.rpmLimiterPGain
= mixerConfig()->rpm_limit_p
* 15e-6f
;
373 mixerRuntime
.rpmLimiterIGain
= mixerConfig()->rpm_limit_i
* 1e-3f
* pidGetDT();
374 mixerRuntime
.rpmLimiterDGain
= mixerConfig()->rpm_limit_d
* 3e-7f
* pidGetPidFrequency();
375 mixerRuntime
.rpmLimiterI
= 0.0;
376 pt1FilterInit(&mixerRuntime
.rpmLimiterAverageRpmFilter
, pt1FilterGain(6.0f
, pidGetDT()));
377 pt1FilterInit(&mixerRuntime
.rpmLimiterThrottleScaleOffsetFilter
, pt1FilterGain(2.0f
, pidGetDT()));
378 mixerResetRpmLimiter();
381 mixerRuntime
.ezLandingThreshold
= 2.0f
* currentPidProfile
->ez_landing_threshold
/ 100.0f
;
382 mixerRuntime
.ezLandingLimit
= currentPidProfile
->ez_landing_limit
/ 100.0f
;
383 mixerRuntime
.ezLandingSpeed
= 2.0f
* currentPidProfile
->ez_landing_speed
/ 10.0f
;
387 void mixerResetRpmLimiter(void)
389 mixerRuntime
.rpmLimiterI
= 0.0;
390 mixerRuntime
.rpmLimiterThrottleScale
= constrainf(mixerRuntime
.rpmLimiterRpmLimit
/ motorEstimateMaxRpm(), 0.0f
, 1.0f
);
391 mixerRuntime
.rpmLimiterInitialThrottleScale
= mixerRuntime
.rpmLimiterThrottleScale
;
394 #endif // USE_RPM_LIMIT
396 #ifdef USE_LAUNCH_CONTROL
397 // Create a custom mixer for launch control based on the current settings
398 // but disable the front motors. We don't care about roll or yaw because they
399 // are limited in the PID controller.
400 void loadLaunchControlMixer(void)
402 for (int i
= 0; i
< MAX_SUPPORTED_MOTORS
; i
++) {
403 mixerRuntime
.launchControlMixer
[i
] = mixerRuntime
.currentMixer
[i
];
404 // limit the front motors to minimum output
405 if (mixerRuntime
.launchControlMixer
[i
].pitch
< 0.0f
) {
406 mixerRuntime
.launchControlMixer
[i
].pitch
= 0.0f
;
407 mixerRuntime
.launchControlMixer
[i
].throttle
= 0.0f
;
413 #ifndef USE_QUAD_MIXER_ONLY
415 static void mixerConfigureOutput(void)
417 mixerRuntime
.motorCount
= 0;
419 if (currentMixerMode
== MIXER_CUSTOM
|| currentMixerMode
== MIXER_CUSTOM_TRI
|| currentMixerMode
== MIXER_CUSTOM_AIRPLANE
) {
420 // load custom mixer into currentMixer
421 for (int i
= 0; i
< MAX_SUPPORTED_MOTORS
; i
++) {
423 if (customMotorMixer(i
)->throttle
== 0.0f
) {
426 mixerRuntime
.currentMixer
[i
] = *customMotorMixer(i
);
427 mixerRuntime
.motorCount
++;
430 mixerRuntime
.motorCount
= mixers
[currentMixerMode
].motorCount
;
431 if (mixerRuntime
.motorCount
> MAX_SUPPORTED_MOTORS
) {
432 mixerRuntime
.motorCount
= MAX_SUPPORTED_MOTORS
;
434 // copy motor-based mixers
435 if (mixers
[currentMixerMode
].motor
) {
436 for (int i
= 0; i
< mixerRuntime
.motorCount
; i
++)
437 mixerRuntime
.currentMixer
[i
] = mixers
[currentMixerMode
].motor
[i
];
440 #ifdef USE_LAUNCH_CONTROL
441 loadLaunchControlMixer();
443 mixerResetDisarmedMotors();
446 void mixerLoadMix(int index
, motorMixer_t
*customMixers
)
451 for (int i
= 0; i
< MAX_SUPPORTED_MOTORS
; i
++) {
452 customMixers
[i
].throttle
= 0.0f
;
454 // do we have anything here to begin with?
455 if (mixers
[index
].motor
!= NULL
) {
456 for (int i
= 0; i
< mixers
[index
].motorCount
; i
++) {
457 customMixers
[i
] = mixers
[index
].motor
[i
];
462 static void mixerConfigureOutput(void)
464 mixerRuntime
.motorCount
= QUAD_MOTOR_COUNT
;
465 for (int i
= 0; i
< mixerRuntime
.motorCount
; i
++) {
466 mixerRuntime
.currentMixer
[i
] = mixerQuadX
[i
];
468 #ifdef USE_LAUNCH_CONTROL
469 loadLaunchControlMixer();
471 mixerResetDisarmedMotors();
473 #endif // USE_QUAD_MIXER_ONLY
475 void mixerInit(mixerMode_e mixerMode
)
477 currentMixerMode
= mixerMode
;
479 mixerRuntime
.feature3dEnabled
= featureIsEnabled(FEATURE_3D
);
483 if (mixerIsTricopter()) {
484 mixerTricopterInit();
489 mixerRuntime
.dynIdleI
= 0.0f
;
490 mixerRuntime
.prevMinRps
= 0.0f
;
493 mixerConfigureOutput();
496 void mixerResetDisarmedMotors(void)
498 // set disarmed motor values
499 for (int i
= 0; i
< MAX_SUPPORTED_MOTORS
; i
++) {
500 motor_disarmed
[i
] = mixerRuntime
.disarmMotorOutput
;
504 mixerMode_e
getMixerMode(void)
506 return currentMixerMode
;
509 bool mixerModeIsFixedWing(mixerMode_e mixerMode
)
512 case MIXER_FLYING_WING
:
514 case MIXER_CUSTOM_AIRPLANE
:
523 bool isFixedWing(void)
525 return mixerModeIsFixedWing(currentMixerMode
);
528 float getMotorOutputLow(void)
530 return mixerRuntime
.motorOutputLow
;
533 float getMotorOutputHigh(void)
535 return mixerRuntime
.motorOutputHigh
;