2 * This file is part of Cleanflight.
4 * Cleanflight is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
9 * Cleanflight is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with Cleanflight. If not, see <http://www.gnu.org/licenses/>.
26 #include "build/debug.h"
28 #include "blackbox/blackbox.h"
32 #include "common/axis.h"
33 #include "common/maths.h"
34 #include "common/utils.h"
36 #include "config/feature.h"
37 #include "config/parameter_group.h"
38 #include "config/parameter_group_ids.h"
40 #include "drivers/time.h"
43 #include "fc/config.h"
44 #include "fc/controlrate_profile.h"
45 #include "fc/fc_core.h"
46 #include "fc/rc_controls.h"
47 #include "fc/rc_curves.h"
48 #include "fc/rc_modes.h"
49 #include "fc/runtime_config.h"
50 #include "fc/settings.h"
52 #include "flight/pid.h"
53 #include "flight/failsafe.h"
56 #include "io/beeper.h"
58 #include "navigation/navigation.h"
62 #include "sensors/barometer.h"
63 #include "sensors/battery.h"
64 #include "sensors/sensors.h"
65 #include "sensors/gyro.h"
66 #include "sensors/acceleration.h"
68 #define AIRMODE_DEADBAND 25
69 #define MIN_RC_TICK_INTERVAL_MS 20
70 #define DEFAULT_RC_SWITCH_DISARM_DELAY_MS 250 // Wait at least 250ms before disarming via switch
71 #define DEFAULT_PREARM_TIMEOUT 10000 // Prearm is invalidated after 10 seconds
73 stickPositions_e rcStickPositions
;
75 FASTRAM
int16_t rcCommand
[4]; // interval [1000;2000] for THROTTLE and [-500;+500] for ROLL/PITCH/YAW
77 PG_REGISTER_WITH_RESET_TEMPLATE(rcControlsConfig_t
, rcControlsConfig
, PG_RC_CONTROLS_CONFIG
, 3);
79 PG_RESET_TEMPLATE(rcControlsConfig_t
, rcControlsConfig
,
80 .deadband
= SETTING_DEADBAND_DEFAULT
,
81 .yaw_deadband
= SETTING_YAW_DEADBAND_DEFAULT
,
82 .pos_hold_deadband
= SETTING_POS_HOLD_DEADBAND_DEFAULT
,
83 .control_deadband
= SETTING_CONTROL_DEADBAND_DEFAULT
,
84 .alt_hold_deadband
= SETTING_ALT_HOLD_DEADBAND_DEFAULT
,
85 .mid_throttle_deadband
= SETTING_3D_DEADBAND_THROTTLE_DEFAULT
,
86 .airmodeHandlingType
= SETTING_AIRMODE_TYPE_DEFAULT
,
87 .airmodeThrottleThreshold
= SETTING_AIRMODE_THROTTLE_THRESHOLD_DEFAULT
,
90 PG_REGISTER_WITH_RESET_TEMPLATE(armingConfig_t
, armingConfig
, PG_ARMING_CONFIG
, 2);
92 PG_RESET_TEMPLATE(armingConfig_t
, armingConfig
,
93 .fixed_wing_auto_arm
= SETTING_FIXED_WING_AUTO_ARM_DEFAULT
,
94 .disarm_kill_switch
= SETTING_DISARM_KILL_SWITCH_DEFAULT
,
95 .switchDisarmDelayMs
= SETTING_SWITCH_DISARM_DELAY_DEFAULT
,
96 .prearmTimeoutMs
= SETTING_PREARM_TIMEOUT_DEFAULT
,
99 bool areSticksInApModePosition(uint16_t ap_mode
)
101 return ABS(rcCommand
[ROLL
]) < ap_mode
&& ABS(rcCommand
[PITCH
]) < ap_mode
;
104 bool areSticksDeflected(void)
106 return (ABS(rcCommand
[ROLL
]) > rcControlsConfig()->control_deadband
) || (ABS(rcCommand
[PITCH
]) > rcControlsConfig()->control_deadband
) || (ABS(rcCommand
[YAW
]) > rcControlsConfig()->control_deadband
);
109 bool isRollPitchStickDeflected(uint8_t deadband
)
111 return (ABS(rcCommand
[ROLL
]) > deadband
) || (ABS(rcCommand
[PITCH
]) > deadband
);
114 throttleStatus_e FAST_CODE NOINLINE
calculateThrottleStatus(throttleStatusType_e type
)
116 int value
= rxGetChannelValue(THROTTLE
); // THROTTLE_STATUS_TYPE_RC
117 if (type
== THROTTLE_STATUS_TYPE_COMMAND
) {
118 value
= rcCommand
[THROTTLE
];
121 const uint16_t mid_throttle_deadband
= rcControlsConfig()->mid_throttle_deadband
;
122 bool midThrottle
= value
> (PWM_RANGE_MIDDLE
- mid_throttle_deadband
) && value
< (PWM_RANGE_MIDDLE
+ mid_throttle_deadband
);
123 if ((feature(FEATURE_REVERSIBLE_MOTORS
) && midThrottle
) || (!feature(FEATURE_REVERSIBLE_MOTORS
) && (value
< rxConfig()->mincheck
))) {
127 return THROTTLE_HIGH
;
130 bool throttleStickIsLow(void)
132 return calculateThrottleStatus(feature(FEATURE_REVERSIBLE_MOTORS
) ? THROTTLE_STATUS_TYPE_COMMAND
: THROTTLE_STATUS_TYPE_RC
) == THROTTLE_LOW
;
135 int16_t throttleStickMixedValue(void)
137 int16_t throttleValue
;
139 throttleValue
= constrain(rxGetChannelValue(THROTTLE
), rxConfig()->mincheck
, PWM_RANGE_MAX
);
140 throttleValue
= (uint16_t)(throttleValue
- rxConfig()->mincheck
) * PWM_RANGE_MIN
/ (PWM_RANGE_MAX
- rxConfig()->mincheck
); // [MINCHECK;2000] -> [0;1000]
141 return rcLookupThrottle(throttleValue
);
144 rollPitchStatus_e
calculateRollPitchCenterStatus(void)
146 if (((rxGetChannelValue(PITCH
) < (PWM_RANGE_MIDDLE
+ AIRMODE_DEADBAND
)) && (rxGetChannelValue(PITCH
) > (PWM_RANGE_MIDDLE
-AIRMODE_DEADBAND
)))
147 && ((rxGetChannelValue(ROLL
) < (PWM_RANGE_MIDDLE
+ AIRMODE_DEADBAND
)) && (rxGetChannelValue(ROLL
) > (PWM_RANGE_MIDDLE
-AIRMODE_DEADBAND
))))
153 stickPositions_e
getRcStickPositions(void)
155 return rcStickPositions
;
158 bool checkStickPosition(stickPositions_e stickPos
)
160 const uint8_t mask
[4] = { ROL_LO
| ROL_HI
, PIT_LO
| PIT_HI
, YAW_LO
| YAW_HI
, THR_LO
| THR_HI
};
161 for (int i
= 0; i
< 4; i
++) {
162 if (((stickPos
& mask
[i
]) != 0) && ((stickPos
& mask
[i
]) != (rcStickPositions
& mask
[i
]))) {
170 static void updateRcStickPositions(void)
172 stickPositions_e tmp
= 0;
174 tmp
|= ((rxGetChannelValue(ROLL
) > rxConfig()->mincheck
) ? 0x02 : 0x00) << (ROLL
* 2);
175 tmp
|= ((rxGetChannelValue(ROLL
) < rxConfig()->maxcheck
) ? 0x01 : 0x00) << (ROLL
* 2);
177 tmp
|= ((rxGetChannelValue(PITCH
) > rxConfig()->mincheck
) ? 0x02 : 0x00) << (PITCH
* 2);
178 tmp
|= ((rxGetChannelValue(PITCH
) < rxConfig()->maxcheck
) ? 0x01 : 0x00) << (PITCH
* 2);
180 tmp
|= ((rxGetChannelValue(YAW
) > rxConfig()->mincheck
) ? 0x02 : 0x00) << (YAW
* 2);
181 tmp
|= ((rxGetChannelValue(YAW
) < rxConfig()->maxcheck
) ? 0x01 : 0x00) << (YAW
* 2);
183 tmp
|= ((rxGetChannelValue(THROTTLE
) > rxConfig()->mincheck
) ? 0x02 : 0x00) << (THROTTLE
* 2);
184 tmp
|= ((rxGetChannelValue(THROTTLE
) < rxConfig()->maxcheck
) ? 0x01 : 0x00) << (THROTTLE
* 2);
186 rcStickPositions
= tmp
;
189 void processRcStickPositions(bool isThrottleLow
)
191 static timeMs_t lastTickTimeMs
= 0;
192 static uint8_t rcDelayCommand
; // this indicates the number of time (multiple of RC measurement at 50Hz) the sticks must be maintained to run or switch off motors
193 static uint32_t rcSticks
; // this hold sticks position for command combos
194 static timeMs_t rcDisarmTimeMs
; // this is an extra guard for disarming through switch to prevent that one frame can disarm it
195 const timeMs_t currentTimeMs
= millis();
197 updateRcStickPositions();
199 uint32_t stTmp
= getRcStickPositions();
200 if (stTmp
== rcSticks
) {
201 if (rcDelayCommand
< 250) {
202 if ((currentTimeMs
- lastTickTimeMs
) >= MIN_RC_TICK_INTERVAL_MS
) {
203 lastTickTimeMs
= currentTimeMs
;
214 bool armingSwitchIsActive
= IS_RC_MODE_ACTIVE(BOXARM
);
216 if (STATE(AIRPLANE
) && feature(FEATURE_MOTOR_STOP
) && armingConfig()->fixed_wing_auto_arm
) {
217 // Auto arm on throttle when using fixedwing and motorstop
218 if (!isThrottleLow
) {
224 if (armingSwitchIsActive
) {
225 rcDisarmTimeMs
= currentTimeMs
;
228 emergencyArmingUpdate(armingSwitchIsActive
);
229 // Disarming via ARM BOX
230 // Don't disarm via switch if failsafe is active or receiver doesn't receive data - we can't trust receiver
231 // and can't afford to risk disarming in the air
232 if (ARMING_FLAG(ARMED
) && !IS_RC_MODE_ACTIVE(BOXFAILSAFE
) && rxIsReceivingSignal() && !failsafeIsActive()) {
233 const timeMs_t disarmDelay
= currentTimeMs
- rcDisarmTimeMs
;
234 if (disarmDelay
> armingConfig()->switchDisarmDelayMs
) {
235 if (armingConfig()->disarm_kill_switch
|| isThrottleLow
) {
236 disarm(DISARM_SWITCH
);
241 rcDisarmTimeMs
= currentTimeMs
;
245 // KILLSWITCH disarms instantly
246 if (IS_RC_MODE_ACTIVE(BOXKILLSWITCH
)) {
247 disarm(DISARM_KILLSWITCH
);
251 if (rcDelayCommand
!= 20) {
255 /* Disable stick commands when armed, in CLI mode or CMS is active */
256 bool disableStickCommands
= ARMING_FLAG(ARMED
) || cliMode
;
258 disableStickCommands
= disableStickCommands
|| cmsInMenu
;
260 if (disableStickCommands
) {
264 /* REMAINING SECTION HANDLES STICK COMANDS ONLY */
267 if (rcSticks
== THR_LO
+ YAW_LO
+ PIT_LO
+ ROL_CE
) {
268 gyroStartCalibration();
273 #if defined(NAV_NON_VOLATILE_WAYPOINT_STORAGE)
274 // Save waypoint list
275 if (rcSticks
== THR_LO
+ YAW_CE
+ PIT_HI
+ ROL_LO
) {
276 const bool success
= saveNonVolatileWaypointList();
277 beeper(success
? BEEPER_ACTION_SUCCESS
: BEEPER_ACTION_FAIL
);
280 // Load waypoint list
281 if (rcSticks
== THR_LO
+ YAW_CE
+ PIT_HI
+ ROL_HI
) {
282 const bool success
= loadNonVolatileWaypointList(false);
283 beeper(success
? BEEPER_ACTION_SUCCESS
: BEEPER_ACTION_FAIL
);
285 #ifdef USE_MULTI_MISSION
286 // Increment multi mission index up
287 if (rcSticks
== THR_LO
+ YAW_CE
+ PIT_CE
+ ROL_HI
) {
288 selectMultiMissionIndex(1);
293 // Decrement multi mission index down
294 if (rcSticks
== THR_LO
+ YAW_CE
+ PIT_CE
+ ROL_LO
) {
295 selectMultiMissionIndex(-1);
300 if (rcSticks
== THR_LO
+ YAW_CE
+ PIT_LO
+ ROL_HI
) {
302 beeper(BEEPER_ACTION_FAIL
); // The above cannot fail, but traditionally, we play FAIL for not-loading
306 // Multiple configuration profiles
307 if (feature(FEATURE_TX_PROF_SEL
)) {
311 if (rcSticks
== THR_LO
+ YAW_LO
+ PIT_CE
+ ROL_LO
) // ROLL left -> Profile 1
313 else if (rcSticks
== THR_LO
+ YAW_LO
+ PIT_HI
+ ROL_CE
) // PITCH up -> Profile 2
315 else if (rcSticks
== THR_LO
+ YAW_LO
+ PIT_CE
+ ROL_HI
) // ROLL right -> Profile 3
319 setConfigProfileAndWriteEEPROM(i
- 1);
325 // Multiple battery configuration profiles
326 if (rcSticks
== THR_HI
+ YAW_LO
+ PIT_CE
+ ROL_LO
) // ROLL left -> Profile 1
328 else if (rcSticks
== THR_HI
+ YAW_LO
+ PIT_HI
+ ROL_CE
) // PITCH up -> Profile 2
330 else if (rcSticks
== THR_HI
+ YAW_LO
+ PIT_CE
+ ROL_HI
) // ROLL right -> Profile 3
334 setConfigBatteryProfileAndWriteEEPROM(i
- 1);
335 batteryDisableProfileAutoswitch();
336 activateBatteryProfile();
343 if (rcSticks
== THR_LO
+ YAW_LO
+ PIT_LO
+ ROL_HI
) {
344 saveConfigAndNotify();
348 if (rcSticks
== THR_HI
+ YAW_LO
+ PIT_LO
+ ROL_CE
) {
349 accStartCalibration();
354 if (rcSticks
== THR_HI
+ YAW_HI
+ PIT_LO
+ ROL_CE
) {
355 ENABLE_STATE(CALIBRATE_MAG
);
359 // Accelerometer Trim
360 if (rcSticks
== THR_HI
+ YAW_CE
+ PIT_HI
+ ROL_CE
) {
361 applyAndSaveBoardAlignmentDelta(0, -2);
364 } else if (rcSticks
== THR_HI
+ YAW_CE
+ PIT_LO
+ ROL_CE
) {
365 applyAndSaveBoardAlignmentDelta(0, 2);
368 } else if (rcSticks
== THR_HI
+ YAW_CE
+ PIT_CE
+ ROL_HI
) {
369 applyAndSaveBoardAlignmentDelta(-2, 0);
372 } else if (rcSticks
== THR_HI
+ YAW_CE
+ PIT_CE
+ ROL_LO
) {
373 applyAndSaveBoardAlignmentDelta(2, 0);
379 int32_t getRcStickDeflection(int32_t axis
) {
380 return MIN(ABS(rxGetChannelValue(axis
) - PWM_RANGE_MIDDLE
), 500);