2 * This file is part of Cleanflight.
4 * Cleanflight is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
9 * Cleanflight is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with Cleanflight. If not, see <http://www.gnu.org/licenses/>.
26 #include "blackbox/blackbox.h"
28 #include "build/build_config.h"
30 #include "common/axis.h"
31 #include "common/maths.h"
32 #include "common/utils.h"
34 #include "drivers/system.h"
36 #include "config/parameter_group.h"
37 #include "config/parameter_group_ids.h"
38 #include "config/feature.h"
40 #include "flight/pid.h"
42 #include "io/beeper.h"
43 #include "io/motors.h"
45 #include "fc/config.h"
46 #include "fc/controlrate_profile.h"
47 #include "fc/rc_adjustments.h"
48 #include "fc/rc_controls.h"
53 PG_REGISTER_ARRAY(adjustmentRange_t
, MAX_ADJUSTMENT_RANGE_COUNT
, adjustmentRanges
, PG_ADJUSTMENT_RANGE_CONFIG
, 0);
55 static pidProfile_t
*pidProfile
;
57 static void blackboxLogInflightAdjustmentEvent(adjustmentFunction_e adjustmentFunction
, int32_t newValue
)
60 UNUSED(adjustmentFunction
);
63 if (blackboxConfig()->device
) {
64 flightLogEvent_inflightAdjustment_t eventData
;
65 eventData
.adjustmentFunction
= adjustmentFunction
;
66 eventData
.newValue
= newValue
;
67 eventData
.floatFlag
= false;
68 blackboxLogEvent(FLIGHT_LOG_EVENT_INFLIGHT_ADJUSTMENT
, (flightLogEventData_t
*)&eventData
);
74 static void blackboxLogInflightAdjustmentEventFloat(adjustmentFunction_e adjustmentFunction
, float newFloatValue
)
77 UNUSED(adjustmentFunction
);
78 UNUSED(newFloatValue
);
80 if (blackboxConfig()->device
) {
81 flightLogEvent_inflightAdjustment_t eventData
;
82 eventData
.adjustmentFunction
= adjustmentFunction
;
83 eventData
.newFloatValue
= newFloatValue
;
84 eventData
.floatFlag
= true;
85 blackboxLogEvent(FLIGHT_LOG_EVENT_INFLIGHT_ADJUSTMENT
, (flightLogEventData_t
*)&eventData
);
91 static uint8_t adjustmentStateMask
= 0;
93 #define MARK_ADJUSTMENT_FUNCTION_AS_BUSY(adjustmentIndex) adjustmentStateMask |= (1 << adjustmentIndex)
94 #define MARK_ADJUSTMENT_FUNCTION_AS_READY(adjustmentIndex) adjustmentStateMask &= ~(1 << adjustmentIndex)
96 #define IS_ADJUSTMENT_FUNCTION_BUSY(adjustmentIndex) (adjustmentStateMask & (1 << adjustmentIndex))
98 // sync with adjustmentFunction_e
99 static const adjustmentConfig_t defaultAdjustmentConfigs
[ADJUSTMENT_FUNCTION_COUNT
- 1] = {
101 .adjustmentFunction
= ADJUSTMENT_RC_RATE
,
102 .mode
= ADJUSTMENT_MODE_STEP
,
103 .data
= { .stepConfig
= { .step
= 1 }}
106 .adjustmentFunction
= ADJUSTMENT_RC_EXPO
,
107 .mode
= ADJUSTMENT_MODE_STEP
,
108 .data
= { .stepConfig
= { .step
= 1 }}
111 .adjustmentFunction
= ADJUSTMENT_THROTTLE_EXPO
,
112 .mode
= ADJUSTMENT_MODE_STEP
,
113 .data
= { .stepConfig
= { .step
= 1 }}
116 .adjustmentFunction
= ADJUSTMENT_PITCH_ROLL_RATE
,
117 .mode
= ADJUSTMENT_MODE_STEP
,
118 .data
= { .stepConfig
= { .step
= 1 }}
121 .adjustmentFunction
= ADJUSTMENT_YAW_RATE
,
122 .mode
= ADJUSTMENT_MODE_STEP
,
123 .data
= { .stepConfig
= { .step
= 1 }}
126 .adjustmentFunction
= ADJUSTMENT_PITCH_ROLL_P
,
127 .mode
= ADJUSTMENT_MODE_STEP
,
128 .data
= { .stepConfig
= { .step
= 1 }}
131 .adjustmentFunction
= ADJUSTMENT_PITCH_ROLL_I
,
132 .mode
= ADJUSTMENT_MODE_STEP
,
133 .data
= { .stepConfig
= { .step
= 1 }}
136 .adjustmentFunction
= ADJUSTMENT_PITCH_ROLL_D
,
137 .mode
= ADJUSTMENT_MODE_STEP
,
138 .data
= { .stepConfig
= { .step
= 1 }}
141 .adjustmentFunction
= ADJUSTMENT_YAW_P
,
142 .mode
= ADJUSTMENT_MODE_STEP
,
143 .data
= { .stepConfig
= { .step
= 1 }}
146 .adjustmentFunction
= ADJUSTMENT_YAW_I
,
147 .mode
= ADJUSTMENT_MODE_STEP
,
148 .data
= { .stepConfig
= { .step
= 1 }}
151 .adjustmentFunction
= ADJUSTMENT_YAW_D
,
152 .mode
= ADJUSTMENT_MODE_STEP
,
153 .data
= { .stepConfig
= { .step
= 1 }}
156 .adjustmentFunction
= ADJUSTMENT_RATE_PROFILE
,
157 .mode
= ADJUSTMENT_MODE_SELECT
,
158 .data
= { .selectConfig
= { .switchPositions
= 3 }}
161 .adjustmentFunction
= ADJUSTMENT_PITCH_RATE
,
162 .mode
= ADJUSTMENT_MODE_STEP
,
163 .data
= { .stepConfig
= { .step
= 1 }}
166 .adjustmentFunction
= ADJUSTMENT_ROLL_RATE
,
167 .mode
= ADJUSTMENT_MODE_STEP
,
168 .data
= { .stepConfig
= { .step
= 1 }}
171 .adjustmentFunction
= ADJUSTMENT_PITCH_P
,
172 .mode
= ADJUSTMENT_MODE_STEP
,
173 .data
= { .stepConfig
= { .step
= 1 }}
176 .adjustmentFunction
= ADJUSTMENT_PITCH_I
,
177 .mode
= ADJUSTMENT_MODE_STEP
,
178 .data
= { .stepConfig
= { .step
= 1 }}
181 .adjustmentFunction
= ADJUSTMENT_PITCH_D
,
182 .mode
= ADJUSTMENT_MODE_STEP
,
183 .data
= { .stepConfig
= { .step
= 1 }}
186 .adjustmentFunction
= ADJUSTMENT_ROLL_P
,
187 .mode
= ADJUSTMENT_MODE_STEP
,
188 .data
= { .stepConfig
= { .step
= 1 }}
191 .adjustmentFunction
= ADJUSTMENT_ROLL_I
,
192 .mode
= ADJUSTMENT_MODE_STEP
,
193 .data
= { .stepConfig
= { .step
= 1 }}
196 .adjustmentFunction
= ADJUSTMENT_ROLL_D
,
197 .mode
= ADJUSTMENT_MODE_STEP
,
198 .data
= { .stepConfig
= { .step
= 1 }}
201 .adjustmentFunction
= ADJUSTMENT_RC_RATE_YAW
,
202 .mode
= ADJUSTMENT_MODE_STEP
,
203 .data
= { .stepConfig
= { .step
= 1 }}
206 .adjustmentFunction
= ADJUSTMENT_D_SETPOINT
,
207 .mode
= ADJUSTMENT_MODE_STEP
,
208 .data
= { .stepConfig
= { .step
= 1 }}
211 .adjustmentFunction
= ADJUSTMENT_D_SETPOINT_TRANSITION
,
212 .mode
= ADJUSTMENT_MODE_STEP
,
213 .data
= { .stepConfig
= { .step
= 1 }}
216 .adjustmentFunction
= ADJUSTMENT_HORIZON_STRENGTH
,
217 .mode
= ADJUSTMENT_MODE_SELECT
,
218 .data
= { .selectConfig
= { .switchPositions
= 255 }}
223 #define ADJUSTMENT_FUNCTION_CONFIG_INDEX_OFFSET 1
225 static adjustmentState_t adjustmentStates
[MAX_SIMULTANEOUS_ADJUSTMENT_COUNT
];
227 static void configureAdjustment(uint8_t index
, uint8_t auxSwitchChannelIndex
, const adjustmentConfig_t
*adjustmentConfig
)
229 adjustmentState_t
*adjustmentState
= &adjustmentStates
[index
];
231 if (adjustmentState
->config
== adjustmentConfig
) {
232 // already configured
235 adjustmentState
->auxChannelIndex
= auxSwitchChannelIndex
;
236 adjustmentState
->config
= adjustmentConfig
;
237 adjustmentState
->timeoutAt
= 0;
239 MARK_ADJUSTMENT_FUNCTION_AS_READY(index
);
242 static void applyStepAdjustment(controlRateConfig_t
*controlRateConfig
, uint8_t adjustmentFunction
, int delta
)
247 beeperConfirmationBeeps(2);
249 beeperConfirmationBeeps(1);
251 switch(adjustmentFunction
) {
252 case ADJUSTMENT_RC_RATE
:
253 newValue
= constrain((int)controlRateConfig
->rcRate8
+ delta
, 0, 250); // FIXME magic numbers repeated in cli.c
254 controlRateConfig
->rcRate8
= newValue
;
255 blackboxLogInflightAdjustmentEvent(ADJUSTMENT_RC_RATE
, newValue
);
257 case ADJUSTMENT_RC_EXPO
:
258 newValue
= constrain((int)controlRateConfig
->rcExpo8
+ delta
, 0, 100); // FIXME magic numbers repeated in cli.c
259 controlRateConfig
->rcExpo8
= newValue
;
260 blackboxLogInflightAdjustmentEvent(ADJUSTMENT_RC_EXPO
, newValue
);
262 case ADJUSTMENT_THROTTLE_EXPO
:
263 newValue
= constrain((int)controlRateConfig
->thrExpo8
+ delta
, 0, 100); // FIXME magic numbers repeated in cli.c
264 controlRateConfig
->thrExpo8
= newValue
;
265 generateThrottleCurve();
266 blackboxLogInflightAdjustmentEvent(ADJUSTMENT_THROTTLE_EXPO
, newValue
);
268 case ADJUSTMENT_PITCH_ROLL_RATE
:
269 case ADJUSTMENT_PITCH_RATE
:
270 newValue
= constrain((int)controlRateConfig
->rates
[FD_PITCH
] + delta
, 0, CONTROL_RATE_CONFIG_ROLL_PITCH_RATE_MAX
);
271 controlRateConfig
->rates
[FD_PITCH
] = newValue
;
272 blackboxLogInflightAdjustmentEvent(ADJUSTMENT_PITCH_RATE
, newValue
);
273 if (adjustmentFunction
== ADJUSTMENT_PITCH_RATE
) {
276 // follow though for combined ADJUSTMENT_PITCH_ROLL_RATE
277 case ADJUSTMENT_ROLL_RATE
:
278 newValue
= constrain((int)controlRateConfig
->rates
[FD_ROLL
] + delta
, 0, CONTROL_RATE_CONFIG_ROLL_PITCH_RATE_MAX
);
279 controlRateConfig
->rates
[FD_ROLL
] = newValue
;
280 blackboxLogInflightAdjustmentEvent(ADJUSTMENT_ROLL_RATE
, newValue
);
282 case ADJUSTMENT_YAW_RATE
:
283 newValue
= constrain((int)controlRateConfig
->rates
[FD_YAW
] + delta
, 0, CONTROL_RATE_CONFIG_YAW_RATE_MAX
);
284 controlRateConfig
->rates
[FD_YAW
] = newValue
;
285 blackboxLogInflightAdjustmentEvent(ADJUSTMENT_YAW_RATE
, newValue
);
287 case ADJUSTMENT_PITCH_ROLL_P
:
288 case ADJUSTMENT_PITCH_P
:
289 newValue
= constrain((int)pidProfile
->P8
[PIDPITCH
] + delta
, 0, 200); // FIXME magic numbers repeated in cli.c
290 pidProfile
->P8
[PIDPITCH
] = newValue
;
291 blackboxLogInflightAdjustmentEvent(ADJUSTMENT_PITCH_P
, newValue
);
293 if (adjustmentFunction
== ADJUSTMENT_PITCH_P
) {
296 // follow though for combined ADJUSTMENT_PITCH_ROLL_P
297 case ADJUSTMENT_ROLL_P
:
298 newValue
= constrain((int)pidProfile
->P8
[PIDROLL
] + delta
, 0, 200); // FIXME magic numbers repeated in cli.c
299 pidProfile
->P8
[PIDROLL
] = newValue
;
300 blackboxLogInflightAdjustmentEvent(ADJUSTMENT_ROLL_P
, newValue
);
302 case ADJUSTMENT_PITCH_ROLL_I
:
303 case ADJUSTMENT_PITCH_I
:
304 newValue
= constrain((int)pidProfile
->I8
[PIDPITCH
] + delta
, 0, 200); // FIXME magic numbers repeated in cli.c
305 pidProfile
->I8
[PIDPITCH
] = newValue
;
306 blackboxLogInflightAdjustmentEvent(ADJUSTMENT_PITCH_I
, newValue
);
308 if (adjustmentFunction
== ADJUSTMENT_PITCH_I
) {
311 // follow though for combined ADJUSTMENT_PITCH_ROLL_I
312 case ADJUSTMENT_ROLL_I
:
313 newValue
= constrain((int)pidProfile
->I8
[PIDROLL
] + delta
, 0, 200); // FIXME magic numbers repeated in cli.c
314 pidProfile
->I8
[PIDROLL
] = newValue
;
315 blackboxLogInflightAdjustmentEvent(ADJUSTMENT_ROLL_I
, newValue
);
317 case ADJUSTMENT_PITCH_ROLL_D
:
318 case ADJUSTMENT_PITCH_D
:
319 newValue
= constrain((int)pidProfile
->D8
[PIDPITCH
] + delta
, 0, 200); // FIXME magic numbers repeated in cli.c
320 pidProfile
->D8
[PIDPITCH
] = newValue
;
321 blackboxLogInflightAdjustmentEvent(ADJUSTMENT_PITCH_D
, newValue
);
323 if (adjustmentFunction
== ADJUSTMENT_PITCH_D
) {
326 // follow though for combined ADJUSTMENT_PITCH_ROLL_D
327 case ADJUSTMENT_ROLL_D
:
328 newValue
= constrain((int)pidProfile
->D8
[PIDROLL
] + delta
, 0, 200); // FIXME magic numbers repeated in cli.c
329 pidProfile
->D8
[PIDROLL
] = newValue
;
330 blackboxLogInflightAdjustmentEvent(ADJUSTMENT_ROLL_D
, newValue
);
332 case ADJUSTMENT_YAW_P
:
333 newValue
= constrain((int)pidProfile
->P8
[PIDYAW
] + delta
, 0, 200); // FIXME magic numbers repeated in cli.c
334 pidProfile
->P8
[PIDYAW
] = newValue
;
335 blackboxLogInflightAdjustmentEvent(ADJUSTMENT_YAW_P
, newValue
);
337 case ADJUSTMENT_YAW_I
:
338 newValue
= constrain((int)pidProfile
->I8
[PIDYAW
] + delta
, 0, 200); // FIXME magic numbers repeated in cli.c
339 pidProfile
->I8
[PIDYAW
] = newValue
;
340 blackboxLogInflightAdjustmentEvent(ADJUSTMENT_YAW_I
, newValue
);
342 case ADJUSTMENT_YAW_D
:
343 newValue
= constrain((int)pidProfile
->D8
[PIDYAW
] + delta
, 0, 200); // FIXME magic numbers repeated in cli.c
344 pidProfile
->D8
[PIDYAW
] = newValue
;
345 blackboxLogInflightAdjustmentEvent(ADJUSTMENT_YAW_D
, newValue
);
347 case ADJUSTMENT_RC_RATE_YAW
:
348 newValue
= constrain((int)controlRateConfig
->rcYawRate8
+ delta
, 0, 300); // FIXME magic numbers repeated in cli.c
349 controlRateConfig
->rcYawRate8
= newValue
;
350 blackboxLogInflightAdjustmentEvent(ADJUSTMENT_RC_RATE_YAW
, newValue
);
352 case ADJUSTMENT_D_SETPOINT
:
353 newValue
= constrain((int)pidProfile
->dtermSetpointWeight
+ delta
, 0, 254); // FIXME magic numbers repeated in cli.c
354 pidProfile
->dtermSetpointWeight
= newValue
;
355 blackboxLogInflightAdjustmentEvent(ADJUSTMENT_D_SETPOINT
, newValue
);
357 case ADJUSTMENT_D_SETPOINT_TRANSITION
:
358 newValue
= constrain((int)pidProfile
->setpointRelaxRatio
+ delta
, 0, 100); // FIXME magic numbers repeated in cli.c
359 pidProfile
->setpointRelaxRatio
= newValue
;
360 blackboxLogInflightAdjustmentEvent(ADJUSTMENT_D_SETPOINT_TRANSITION
, newValue
);
367 static void applySelectAdjustment(uint8_t adjustmentFunction
, uint8_t position
)
371 switch(adjustmentFunction
) {
372 case ADJUSTMENT_RATE_PROFILE
:
374 if (getCurrentControlRateProfileIndex() != position
) {
375 changeControlRateProfile(position
);
376 blackboxLogInflightAdjustmentEvent(ADJUSTMENT_RATE_PROFILE
, position
);
377 beeps
= position
+ 1;
381 case ADJUSTMENT_HORIZON_STRENGTH
:
383 uint8_t newValue
= constrain(position
, 0, 200); // FIXME magic numbers repeated in serial_cli.c
384 if(pidProfile
->D8
[PIDLEVEL
] != newValue
) {
385 beeps
= ((newValue
- pidProfile
->D8
[PIDLEVEL
]) / 8) + 1;
386 pidProfile
->D8
[PIDLEVEL
] = newValue
;
387 blackboxLogInflightAdjustmentEvent(ADJUSTMENT_HORIZON_STRENGTH
, position
);
394 beeperConfirmationBeeps(beeps
);
399 #define RESET_FREQUENCY_2HZ (1000 / 2)
401 void processRcAdjustments(controlRateConfig_t
*controlRateConfig
)
403 const uint32_t now
= millis();
405 const bool canUseRxData
= rxIsReceivingSignal();
407 for (int adjustmentIndex
= 0; adjustmentIndex
< MAX_SIMULTANEOUS_ADJUSTMENT_COUNT
; adjustmentIndex
++) {
408 adjustmentState_t
*adjustmentState
= &adjustmentStates
[adjustmentIndex
];
410 if (!adjustmentState
->config
) {
413 const uint8_t adjustmentFunction
= adjustmentState
->config
->adjustmentFunction
;
414 if (adjustmentFunction
== ADJUSTMENT_NONE
) {
418 const int32_t signedDiff
= now
- adjustmentState
->timeoutAt
;
419 const bool canResetReadyStates
= signedDiff
>= 0L;
421 if (canResetReadyStates
) {
422 adjustmentState
->timeoutAt
= now
+ RESET_FREQUENCY_2HZ
;
423 MARK_ADJUSTMENT_FUNCTION_AS_READY(adjustmentIndex
);
430 const uint8_t channelIndex
= NON_AUX_CHANNEL_COUNT
+ adjustmentState
->auxChannelIndex
;
432 if (adjustmentState
->config
->mode
== ADJUSTMENT_MODE_STEP
) {
434 if (rcData
[channelIndex
] > rxConfig()->midrc
+ 200) {
435 delta
= adjustmentState
->config
->data
.stepConfig
.step
;
436 } else if (rcData
[channelIndex
] < rxConfig()->midrc
- 200) {
437 delta
= 0 - adjustmentState
->config
->data
.stepConfig
.step
;
439 // returning the switch to the middle immediately resets the ready state
440 MARK_ADJUSTMENT_FUNCTION_AS_READY(adjustmentIndex
);
441 adjustmentState
->timeoutAt
= now
+ RESET_FREQUENCY_2HZ
;
444 if (IS_ADJUSTMENT_FUNCTION_BUSY(adjustmentIndex
)) {
448 applyStepAdjustment(controlRateConfig
,adjustmentFunction
,delta
);
449 pidInitConfig(pidProfile
);
450 } else if (adjustmentState
->config
->mode
== ADJUSTMENT_MODE_SELECT
) {
451 const uint16_t rangeWidth
= ((2100 - 900) / adjustmentState
->config
->data
.selectConfig
.switchPositions
);
452 const uint8_t position
= (constrain(rcData
[channelIndex
], 900, 2100 - 1) - 900) / rangeWidth
;
453 applySelectAdjustment(adjustmentFunction
, position
);
455 MARK_ADJUSTMENT_FUNCTION_AS_BUSY(adjustmentIndex
);
459 void resetAdjustmentStates(void)
461 memset(adjustmentStates
, 0, sizeof(adjustmentStates
));
464 void updateAdjustmentStates(void)
466 for (int index
= 0; index
< MAX_ADJUSTMENT_RANGE_COUNT
; index
++) {
467 const adjustmentRange_t
* const adjustmentRange
= adjustmentRanges(index
);
468 if (isRangeActive(adjustmentRange
->auxChannelIndex
, &adjustmentRange
->range
)) {
469 const adjustmentConfig_t
*adjustmentConfig
= &defaultAdjustmentConfigs
[adjustmentRange
->adjustmentFunction
- ADJUSTMENT_FUNCTION_CONFIG_INDEX_OFFSET
];
470 configureAdjustment(adjustmentRange
->adjustmentIndex
, adjustmentRange
->auxSwitchChannelIndex
, adjustmentConfig
);
475 void useAdjustmentConfig(pidProfile_t
*pidProfileToUse
)
477 pidProfile
= pidProfileToUse
;