Don't handle non-MSP characters on VTX MSP port (#14091)
[betaflight.git] / src / main / flight / failsafe.c
blob28af2d6aacaf83b644f44a3c3edf2553b4f34f82
1 /*
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)
8 * any later version.
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/>.
21 #include <stdbool.h>
22 #include <stdint.h>
24 #include "platform.h"
26 #include "build/debug.h"
28 #include "common/axis.h"
30 #include "pg/pg.h"
31 #include "pg/pg_ids.h"
32 #include "pg/rx.h"
34 #include "drivers/time.h"
36 #include "config/config.h"
37 #include "fc/core.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"
46 #include "rx/rx.h"
48 #include "flight/pid.h"
51 * Usage:
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 #ifdef USE_RACE_PRO
66 #define DEFAULT_FAILSAFE_RECOVERY_DELAY 1 // 100ms of valid rx data needed to allow recovery from failsafe and arming block
67 #else
68 #define DEFAULT_FAILSAFE_RECOVERY_DELAY 5 // 500ms of valid rx data needed to allow recovery from failsafe and arming block
69 #endif
71 PG_RESET_TEMPLATE(failsafeConfig_t, failsafeConfig,
72 .failsafe_throttle = 1000, // default throttle off.
73 .failsafe_throttle_low_delay = 100, // default throttle low delay for "just disarm" on failsafe condition
74 .failsafe_delay = 15, // 1.5 sec stage 1 period, can regain control on signal recovery, at idle in drop mode
75 .failsafe_landing_time = 60, // 60 sec allowed in landing phase, if enabled, before disarm
76 .failsafe_switch_mode = FAILSAFE_SWITCH_MODE_STAGE1, // default failsafe switch action is identical to rc link loss
77 .failsafe_procedure = FAILSAFE_PROCEDURE_DROP_IT, // default full failsafe procedure is 0: auto-landing
78 .failsafe_recovery_delay = DEFAULT_FAILSAFE_RECOVERY_DELAY,
79 .failsafe_stick_threshold = 30 // 30 percent of stick deflection to exit GPS Rescue procedure
82 const char * const failsafeProcedureNames[FAILSAFE_PROCEDURE_COUNT] = {
83 "AUTO-LAND",
84 "DROP",
85 #ifdef USE_GPS_RESCUE
86 "GPS-RESCUE",
87 #endif
91 * Should called when the failsafe config needs to be changed - e.g. a different profile has been selected.
93 void failsafeReset(void)
95 failsafeState.rxDataFailurePeriod = failsafeConfig()->failsafe_delay * MILLIS_PER_TENTH_SECOND;
96 if (failsafeState.rxDataFailurePeriod < PERIOD_RXDATA_RECOVERY){
97 // avoid transients and ensure reliable arming for minimum of PERIOD_RXDATA_RECOVERY (100ms)
98 failsafeState.rxDataFailurePeriod = PERIOD_RXDATA_RECOVERY;
100 failsafeState.rxDataRecoveryPeriod = failsafeConfig()->failsafe_recovery_delay * MILLIS_PER_TENTH_SECOND;
101 if (failsafeState.rxDataRecoveryPeriod < PERIOD_RXDATA_RECOVERY) {
102 // PERIOD_RXDATA_RECOVERY (100ms) is the minimum allowed RxData recovery time
103 failsafeState.rxDataRecoveryPeriod = PERIOD_RXDATA_RECOVERY;
105 failsafeState.validRxDataReceivedAt = 0;
106 failsafeState.validRxDataFailedAt = 0;
107 failsafeState.throttleLowPeriod = 0;
108 failsafeState.landingShouldBeFinishedAt = 0;
109 failsafeState.receivingRxDataPeriod = 0;
110 failsafeState.receivingRxDataPeriodPreset = failsafeState.rxDataRecoveryPeriod;
111 failsafeState.phase = FAILSAFE_IDLE;
112 failsafeState.rxLinkState = FAILSAFE_RXLINK_DOWN;
113 failsafeState.boxFailsafeSwitchWasOn = false;
116 void failsafeInit(void)
118 failsafeState.events = 0;
119 failsafeState.monitoring = false;
121 return;
124 failsafePhase_e failsafePhase(void)
126 return failsafeState.phase;
129 bool failsafeIsMonitoring(void)
131 return failsafeState.monitoring;
134 bool failsafeIsActive(void) // real or BOXFAILSAFE induced stage 2 failsafe is currently active
136 return failsafeState.active;
139 void failsafeStartMonitoring(void)
141 failsafeState.monitoring = true;
144 static bool failsafeShouldHaveCausedLandingByNow(void)
146 return (millis() > failsafeState.landingShouldBeFinishedAt);
149 bool failsafeIsReceivingRxData(void)
151 return (failsafeState.rxLinkState == FAILSAFE_RXLINK_UP);
152 // False with BOXFAILSAFE switch or when no valid packets for RXLOSS_TRIGGER_INTERVAL or any flight channel invalid for 300ms,
153 // becomes true immediately BOXFAILSAFE switch reverts, or after recovery period expires when valid packets are received
154 // rxLinkState RXLINK_DOWN (not up) is the trigger for the various failsafe stage 2 outcomes.
157 void failsafeOnRxSuspend(uint32_t usSuspendPeriod)
159 failsafeState.validRxDataReceivedAt += (usSuspendPeriod / 1000); // / 1000 to convert micros to millis
162 void failsafeOnRxResume(void)
164 failsafeState.validRxDataReceivedAt = millis(); // prevent RX link down trigger, restart rx link up
165 failsafeState.rxLinkState = FAILSAFE_RXLINK_UP; // do so while rx link is up
168 void failsafeOnValidDataReceived(void)
169 // runs, after prior a signal loss, immediately when packets are received or the BOXFAILSAFE switch is reverted
170 // rxLinkState will go RXLINK_UP immediately if BOXFAILSAFE goes back ON since receivingRxDataPeriodPreset is set to zero in that case
171 // otherwise RXLINK_UP is delayed for the recovery period (failsafe_recovery_delay, default 500ms, 1-20, min 0.1s)
173 failsafeState.validRxDataReceivedAt = millis();
175 if (failsafeState.validRxDataFailedAt == 0) {
176 // after initialisation, we sometimes only receive valid packets, so validRxDataFailedAt will remain unset (0)
177 // in this setting, the time the signal is first valid is also time it was last valid, so
178 // initialise validRxDataFailedAt to the time of the first valid data
179 failsafeState.validRxDataFailedAt = failsafeState.validRxDataReceivedAt;
180 // prevent arming until we have valid data for rxDataRecoveryPeriod after initialisation
181 // show RXLOSS in OSD to indicate reason we cannot arm
182 setArmingDisabled(ARMING_DISABLED_RX_FAILSAFE);
185 if (cmp32(failsafeState.validRxDataReceivedAt, failsafeState.validRxDataFailedAt) > (int32_t)failsafeState.receivingRxDataPeriodPreset) {
186 // receivingRxDataPeriodPreset is rxDataRecoveryPeriod unless set to zero to allow immediate control recovery after switch induced failsafe
187 // rxDataRecoveryPeriod defaults to 1.0s with minimum of PERIOD_RXDATA_RECOVERY (100ms)
188 // link is not considered 'up', after it has been 'down', until that recovery period has expired
189 failsafeState.rxLinkState = FAILSAFE_RXLINK_UP;
190 // after the rxDataRecoveryPeriod, typically 1s after receiving valid data, clear RXLOSS in OSD and permit arming
191 unsetArmingDisabled(ARMING_DISABLED_RX_FAILSAFE);
195 void failsafeOnValidDataFailed(void)
196 // run from rc.c when packets are lost for more than RXLOSS_TRIGGER_INTERVAL, or immediately BOXFAILSAFE switch is active
197 // after the stage 1 delay has expired, sets the rxLinkState to RXLINK_DOWN, ie not up, causing failsafeIsReceivingRxData to become false
198 // if failsafe is configured to go direct to stage 2, this is emulated immediately in failsafeUpdateState()
200 // set RXLOSS in OSD and block arming after RXLOSS_TRIGGER_INTERVAL of frame loss
201 setArmingDisabled(ARMING_DISABLED_RX_FAILSAFE);
203 failsafeState.validRxDataFailedAt = millis();
204 if ((cmp32(failsafeState.validRxDataFailedAt, failsafeState.validRxDataReceivedAt) > (int32_t)failsafeState.rxDataFailurePeriod)) {
205 // sets rxLinkState = DOWN to initiate stage 2 failsafe after expiry of the Stage 1 period
206 failsafeState.rxLinkState = FAILSAFE_RXLINK_DOWN;
207 // show RXLOSS and block arming
211 void failsafeCheckDataFailurePeriod(void)
212 // runs directly from scheduler, every 10ms, to validate the link
214 if (cmp32(millis(), failsafeState.validRxDataReceivedAt) > (int32_t)failsafeState.rxDataFailurePeriod) {
215 // sets link DOWN after the stage 1 failsafe period, initiating stage 2
216 failsafeState.rxLinkState = FAILSAFE_RXLINK_DOWN;
217 // Prevent arming with no RX link
218 setArmingDisabled(ARMING_DISABLED_RX_FAILSAFE);
222 uint32_t failsafeFailurePeriodMs(void)
224 return failsafeState.rxDataFailurePeriod;
227 FAST_CODE_NOINLINE void failsafeUpdateState(void)
228 // triggered directly, and ONLY, by the scheduler, at 10ms = PERIOD_RXDATA_FAILURE - intervals
230 if (!failsafeIsMonitoring()) {
231 return;
234 bool receivingRxData = failsafeIsReceivingRxData();
235 // returns state of FAILSAFE_RXLINK_UP, which
236 // goes false after the stage 1 delay, whether from signal loss or BOXFAILSAFE switch activation
237 // goes true immediately BOXFAILSAFE switch is reverted, or after recovery delay once signal recovers
238 // essentially means 'should be in failsafe stage 2'
240 DEBUG_SET(DEBUG_FAILSAFE, 2, receivingRxData); // from Rx alone, not considering switch
242 bool armed = ARMING_FLAG(ARMED);
243 beeperMode_e beeperMode = BEEPER_SILENCE;
245 if (IS_RC_MODE_ACTIVE(BOXFAILSAFE) && (failsafeConfig()->failsafe_switch_mode == FAILSAFE_SWITCH_MODE_STAGE2)) {
246 // Force immediate stage 2 responses if mode is failsafe stage2 to emulate immediate loss of signal without waiting
247 receivingRxData = false;
250 // Beep RX lost only if we are not seeing data and are armed or have been armed earlier
251 if (!receivingRxData && (armed || ARMING_FLAG(WAS_EVER_ARMED))) {
252 beeperMode = BEEPER_RX_LOST;
255 bool reprocessState;
257 do {
258 reprocessState = false;
260 switch (failsafeState.phase) {
261 case FAILSAFE_IDLE:
262 failsafeState.boxFailsafeSwitchWasOn = IS_RC_MODE_ACTIVE(BOXFAILSAFE);
263 // store and use the switch state as it was at the start of the failsafe
264 if (armed) {
265 // Track throttle command below minimum time
266 if (calculateThrottleStatus() != THROTTLE_LOW) {
267 failsafeState.throttleLowPeriod = millis() + failsafeConfig()->failsafe_throttle_low_delay * MILLIS_PER_TENTH_SECOND;
269 if (failsafeState.boxFailsafeSwitchWasOn && (failsafeConfig()->failsafe_switch_mode == FAILSAFE_SWITCH_MODE_KILL)) {
270 // Failsafe switch is configured as KILL switch and is switched ON
271 failsafeState.active = true;
272 failsafeState.events++;
273 ENABLE_FLIGHT_MODE(FAILSAFE_MODE);
274 failsafeState.phase = FAILSAFE_LANDED;
275 // go to landed immediately
276 failsafeState.receivingRxDataPeriodPreset = failsafeState.rxDataRecoveryPeriod;
277 // allow re-arming 1 second after Rx recovery, customisable
278 reprocessState = true;
279 } else if (!receivingRxData) {
280 if (millis() > failsafeState.throttleLowPeriod
281 #ifdef USE_GPS_RESCUE
282 && failsafeConfig()->failsafe_procedure != FAILSAFE_PROCEDURE_GPS_RESCUE
283 #endif
285 // JustDisarm if throttle was LOW for at least 'failsafe_throttle_low_delay' before failsafe
286 // protects against false arming when the Tx is powered up after the quad
287 failsafeState.active = true;
288 failsafeState.events++;
289 ENABLE_FLIGHT_MODE(FAILSAFE_MODE);
290 failsafeState.phase = FAILSAFE_LANDED;
291 // go directly to FAILSAFE_LANDED
292 failsafeState.receivingRxDataPeriodPreset = failsafeState.rxDataRecoveryPeriod;
293 // allow re-arming 1 second after Rx recovery, customisable
294 } else {
295 failsafeState.phase = FAILSAFE_RX_LOSS_DETECTED;
297 reprocessState = true;
299 } else {
300 // When NOT armed, enable failsafe mode to show warnings in OSD
301 if (failsafeState.boxFailsafeSwitchWasOn) {
302 ENABLE_FLIGHT_MODE(FAILSAFE_MODE);
303 } else {
304 DISABLE_FLIGHT_MODE(FAILSAFE_MODE);
306 // Throttle low period expired (= low long enough for JustDisarm)
307 failsafeState.throttleLowPeriod = 0;
309 break;
311 case FAILSAFE_RX_LOSS_DETECTED:
312 if (receivingRxData) {
313 failsafeState.phase = FAILSAFE_RX_LOSS_RECOVERED;
314 } else {
315 failsafeState.active = true;
316 failsafeState.events++;
317 switch (failsafeConfig()->failsafe_procedure) {
318 case FAILSAFE_PROCEDURE_AUTO_LANDING:
319 // Enter Stage 2 with settings for landing mode
320 ENABLE_FLIGHT_MODE(FAILSAFE_MODE);
321 failsafeState.phase = FAILSAFE_LANDING;
322 failsafeState.landingShouldBeFinishedAt = millis() + failsafeConfig()->failsafe_landing_time * MILLIS_PER_SECOND;
323 break;
325 case FAILSAFE_PROCEDURE_DROP_IT:
326 ENABLE_FLIGHT_MODE(FAILSAFE_MODE);
327 failsafeState.phase = FAILSAFE_LANDED;
328 // go directly to FAILSAFE_LANDED
329 break;
330 #ifdef USE_GPS_RESCUE
331 case FAILSAFE_PROCEDURE_GPS_RESCUE:
332 ENABLE_FLIGHT_MODE(GPS_RESCUE_MODE);
333 failsafeState.phase = FAILSAFE_GPS_RESCUE;
334 break;
335 #endif
337 if (failsafeState.boxFailsafeSwitchWasOn) {
338 failsafeState.receivingRxDataPeriodPreset = 0;
339 // recover immediately if failsafe was triggered by a switch
340 } else {
341 failsafeState.receivingRxDataPeriodPreset = failsafeState.rxDataRecoveryPeriod;
342 // recover from true link loss failsafe 1 second after RC Link recovers
345 reprocessState = true;
346 break;
348 case FAILSAFE_LANDING:
349 if (receivingRxData) {
350 failsafeState.phase = FAILSAFE_RX_LOSS_RECOVERED;
351 reprocessState = true;
352 } else {
353 if (armed) {
354 beeperMode = BEEPER_RX_LOST_LANDING;
356 if (failsafeShouldHaveCausedLandingByNow() || crashRecoveryModeActive() || !armed) {
357 // to manually disarm while Landing, aux channels must be enabled
358 // note also that disarming via arm box must be possible during failsafe in rc_controls.c
359 // this should be blocked during signal not received periods, to avoid false disarms
360 // but should be allowed otherwise, eg after signal recovers, or during switch initiated failsafe
361 failsafeState.phase = FAILSAFE_LANDED;
362 reprocessState = true;
365 break;
366 #ifdef USE_GPS_RESCUE
367 case FAILSAFE_GPS_RESCUE:
368 if (receivingRxData) {
369 if (areSticksActive(failsafeConfig()->failsafe_stick_threshold) || failsafeState.boxFailsafeSwitchWasOn) {
370 // exits the rescue immediately if failsafe was initiated by switch, otherwise
371 // requires stick input to exit the rescue after a true Rx loss failsafe
372 // NB this test requires stick inputs to be received during GPS Rescue see PR #7936 for rationale
373 failsafeState.phase = FAILSAFE_RX_LOSS_RECOVERED;
374 reprocessState = true;
376 } else {
377 if (armed) {
378 beeperMode = BEEPER_RX_LOST_LANDING;
379 } else {
380 // to manually disarm while in GPS Rescue, aux channels must be enabled
381 failsafeState.phase = FAILSAFE_LANDED;
382 reprocessState = true;
385 break;
386 #endif
387 case FAILSAFE_LANDED:
388 disarm(DISARM_REASON_FAILSAFE);
389 setArmingDisabled(ARMING_DISABLED_FAILSAFE);
390 // prevent accidently rearming by an intermittent rx link
391 failsafeState.receivingRxDataPeriod = millis() + failsafeState.receivingRxDataPeriodPreset;
392 // customise receivingRxDataPeriod according to type of failsafe
393 failsafeState.phase = FAILSAFE_RX_LOSS_MONITORING;
394 reprocessState = true;
395 break;
397 case FAILSAFE_RX_LOSS_MONITORING:
398 // receivingRxData is true when we get valid Rx Data and the recovery period has expired
399 // for switch initiated failsafes, the recovery period is zero
400 if (receivingRxData) {
401 if (millis() > failsafeState.receivingRxDataPeriod) {
402 // rx link is good now
403 failsafeState.phase = FAILSAFE_RX_LOSS_RECOVERED;
404 reprocessState = true;
406 } else {
407 failsafeState.receivingRxDataPeriod = millis() + failsafeState.receivingRxDataPeriodPreset;
409 break;
411 case FAILSAFE_RX_LOSS_RECOVERED:
412 // Entering IDLE, terminating failsafe, reset throttle low timer
413 failsafeState.throttleLowPeriod = millis() + failsafeConfig()->failsafe_throttle_low_delay * MILLIS_PER_TENTH_SECOND;
414 failsafeState.phase = FAILSAFE_IDLE;
415 failsafeState.active = false;
416 #ifdef USE_GPS_RESCUE
417 DISABLE_FLIGHT_MODE(GPS_RESCUE_MODE);
418 #endif
419 DISABLE_FLIGHT_MODE(FAILSAFE_MODE);
420 unsetArmingDisabled(ARMING_DISABLED_FAILSAFE);
421 reprocessState = true;
422 break;
424 default:
425 break;
428 DEBUG_SET(DEBUG_FAILSAFE, 0, failsafeState.boxFailsafeSwitchWasOn);
429 DEBUG_SET(DEBUG_FAILSAFE, 3, failsafeState.phase);
431 } while (reprocessState);
433 if (beeperMode != BEEPER_SILENCE) {
434 beeper(beeperMode);