Merge pull request #11500 from klutvott123/crsf-baud-negotiation-improvements-2
[betaflight.git] / src / main / flight / failsafe.c
blob442dd0f7d8f46daf3cc806e8df7bda3fca443621
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 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] = {
77 "AUTO-LAND",
78 "DROP",
79 #ifdef USE_GPS_RESCUE
80 "GPS-RESCUE",
81 #endif
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 = 0;
105 failsafeState.phase = FAILSAFE_IDLE;
106 failsafeState.rxLinkState = FAILSAFE_RXLINK_DOWN;
109 void failsafeInit(void)
111 failsafeState.events = 0;
112 failsafeState.monitoring = false;
114 return;
117 failsafePhase_e failsafePhase(void)
119 return failsafeState.phase;
122 bool failsafeIsMonitoring(void)
124 return failsafeState.monitoring;
127 bool failsafeIsActive(void)
129 return failsafeState.active;
132 void failsafeStartMonitoring(void)
134 failsafeState.monitoring = true;
137 static bool failsafeShouldHaveCausedLandingByNow(void)
139 return (millis() > failsafeState.landingShouldBeFinishedAt);
142 bool failsafeIsReceivingRxData(void)
144 return (failsafeState.rxLinkState == FAILSAFE_RXLINK_UP);
147 void failsafeOnRxSuspend(uint32_t usSuspendPeriod)
149 failsafeState.validRxDataReceivedAt += (usSuspendPeriod / 1000); // / 1000 to convert micros to millis
152 void failsafeOnRxResume(void)
154 failsafeState.validRxDataReceivedAt = millis(); // prevent RX link down trigger, restart rx link up
155 failsafeState.rxLinkState = FAILSAFE_RXLINK_UP; // do so while rx link is up
158 void failsafeOnValidDataReceived(void)
159 // runs when packets are received for more than the signal validation period (100ms)
161 unsetArmingDisabled(ARMING_DISABLED_RX_FAILSAFE);
162 // clear RXLOSS in OSD immediately we get a good packet, and un-set its arming block
164 failsafeState.validRxDataReceivedAt = millis();
166 if (failsafeState.validRxDataFailedAt == 0) {
167 // after initialisation, we sometimes only receive valid packets,
168 // then we don't know how long the signal has been valid for
169 // in this setting, the time the signal first valid is also time it was last valid, so
170 // initialise validRxDataFailedAt to the time of the first valid data
171 failsafeState.validRxDataFailedAt = failsafeState.validRxDataReceivedAt;
172 setArmingDisabled(ARMING_DISABLED_BST);
173 // prevent arming until we have valid data for rxDataRecoveryPeriod after initialisation
174 // using the BST flag since no other suitable name....
177 if (cmp32(failsafeState.validRxDataReceivedAt, failsafeState.validRxDataFailedAt) > (int32_t)failsafeState.rxDataRecoveryPeriod){
178 // rxDataRecoveryPeriod defaults to 1.0s with minimum of PERIOD_RXDATA_RECOVERY (200ms)
179 failsafeState.rxLinkState = FAILSAFE_RXLINK_UP;
180 unsetArmingDisabled(ARMING_DISABLED_BST);
184 void failsafeOnValidDataFailed(void)
185 // runs when packets are lost for more than the signal validation period (100ms)
187 setArmingDisabled(ARMING_DISABLED_RX_FAILSAFE);
188 // set RXLOSS in OSD and block arming after 100ms of signal loss (is restored in rx.c immediately signal returns)
190 failsafeState.validRxDataFailedAt = millis();
191 if ((cmp32(failsafeState.validRxDataFailedAt, failsafeState.validRxDataReceivedAt) > (int32_t)failsafeState.rxDataFailurePeriod)) {
192 // sets rxLinkState = DOWN to initiate stage 2 failsafe, if no validated signal for the stage 1 period
193 failsafeState.rxLinkState = FAILSAFE_RXLINK_DOWN;
194 // show RXLOSS and block arming
198 void failsafeCheckDataFailurePeriod(void)
199 // runs directly from scheduler, every 10ms, to validate the link
201 if (cmp32(millis(), failsafeState.validRxDataReceivedAt) > (int32_t)failsafeState.rxDataFailurePeriod) {
202 // sets link DOWN after the stage 1 failsafe period, initiating stage 2
203 failsafeState.rxLinkState = FAILSAFE_RXLINK_DOWN;
204 // Prevent arming with no RX link
205 setArmingDisabled(ARMING_DISABLED_RX_FAILSAFE);
209 uint32_t failsafeFailurePeriodMs(void)
211 return failsafeState.rxDataFailurePeriod;
214 FAST_CODE_NOINLINE void failsafeUpdateState(void)
215 // triggered directly, and ONLY, by the cheduler, at 10ms = PERIOD_RXDATA_FAILURE - intervals
217 if (!failsafeIsMonitoring()) {
218 return;
221 bool receivingRxData = failsafeIsReceivingRxData();
222 // should be true when FAILSAFE_RXLINK_UP
223 // FAILSAFE_RXLINK_UP is set in failsafeOnValidDataReceived
224 // failsafeOnValidDataReceived runs from detectAndApplySignalLossBehaviour
226 bool armed = ARMING_FLAG(ARMED);
227 bool failsafeSwitchIsOn = IS_RC_MODE_ACTIVE(BOXFAILSAFE);
228 beeperMode_e beeperMode = BEEPER_SILENCE;
230 if (failsafeSwitchIsOn && (failsafeConfig()->failsafe_switch_mode == FAILSAFE_SWITCH_MODE_STAGE2)) {
231 // Aux switch set to failsafe stage2 emulates loss of signal without waiting
232 failsafeOnValidDataFailed();
233 receivingRxData = false;
236 // Beep RX lost only if we are not seeing data and we have been armed earlier
237 if (!receivingRxData && (armed || ARMING_FLAG(WAS_EVER_ARMED))) {
238 beeperMode = BEEPER_RX_LOST;
241 bool reprocessState;
243 do {
244 reprocessState = false;
246 switch (failsafeState.phase) {
247 case FAILSAFE_IDLE:
248 if (armed) {
249 // Track throttle command below minimum time
250 if (calculateThrottleStatus() != THROTTLE_LOW) {
251 failsafeState.throttleLowPeriod = millis() + failsafeConfig()->failsafe_throttle_low_delay * MILLIS_PER_TENTH_SECOND;
253 if (failsafeSwitchIsOn && (failsafeConfig()->failsafe_switch_mode == FAILSAFE_SWITCH_MODE_KILL)) {
254 // Failsafe switch is configured as KILL switch and is switched ON
255 failsafeState.active = true;
256 failsafeState.events++;
257 ENABLE_FLIGHT_MODE(FAILSAFE_MODE);
258 failsafeState.phase = FAILSAFE_LANDED;
259 // go to landed immediately
260 failsafeState.receivingRxDataPeriodPreset = failsafeState.rxDataRecoveryPeriod;
261 // allow re-arming 1 second after Rx recovery, customisable
262 reprocessState = true;
263 } else if (!receivingRxData) {
264 if (millis() > failsafeState.throttleLowPeriod
265 #ifdef USE_GPS_RESCUE
266 && failsafeConfig()->failsafe_procedure != FAILSAFE_PROCEDURE_GPS_RESCUE
267 #endif
269 // JustDisarm if throttle was LOW for at least 'failsafe_throttle_low_delay' before failsafe
270 // protects against false arming when the Tx is powered up after the quad
271 failsafeState.active = true;
272 failsafeState.events++;
273 ENABLE_FLIGHT_MODE(FAILSAFE_MODE);
274 failsafeState.phase = FAILSAFE_LANDED;
275 // go directly to FAILSAFE_LANDED
276 failsafeState.receivingRxDataPeriodPreset = failsafeState.rxDataRecoveryPeriod;
277 // allow re-arming 1 second after Rx recovery, customisable
278 } else {
279 failsafeState.phase = FAILSAFE_RX_LOSS_DETECTED;
281 reprocessState = true;
283 } else {
284 // When NOT armed, show rxLinkState of failsafe switch in GUI (failsafe mode)
285 if (failsafeSwitchIsOn) {
286 ENABLE_FLIGHT_MODE(FAILSAFE_MODE);
287 } else {
288 DISABLE_FLIGHT_MODE(FAILSAFE_MODE);
290 // Throttle low period expired (= low long enough for JustDisarm)
291 failsafeState.throttleLowPeriod = 0;
293 break;
295 case FAILSAFE_RX_LOSS_DETECTED:
296 if (receivingRxData) {
297 failsafeState.phase = FAILSAFE_RX_LOSS_RECOVERED;
298 } else {
299 failsafeState.active = true;
300 failsafeState.events++;
301 switch (failsafeConfig()->failsafe_procedure) {
302 case FAILSAFE_PROCEDURE_AUTO_LANDING:
303 // Enter Stage 2 with settings for landing mode
304 ENABLE_FLIGHT_MODE(FAILSAFE_MODE);
305 failsafeState.phase = FAILSAFE_LANDING;
306 failsafeState.receivingRxDataPeriodPreset = failsafeState.rxDataRecoveryPeriod;
307 // allow re-arming 1 second after Rx recovery
308 failsafeState.landingShouldBeFinishedAt = millis() + failsafeConfig()->failsafe_off_delay * MILLIS_PER_TENTH_SECOND;
309 break;
311 case FAILSAFE_PROCEDURE_DROP_IT:
312 ENABLE_FLIGHT_MODE(FAILSAFE_MODE);
313 failsafeState.phase = FAILSAFE_LANDED;
314 // go directly to FAILSAFE_LANDED
315 failsafeState.receivingRxDataPeriodPreset = failsafeState.rxDataRecoveryPeriod;
316 // allow re-arming 1 second after Rx recovery
317 break;
318 #ifdef USE_GPS_RESCUE
319 case FAILSAFE_PROCEDURE_GPS_RESCUE:
320 ENABLE_FLIGHT_MODE(GPS_RESCUE_MODE);
321 failsafeState.phase = FAILSAFE_GPS_RESCUE;
322 failsafeState.receivingRxDataPeriodPreset = failsafeState.rxDataRecoveryPeriod;
323 // allow re-arming 3 seconds after Rx recovery
324 break;
325 #endif
328 reprocessState = true;
329 break;
331 case FAILSAFE_LANDING:
332 if (receivingRxData) {
333 failsafeState.phase = FAILSAFE_RX_LOSS_RECOVERED;
334 reprocessState = true;
335 } else {
336 if (armed) {
337 beeperMode = BEEPER_RX_LOST_LANDING;
339 if (failsafeShouldHaveCausedLandingByNow() || crashRecoveryModeActive() || !armed) {
340 // to manually disarm while Landing, aux channels must be enabled
341 failsafeState.phase = FAILSAFE_LANDED;
342 reprocessState = true;
345 break;
346 #ifdef USE_GPS_RESCUE
347 case FAILSAFE_GPS_RESCUE:
348 if (receivingRxData) {
349 if (areSticksActive(failsafeConfig()->failsafe_stick_threshold)) {
350 // this test requires stick inputs to be received during GPS Rescue see PR #7936 for rationale
351 failsafeState.phase = FAILSAFE_RX_LOSS_RECOVERED;
352 reprocessState = true;
354 } else {
355 if (armed) {
356 beeperMode = BEEPER_RX_LOST_LANDING;
357 } else {
358 // to manually disarm while in GPS Rescue, aux channels must be enabled
359 failsafeState.phase = FAILSAFE_LANDED;
360 reprocessState = true;
363 break;
364 #endif
365 case FAILSAFE_LANDED:
366 disarm(DISARM_REASON_FAILSAFE);
367 setArmingDisabled(ARMING_DISABLED_FAILSAFE);
368 // prevent accidently rearming by an intermittent rx link
369 failsafeState.receivingRxDataPeriod = millis() + failsafeState.receivingRxDataPeriodPreset;
370 // customise receivingRxDataPeriod according to type of failsafe
371 failsafeState.phase = FAILSAFE_RX_LOSS_MONITORING;
372 reprocessState = true;
373 break;
375 case FAILSAFE_RX_LOSS_MONITORING:
376 // Monitoring the rx link, allow rearming when it has become good for > `receivingRxDataPeriodPreset` time.
377 if (receivingRxData) {
378 if (millis() > failsafeState.receivingRxDataPeriod) {
379 // rx link is good now
380 failsafeState.phase = FAILSAFE_RX_LOSS_RECOVERED;
381 reprocessState = true;
383 } else {
384 failsafeState.receivingRxDataPeriod = millis() + failsafeState.receivingRxDataPeriodPreset;
386 break;
388 case FAILSAFE_RX_LOSS_RECOVERED:
389 // Entering IDLE with the requirement that throttle first must be at min_check for failsafe_throttle_low_delay period.
390 // This is to prevent that JustDisarm is activated on the next iteration.
391 // Because that would have the effect of shutting down failsafe handling on intermittent connections.
392 failsafeState.throttleLowPeriod = millis() + failsafeConfig()->failsafe_throttle_low_delay * MILLIS_PER_TENTH_SECOND;
393 failsafeState.phase = FAILSAFE_IDLE;
394 failsafeState.active = false;
395 #ifdef USE_GPS_RESCUE
396 DISABLE_FLIGHT_MODE(GPS_RESCUE_MODE);
397 #endif
398 DISABLE_FLIGHT_MODE(FAILSAFE_MODE);
399 unsetArmingDisabled(ARMING_DISABLED_FAILSAFE);
400 reprocessState = true;
401 break;
403 default:
404 break;
406 } while (reprocessState);
408 if (beeperMode != BEEPER_SILENCE) {
409 beeper(beeperMode);