8 #include "common/maths.h"
9 #include "common/axis.h"
11 #include "drivers/accgyro_common.h"
12 #include "drivers/light_ledring.h"
13 #include "drivers/light_led.h"
14 #include "drivers/gpio_common.h"
15 #include "drivers/system_common.h"
16 #include "drivers/serial_common.h"
18 #include "boardalignment.h"
23 #include "flight_imu.h"
24 #include "flight_common.h"
25 #include "flight_mixer.h"
27 #include "gps_common.h"
28 #include "sensors_common.h"
29 #include "sensors_sonar.h"
30 #include "sensors_compass.h"
31 #include "sensors_acceleration.h"
32 #include "sensors_barometer.h"
33 #include "sensors_gyro.h"
34 #include "serial_cli.h"
35 #include "serial_common.h"
36 #include "statusindicator.h"
37 #include "rc_controls.h"
38 #include "rc_curves.h"
39 #include "rx_common.h"
40 #include "telemetry_common.h"
42 #include "runtime_config.h"
44 #include "config_profile.h"
45 #include "config_master.h"
55 /* for VBAT monitoring frequency */
56 #define VBATFREQ 6 // to read battery voltage - nth number of loop iterations
59 uint32_t currentTime
= 0;
60 uint32_t previousTime
= 0;
61 uint16_t cycleTime
= 0; // this is the number in micro second to achieve a full loop, it can differ a little and is taken into account in the PID loop
62 int16_t headFreeModeHold
;
64 int16_t telemTemperature1
; // gyro sensor temperature
66 uint16_t rssi
; // range: [0;1023]
68 extern uint8_t dynP8
[3], dynI8
[3], dynD8
[3];
69 extern failsafe_t
*failsafe
;
71 typedef void (* pidControllerFuncPtr
)(pidProfile_t
*pidProfile
, controlRateConfig_t
*controlRateConfig
, uint16_t max_angle_inclination
, rollAndPitchTrims_t
*accelerometerTrims
);
73 extern pidControllerFuncPtr pid_controller
;
75 // Automatic ACC Offset Calibration
76 bool AccInflightCalibrationArmed
= false;
77 bool AccInflightCalibrationMeasurementDone
= false;
78 bool AccInflightCalibrationSavetoEEProm
= false;
79 bool AccInflightCalibrationActive
= false;
80 uint16_t InflightcalibratingA
= 0;
84 static uint32_t calibratedAccTime
;
86 int32_t axis
, prop1
, prop2
;
88 static uint8_t batteryWarningEnabled
= false;
90 static uint8_t vbatTimer
= 0;
92 // PITCH & ROLL only dynamic PID adjustemnt, depending on throttle value
93 if (rcData
[THROTTLE
] < currentProfile
.tpa_breakpoint
) {
96 if (rcData
[THROTTLE
] < 2000) {
97 prop2
= 100 - (uint16_t)currentProfile
.dynThrPID
* (rcData
[THROTTLE
] - currentProfile
.tpa_breakpoint
) / (2000 - currentProfile
.tpa_breakpoint
);
99 prop2
= 100 - currentProfile
.dynThrPID
;
103 for (axis
= 0; axis
< 3; axis
++) {
104 tmp
= min(abs(rcData
[axis
] - masterConfig
.rxConfig
.midrc
), 500);
105 if (axis
!= 2) { // ROLL & PITCH
106 if (currentProfile
.deadband
) {
107 if (tmp
> currentProfile
.deadband
) {
108 tmp
-= currentProfile
.deadband
;
115 rcCommand
[axis
] = lookupPitchRollRC
[tmp2
] + (tmp
- tmp2
* 100) * (lookupPitchRollRC
[tmp2
+ 1] - lookupPitchRollRC
[tmp2
]) / 100;
116 prop1
= 100 - (uint16_t)currentProfile
.controlRateConfig
.rollPitchRate
* tmp
/ 500;
117 prop1
= (uint16_t)prop1
* prop2
/ 100;
119 if (currentProfile
.yaw_deadband
) {
120 if (tmp
> currentProfile
.yaw_deadband
) {
121 tmp
-= currentProfile
.yaw_deadband
;
126 rcCommand
[axis
] = tmp
* -masterConfig
.yaw_control_direction
;
127 prop1
= 100 - (uint16_t)currentProfile
.controlRateConfig
.yawRate
* abs(tmp
) / 500;
129 dynP8
[axis
] = (uint16_t)currentProfile
.pidProfile
.P8
[axis
] * prop1
/ 100;
130 dynI8
[axis
] = (uint16_t)currentProfile
.pidProfile
.I8
[axis
] * prop1
/ 100;
131 dynD8
[axis
] = (uint16_t)currentProfile
.pidProfile
.D8
[axis
] * prop1
/ 100;
132 if (rcData
[axis
] < masterConfig
.rxConfig
.midrc
)
133 rcCommand
[axis
] = -rcCommand
[axis
];
136 tmp
= constrain(rcData
[THROTTLE
], masterConfig
.rxConfig
.mincheck
, PWM_RANGE_MAX
);
137 tmp
= (uint32_t)(tmp
- masterConfig
.rxConfig
.mincheck
) * PWM_RANGE_MIN
/ (PWM_RANGE_MAX
- masterConfig
.rxConfig
.mincheck
); // [MINCHECK;2000] -> [0;1000]
139 rcCommand
[THROTTLE
] = lookupThrottleRC
[tmp2
] + (tmp
- tmp2
* 100) * (lookupThrottleRC
[tmp2
+ 1] - lookupThrottleRC
[tmp2
]) / 100; // [0;1000] -> expo -> [MINTHROTTLE;MAXTHROTTLE]
141 if (f
.HEADFREE_MODE
) {
142 float radDiff
= degreesToRadians(heading
- headFreeModeHold
);
143 float cosDiff
= cosf(radDiff
);
144 float sinDiff
= sinf(radDiff
);
145 int16_t rcCommand_PITCH
= rcCommand
[PITCH
] * cosDiff
+ rcCommand
[ROLL
] * sinDiff
;
146 rcCommand
[ROLL
] = rcCommand
[ROLL
] * cosDiff
- rcCommand
[PITCH
] * sinDiff
;
147 rcCommand
[PITCH
] = rcCommand_PITCH
;
150 if (feature(FEATURE_VBAT
)) {
151 if (!(++vbatTimer
% VBATFREQ
)) {
152 updateBatteryVoltage();
154 batteryWarningEnabled
= shouldSoundBatteryAlarm();
158 buzzer(batteryWarningEnabled
); // external buzzer routine that handles buzzer events globally now
160 if ((!isAccelerationCalibrationComplete() && sensors(SENSOR_ACC
)) || (!isGyroCalibrationComplete())) { // Calibration phasis
163 if (f
.ACC_CALIBRATED
)
168 checkTelemetryState();
172 if (feature(FEATURE_LED_RING
)) {
173 static uint32_t LEDTime
;
174 if ((int32_t)(currentTime
- LEDTime
) >= 0) {
175 LEDTime
= currentTime
+ 50000;
176 ledringState(f
.ARMED
, angle
[AI_PITCH
], angle
[AI_ROLL
]);
181 if ((int32_t)(currentTime
- calibratedAccTime
) >= 0) {
182 if (!f
.SMALL_ANGLE
) {
183 f
.ACC_CALIBRATED
= 0; // the multi uses ACC and is not calibrated or is too much inclinated
185 calibratedAccTime
= currentTime
+ 500000;
187 f
.ACC_CALIBRATED
= 1;
193 if (!cliMode
&& feature(FEATURE_TELEMETRY
)) {
197 if (sensors(SENSOR_GPS
)) {
198 updateGpsIndicator(currentTime
);
201 // Read out gyro temperature. can use it for something somewhere. maybe get MCU temperature instead? lots of fun possibilities.
202 if (gyro
.temperature
)
203 gyro
.temperature(&telemTemperature1
);
209 static void mwArm(void)
211 if (isGyroCalibrationComplete() && f
.ACC_CALIBRATED
) {
212 // TODO: feature(FEATURE_FAILSAFE) && failsafeCnt < 2
213 // TODO: && ( !feature || ( feature && ( failsafecnt > 2) )
214 if (!f
.ARMED
) { // arm now!
216 headFreeModeHold
= heading
;
218 } else if (!f
.ARMED
) {
219 blinkLedAndSoundBeeper(2, 255, 1);
223 static void mwVario(void)
230 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
231 static uint8_t rcSticks
; // this hold sticks position for command combos
234 static uint32_t rcTime
= 0;
236 static int16_t initialThrottleHold
;
238 static uint32_t loopTime
;
239 uint16_t auxState
= 0;
240 bool isThrottleLow
= false;
241 bool rcReady
= false;
243 // calculate rc stuff from serial-based receivers (spek/sbus)
244 if (feature(FEATURE_SERIALRX
)) {
245 rcReady
= isSerialRxFrameComplete(&masterConfig
.rxConfig
);
248 if (((int32_t)(currentTime
- rcTime
) >= 0) || rcReady
) { // 50Hz or data driven
250 rcTime
= currentTime
+ 20000;
251 computeRC(&masterConfig
.rxConfig
, &rxRuntimeConfig
);
253 // in 3D mode, we need to be able to disarm by switch at any time
254 if (feature(FEATURE_3D
)) {
255 if (!rcOptions
[BOXARM
])
259 // Read value of AUX channel as rssi
260 // 0 is disable, 1-4 is AUX{1..4}
261 if (masterConfig
.rssi_aux_channel
> 0) {
262 const int16_t rssiChannelData
= rcData
[AUX1
+ masterConfig
.rssi_aux_channel
- 1];
263 // Range of rssiChannelData is [1000;2000]. rssi should be in [0;1023];
264 rssi
= (uint16_t)((constrain(rssiChannelData
- 1000, 0, 1000) / 1000.0f
) * 1023.0f
);
267 if (feature(FEATURE_FAILSAFE
)) {
268 failsafe
->vTable
->updateState();
271 // ------------------ STICKS COMMAND HANDLER --------------------
272 // checking sticks positions
273 for (i
= 0; i
< 4; i
++) {
275 if (rcData
[i
] > masterConfig
.rxConfig
.mincheck
)
276 stTmp
|= 0x80; // check for MIN
277 if (rcData
[i
] < masterConfig
.rxConfig
.maxcheck
)
278 stTmp
|= 0x40; // check for MAX
280 if (stTmp
== rcSticks
) {
281 if (rcDelayCommand
< 250)
288 if (feature(FEATURE_3D
) && (rcData
[THROTTLE
] > (masterConfig
.rxConfig
.midrc
- masterConfig
.flight3DConfig
.deadband3d_throttle
) && rcData
[THROTTLE
] < (masterConfig
.rxConfig
.midrc
+ masterConfig
.flight3DConfig
.deadband3d_throttle
)))
289 isThrottleLow
= true;
290 else if (!feature(FEATURE_3D
) && (rcData
[THROTTLE
] < masterConfig
.rxConfig
.mincheck
))
291 isThrottleLow
= true;
295 if (currentProfile
.activate
[BOXARM
] > 0) { // Arming/Disarming via ARM BOX
296 if (rcOptions
[BOXARM
] && f
.OK_TO_ARM
)
303 if (rcDelayCommand
== 20) {
304 if (f
.ARMED
) { // actions during armed
305 // Disarm on throttle down + yaw
306 if (currentProfile
.activate
[BOXARM
] == 0 && (rcSticks
== THR_LO
+ YAW_LO
+ PIT_CE
+ ROL_CE
))
308 // Disarm on roll (only when retarded_arm is enabled)
309 if (masterConfig
.retarded_arm
&& currentProfile
.activate
[BOXARM
] == 0 && (rcSticks
== THR_LO
+ YAW_CE
+ PIT_CE
+ ROL_LO
))
311 } else { // actions during not armed
314 if (rcSticks
== THR_LO
+ YAW_LO
+ PIT_LO
+ ROL_CE
) {
315 gyroSetCalibrationCycles(CALIBRATING_GYRO_CYCLES
);
316 if (feature(FEATURE_GPS
))
317 GPS_reset_home_position();
319 if (sensors(SENSOR_BARO
))
320 baroSetCalibrationCycles(10); // calibrate baro to new ground level (10 * 25 ms = ~250 ms non blocking)
322 if (!sensors(SENSOR_MAG
))
323 heading
= 0; // reset heading to zero after gyro calibration
324 // Inflight ACC Calibration
325 } else if (feature(FEATURE_INFLIGHT_ACC_CAL
) && (rcSticks
== THR_LO
+ YAW_LO
+ PIT_HI
+ ROL_HI
)) {
326 if (AccInflightCalibrationMeasurementDone
) { // trigger saving into eeprom after landing
327 AccInflightCalibrationMeasurementDone
= false;
328 AccInflightCalibrationSavetoEEProm
= true;
330 AccInflightCalibrationArmed
= !AccInflightCalibrationArmed
;
331 if (AccInflightCalibrationArmed
) {
332 queueConfirmationBeep(2);
334 queueConfirmationBeep(3);
339 // Multiple configuration profiles
340 if (rcSticks
== THR_LO
+ YAW_LO
+ PIT_CE
+ ROL_LO
) // ROLL left -> Profile 1
342 else if (rcSticks
== THR_LO
+ YAW_LO
+ PIT_HI
+ ROL_CE
) // PITCH up -> Profile 2
344 else if (rcSticks
== THR_LO
+ YAW_LO
+ PIT_CE
+ ROL_HI
) // ROLL right -> Profile 3
347 masterConfig
.current_profile_index
= i
- 1;
350 blinkLedAndSoundBeeper(2, 40, i
);
351 // TODO alarmArray[0] = i;
355 if (currentProfile
.activate
[BOXARM
] == 0 && (rcSticks
== THR_LO
+ YAW_HI
+ PIT_CE
+ ROL_CE
))
358 else if (masterConfig
.retarded_arm
&& currentProfile
.activate
[BOXARM
] == 0 && (rcSticks
== THR_LO
+ YAW_CE
+ PIT_CE
+ ROL_HI
))
361 else if (rcSticks
== THR_HI
+ YAW_LO
+ PIT_LO
+ ROL_CE
)
362 accSetCalibrationCycles(CALIBRATING_ACC_CYCLES
);
364 else if (rcSticks
== THR_HI
+ YAW_HI
+ PIT_LO
+ ROL_CE
)
368 if (rcSticks
== THR_HI
+ YAW_CE
+ PIT_HI
+ ROL_CE
) {
369 currentProfile
.accelerometerTrims
.trims
.pitch
+= 2;
371 } else if (rcSticks
== THR_HI
+ YAW_CE
+ PIT_LO
+ ROL_CE
) {
372 currentProfile
.accelerometerTrims
.trims
.pitch
-= 2;
374 } else if (rcSticks
== THR_HI
+ YAW_CE
+ PIT_CE
+ ROL_HI
) {
375 currentProfile
.accelerometerTrims
.trims
.roll
+= 2;
377 } else if (rcSticks
== THR_HI
+ YAW_CE
+ PIT_CE
+ ROL_LO
) {
378 currentProfile
.accelerometerTrims
.trims
.roll
-= 2;
382 copyCurrentProfileToProfileSlot(masterConfig
.current_profile_index
);
384 readEEPROMAndNotify();
385 rcDelayCommand
= 0; // allow autorepetition
390 if (feature(FEATURE_INFLIGHT_ACC_CAL
)) {
391 if (AccInflightCalibrationArmed
&& f
.ARMED
&& rcData
[THROTTLE
] > masterConfig
.rxConfig
.mincheck
&& !rcOptions
[BOXARM
]) { // Copter is airborne and you are turning it off via boxarm : start measurement
392 InflightcalibratingA
= 50;
393 AccInflightCalibrationArmed
= false;
395 if (rcOptions
[BOXCALIB
]) { // Use the Calib Option to activate : Calib = TRUE Meausrement started, Land and Calib = 0 measurement stored
396 if (!AccInflightCalibrationActive
&& !AccInflightCalibrationMeasurementDone
)
397 InflightcalibratingA
= 50;
398 AccInflightCalibrationActive
= true;
399 } else if (AccInflightCalibrationMeasurementDone
&& !f
.ARMED
) {
400 AccInflightCalibrationMeasurementDone
= false;
401 AccInflightCalibrationSavetoEEProm
= true;
405 // Check AUX switches
406 for (i
= 0; i
< 4; i
++)
407 auxState
|= (rcData
[AUX1
+ i
] < 1300) << (3 * i
) | (1300 < rcData
[AUX1
+ i
] && rcData
[AUX1
+ i
] < 1700) << (3 * i
+ 1) | (rcData
[AUX1
+ i
] > 1700) << (3 * i
+ 2);
408 for (i
= 0; i
< CHECKBOX_ITEM_COUNT
; i
++)
409 rcOptions
[i
] = (auxState
& currentProfile
.activate
[i
]) > 0;
411 if ((rcOptions
[BOXANGLE
] || (feature(FEATURE_FAILSAFE
) && failsafe
->vTable
->hasTimerElapsed())) && (sensors(SENSOR_ACC
))) {
412 // bumpless transfer to Level mode
418 f
.ANGLE_MODE
= 0; // failsafe support
421 if (rcOptions
[BOXHORIZON
]) {
423 if (!f
.HORIZON_MODE
) {
431 if ((rcOptions
[BOXARM
]) == 0)
433 if (f
.ANGLE_MODE
|| f
.HORIZON_MODE
) {
440 if (sensors(SENSOR_BARO
)) {
441 // Baro alt hold activate
442 if (rcOptions
[BOXBARO
]) {
446 initialThrottleHold
= rcCommand
[THROTTLE
];
453 // Vario signalling activate
454 if (feature(FEATURE_VARIO
)) {
455 if (rcOptions
[BOXVARIO
]) {
467 if (sensors(SENSOR_ACC
) || sensors(SENSOR_MAG
)) {
468 if (rcOptions
[BOXMAG
]) {
476 if (rcOptions
[BOXHEADFREE
]) {
477 if (!f
.HEADFREE_MODE
) {
483 if (rcOptions
[BOXHEADADJ
]) {
484 headFreeModeHold
= heading
; // acquire new heading
489 if (sensors(SENSOR_GPS
)) {
490 updateGpsWaypointsAndMode();
493 if (rcOptions
[BOXPASSTHRU
]) {
499 if (masterConfig
.mixerConfiguration
== MULTITYPE_FLYING_WING
|| masterConfig
.mixerConfiguration
== MULTITYPE_AIRPLANE
) {
502 } else { // not in rc loop
503 static int taskOrder
= 0; // never call all function in the same loop, to avoid high delay spikes
508 if (sensors(SENSOR_MAG
) && compassGetADC(&masterConfig
.magZero
))
514 if (sensors(SENSOR_BARO
) && baroUpdate(currentTime
) != BAROMETER_ACTION_NOT_READY
)
520 if (sensors(SENSOR_BARO
) && getEstimatedAltitude())
524 // if GPS feature is enabled, gpsThread() will be called at some intervals to check for stuck
525 // hardware, wrong baud rates, init GPS if needed, etc. Don't use SENSOR_GPS here as gpsThread() can and will
526 // change this based on available hardware
528 if (feature(FEATURE_GPS
)) {
535 if (sensors(SENSOR_SONAR
)) {
539 if (feature(FEATURE_VARIO
) && f
.VARIO_MODE
)
545 currentTime
= micros();
546 if (masterConfig
.looptime
== 0 || (int32_t)(currentTime
- loopTime
) >= 0) {
547 loopTime
= currentTime
+ masterConfig
.looptime
;
551 // Measure loop rate just afer reading the sensors
552 currentTime
= micros();
553 cycleTime
= (int32_t)(currentTime
- previousTime
);
554 previousTime
= currentTime
;
557 if (sensors(SENSOR_MAG
)) {
558 if (abs(rcCommand
[YAW
]) < 70 && f
.MAG_MODE
) {
559 int16_t dif
= heading
- magHold
;
564 dif
*= -masterConfig
.yaw_control_direction
;
566 rcCommand
[YAW
] -= dif
* currentProfile
.pidProfile
.P8
[PIDMAG
] / 30; // 18 deg
573 if (sensors(SENSOR_BARO
)) {
575 static uint8_t isAltHoldChanged
= 0;
576 static int16_t AltHoldCorr
= 0;
578 // multirotor alt hold
579 if (currentProfile
.alt_hold_fast_change
) {
581 if (abs(rcCommand
[THROTTLE
] - initialThrottleHold
) > currentProfile
.alt_hold_throttle_neutral
) {
583 isAltHoldChanged
= 1;
584 rcCommand
[THROTTLE
] += (rcCommand
[THROTTLE
] > initialThrottleHold
) ? -currentProfile
.alt_hold_throttle_neutral
: currentProfile
.alt_hold_throttle_neutral
;
586 if (isAltHoldChanged
) {
588 isAltHoldChanged
= 0;
590 rcCommand
[THROTTLE
] = constrain(initialThrottleHold
+ BaroPID
, masterConfig
.escAndServoConfig
.minthrottle
+ 100, masterConfig
.escAndServoConfig
.maxthrottle
);
593 // slow alt changes for apfags
594 if (abs(rcCommand
[THROTTLE
] - initialThrottleHold
) > currentProfile
.alt_hold_throttle_neutral
) {
595 // Slowly increase/decrease AltHold proportional to stick movement ( +100 throttle gives ~ +50 cm in 1 second with cycle time about 3-4ms)
596 AltHoldCorr
+= rcCommand
[THROTTLE
] - initialThrottleHold
;
597 AltHold
+= AltHoldCorr
/ 2000;
599 isAltHoldChanged
= 1;
600 } else if (isAltHoldChanged
) {
603 isAltHoldChanged
= 0;
605 rcCommand
[THROTTLE
] = constrain(initialThrottleHold
+ BaroPID
, masterConfig
.escAndServoConfig
.minthrottle
+ 100, masterConfig
.escAndServoConfig
.maxthrottle
);
608 // handle fixedwing-related althold. UNTESTED! and probably wrong
609 // most likely need to check changes on pitch channel and 'reset' althold similar to
610 // how throttle does it on multirotor
611 rcCommand
[PITCH
] += BaroPID
* masterConfig
.fixedwing_althold_dir
;
617 if (currentProfile
.throttle_correction_value
&& (f
.ANGLE_MODE
|| f
.HORIZON_MODE
)) {
618 rcCommand
[THROTTLE
] += throttleAngleCorrection
;
621 if (sensors(SENSOR_GPS
)) {
622 if ((f
.GPS_HOME_MODE
|| f
.GPS_HOLD_MODE
) && f
.GPS_FIX_HOME
) {
623 updateGpsStateForHomeAndHoldMode();
627 // PID - note this is function pointer set by setPIDController()
628 pid_controller(¤tProfile
.pidProfile
, ¤tProfile
.controlRateConfig
, masterConfig
.max_angle_inclination
, ¤tProfile
.accelerometerTrims
);