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/>.
26 #include "build/debug.h"
28 #include "common/axis.h"
31 #include "pg/pg_ids.h"
34 #include "drivers/time.h"
36 #include "config/config.h"
38 #include "fc/rc_controls.h"
39 #include "fc/rc_modes.h"
40 #include "fc/runtime_config.h"
42 #include "flight/failsafe.h"
44 #include "io/beeper.h"
48 #include "flight/pid.h"
53 * failsafeInit() and failsafeReset() must be called before the other methods are used.
55 * failsafeInit() and failsafeReset() can be called in any order.
56 * failsafeInit() should only be called once.
58 * enable() should be called after system initialisation.
61 static failsafeState_t failsafeState
;
63 PG_REGISTER_WITH_RESET_TEMPLATE(failsafeConfig_t
, failsafeConfig
, PG_FAILSAFE_CONFIG
, 2);
65 PG_RESET_TEMPLATE(failsafeConfig_t
, failsafeConfig
,
66 .failsafe_throttle
= 1000, // default throttle off.
67 .failsafe_throttle_low_delay
= 100, // default throttle low delay for "just disarm" on failsafe condition
68 .failsafe_delay
= 15, // 1.5 sec stage 1 period, can regain control on signal recovery, at idle in drop mode
69 .failsafe_off_delay
= 10, // 1 sec in landing phase, if enabled
70 .failsafe_switch_mode
= FAILSAFE_SWITCH_MODE_STAGE1
, // default failsafe switch action is identical to rc link loss
71 .failsafe_procedure
= FAILSAFE_PROCEDURE_DROP_IT
, // default full failsafe procedure is 0: auto-landing
72 .failsafe_recovery_delay
= 10, // 1 sec of valid rx data needed to allow recovering from failsafe procedure
73 .failsafe_stick_threshold
= 30 // 30 percent of stick deflection to exit GPS Rescue procedure
76 const char * const failsafeProcedureNames
[FAILSAFE_PROCEDURE_COUNT
] = {
85 * Should called when the failsafe config needs to be changed - e.g. a different profile has been selected.
87 void failsafeReset(void)
89 failsafeState
.rxDataFailurePeriod
= failsafeConfig()->failsafe_delay
* MILLIS_PER_TENTH_SECOND
;
90 if (failsafeState
.rxDataFailurePeriod
< PERIOD_RXDATA_RECOVERY
){
91 // avoid transients and ensure reliable arming for minimum of PERIOD_RXDATA_RECOVERY (200ms)
92 failsafeState
.rxDataFailurePeriod
= PERIOD_RXDATA_RECOVERY
;
94 failsafeState
.rxDataRecoveryPeriod
= failsafeConfig()->failsafe_recovery_delay
* MILLIS_PER_TENTH_SECOND
;
95 if (failsafeState
.rxDataRecoveryPeriod
< PERIOD_RXDATA_RECOVERY
) {
96 // PERIOD_RXDATA_RECOVERY (200ms) is the minimum allowed RxData recovery time
97 failsafeState
.rxDataRecoveryPeriod
= PERIOD_RXDATA_RECOVERY
;
99 failsafeState
.validRxDataReceivedAt
= 0;
100 failsafeState
.validRxDataFailedAt
= 0;
101 failsafeState
.throttleLowPeriod
= 0;
102 failsafeState
.landingShouldBeFinishedAt
= 0;
103 failsafeState
.receivingRxDataPeriod
= 0;
104 failsafeState
.receivingRxDataPeriodPreset
= failsafeState
.rxDataRecoveryPeriod
;
105 failsafeState
.phase
= FAILSAFE_IDLE
;
106 failsafeState
.rxLinkState
= FAILSAFE_RXLINK_DOWN
;
107 failsafeState
.boxFailsafeSwitchWasOn
= false;
110 void failsafeInit(void)
112 failsafeState
.events
= 0;
113 failsafeState
.monitoring
= false;
118 failsafePhase_e
failsafePhase(void)
120 return failsafeState
.phase
;
123 bool failsafeIsMonitoring(void)
125 return failsafeState
.monitoring
;
128 bool failsafeIsActive(void) // real or BOXFAILSAFE induced stage 2 failsafe is currently active
130 return failsafeState
.active
;
133 void failsafeStartMonitoring(void)
135 failsafeState
.monitoring
= true;
138 static bool failsafeShouldHaveCausedLandingByNow(void)
140 return (millis() > failsafeState
.landingShouldBeFinishedAt
);
143 bool failsafeIsReceivingRxData(void)
145 return (failsafeState
.rxLinkState
== FAILSAFE_RXLINK_UP
);
146 // False with BOXFAILSAFE switch or when no valid packets for 100ms or any flight channel invalid for 300ms,
147 // becomes true immediately BOXFAILSAFE switch reverts, or after recovery period expires when valid packets are received
148 // rxLinkState RXLINK_DOWN (not up) is the trigger for the various failsafe stage 2 outcomes.
151 void failsafeOnRxSuspend(uint32_t usSuspendPeriod
)
153 failsafeState
.validRxDataReceivedAt
+= (usSuspendPeriod
/ 1000); // / 1000 to convert micros to millis
156 void failsafeOnRxResume(void)
158 failsafeState
.validRxDataReceivedAt
= millis(); // prevent RX link down trigger, restart rx link up
159 failsafeState
.rxLinkState
= FAILSAFE_RXLINK_UP
; // do so while rx link is up
162 void failsafeOnValidDataReceived(void)
164 // runs, after prior a signal loss, immediately when packets are received or the BOXFAILSAFE switch is reverted
165 // rxLinkState will go RXLINK_UP immediately if BOXFAILSAFE goes back ON since receivingRxDataPeriodPreset is set to zero in that case
166 // otherwise RXLINK_UP is delayed for the recovery period (failsafe_recovery_delay, default 1s, 0-20, min 0.2s)
168 unsetArmingDisabled(ARMING_DISABLED_RX_FAILSAFE
);
169 // clear RXLOSS in OSD immediately we get a good packet, and un-set its arming block
171 failsafeState
.validRxDataReceivedAt
= millis();
173 if (failsafeState
.validRxDataFailedAt
== 0) {
174 // after initialisation, we sometimes only receive valid packets,
175 // then we don't know how long the signal has been valid for
176 // in this setting, the time the signal first valid is also time it was last valid, so
177 // initialise validRxDataFailedAt to the time of the first valid data
178 failsafeState
.validRxDataFailedAt
= failsafeState
.validRxDataReceivedAt
;
179 setArmingDisabled(ARMING_DISABLED_BST
);
180 // prevent arming until we have valid data for rxDataRecoveryPeriod after initialisation
181 // using the BST flag since no other suitable name....
184 if (cmp32(failsafeState
.validRxDataReceivedAt
, failsafeState
.validRxDataFailedAt
) > (int32_t)failsafeState
.receivingRxDataPeriodPreset
) {
185 // receivingRxDataPeriodPreset is rxDataRecoveryPeriod unless set to zero to allow immediate control recovery after switch induced failsafe
186 // rxDataRecoveryPeriod defaults to 1.0s with minimum of PERIOD_RXDATA_RECOVERY (200ms)
187 // link is not considered 'up', after it has been 'down', until that recovery period has expired
188 failsafeState
.rxLinkState
= FAILSAFE_RXLINK_UP
;
189 unsetArmingDisabled(ARMING_DISABLED_BST
);
193 void failsafeOnValidDataFailed(void)
194 // run from rc.c when packets are lost for more than the signal validation period (100ms), or immediately BOXFAILSAFE switch is active
195 // after the stage 1 delay has expired, sets the rxLinkState to RXLINK_DOWN, ie not up, causing failsafeIsReceivingRxData to become false
196 // if failsafe is configured to go direct to stage 2, this is emulated immediately in failsafeUpdateState()
198 setArmingDisabled(ARMING_DISABLED_RX_FAILSAFE
);
199 // set RXLOSS in OSD and block arming after 100ms of signal loss (is restored in rx.c immediately signal returns)
201 failsafeState
.validRxDataFailedAt
= millis();
202 if ((cmp32(failsafeState
.validRxDataFailedAt
, failsafeState
.validRxDataReceivedAt
) > (int32_t)failsafeState
.rxDataFailurePeriod
)) {
203 // sets rxLinkState = DOWN to initiate stage 2 failsafe
204 failsafeState
.rxLinkState
= FAILSAFE_RXLINK_DOWN
;
205 // show RXLOSS and block arming
209 void failsafeCheckDataFailurePeriod(void)
210 // runs directly from scheduler, every 10ms, to validate the link
212 if (cmp32(millis(), failsafeState
.validRxDataReceivedAt
) > (int32_t)failsafeState
.rxDataFailurePeriod
) {
213 // sets link DOWN after the stage 1 failsafe period, initiating stage 2
214 failsafeState
.rxLinkState
= FAILSAFE_RXLINK_DOWN
;
215 // Prevent arming with no RX link
216 setArmingDisabled(ARMING_DISABLED_RX_FAILSAFE
);
220 uint32_t failsafeFailurePeriodMs(void)
222 return failsafeState
.rxDataFailurePeriod
;
225 FAST_CODE_NOINLINE
void failsafeUpdateState(void)
226 // triggered directly, and ONLY, by the scheduler, at 10ms = PERIOD_RXDATA_FAILURE - intervals
228 if (!failsafeIsMonitoring()) {
232 bool receivingRxData
= failsafeIsReceivingRxData();
233 // returns state of FAILSAFE_RXLINK_UP, which
234 // goes false after the stage 1 delay, whether from signal loss or BOXFAILSAFE switch activation
235 // goes true immediately BOXFAILSAFE switch is reverted, or after recovery delay once signal recovers
236 // essentially means 'should be in failsafe stage 2'
238 DEBUG_SET(DEBUG_FAILSAFE
, 2, receivingRxData
); // from Rx alone, not considering switch
240 bool armed
= ARMING_FLAG(ARMED
);
241 beeperMode_e beeperMode
= BEEPER_SILENCE
;
243 if (IS_RC_MODE_ACTIVE(BOXFAILSAFE
) && (failsafeConfig()->failsafe_switch_mode
== FAILSAFE_SWITCH_MODE_STAGE2
)) {
244 // Force immediate stage 2 responses if mode is failsafe stage2 to emulate immediate loss of signal without waiting
245 receivingRxData
= false;
248 // Beep RX lost only if we are not seeing data and are armed or have been armed earlier
249 if (!receivingRxData
&& (armed
|| ARMING_FLAG(WAS_EVER_ARMED
))) {
250 beeperMode
= BEEPER_RX_LOST
;
256 reprocessState
= false;
258 switch (failsafeState
.phase
) {
260 failsafeState
.boxFailsafeSwitchWasOn
= IS_RC_MODE_ACTIVE(BOXFAILSAFE
);
261 // store and use the switch state as it was at the start of the failsafe
263 // Track throttle command below minimum time
264 if (calculateThrottleStatus() != THROTTLE_LOW
) {
265 failsafeState
.throttleLowPeriod
= millis() + failsafeConfig()->failsafe_throttle_low_delay
* MILLIS_PER_TENTH_SECOND
;
267 if (failsafeState
.boxFailsafeSwitchWasOn
&& (failsafeConfig()->failsafe_switch_mode
== FAILSAFE_SWITCH_MODE_KILL
)) {
268 // Failsafe switch is configured as KILL switch and is switched ON
269 failsafeState
.active
= true;
270 failsafeState
.events
++;
271 ENABLE_FLIGHT_MODE(FAILSAFE_MODE
);
272 failsafeState
.phase
= FAILSAFE_LANDED
;
273 // go to landed immediately
274 failsafeState
.receivingRxDataPeriodPreset
= failsafeState
.rxDataRecoveryPeriod
;
275 // allow re-arming 1 second after Rx recovery, customisable
276 reprocessState
= true;
277 } else if (!receivingRxData
) {
278 if (millis() > failsafeState
.throttleLowPeriod
279 #ifdef USE_GPS_RESCUE
280 && failsafeConfig()->failsafe_procedure
!= FAILSAFE_PROCEDURE_GPS_RESCUE
283 // JustDisarm if throttle was LOW for at least 'failsafe_throttle_low_delay' before failsafe
284 // protects against false arming when the Tx is powered up after the quad
285 failsafeState
.active
= true;
286 failsafeState
.events
++;
287 ENABLE_FLIGHT_MODE(FAILSAFE_MODE
);
288 failsafeState
.phase
= FAILSAFE_LANDED
;
289 // go directly to FAILSAFE_LANDED
290 failsafeState
.receivingRxDataPeriodPreset
= failsafeState
.rxDataRecoveryPeriod
;
291 // allow re-arming 1 second after Rx recovery, customisable
293 failsafeState
.phase
= FAILSAFE_RX_LOSS_DETECTED
;
295 reprocessState
= true;
298 // When NOT armed, enable failsafe mode to show warnings in OSD
299 if (failsafeState
.boxFailsafeSwitchWasOn
) {
300 ENABLE_FLIGHT_MODE(FAILSAFE_MODE
);
302 DISABLE_FLIGHT_MODE(FAILSAFE_MODE
);
304 // Throttle low period expired (= low long enough for JustDisarm)
305 failsafeState
.throttleLowPeriod
= 0;
309 case FAILSAFE_RX_LOSS_DETECTED
:
310 if (receivingRxData
) {
311 failsafeState
.phase
= FAILSAFE_RX_LOSS_RECOVERED
;
313 failsafeState
.active
= true;
314 failsafeState
.events
++;
315 switch (failsafeConfig()->failsafe_procedure
) {
316 case FAILSAFE_PROCEDURE_AUTO_LANDING
:
317 // Enter Stage 2 with settings for landing mode
318 ENABLE_FLIGHT_MODE(FAILSAFE_MODE
);
319 failsafeState
.phase
= FAILSAFE_LANDING
;
320 failsafeState
.landingShouldBeFinishedAt
= millis() + failsafeConfig()->failsafe_off_delay
* MILLIS_PER_TENTH_SECOND
;
323 case FAILSAFE_PROCEDURE_DROP_IT
:
324 ENABLE_FLIGHT_MODE(FAILSAFE_MODE
);
325 failsafeState
.phase
= FAILSAFE_LANDED
;
326 // go directly to FAILSAFE_LANDED
328 #ifdef USE_GPS_RESCUE
329 case FAILSAFE_PROCEDURE_GPS_RESCUE
:
330 ENABLE_FLIGHT_MODE(GPS_RESCUE_MODE
);
331 failsafeState
.phase
= FAILSAFE_GPS_RESCUE
;
335 if (failsafeState
.boxFailsafeSwitchWasOn
) {
336 failsafeState
.receivingRxDataPeriodPreset
= 0;
337 // recover immediately if failsafe was triggered by a switch
339 failsafeState
.receivingRxDataPeriodPreset
= failsafeState
.rxDataRecoveryPeriod
;
340 // recover from true link loss failsafe 1 second after RC Link recovers
343 reprocessState
= true;
346 case FAILSAFE_LANDING
:
347 if (receivingRxData
) {
348 failsafeState
.phase
= FAILSAFE_RX_LOSS_RECOVERED
;
349 reprocessState
= true;
352 beeperMode
= BEEPER_RX_LOST_LANDING
;
354 if (failsafeShouldHaveCausedLandingByNow() || crashRecoveryModeActive() || !armed
) {
355 // to manually disarm while Landing, aux channels must be enabled
356 // note also that disarming via arm box must be possible during failsafe in rc_controls.c
357 // this should be blocked during signal not received periods, to avoid false disarms
358 // but should be allowed otherwise, eg after signal recovers, or during switch initiated failsafe
359 failsafeState
.phase
= FAILSAFE_LANDED
;
360 reprocessState
= true;
364 #ifdef USE_GPS_RESCUE
365 case FAILSAFE_GPS_RESCUE
:
366 if (receivingRxData
) {
367 if (areSticksActive(failsafeConfig()->failsafe_stick_threshold
) || failsafeState
.boxFailsafeSwitchWasOn
) {
368 // exits the rescue immediately if failsafe was initiated by switch, otherwise
369 // requires stick input to exit the rescue after a true Rx loss failsafe
370 // NB this test requires stick inputs to be received during GPS Rescue see PR #7936 for rationale
371 failsafeState
.phase
= FAILSAFE_RX_LOSS_RECOVERED
;
372 reprocessState
= true;
376 beeperMode
= BEEPER_RX_LOST_LANDING
;
378 // to manually disarm while in GPS Rescue, aux channels must be enabled
379 failsafeState
.phase
= FAILSAFE_LANDED
;
380 reprocessState
= true;
385 case FAILSAFE_LANDED
:
386 disarm(DISARM_REASON_FAILSAFE
);
387 setArmingDisabled(ARMING_DISABLED_FAILSAFE
);
388 // prevent accidently rearming by an intermittent rx link
389 failsafeState
.receivingRxDataPeriod
= millis() + failsafeState
.receivingRxDataPeriodPreset
;
390 // customise receivingRxDataPeriod according to type of failsafe
391 failsafeState
.phase
= FAILSAFE_RX_LOSS_MONITORING
;
392 reprocessState
= true;
395 case FAILSAFE_RX_LOSS_MONITORING
:
396 // receivingRxData is true when we get valid Rx Data and the recovery period has expired
397 // for switch initiated failsafes, the recovery period is zero
398 if (receivingRxData
) {
399 if (millis() > failsafeState
.receivingRxDataPeriod
) {
400 // rx link is good now
401 failsafeState
.phase
= FAILSAFE_RX_LOSS_RECOVERED
;
402 reprocessState
= true;
405 failsafeState
.receivingRxDataPeriod
= millis() + failsafeState
.receivingRxDataPeriodPreset
;
409 case FAILSAFE_RX_LOSS_RECOVERED
:
410 // Entering IDLE, terminating failsafe, reset throttle low timer
411 failsafeState
.throttleLowPeriod
= millis() + failsafeConfig()->failsafe_throttle_low_delay
* MILLIS_PER_TENTH_SECOND
;
412 failsafeState
.phase
= FAILSAFE_IDLE
;
413 failsafeState
.active
= false;
414 #ifdef USE_GPS_RESCUE
415 DISABLE_FLIGHT_MODE(GPS_RESCUE_MODE
);
417 DISABLE_FLIGHT_MODE(FAILSAFE_MODE
);
418 unsetArmingDisabled(ARMING_DISABLED_FAILSAFE
);
419 reprocessState
= true;
426 DEBUG_SET(DEBUG_FAILSAFE
, 0, failsafeState
.boxFailsafeSwitchWasOn
);
427 DEBUG_SET(DEBUG_FAILSAFE
, 3, failsafeState
.phase
);
429 } while (reprocessState
);
431 if (beeperMode
!= BEEPER_SILENCE
) {