diff --git a/src/deck/drivers/interface/servo.h b/src/deck/drivers/interface/servo.h
new file mode 100644
index 0000000000..bc58675d09
--- /dev/null
+++ b/src/deck/drivers/interface/servo.h
@@ -0,0 +1,71 @@
+/**
+ * || ____ _ __
+ * +------+ / __ )(_) /_______________ _____ ___
+ * | 0xBC | / __ / / __/ ___/ ___/ __ `/_ / / _ \
+ * +------+ / /_/ / / /_/ /__/ / / /_/ / / /_/ __/
+ * || || /_____/_/\__/\___/_/ \__,_/ /___/\___/
+ *
+ * Crazyflie control firmware
+ *
+ * Copyright (C) 2011-2012 Bitcraze AB
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, in version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ * servo.h - servo header file
+ *
+ * The motors PWM ratio is a value on 16 bits (from 0 to 0xFFFF)
+ * the functions of the driver will make the conversions to the actual PWM
+ * precision (ie. if the precision is 8bits 0xFFFF and 0xFF00 are equivalents).
+ *
+ * The precision is set in number of bits by the define MOTORS_PWM_BITS
+ * The timer prescaler is set by MOTORS_PWM_PRESCALE
+ */
+#ifndef __SERVO_H__
+#define __SERVO_H__
+
+#include
+#include
+#include "config.h"
+
+/******** Defines ********/
+#define SERVO_PWM_PERIOD 1000 // ARR register content
+#define SERVO_PWM_FREQUENCY_HZ 50 // target servo pwm frequency
+#define SERVO_PWM_PRESCALE (uint16_t) (1680) // 84mhz / (50hz * ARR)
+
+/**
+ * Servo Initialization
+ */
+void servoInit();
+
+bool servoTest(void);
+
+/**
+ *
+ * @brief Set servo angle.
+ * @param: angle: desired servo angle in degrees
+ */
+void servoSetAngle(uint8_t angle);
+
+/**
+ * Servo angle parameter callback. When servo angle is changed, call
+ * function to change servo angle automatically
+ * */
+void servoAngleCallBack(void);
+
+/**
+ * Saturate servo angle. Min is 0, max is defined by parameter 'servoRange'
+ * @param angle: pointer to desired angle
+ * */
+uint8_t saturateAngle(uint8_t angle);
+
+#endif /* __SERVO_H__ */
diff --git a/src/deck/drivers/src/Kbuild b/src/deck/drivers/src/Kbuild
index 79ff5d316b..52765dfab6 100644
--- a/src/deck/drivers/src/Kbuild
+++ b/src/deck/drivers/src/Kbuild
@@ -15,6 +15,7 @@ obj-$(CONFIG_DECK_LOCO) += lpsTdoa3Tag.o
obj-$(CONFIG_DECK_LOCO) += lpsTwrTag.o
obj-$(CONFIG_DECK_MULTIRANGER) += multiranger.o
obj-$(CONFIG_DECK_OA) += oa.o
+obj-$(CONFIG_DECK_SERVO) += servo.o
obj-$(CONFIG_DECK_USD) += usddeck.o
obj-$(CONFIG_DECK_ZRANGER) += zranger.o
obj-$(CONFIG_DECK_ZRANGER2) += zranger2.o
diff --git a/src/deck/drivers/src/Kconfig b/src/deck/drivers/src/Kconfig
index 1db2aa01b4..1c4ee4ff58 100644
--- a/src/deck/drivers/src/Kconfig
+++ b/src/deck/drivers/src/Kconfig
@@ -390,6 +390,37 @@ config DECK_OA
help
Codename: Obstacle Avoidance driver, obsolete deck.
+config DECK_SERVO
+ bool "Support the servo deck (prototype/via BigQuad deck)"
+ default n
+ help
+ Servo PWM driver.
+
+choice
+ prompt "Servo pin"
+ depends on DECK_SERVO
+ default DECK_SERVO_USE_TX2
+
+ config DECK_SERVO_USE_TX2
+ bool "PA2/TX2"
+
+ config DECK_SERVO_USE_RX2
+ bool "PA3/RX2"
+
+ config DECK_SERVO_USE_IO1
+ bool "PB8/IO_1"
+
+ config DECK_SERVO_USE_IO2
+ bool "PB5/IO_2"
+
+ config DECK_SERVO_USE_IO3
+ bool "PB4/IO_3"
+
+ config DECK_SERVO_USE_MOSI
+ bool "PA7/MOSI"
+
+endchoice
+
config DECK_USD
bool "Support the Micro SD card deck"
default y
diff --git a/src/deck/drivers/src/servo.c b/src/deck/drivers/src/servo.c
new file mode 100644
index 0000000000..158557c9eb
--- /dev/null
+++ b/src/deck/drivers/src/servo.c
@@ -0,0 +1,259 @@
+/**
+ * || ____ _ __
+ * +------+ / __ )(_) /_______________ _____ ___
+ * | 0xBC | / __ / / __/ ___/ ___/ __ `/_ / / _ \
+ * +------+ / /_/ / / /_/ /__/ / / /_/ / / /_/ __/
+ * || || /_____/_/\__/\___/_/ \__,_/ /___/\___/
+ *
+ * Crazyflie control firmware
+ *
+ * Copyright (C) 2011-2024 Bitcraze AB
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, in version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ * servo.c - Servo driver
+ *
+ * Using contributions from: Eric Ewing, Will Wu
+ */
+#define DEBUG_MODULE "SERVO"
+
+#include
+
+/* ST includes */
+#include "stm32fxxx.h"
+
+//FreeRTOS includes
+#include "FreeRTOS.h"
+#include "task.h"
+#include "debug.h"
+#include
+#include
+#include "motors.h"
+#include "param.h"
+#include "deck.h"
+
+static uint16_t servo_MIN_us = 1000;
+static uint16_t servo_MAX_us = 2000;
+
+#include "servo.h"
+
+// #define DEBUG_SERVO
+
+static bool isInit = false;
+
+const MotorPerifDef* servoMap;
+extern const MotorPerifDef* servoMapIO1;
+extern const MotorPerifDef* servoMapIO2;
+extern const MotorPerifDef* servoMapIO3;
+extern const MotorPerifDef* servoMapRX2;
+extern const MotorPerifDef* servoMapTX2;
+extern const MotorPerifDef* servoMapMOSI;
+
+/* Public functions */
+static uint8_t servo_idle = 90;
+static uint8_t s_servo_angle;
+static uint8_t servo_range = 180; // in degrees
+
+void servoMapInit(const MotorPerifDef* servoMapSelect)
+{
+ servoMap = servoMapSelect;
+
+ GPIO_InitTypeDef GPIO_InitStructure;
+ TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
+ TIM_OCInitTypeDef TIM_OCInitStructure;
+
+ //clock the servo pin and the timers
+ RCC_AHB1PeriphClockCmd(servoMap->gpioPerif, ENABLE);
+ RCC_APB1PeriphClockCmd(servoMap->timPerif, ENABLE);
+
+ //configure gpio for timer out
+ GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
+ GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
+ GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
+ GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
+ GPIO_InitStructure.GPIO_Pin = servoMap->gpioPin;
+ GPIO_Init(servoMap->gpioPort, &GPIO_InitStructure);
+
+ //map timer to alternate function
+ GPIO_PinAFConfig(servoMap->gpioPort, servoMap->gpioPinSource, servoMap->gpioAF);
+
+ //Timer configuration
+ TIM_TimeBaseStructure.TIM_Period = SERVO_PWM_PERIOD;
+ TIM_TimeBaseStructure.TIM_Prescaler = SERVO_PWM_PRESCALE;
+ TIM_TimeBaseStructure.TIM_ClockDivision = 0;
+ TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
+ TIM_TimeBaseStructure.TIM_RepetitionCounter = 0;
+ TIM_TimeBaseInit(servoMap->tim, &TIM_TimeBaseStructure);
+
+ // PWM channels configuration
+ TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
+ TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
+ TIM_OCInitStructure.TIM_Pulse = 0;
+ TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
+ TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Set;
+
+ // Configure OC1
+ servoMap->ocInit(servoMap->tim, &TIM_OCInitStructure);
+ servoMap->preloadConfig(servoMap->tim, TIM_OCPreload_Enable);
+
+
+ //Enable the timer PWM outputs
+ TIM_CtrlPWMOutputs(servoMap->tim, ENABLE);
+ servoMap->setCompare(servoMap->tim, 0x00);
+
+ //Enable the timer
+ TIM_Cmd(servoMap->tim, ENABLE);
+}
+
+void servoInit()
+{
+ if (isInit){
+ return;
+ }
+
+ #ifdef CONFIG_DECK_SERVO_USE_IO1
+ servoMapInit(servoMapIO1);
+ DEBUG_PRINT("Init on IO1 [OK]\n");
+ #elif CONFIG_DECK_SERVO_USE_IO2
+ servoMapInit(servoMapIO2);
+ DEBUG_PRINT("Init on IO2 [OK]\n");
+ #elif CONFIG_DECK_SERVO_USE_IO3
+ servoMapInit(servoMapIO3);
+ DEBUG_PRINT("Init on IO3 [OK]\n");
+ #elif CONFIG_DECK_SERVO_USE_RX2
+ servoMapInit(servoMapRX2);
+ DEBUG_PRINT("Init on RX2 [OK]\n"); // not working on Bolt 1.1...
+ #elif CONFIG_DECK_SERVO_USE_MOSI
+ servoMapInit(servoMapMOSI);
+ DEBUG_PRINT("Init on MOSI [OK]\n");
+ #elif CONFIG_DECK_SERVO_USE_TX2
+ servoMapInit(servoMapTX2);
+ DEBUG_PRINT("Init on TX2 [OK]\n"); // not working on Bolt 1.1...
+ #else
+ isInit = false
+ DEBUG_PRINT("Failed to configure servo pin!\n");
+ return;
+ #endif
+
+ servoSetAngle(saturateAngle(servo_idle));
+
+ s_servo_angle = servo_idle;
+
+ isInit = true;
+}
+
+bool servoTest(void)
+{
+ return isInit;
+}
+
+void servoSetAngle(uint8_t angle)
+{
+ // set CCR register
+ // Duty% = CCR/ARR*100, so CCR = Duty%/100 * ARR
+
+ double pulse_length_us = (double)(angle) / servo_range * (servo_MAX_us - servo_MIN_us) + servo_MIN_us;
+ double pulse_length_s = pulse_length_us / 1000000;
+ const uint32_t ccr_val = (uint32_t)(pulse_length_s * SERVO_PWM_PERIOD * SERVO_PWM_FREQUENCY_HZ);
+ servoMap->setCompare(servoMap->tim, ccr_val);
+
+ #ifdef DEBUG_SERVO
+ DEBUG_PRINT("Set Angle: %u deg, pulse width: %f us \n", angle, pulse_length_us);
+ #endif
+}
+
+uint8_t saturateAngle(uint8_t angle)
+{
+ if (angle > servo_range) {
+ return servo_range;
+ }
+ else if (angle < 0) {
+ return 0;
+ }
+ else {
+ return angle;
+ }
+
+}
+
+void servoAngleCallBack(void)
+{
+ servoSetAngle(saturateAngle(s_servo_angle));
+}
+
+static const DeckDriver servo_deck = {
+ .vid = 0x00,
+ .pid = 0x00,
+ .name = "bcServo",
+
+ #ifdef CONFIG_DECK_SERVO_USE_IO1
+ .usedPeriph = DECK_USING_TIMER4,
+ .usedGpio = DECK_USING_IO_1,
+ #elif CONFIG_DECK_SERVO_USE_IO2
+ .usedPeriph = DECK_USING_TIMER3,
+ .usedGpio = DECK_USING_IO_2,
+ #elif CONFIG_DECK_SERVO_USE_IO3
+ .usedPeriph = DECK_USING_TIMER3,
+ .usedGpio = DECK_USING_IO_3,
+ #elif CONFIG_DECK_SERVO_USE_RX2
+ .usedPeriph = DECK_USING_TIMER5,
+ .usedGpio = DECK_USING_PA3,
+ #elif CONFIG_DECK_SERVO_USE_MOSI
+ .usedPeriph = DECK_USING_TIMER14,
+ .usedGpio = DECK_USING_PA7,
+ #elif CONFIG_DECK_SERVO_USE_TX2
+ .usedPeriph = DECK_USING_TIMER5,
+ .usedGpio = DECK_USING_PA2,
+ #else
+ .usedPeriph = 0,
+ .usedGpio = 0,
+ #endif
+
+ .init = servoInit,
+ .test = servoTest,
+};
+
+DECK_DRIVER(servo_deck);
+
+PARAM_GROUP_START(deck)
+
+PARAM_ADD_CORE(PARAM_UINT8 | PARAM_RONLY, bcServo, &isInit)
+PARAM_GROUP_STOP(deck)
+
+/**
+ * "Servo" deck parameters
+ */
+PARAM_GROUP_START(servo)
+
+/**
+ * @brief PWM pulse width for minimal servo position (in microseconds)
+ */
+PARAM_ADD(PARAM_UINT16 | PARAM_PERSISTENT, servoMINus, &servo_MIN_us)
+/**
+ * @brief PWM pulse width for maximal servo position (in microseconds)
+ */
+PARAM_ADD(PARAM_UINT16 | PARAM_PERSISTENT, servoMAXus, &servo_MAX_us)
+/**
+ * @brief Servo range, i.e. angle between the min and max positions (in degrees)
+ */
+PARAM_ADD(PARAM_UINT8 | PARAM_PERSISTENT, servoRange, &servo_range)
+/**
+ * @brief Servo idle (startup) angular position (in degrees, min = 0, max = servoRange)
+ */
+PARAM_ADD(PARAM_UINT8 | PARAM_PERSISTENT, servoIdle, &servo_idle)
+/**
+ * @brief Servo angular position (in degrees, min = 0, max = servoRange)
+ */
+PARAM_ADD_WITH_CALLBACK(PARAM_UINT8 , servoAngle, &s_servo_angle, &servoAngleCallBack)
+
+PARAM_GROUP_STOP(servo)
diff --git a/src/deck/interface/deck_core.h b/src/deck/interface/deck_core.h
index c13deaccd5..47dc23d867 100644
--- a/src/deck/interface/deck_core.h
+++ b/src/deck/interface/deck_core.h
@@ -69,6 +69,7 @@ bool deckTest(void);
#define DECK_USING_TIMER10 (1 << 16)
#define DECK_USING_TIMER14 (1 << 15)
#define DECK_USING_TIMER9 (1 << 17)
+#define DECK_USING_TIMER4 (1 << 18)
struct deckInfo_s;
struct deckFwUpdate_s;
diff --git a/src/drivers/src/motors_def.c b/src/drivers/src/motors_def.c
index 0e334efdcd..d9ef9658c1 100644
--- a/src/drivers/src/motors_def.c
+++ b/src/drivers/src/motors_def.c
@@ -623,7 +623,7 @@ static const MotorPerifDef MOTORS_PB5_TIM3_CH2_BRUSHLESS_OD =
};
// Deck IO3, PB4, TIM3_CH1
-static const MotorPerifDef MOTORS_PB4_TIM2_CH1_BRUSHLESS_OD =
+static const MotorPerifDef MOTORS_PB4_TIM3_CH1_BRUSHLESS_OD =
{
.drvType = BRUSHLESS,
.gpioPerif = RCC_AHB1Periph_GPIOB,
@@ -743,7 +743,7 @@ const MotorPerifDef* motorMapDefaultBrushed[NBR_OF_MOTORS] =
const MotorPerifDef* motorMapBigQuadDeck[NBR_OF_MOTORS] =
{
&MOTORS_PA2_TIM2_CH3_BRUSHLESS_OD,
- &MOTORS_PB4_TIM2_CH1_BRUSHLESS_OD,
+ &MOTORS_PB4_TIM3_CH1_BRUSHLESS_OD,
&MOTORS_PB5_TIM3_CH2_BRUSHLESS_OD,
&MOTORS_PA3_TIM2_CH4_BRUSHLESS_OD
};
@@ -805,3 +805,32 @@ const MotorPerifDef* motorMapCF21Brushless[NBR_OF_MOTORS] =
&MOTORS_PB10_TIM2_CH3_BRUSHLESS_OD
};
+/**
+ * Servo mapped to the Bigquad CPPM (MOSI) port
+ */
+const MotorPerifDef* servoMapMOSI = &MOTORS_PA7_TIM14_CH1_BRUSHLESS_OD;
+
+/**
+ * Servo mapped to the Bigquad M1 / TX2 port
+ */
+const MotorPerifDef* servoMapTX2 = &MOTORS_PA2_TIM5_CH3_BRUSHLESS_OD;
+
+/**
+ * Servo mapped to the Bigquad M3 / IO2 port
+ */
+const MotorPerifDef* servoMapIO2 = &MOTORS_PB5_TIM3_CH2_BRUSHLESS_OD;
+
+/**
+ * Servo mapped to the Bigquad M2 / IO3 port
+ */
+const MotorPerifDef* servoMapIO3 = &MOTORS_PB4_TIM3_CH1_BRUSHLESS_OD;
+
+/**
+ * Servo mapped to the Bigquad M4 / RX2 port
+ */
+const MotorPerifDef* servoMapRX2 = &MOTORS_PA3_TIM5_CH4_BRUSHLESS_OD;
+
+/**
+ * Servo mapped to IO1 port
+ */
+const MotorPerifDef* servoMapIO1 = &MOTORS_PB8_TIM4_CH3_BRUSHLESS_OD;
diff --git a/src/modules/interface/supervisor.h b/src/modules/interface/supervisor.h
index 48de77d8ed..46e2be9b4c 100644
--- a/src/modules/interface/supervisor.h
+++ b/src/modules/interface/supervisor.h
@@ -110,9 +110,26 @@ bool supervisorCanArm();
bool supervisorIsArmed();
/**
- * @brief Query if the system is locked (due to a crash)
+ * @brief Query if the system is locked (due to an emergency stop or watchdog timeout)
*
* @return true
* @return false
*/
bool supervisorIsLocked();
+
+/**
+ * @brief Query if the system is crashed
+ *
+ * @return true
+ * @return false
+ */
+bool supervisorIsCrashed();
+
+/**
+ * @brief Request the system to be recover if crashed.
+ *
+ * @param doRecover true - request recovery. false - request crashed.
+ * @return true The request was granted
+ * @return false The request could not be granted
+ */
+bool supervisorRequestCrashRecovery(const bool doRecover);
\ No newline at end of file
diff --git a/src/modules/interface/supervisor_state_machine.h b/src/modules/interface/supervisor_state_machine.h
index 4c62a9d590..164284d572 100644
--- a/src/modules/interface/supervisor_state_machine.h
+++ b/src/modules/interface/supervisor_state_machine.h
@@ -37,6 +37,7 @@ typedef enum {
supervisorStateWarningLevelOut,
supervisorStateExceptFreeFall,
supervisorStateLocked,
+ supervisorStateCrashed,
supervisorState_NrOfStates,
} supervisorState_t;
@@ -48,6 +49,8 @@ typedef enum {
supervisorConditionCommanderWdtWarning,
supervisorConditionCommanderWdtTimeout,
supervisorConditionEmergencyStop,
+ supervisorConditionIsCrashed,
+ supervisorConditionLandingTimeout,
supervisorCondition_NrOfConditions,
} supervisorConditions_t;
@@ -61,6 +64,8 @@ typedef uint32_t supervisorConditionBits_t;
#define SUPERVISOR_CB_COMMANDER_WDT_WARNING (1 << supervisorConditionCommanderWdtWarning)
#define SUPERVISOR_CB_COMMANDER_WDT_TIMEOUT (1 << supervisorConditionCommanderWdtTimeout)
#define SUPERVISOR_CB_EMERGENCY_STOP (1 << supervisorConditionEmergencyStop)
+#define SUPERVISOR_CB_CRASHED (1 << supervisorConditionIsCrashed)
+#define SUPERVISOR_CB_LANDING_TIMEOUT (1 << supervisorConditionLandingTimeout)
// Enum that is used to describe how to combine the bits in the required field
diff --git a/src/modules/src/controller/controller_brescianini.c b/src/modules/src/controller/controller_brescianini.c
index d17d97ab6b..905f9ba300 100644
--- a/src/modules/src/controller/controller_brescianini.c
+++ b/src/modules/src/controller/controller_brescianini.c
@@ -38,6 +38,7 @@
#include "num.h"
#include "math3d.h"
#include "physicalConstants.h"
+#include "platform_defaults.h"
static struct mat33 CRAZYFLIE_INERTIA =
{{{16.6e-6f, 0.83e-6f, 0.72e-6f},
diff --git a/src/modules/src/controller/controller_mellinger.c b/src/modules/src/controller/controller_mellinger.c
index f15f5e41c0..40bc2c285f 100644
--- a/src/modules/src/controller/controller_mellinger.c
+++ b/src/modules/src/controller/controller_mellinger.c
@@ -42,6 +42,7 @@ We added the following:
#include "position_controller.h"
#include "controller_mellinger.h"
#include "physicalConstants.h"
+#include "platform_defaults.h"
// Global state variable used in the
// firmware as the only instance and in bindings
diff --git a/src/modules/src/platformservice.c b/src/modules/src/platformservice.c
index 28a3846c86..9a9972a852 100644
--- a/src/modules/src/platformservice.c
+++ b/src/modules/src/platformservice.c
@@ -54,6 +54,7 @@ typedef enum {
typedef enum {
setContinuousWave = 0x00,
armSystem = 0x01,
+ recoverSystem = 0x02,
} PlatformCommand;
typedef enum {
@@ -135,6 +136,14 @@ static void platformCommandProcess(CRTPPacket *p)
p->size = 2;
break;
}
+ case recoverSystem:
+ {
+ const bool success = supervisorRequestCrashRecovery(true);
+ data[0] = success;
+ data[1] = !supervisorIsCrashed();
+ p->size = 2;
+ break;
+ }
default:
break;
}
diff --git a/src/modules/src/power_distribution_quadrotor.c b/src/modules/src/power_distribution_quadrotor.c
index c37b7448ed..a58e1e4155 100644
--- a/src/modules/src/power_distribution_quadrotor.c
+++ b/src/modules/src/power_distribution_quadrotor.c
@@ -33,6 +33,7 @@
#include "autoconf.h"
#include "config.h"
#include "math.h"
+#include "platform_defaults.h"
#ifndef CONFIG_MOTORS_DEFAULT_IDLE_THRUST
# define DEFAULT_IDLE_THRUST 0
@@ -41,7 +42,7 @@
#endif
static uint32_t idleThrust = DEFAULT_IDLE_THRUST;
-static float armLength = 0.046f; // m;
+static float armLength = ARM_LENGTH; // m
static float thrustToTorque = 0.005964552f;
// thrust = a * pwm^2 + b * pwm
diff --git a/src/modules/src/supervisor.c b/src/modules/src/supervisor.c
index 2e209829ab..66649ca353 100644
--- a/src/modules/src/supervisor.c
+++ b/src/modules/src/supervisor.c
@@ -61,11 +61,14 @@
#define AUTO_ARMING 0
#endif
+static uint16_t landingTimeoutDuration = LANDING_TIMEOUT_MS;
+
typedef struct {
bool canFly;
bool isFlying;
bool isTumbled;
bool isArmingActivated;
+ bool isCrashed;
uint16_t infoBitfield;
uint8_t paramEmergencyStop;
@@ -78,6 +81,9 @@ typedef struct {
// The time (in ticks) of the latest high thrust event. 0=no high thrust event yet
uint32_t latestThrustTick;
+ // The time (in ticks) of the latest landing event. 0=no landing event yet
+ uint32_t latestLandingTick;
+
supervisorState_t state;
// Copy of latest conditions, for logging
@@ -115,6 +121,38 @@ bool supervisorIsLocked() {
return supervisorStateLocked == supervisorMem.state;
}
+bool supervisorIsCrashed() {
+ return supervisorMem.isCrashed;
+}
+
+static void supervisorSetLatestLandingTime(SupervisorMem_t* this, const uint32_t currentTick) {
+ this->latestLandingTick = currentTick;
+}
+
+bool supervisorIsLandingTimeout(SupervisorMem_t* this, const uint32_t currentTick) {
+ if (0 == this->latestLandingTick) {
+ return false;
+ }
+
+ const uint32_t landingTime = currentTick - this->latestLandingTick;
+ return landingTime > M2T(landingTimeoutDuration);
+}
+
+bool supervisorRequestCrashRecovery(const bool doRecovery) {
+
+ if (doRecovery && !supervisorIsCrashed()) {
+ return true;
+ } else if (doRecovery && supervisorIsCrashed() && !supervisorIsTumbled()) {
+ supervisorMem.isCrashed = false;
+ return true;
+ } else if (!doRecovery) {
+ supervisorMem.isCrashed = true;
+ return true;
+ }
+
+ return false;
+}
+
bool supervisorRequestArming(const bool doArm) {
if (doArm == supervisorMem.isArmingActivated) {
return true;
@@ -215,25 +253,39 @@ static bool checkEmergencyStopWatchdog(const uint32_t tick) {
return isOk;
}
-static void postTransitionActions(SupervisorMem_t* this, const supervisorState_t previousState) {
+static void postTransitionActions(SupervisorMem_t* this, const supervisorState_t previousState, const uint32_t currentTick) {
const supervisorState_t newState = this->state;
if (newState == supervisorStateReadyToFly) {
DEBUG_PRINT("Ready to fly\n");
}
+ if (newState == supervisorStateLanded) {
+ supervisorSetLatestLandingTime(this, currentTick);
+ }
+
+ if ((previousState == supervisorStateLanded) && (newState == supervisorStateReset)) {
+ DEBUG_PRINT("Landing timeout, disarming\n");
+ }
+
if (newState == supervisorStateLocked) {
DEBUG_PRINT("Locked, reboot required\n");
}
+ if (newState == supervisorStateCrashed) {
+ DEBUG_PRINT("Crashed, recovery required\n");
+ supervisorRequestCrashRecovery(false);
+ }
+
if ((previousState == supervisorStateNotInitialized || previousState == supervisorStateReadyToFly || previousState == supervisorStateFlying) &&
- newState != supervisorStateReadyToFly && newState != supervisorStateFlying) {
+ newState != supervisorStateReadyToFly && newState != supervisorStateFlying && newState != supervisorStateLanded) {
DEBUG_PRINT("Can not fly\n");
}
if (newState != supervisorStateReadyToFly &&
newState != supervisorStateFlying &&
- newState != supervisorStateWarningLevelOut) {
+ newState != supervisorStateWarningLevelOut &&
+ newState != supervisorStateLanded) {
supervisorRequestArming(false);
}
@@ -282,6 +334,14 @@ static supervisorConditionBits_t updateAndPopulateConditions(SupervisorMem_t* th
conditions |= SUPERVISOR_CB_EMERGENCY_STOP;
}
+ if (supervisorIsCrashed()) {
+ conditions |= SUPERVISOR_CB_CRASHED;
+ }
+
+ if (supervisorIsLandingTimeout(this, currentTick)) {
+ conditions |= SUPERVISOR_CB_LANDING_TIMEOUT;
+ }
+
return conditions;
}
@@ -312,6 +372,9 @@ static void updateLogData(SupervisorMem_t* this, const supervisorConditionBits_t
if (supervisorStateLocked == this->state) {
this->infoBitfield |= 0x0040;
}
+ if (this->isCrashed) {
+ this->infoBitfield |= 0x0080;
+ }
}
void supervisorUpdate(const sensorData_t *sensors, const setpoint_t* setpoint, stabilizerStep_t stabilizerStep) {
@@ -327,7 +390,7 @@ void supervisorUpdate(const sensorData_t *sensors, const setpoint_t* setpoint, s
if (this->state != newState) {
const supervisorState_t previousState = this->state;
this->state = newState;
- postTransitionActions(this, previousState);
+ postTransitionActions(this, previousState, currentTick);
}
this->latestConditions = conditions;
@@ -343,6 +406,8 @@ void supervisorOverrideSetpoint(setpoint_t* setpoint) {
switch(this->state){
case supervisorStateReadyToFly:
// Fall through
+ case supervisorStateLanded:
+ // Fall through
case supervisorStateFlying:
// Do nothing
break;
@@ -370,7 +435,8 @@ bool supervisorAreMotorsAllowedToRun() {
SupervisorMem_t* this = &supervisorMem;
return (this->state == supervisorStateReadyToFly) ||
(this->state == supervisorStateFlying) ||
- (this->state == supervisorStateWarningLevelOut);
+ (this->state == supervisorStateWarningLevelOut) ||
+ (this->state == supervisorStateLanded);
}
void infoDump(const SupervisorMem_t* this) {
@@ -462,4 +528,10 @@ PARAM_GROUP_START(supervisor)
* @brief Set to nonzero to dump information about the current supervisor state to the console log
*/
PARAM_ADD(PARAM_UINT8, infdmp, &supervisorMem.doinfodump)
+
+/**
+ * @brief Landing timeout duration (ms)
+ */
+PARAM_ADD(PARAM_UINT16 | PARAM_PERSISTENT, landedTimeout, &landingTimeoutDuration)
+
PARAM_GROUP_STOP(supervisor)
diff --git a/src/modules/src/supervisor_state_machine.c b/src/modules/src/supervisor_state_machine.c
index b3cd69ba75..2ccb2c7e65 100644
--- a/src/modules/src/supervisor_state_machine.c
+++ b/src/modules/src/supervisor_state_machine.c
@@ -47,6 +47,7 @@ static const char* const stateNames[] = {
"Warning, level out",
"Exception, free fall",
"Locked",
+ "Crashed",
};
static_assert(sizeof(stateNames) / sizeof(stateNames[0]) == supervisorState_NrOfStates);
@@ -57,6 +58,8 @@ static const char* const conditionNames[] = {
"commanderWdtWarning",
"commanderWdtTimeout",
"emergencyStop",
+ "isCrashed",
+ "landingTimeout",
};
static_assert(sizeof(conditionNames) / sizeof(conditionNames[0]) == supervisorCondition_NrOfConditions);
@@ -162,7 +165,16 @@ static SupervisorStateTransition_t transitionsFlying[] = {
{
.newState = supervisorStateExceptFreeFall,
- .triggers = SUPERVISOR_CB_COMMANDER_WDT_TIMEOUT | SUPERVISOR_CB_CONF_IS_TUMBLED | SUPERVISOR_CB_EMERGENCY_STOP,
+ .triggers = SUPERVISOR_CB_COMMANDER_WDT_TIMEOUT | SUPERVISOR_CB_EMERGENCY_STOP,
+ .negatedTriggers = SUPERVISOR_CB_ARMED,
+ .triggerCombiner = supervisorAny,
+
+ .blockerCombiner = supervisorNever,
+ },
+ {
+ .newState = supervisorStateCrashed,
+
+ .triggers = SUPERVISOR_CB_CONF_IS_TUMBLED,
.negatedTriggers = SUPERVISOR_CB_ARMED,
.triggerCombiner = supervisorAny,
@@ -192,10 +204,21 @@ static SupervisorStateTransition_t transitionsLanded[] = {
{
.newState = supervisorStateReset,
- .triggerCombiner = supervisorAlways,
+ .triggers = SUPERVISOR_CB_LANDING_TIMEOUT,
+ .negatedTriggers = SUPERVISOR_CB_NONE,
+ .triggerCombiner = supervisorAll,
.blockerCombiner = supervisorNever,
},
+ {
+ .newState = supervisorStateFlying,
+
+ .triggers = SUPERVISOR_CB_IS_FLYING,
+ .negatedTriggers = SUPERVISOR_CB_NONE,
+ .triggerCombiner = supervisorAll,
+
+ .blockerCombiner = supervisorNever,
+ }
};
static SupervisorStateTransition_t transitionsReset[] = {
@@ -249,6 +272,28 @@ static SupervisorStateTransition_t transitionsLocked[] = {
},
};
+
+static SupervisorStateTransition_t transitionsTumbled[] = {
+ {
+ .newState = supervisorStatePreFlChecksNotPassed,
+
+ .triggers = SUPERVISOR_CB_NONE,
+ .negatedTriggers = SUPERVISOR_CB_CRASHED | SUPERVISOR_CB_IS_TUMBLED,
+ .triggerCombiner = supervisorAll,
+
+ .blockerCombiner = supervisorNever
+ },
+ {
+ .newState = supervisorStateLocked,
+
+ .triggers = SUPERVISOR_CB_EMERGENCY_STOP,
+ .negatedTriggers = SUPERVISOR_CB_NONE,
+ .triggerCombiner = supervisorAll,
+
+ .blockerCombiner = supervisorNever
+ },
+};
+
SupervisorStateTransitionList_t transitionLists[] = {
{SUPERVISOR_TRANSITION_ENTRY(transitionsNotInitialized)},
{SUPERVISOR_TRANSITION_ENTRY(transitionsPreFlChecksNotPassed)},
@@ -260,6 +305,7 @@ SupervisorStateTransitionList_t transitionLists[] = {
{SUPERVISOR_TRANSITION_ENTRY(transitionsWarningLevelOut)},
{SUPERVISOR_TRANSITION_ENTRY(transitionsExceptFreeFall)},
{SUPERVISOR_TRANSITION_ENTRY(transitionsLocked)},
+ {SUPERVISOR_TRANSITION_ENTRY(transitionsTumbled)},
};
static_assert(sizeof(transitionLists) / sizeof(transitionLists[0]) == supervisorState_NrOfStates);
diff --git a/src/platform/interface/platform_defaults.h b/src/platform/interface/platform_defaults.h
index af8d5d0f86..e85abfb7de 100644
--- a/src/platform/interface/platform_defaults.h
+++ b/src/platform/interface/platform_defaults.h
@@ -44,6 +44,17 @@
#include "platform_defaults_flapper.h"
#endif
+// Drone physical constants
+// TODO provide those in each config. Those are values of cf2 platform
+#ifndef ARM_LENGTH
+ // m
+ #define ARM_LENGTH 0.046f
+#endif
+#ifndef CF_MASS
+ // kg
+ #define CF_MASS 0.027f
+#endif
+
// IMU alignment on the airframe
#ifndef IMU_PHI
#define IMU_PHI 0.0f
@@ -114,6 +125,11 @@
#define SUPERVISOR_TUMBLE_CHECK_ENABLE true
#endif
+// Landing timeout before disarming
+#ifndef LANDING_TIMEOUT_MS
+ #define LANDING_TIMEOUT_MS 3000
+#endif
+
// Health test parameters
#ifndef HEALTH_BRUSHED_ON_PERIOD_MSEC
diff --git a/src/platform/interface/platform_defaults_cf2.h b/src/platform/interface/platform_defaults_cf2.h
index 147ac41aa4..9c906585f2 100644
--- a/src/platform/interface/platform_defaults_cf2.h
+++ b/src/platform/interface/platform_defaults_cf2.h
@@ -42,6 +42,12 @@
// Requires kbuild config ENABLE_AUTO_SHUTDOWN to be activated.
#define DEFAULT_SYSTEM_SHUTDOWN_TIMEOUT_MIN 5
+// Drone physical constants
+// m
+#define ARM_LENGTH 0.046f
+// kg
+#define CF_MASS 0.027f
+
// Default PID gains
#define PID_ROLL_RATE_KP 250.0
#define PID_ROLL_RATE_KI 500.0
diff --git a/src/utils/interface/physicalConstants.h b/src/utils/interface/physicalConstants.h
index 7b94316ce4..d107c25b3e 100644
--- a/src/utils/interface/physicalConstants.h
+++ b/src/utils/interface/physicalConstants.h
@@ -47,7 +47,3 @@
#ifndef ARCMINUTE
#define ARCMINUTE (M_PI_F / 10800.0f)
#endif
-
-#ifndef CF_MASS
- #define CF_MASS (0.027f) // in kg
-#endif
diff --git a/src/utils/interface/pid.h b/src/utils/interface/pid.h
index 50248f0aec..cbdfb828d4 100644
--- a/src/utils/interface/pid.h
+++ b/src/utils/interface/pid.h
@@ -38,7 +38,7 @@ typedef struct
{
float desired; //< set point
float error; //< error
- float prevError; //< previous error
+ float prevMeasured; //< previous measurement
float integ; //< integral
float deriv; //< derivative
float kp; //< proportional gain
diff --git a/src/utils/src/pid.c b/src/utils/src/pid.c
index 67b00ba9a0..7152e9fad8 100644
--- a/src/utils/src/pid.c
+++ b/src/utils/src/pid.c
@@ -36,7 +36,7 @@ void pidInit(PidObject* pid, const float desired, const float kp,
bool enableDFilter)
{
pid->error = 0;
- pid->prevError = 0;
+ pid->prevMeasured = 0;
pid->integ = 0;
pid->deriv = 0;
pid->desired = desired;
@@ -56,86 +56,82 @@ void pidInit(PidObject* pid, const float desired, const float kp,
float pidUpdate(PidObject* pid, const float measured, const bool updateError)
{
- float output = 0.0f;
+ float output = 0.0f;
- if (updateError)
- {
- pid->error = pid->desired - measured;
- }
-
- pid->outP = pid->kp * pid->error;
- output += pid->outP;
+ if (updateError)
+ {
+ pid->error = pid->desired - measured;
+ }
- float deriv = (pid->error - pid->prevError) / pid->dt;
-
- #if CONFIG_CONTROLLER_PID_FILTER_ALL
+ pid->outP = pid->kp * pid->error;
+ output += pid->outP;
+
+ float deriv = -(measured - pid->prevMeasured) / pid->dt;
+
+ #if CONFIG_CONTROLLER_PID_FILTER_ALL
+ pid->deriv = deriv;
+ #else
+ if (pid->enableDFilter){
+ pid->deriv = lpf2pApply(&pid->dFilter, deriv);
+ } else {
pid->deriv = deriv;
- #else
- if (pid->enableDFilter){
- pid->deriv = lpf2pApply(&pid->dFilter, deriv);
- } else {
- pid->deriv = deriv;
- }
- #endif
- if (isnan(pid->deriv)) {
- pid->deriv = 0;
}
- pid->outD = pid->kd * pid->deriv;
- output += pid->outD;
+ #endif
+ if (isnan(pid->deriv)) {
+ pid->deriv = 0;
+ }
+ pid->outD = pid->kd * pid->deriv;
+ output += pid->outD;
- pid->integ += pid->error * pid->dt;
+ pid->integ += pid->error * pid->dt;
- // Constrain the integral (unless the iLimit is zero)
- if(pid->iLimit != 0)
- {
- pid->integ = constrain(pid->integ, -pid->iLimit, pid->iLimit);
- }
+ // Constrain the integral (unless the iLimit is zero)
+ if(pid->iLimit != 0)
+ {
+ pid->integ = constrain(pid->integ, -pid->iLimit, pid->iLimit);
+ }
+
+ pid->outI = pid->ki * pid->integ;
+ output += pid->outI;
- pid->outI = pid->ki * pid->integ;
- output += pid->outI;
-
- pid->outFF = pid->kff * pid->desired;
- output += pid->outFF;
-
- #if CONFIG_CONTROLLER_PID_FILTER_ALL
- //filter complete output instead of only D component to compensate for increased noise from increased barometer influence
- if (pid->enableDFilter)
- {
- output = lpf2pApply(&pid->dFilter, output);
- }
- else {
- output = output;
- }
- if (isnan(output)) {
- output = 0;
- }
- #endif
-
-
-
- // Constrain the total PID output (unless the outputLimit is zero)
- if(pid->outputLimit != 0)
+ pid->outFF = pid->kff * pid->desired;
+ output += pid->outFF;
+
+ #if CONFIG_CONTROLLER_PID_FILTER_ALL
+ //filter complete output instead of only D component to compensate for increased noise from increased barometer influence
+ if (pid->enableDFilter)
{
- output = constrain(output, -pid->outputLimit, pid->outputLimit);
+ output = lpf2pApply(&pid->dFilter, output);
+ }
+ else {
+ output = output;
+ }
+ if (isnan(output)) {
+ output = 0;
}
+ #endif
+ // Constrain the total PID output (unless the outputLimit is zero)
+ if(pid->outputLimit != 0)
+ {
+ output = constrain(output, -pid->outputLimit, pid->outputLimit);
+ }
- pid->prevError = pid->error;
+ pid->prevMeasured = measured;
- return output;
+ return output;
}
void pidSetIntegralLimit(PidObject* pid, const float limit) {
pid->iLimit = limit;
}
-
void pidReset(PidObject* pid)
{
- pid->error = 0;
- pid->prevError = 0;
- pid->integ = 0;
- pid->deriv = 0;
+ pid->error = 0;
+ pid->prevMeasured = 0;
+ pid->integ = 0;
+ pid->deriv = 0;
}
void pidSetError(PidObject* pid, const float error)