8 uint8_t toggleBeep
= 0;
9 uint32_t currentTime
= 0;
10 uint32_t previousTime
= 0;
11 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
12 int16_t headFreeModeHold
;
14 int16_t annex650_overrun_count
= 0;
15 uint8_t vbat
; // battery voltage in 0.1V steps
16 int16_t telemTemperature1
; // gyro sensor temperature
18 int16_t failsafeCnt
= 0;
19 int16_t failsafeEvents
= 0;
20 int16_t rcData
[8] = { 1502, 1502, 1502, 1502, 1502, 1502, 1502, 1502 }; // interval [1000;2000]
21 int16_t rcCommand
[4]; // interval [1000;2000] for THROTTLE and [-500;+500] for ROLL/PITCH/YAW
22 int16_t lookupPitchRollRC
[6]; // lookup table for expo & RC rate PITCH+ROLL
23 int16_t lookupThrottleRC
[11]; // lookup table for expo & mid THROTTLE
24 uint16_t rssi
; // range: [0;1023]
25 rcReadRawDataPtr rcReadRawFunc
= NULL
; // receive data from default (pwm/ppm) or additional (spek/sbus/?? receiver drivers)
27 static void pidMultiWii(void);
28 static void pidRewrite(void);
29 pidControllerFuncPtr pid_controller
= pidMultiWii
; // which pid controller are we using, defaultMultiWii
31 uint8_t dynP8
[3], dynI8
[3], dynD8
[3];
32 uint8_t rcOptions
[CHECKBOXITEMS
];
36 // **********************
38 // **********************
43 uint16_t GPS_distanceToHome
; // distance to home point in meters
44 int16_t GPS_directionToHome
; // direction to home or hol point in degrees
45 uint16_t GPS_altitude
, GPS_speed
; // altitude in 0.1m and speed in 0.1m/s
46 uint8_t GPS_update
= 0; // it's a binary toogle to distinct a GPS position update
47 int16_t GPS_angle
[2] = { 0, 0 }; // it's the angles that must be applied for GPS correction
48 uint16_t GPS_ground_course
= 0; // degrees * 10
49 uint8_t GPS_Present
= 0; // Checksum from Gps serial
50 uint8_t GPS_Enable
= 0;
52 int16_t nav_rated
[2]; // Adding a rate controller to the navigation to make it smoother
53 int8_t nav_mode
= NAV_MODE_NONE
; // Navigation mode
54 uint8_t GPS_numCh
; // Number of channels
55 uint8_t GPS_svinfo_chn
[16]; // Channel number
56 uint8_t GPS_svinfo_svid
[16]; // Satellite ID
57 uint8_t GPS_svinfo_quality
[16]; // Bitfield Qualtity
58 uint8_t GPS_svinfo_cno
[16]; // Carrier to Noise Ratio (Signal Strength)
60 // Automatic ACC Offset Calibration
61 uint16_t InflightcalibratingA
= 0;
62 int16_t AccInflightCalibrationArmed
;
63 uint16_t AccInflightCalibrationMeasurementDone
= 0;
64 uint16_t AccInflightCalibrationSavetoEEProm
= 0;
65 uint16_t AccInflightCalibrationActive
= 0;
67 // Battery monitoring stuff
68 uint8_t batteryCellCount
= 3; // cell count
69 uint16_t batteryWarningVoltage
; // annoying buzzer after this one, battery ready to be dead
71 void blinkLED(uint8_t num
, uint8_t wait
, uint8_t repeat
)
75 for (r
= 0; r
< repeat
; r
++) {
76 for (i
= 0; i
< num
; i
++) {
77 LED0_TOGGLE
; // switch LEDPIN state
86 #define BREAKPOINT 1500
88 // this code is executed at each loop and won't interfere with control loop if it lasts less than 650 microseconds
91 static uint32_t calibratedAccTime
;
93 static uint8_t buzzerFreq
; //delay between buzzer ring
94 static uint8_t vbatTimer
= 0;
95 uint8_t axis
, prop1
, prop2
;
96 static uint8_t ind
= 0;
98 static uint16_t vbatRawArray
[8];
101 // PITCH & ROLL only dynamic PID adjustemnt, depending on throttle value
102 if (rcData
[THROTTLE
] < BREAKPOINT
) {
105 if (rcData
[THROTTLE
] < 2000) {
106 prop2
= 100 - (uint16_t) cfg
.dynThrPID
* (rcData
[THROTTLE
] - BREAKPOINT
) / (2000 - BREAKPOINT
);
108 prop2
= 100 - cfg
.dynThrPID
;
112 for (axis
= 0; axis
< 3; axis
++) {
113 tmp
= min(abs(rcData
[axis
] - mcfg
.midrc
), 500);
114 if (axis
!= 2) { // ROLL & PITCH
116 if (tmp
> cfg
.deadband
) {
124 rcCommand
[axis
] = lookupPitchRollRC
[tmp2
] + (tmp
- tmp2
* 100) * (lookupPitchRollRC
[tmp2
+ 1] - lookupPitchRollRC
[tmp2
]) / 100;
125 prop1
= 100 - (uint16_t) cfg
.rollPitchRate
* tmp
/ 500;
126 prop1
= (uint16_t) prop1
*prop2
/ 100;
128 if (cfg
.yawdeadband
) {
129 if (tmp
> cfg
.yawdeadband
) {
130 tmp
-= cfg
.yawdeadband
;
135 rcCommand
[axis
] = tmp
;
136 prop1
= 100 - (uint16_t) cfg
.yawRate
* tmp
/ 500;
138 dynP8
[axis
] = (uint16_t) cfg
.P8
[axis
] * prop1
/ 100;
139 dynI8
[axis
] = (uint16_t) cfg
.I8
[axis
] * prop1
/ 100;
140 dynD8
[axis
] = (uint16_t) cfg
.D8
[axis
] * prop1
/ 100;
141 if (rcData
[axis
] < mcfg
.midrc
)
142 rcCommand
[axis
] = -rcCommand
[axis
];
145 tmp
= constrain(rcData
[THROTTLE
], mcfg
.mincheck
, 2000);
146 tmp
= (uint32_t) (tmp
- mcfg
.mincheck
) * 1000 / (2000 - mcfg
.mincheck
); // [MINCHECK;2000] -> [0;1000]
148 rcCommand
[THROTTLE
] = lookupThrottleRC
[tmp2
] + (tmp
- tmp2
* 100) * (lookupThrottleRC
[tmp2
+ 1] - lookupThrottleRC
[tmp2
]) / 100; // [0;1000] -> expo -> [MINTHROTTLE;MAXTHROTTLE]
150 if(f
.HEADFREE_MODE
) {
151 float radDiff
= (heading
- headFreeModeHold
) * M_PI
/ 180.0f
;
152 float cosDiff
= cosf(radDiff
);
153 float sinDiff
= sinf(radDiff
);
154 int16_t rcCommand_PITCH
= rcCommand
[PITCH
] * cosDiff
+ rcCommand
[ROLL
] * sinDiff
;
155 rcCommand
[ROLL
] = rcCommand
[ROLL
] * cosDiff
- rcCommand
[PITCH
] * sinDiff
;
156 rcCommand
[PITCH
] = rcCommand_PITCH
;
159 if (feature(FEATURE_VBAT
)) {
160 if (!(++vbatTimer
% VBATFREQ
)) {
161 vbatRawArray
[(ind
++) % 8] = adcGetChannel(ADC_BATTERY
);
162 for (i
= 0; i
< 8; i
++)
163 vbatRaw
+= vbatRawArray
[i
];
164 vbat
= batteryAdcToVoltage(vbatRaw
/ 8);
166 if ((vbat
> batteryWarningVoltage
) || (vbat
< mcfg
.vbatmincellvoltage
)) { // VBAT ok, buzzer off
169 buzzerFreq
= 4; // low battery
172 buzzer(buzzerFreq
); // external buzzer routine that handles buzzer events globally now
174 if ((calibratingA
> 0 && sensors(SENSOR_ACC
)) || (calibratingG
> 0)) { // Calibration phasis
177 if (f
.ACC_CALIBRATED
)
181 // This will switch to/from 9600 or 115200 baud depending on state. Of course, it should only do it on changes.
182 if (feature(FEATURE_TELEMETRY
))
183 initTelemetry(f
.ARMED
);
187 if (feature(FEATURE_LED_RING
)) {
188 static uint32_t LEDTime
;
189 if ((int32_t)(currentTime
- LEDTime
) >= 0) {
190 LEDTime
= currentTime
+ 50000;
196 if ((int32_t)(currentTime
- calibratedAccTime
) >= 0) {
197 if (!f
.SMALL_ANGLES_25
) {
198 f
.ACC_CALIBRATED
= 0; // the multi uses ACC and is not calibrated or is too much inclinated
200 calibratedAccTime
= currentTime
+ 500000;
202 f
.ACC_CALIBRATED
= 1;
208 if (sensors(SENSOR_GPS
)) {
209 static uint32_t GPSLEDTime
;
210 if ((int32_t)(currentTime
- GPSLEDTime
) >= 0 && (GPS_numSat
>= 5)) {
211 GPSLEDTime
= currentTime
+ 150000;
216 // Read out gyro temperature. can use it for something somewhere. maybe get MCU temperature instead? lots of fun possibilities.
217 if (gyro
.temperature
)
218 gyro
.temperature(&telemTemperature1
);
224 uint16_t pwmReadRawRC(uint8_t chan
)
228 data
= pwmRead(mcfg
.rcmap
[chan
]);
229 if (data
< 750 || data
> 2250)
237 static int16_t rcData4Values
[8][4], rcDataMean
[8];
238 static uint8_t rc4ValuesIndex
= 0;
242 for (chan
= 0; chan
< 8; chan
++) {
243 rcData4Values
[chan
][rc4ValuesIndex
% 4] = rcReadRawFunc(chan
);
244 rcDataMean
[chan
] = 0;
245 for (a
= 0; a
< 4; a
++)
246 rcDataMean
[chan
] += rcData4Values
[chan
][a
];
248 rcDataMean
[chan
] = (rcDataMean
[chan
] + 2) / 4;
249 if (rcDataMean
[chan
] < rcData
[chan
] - 3)
250 rcData
[chan
] = rcDataMean
[chan
] + 2;
251 if (rcDataMean
[chan
] > rcData
[chan
] + 3)
252 rcData
[chan
] = rcDataMean
[chan
] - 2;
256 static void mwArm(void)
258 if (calibratingG
== 0 && f
.ACC_CALIBRATED
) {
259 // TODO: feature(FEATURE_FAILSAFE) && failsafeCnt < 2
260 // TODO: && ( !feature || ( feature && ( failsafecnt > 2) )
261 if (!f
.ARMED
) { // arm now!
263 headFreeModeHold
= heading
;
265 } else if (!f
.ARMED
) {
270 static void mwDisarm(void)
276 static void mwVario(void)
281 static int32_t errorGyroI
[3] = { 0, 0, 0 };
282 static int32_t errorAngleI
[2] = { 0, 0 };
284 static void pidMultiWii(void)
287 int32_t error
, errorAngle
;
288 int32_t PTerm
, ITerm
, PTermACC
, ITermACC
= 0, PTermGYRO
= 0, ITermGYRO
= 0, DTerm
;
289 static int16_t lastGyro
[3] = { 0, 0, 0 };
290 static int32_t delta1
[3], delta2
[3];
294 // **** PITCH & ROLL & YAW PID ****
295 prop
= max(abs(rcCommand
[PITCH
]), abs(rcCommand
[ROLL
])); // range [0;500]
296 for (axis
= 0; axis
< 3; axis
++) {
297 if ((f
.ANGLE_MODE
|| f
.HORIZON_MODE
) && axis
< 2) { // MODE relying on ACC
298 // 50 degrees max inclination
299 errorAngle
= constrain(2 * rcCommand
[axis
] + GPS_angle
[axis
], -500, +500) - angle
[axis
] + cfg
.angleTrim
[axis
];
300 PTermACC
= errorAngle
* cfg
.P8
[PIDLEVEL
] / 100; // 32 bits is needed for calculation: errorAngle*P8[PIDLEVEL] could exceed 32768 16 bits is ok for result
301 PTermACC
= constrain(PTermACC
, -cfg
.D8
[PIDLEVEL
] * 5, +cfg
.D8
[PIDLEVEL
] * 5);
303 errorAngleI
[axis
] = constrain(errorAngleI
[axis
] + errorAngle
, -10000, +10000); // WindUp
304 ITermACC
= (errorAngleI
[axis
] * cfg
.I8
[PIDLEVEL
]) >> 12;
306 if (!f
.ANGLE_MODE
|| f
.HORIZON_MODE
|| axis
== 2) { // MODE relying on GYRO or YAW axis
307 error
= (int32_t)rcCommand
[axis
] * 10 * 8 / cfg
.P8
[axis
];
308 error
-= gyroData
[axis
];
310 PTermGYRO
= rcCommand
[axis
];
312 errorGyroI
[axis
] = constrain(errorGyroI
[axis
] + error
, -16000, +16000); // WindUp
313 if (abs(gyroData
[axis
]) > 640)
314 errorGyroI
[axis
] = 0;
315 ITermGYRO
= (errorGyroI
[axis
] / 125 * cfg
.I8
[axis
]) >> 6;
317 if (f
.HORIZON_MODE
&& axis
< 2) {
318 PTerm
= (PTermACC
* (500 - prop
) + PTermGYRO
* prop
) / 500;
319 ITerm
= (ITermACC
* (500 - prop
) + ITermGYRO
* prop
) / 500;
321 if (f
.ANGLE_MODE
&& axis
< 2) {
330 PTerm
-= (int32_t)gyroData
[axis
] * dynP8
[axis
] / 10 / 8; // 32 bits is needed for calculation
331 delta
= gyroData
[axis
] - lastGyro
[axis
];
332 lastGyro
[axis
] = gyroData
[axis
];
333 deltaSum
= delta1
[axis
] + delta2
[axis
] + delta
;
334 delta2
[axis
] = delta1
[axis
];
335 delta1
[axis
] = delta
;
336 DTerm
= (deltaSum
* dynD8
[axis
]) / 32;
337 axisPID
[axis
] = PTerm
+ ITerm
- DTerm
;
341 #define GYRO_I_MAX 256
343 static void pidRewrite(void)
347 int32_t delta
, deltaSum
;
348 static int32_t delta1
[3], delta2
[3];
349 int32_t PTerm
, ITerm
, DTerm
;
350 static int32_t lastError
[3] = { 0, 0, 0 };
351 int32_t AngleRateTmp
, RateError
;
353 // ----------PID controller----------
354 for (axis
= 0; axis
< 3; axis
++) {
355 // -----Get the desired angle rate depending on flight mode
356 if ((f
.ANGLE_MODE
|| f
.HORIZON_MODE
) && axis
< 2 ) { // MODE relying on ACC
357 // calculate error and limit the angle to 50 degrees max inclination
358 errorAngle
= constrain((rcCommand
[axis
] << 1) + GPS_angle
[axis
], -500, +500) - angle
[axis
] + cfg
.angleTrim
[axis
]; // 16 bits is ok here
360 if (axis
== 2) { // YAW is always gyro-controlled (MAG correction is applied to rcCommand)
361 AngleRateTmp
= (((int32_t)(cfg
.yawRate
+ 27) * rcCommand
[2]) >> 5);
363 if (!f
.ANGLE_MODE
) { //control is GYRO based (ACRO and HORIZON - direct sticks control is applied to rate PID
364 AngleRateTmp
= ((int32_t) (cfg
.rollPitchRate
+ 27) * rcCommand
[axis
]) >> 4;
365 if (f
.HORIZON_MODE
) {
366 // mix up angle error to desired AngleRateTmp to add a little auto-level feel
367 AngleRateTmp
+= (errorAngle
* cfg
.I8
[PIDLEVEL
]) >> 8;
369 } else { // it's the ANGLE mode - control is angle based, so control loop is needed
370 AngleRateTmp
= (errorAngle
* cfg
.P8
[PIDLEVEL
]) >> 4;
374 // --------low-level gyro-based PID. ----------
375 // Used in stand-alone mode for ACRO, controlled by higher level regulators in other modes
376 // -----calculate scaled error.AngleRates
377 // multiplication of rcCommand corresponds to changing the sticks scaling here
378 RateError
= AngleRateTmp
- gyroData
[axis
];
380 // -----calculate P component
381 PTerm
= (RateError
* cfg
.P8
[axis
]) >> 7;
382 // -----calculate I component
383 // there should be no division before accumulating the error to integrator, because the precision would be reduced.
384 // Precision is critical, as I prevents from long-time drift. Thus, 32 bits integrator is used.
385 // Time correction (to avoid different I scaling for different builds based on average cycle time)
386 // is normalized to cycle time = 2048.
387 errorGyroI
[axis
] = errorGyroI
[axis
] + ((RateError
* cycleTime
) >> 11) * cfg
.I8
[axis
];
389 // limit maximum integrator value to prevent WindUp - accumulating extreme values when system is saturated.
390 // I coefficient (I8) moved before integration to make limiting independent from PID settings
391 errorGyroI
[axis
] = constrain(errorGyroI
[axis
], (int32_t)-GYRO_I_MAX
<< 13, (int32_t)+GYRO_I_MAX
<< 13);
392 ITerm
= errorGyroI
[axis
] >> 13;
394 //-----calculate D-term
395 delta
= RateError
- lastError
[axis
]; // 16 bits is ok here, the dif between 2 consecutive gyro reads is limited to 800
396 lastError
[axis
] = RateError
;
398 // Correct difference by cycle time. Cycle time is jittery (can be different 2 times), so calculated difference
399 // would be scaled by different dt each time. Division by dT fixes that.
400 delta
= (delta
* ((uint16_t)0xFFFF / (cycleTime
>> 4))) >> 6;
401 // add moving average here to reduce noise
402 deltaSum
= delta1
[axis
] + delta2
[axis
] + delta
;
403 delta2
[axis
] = delta1
[axis
];
404 delta1
[axis
] = delta
;
405 DTerm
= (deltaSum
* cfg
.D8
[axis
]) >> 8;
407 // -----calculate total PID output
408 axisPID
[axis
] = PTerm
+ ITerm
+ DTerm
;
412 void setPIDController(int type
)
417 pid_controller
= pidMultiWii
;
420 pid_controller
= pidRewrite
;
427 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
428 static uint8_t rcSticks
; // this hold sticks position for command combos
431 static uint32_t rcTime
= 0;
432 static int16_t initialThrottleHold
;
433 static uint32_t loopTime
;
434 uint16_t auxState
= 0;
435 static uint8_t GPSNavReset
= 1;
437 // this will return false if spektrum is disabled. shrug.
438 if (spektrumFrameComplete())
441 if ((int32_t)(currentTime
- rcTime
) >= 0) { // 50Hz
442 rcTime
= currentTime
+ 20000;
443 // TODO clean this up. computeRC should handle this check
444 if (!feature(FEATURE_SPEKTRUM
))
448 if (feature(FEATURE_FAILSAFE
)) {
449 if (failsafeCnt
> (5 * cfg
.failsafe_delay
) && f
.ARMED
) { // Stabilize, and set Throttle to specified level
450 for (i
= 0; i
< 3; i
++)
451 rcData
[i
] = mcfg
.midrc
; // after specified guard time after RC signal is lost (in 0.1sec)
452 rcData
[THROTTLE
] = cfg
.failsafe_throttle
;
453 if (failsafeCnt
> 5 * (cfg
.failsafe_delay
+ cfg
.failsafe_off_delay
)) { // Turn OFF motors after specified Time (in 0.1sec)
454 mwDisarm(); // This will prevent the copter to automatically rearm if failsafe shuts it down and prevents
455 f
.OK_TO_ARM
= 0; // to restart accidentely by just reconnect to the tx - you will have to switch off first to rearm
459 if (failsafeCnt
> (5 * cfg
.failsafe_delay
) && !f
.ARMED
) { // Turn off "Ok To arm to prevent the motors from spinning after repowering the RX with low throttle and aux to arm
460 mwDisarm(); // This will prevent the copter to automatically rearm if failsafe shuts it down and prevents
461 f
.OK_TO_ARM
= 0; // to restart accidentely by just reconnect to the tx - you will have to switch off first to rearm
465 // end of failsafe routine - next change is made with RcOptions setting
467 // ------------------ STICKS COMMAND HANDLER --------------------
468 // checking sticks positions
469 for (i
= 0; i
< 4; i
++) {
471 if (rcData
[i
] > mcfg
.mincheck
)
472 stTmp
|= 0x80; // check for MIN
473 if (rcData
[i
] < mcfg
.maxcheck
)
474 stTmp
|= 0x40; // check for MAX
476 if (stTmp
== rcSticks
) {
477 if (rcDelayCommand
< 250)
484 if (rcData
[THROTTLE
] < mcfg
.mincheck
) {
485 errorGyroI
[ROLL
] = 0;
486 errorGyroI
[PITCH
] = 0;
488 errorAngleI
[ROLL
] = 0;
489 errorAngleI
[PITCH
] = 0;
490 if (cfg
.activate
[BOXARM
] > 0) { // Arming/Disarming via ARM BOX
491 if (rcOptions
[BOXARM
] && f
.OK_TO_ARM
)
498 if (rcDelayCommand
== 20) {
499 if (f
.ARMED
) { // actions during armed
500 // Disarm on throttle down + yaw
501 if (cfg
.activate
[BOXARM
] == 0 && (rcSticks
== THR_LO
+ YAW_LO
+ PIT_CE
+ ROL_CE
))
503 // Disarm on roll (only when retarded_arm is enabled)
504 if (mcfg
.retarded_arm
&& cfg
.activate
[BOXARM
] == 0 && (rcSticks
== THR_LO
+ YAW_CE
+ PIT_CE
+ ROL_LO
))
506 } else { // actions during not armed
509 if (rcSticks
== THR_LO
+ YAW_LO
+ PIT_LO
+ ROL_CE
) {
511 if (feature(FEATURE_GPS
))
512 GPS_reset_home_position();
513 if (sensors(SENSOR_BARO
))
514 calibratingB
= 10; // calibrate baro to new ground level (10 * 25 ms = ~250 ms non blocking)
515 // Inflight ACC Calibration
516 } else if (feature(FEATURE_INFLIGHT_ACC_CAL
) && (rcSticks
== THR_LO
+ YAW_LO
+ PIT_HI
+ ROL_HI
)) {
517 if (AccInflightCalibrationMeasurementDone
) { // trigger saving into eeprom after landing
518 AccInflightCalibrationMeasurementDone
= 0;
519 AccInflightCalibrationSavetoEEProm
= 1;
521 AccInflightCalibrationArmed
= !AccInflightCalibrationArmed
;
522 if (AccInflightCalibrationArmed
) {
530 // Multiple configuration profiles
531 if (rcSticks
== THR_LO
+ YAW_LO
+ PIT_CE
+ ROL_LO
) // ROLL left -> Profile 1
533 else if (rcSticks
== THR_LO
+ YAW_LO
+ PIT_HI
+ ROL_CE
) // PITCH up -> Profile 2
535 else if (rcSticks
== THR_LO
+ YAW_LO
+ PIT_CE
+ ROL_HI
) // ROLL right -> Profile 3
538 mcfg
.current_profile
= i
- 1;
539 writeEEPROM(0, false);
541 // TODO alarmArray[0] = i;
545 if (cfg
.activate
[BOXARM
] == 0 && (rcSticks
== THR_LO
+ YAW_HI
+ PIT_CE
+ ROL_CE
))
548 else if (mcfg
.retarded_arm
&& cfg
.activate
[BOXARM
] == 0 && (rcSticks
== THR_LO
+ YAW_CE
+ PIT_CE
+ ROL_HI
))
551 else if (rcSticks
== THR_HI
+ YAW_LO
+ PIT_LO
+ ROL_CE
)
554 else if (rcSticks
== THR_HI
+ YAW_HI
+ PIT_LO
+ ROL_CE
)
558 if (rcSticks
== THR_HI
+ YAW_CE
+ PIT_HI
+ ROL_CE
) {
559 cfg
.angleTrim
[PITCH
] += 2;
561 } else if (rcSticks
== THR_HI
+ YAW_CE
+ PIT_LO
+ ROL_CE
) {
562 cfg
.angleTrim
[PITCH
] -= 2;
564 } else if (rcSticks
== THR_HI
+ YAW_CE
+ PIT_CE
+ ROL_HI
) {
565 cfg
.angleTrim
[ROLL
] += 2;
567 } else if (rcSticks
== THR_HI
+ YAW_CE
+ PIT_CE
+ ROL_LO
) {
568 cfg
.angleTrim
[ROLL
] -= 2;
572 writeEEPROM(1, true);
573 rcDelayCommand
= 0; // allow autorepetition
578 if (feature(FEATURE_INFLIGHT_ACC_CAL
)) {
579 if (AccInflightCalibrationArmed
&& f
.ARMED
&& rcData
[THROTTLE
] > mcfg
.mincheck
&& !rcOptions
[BOXARM
]) { // Copter is airborne and you are turning it off via boxarm : start measurement
580 InflightcalibratingA
= 50;
581 AccInflightCalibrationArmed
= 0;
583 if (rcOptions
[BOXCALIB
]) { // Use the Calib Option to activate : Calib = TRUE Meausrement started, Land and Calib = 0 measurement stored
584 if (!AccInflightCalibrationActive
&& !AccInflightCalibrationMeasurementDone
)
585 InflightcalibratingA
= 50;
586 } else if (AccInflightCalibrationMeasurementDone
&& !f
.ARMED
) {
587 AccInflightCalibrationMeasurementDone
= 0;
588 AccInflightCalibrationSavetoEEProm
= 1;
592 // Check AUX switches
593 for (i
= 0; i
< 4; i
++)
594 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);
595 for (i
= 0; i
< CHECKBOXITEMS
; i
++)
596 rcOptions
[i
] = (auxState
& cfg
.activate
[i
]) > 0;
598 // note: if FAILSAFE is disable, failsafeCnt > 5 * FAILSAVE_DELAY is always false
599 if ((rcOptions
[BOXANGLE
] || (failsafeCnt
> 5 * cfg
.failsafe_delay
)) && (sensors(SENSOR_ACC
))) {
600 // bumpless transfer to Level mode
602 errorAngleI
[ROLL
] = 0;
603 errorAngleI
[PITCH
] = 0;
607 f
.ANGLE_MODE
= 0; // failsave support
610 if (rcOptions
[BOXHORIZON
]) {
612 if (!f
.HORIZON_MODE
) {
613 errorAngleI
[ROLL
] = 0;
614 errorAngleI
[PITCH
] = 0;
621 if ((rcOptions
[BOXARM
]) == 0)
623 if (f
.ANGLE_MODE
|| f
.HORIZON_MODE
) {
630 if (sensors(SENSOR_BARO
)) {
631 // Baro alt hold activate
632 if (rcOptions
[BOXBARO
]) {
636 initialThrottleHold
= rcCommand
[THROTTLE
];
643 // Vario signalling activate
644 if (feature(FEATURE_VARIO
)) {
645 if (rcOptions
[BOXVARIO
]) {
657 if (sensors(SENSOR_MAG
)) {
658 if (rcOptions
[BOXMAG
]) {
666 if (rcOptions
[BOXHEADFREE
]) {
667 if (!f
.HEADFREE_MODE
) {
673 if (rcOptions
[BOXHEADADJ
]) {
674 headFreeModeHold
= heading
; // acquire new heading
679 if (sensors(SENSOR_GPS
)) {
680 if (f
.GPS_FIX
&& GPS_numSat
>= 5) {
681 // if both GPS_HOME & GPS_HOLD are checked => GPS_HOME is the priority
682 if (rcOptions
[BOXGPSHOME
]) {
683 if (!f
.GPS_HOME_MODE
) {
687 GPS_set_next_wp(&GPS_home
[LAT
], &GPS_home
[LON
]);
688 nav_mode
= NAV_MODE_WP
;
692 if (rcOptions
[BOXGPSHOLD
] && abs(rcCommand
[ROLL
]) < cfg
.ap_mode
&& abs(rcCommand
[PITCH
]) < cfg
.ap_mode
) {
693 if (!f
.GPS_HOLD_MODE
) {
696 GPS_hold
[LAT
] = GPS_coord
[LAT
];
697 GPS_hold
[LON
] = GPS_coord
[LON
];
698 GPS_set_next_wp(&GPS_hold
[LAT
], &GPS_hold
[LON
]);
699 nav_mode
= NAV_MODE_POSHOLD
;
703 // both boxes are unselected here, nav is reset if not already done
704 if (GPSNavReset
== 0) {
713 nav_mode
= NAV_MODE_NONE
;
717 if (rcOptions
[BOXPASSTHRU
]) {
723 if (mcfg
.mixerConfiguration
== MULTITYPE_FLYING_WING
|| mcfg
.mixerConfiguration
== MULTITYPE_AIRPLANE
) {
726 } else { // not in rc loop
727 static int taskOrder
= 0; // never call all function in the same loop, to avoid high delay spikes
734 if (sensors(SENSOR_MAG
) && Mag_getADC())
740 if (sensors(SENSOR_BARO
) && Baro_update())
746 if (sensors(SENSOR_BARO
) && getEstimatedAltitude())
752 if (sensors(SENSOR_SONAR
)) {
757 if (feature(FEATURE_VARIO
) && f
.VARIO_MODE
)
763 currentTime
= micros();
764 if (mcfg
.looptime
== 0 || (int32_t)(currentTime
- loopTime
) >= 0) {
765 loopTime
= currentTime
+ mcfg
.looptime
;
768 // Measure loop rate just afer reading the sensors
769 currentTime
= micros();
770 cycleTime
= (int32_t)(currentTime
- previousTime
);
771 previousTime
= currentTime
;
777 if (sensors(SENSOR_MAG
)) {
778 if (abs(rcCommand
[YAW
]) < 70 && f
.MAG_MODE
) {
779 int16_t dif
= heading
- magHold
;
784 if (f
.SMALL_ANGLES_25
)
785 rcCommand
[YAW
] -= dif
* cfg
.P8
[PIDMAG
] / 30; // 18 deg
792 if (sensors(SENSOR_BARO
)) {
794 static uint8_t isAltHoldChanged
= 0;
795 static int16_t AltHoldCorr
= 0;
796 if (cfg
.alt_hold_fast_change
) {
798 if (abs(rcCommand
[THROTTLE
] - initialThrottleHold
) > cfg
.alt_hold_throttle_neutral
) {
800 isAltHoldChanged
= 1;
801 rcCommand
[THROTTLE
] += (rcCommand
[THROTTLE
] > initialThrottleHold
) ? -cfg
.alt_hold_throttle_neutral
: cfg
.alt_hold_throttle_neutral
;
803 if (isAltHoldChanged
) {
805 isAltHoldChanged
= 0;
807 rcCommand
[THROTTLE
] = initialThrottleHold
+ BaroPID
;
810 // slow alt changes for apfags
811 if (abs(rcCommand
[THROTTLE
] - initialThrottleHold
) > cfg
.alt_hold_throttle_neutral
) {
812 // Slowly increase/decrease AltHold proportional to stick movement ( +100 throttle gives ~ +50 cm in 1 second with cycle time about 3-4ms)
813 AltHoldCorr
+= rcCommand
[THROTTLE
] - initialThrottleHold
;
814 if (abs(AltHoldCorr
) > 500) {
815 AltHold
+= AltHoldCorr
/ 500;
819 isAltHoldChanged
= 1;
820 } else if (isAltHoldChanged
) {
822 isAltHoldChanged
= 0;
824 rcCommand
[THROTTLE
] = initialThrottleHold
+ BaroPID
;
830 if (sensors(SENSOR_GPS
)) {
831 if ((f
.GPS_HOME_MODE
|| f
.GPS_HOLD_MODE
) && f
.GPS_FIX_HOME
) {
832 float sin_yaw_y
= sinf(heading
* 0.0174532925f
);
833 float cos_yaw_x
= cosf(heading
* 0.0174532925f
);
834 if (cfg
.nav_slew_rate
) {
835 nav_rated
[LON
] += constrain(wrap_18000(nav
[LON
] - nav_rated
[LON
]), -cfg
.nav_slew_rate
, cfg
.nav_slew_rate
); // TODO check this on uint8
836 nav_rated
[LAT
] += constrain(wrap_18000(nav
[LAT
] - nav_rated
[LAT
]), -cfg
.nav_slew_rate
, cfg
.nav_slew_rate
);
837 GPS_angle
[ROLL
] = (nav_rated
[LON
] * cos_yaw_x
- nav_rated
[LAT
] * sin_yaw_y
) / 10;
838 GPS_angle
[PITCH
] = (nav_rated
[LON
] * sin_yaw_y
+ nav_rated
[LAT
] * cos_yaw_x
) / 10;
840 GPS_angle
[ROLL
] = (nav
[LON
] * cos_yaw_x
- nav
[LAT
] * sin_yaw_y
) / 10;
841 GPS_angle
[PITCH
] = (nav
[LON
] * sin_yaw_y
+ nav
[LAT
] * cos_yaw_x
) / 10;
846 // PID - note this is function pointer set by setPIDController()