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/>.
28 // FIXME remove this for targets that don't need a CLI. Perhaps use a no-op macro when USE_CLI is not enabled
29 // signal that we're in cli mode
31 extern uint8_t __config_start
; // configured via linker script when building binaries.
32 extern uint8_t __config_end
;
36 #include "blackbox/blackbox.h"
38 #include "build/build_config.h"
39 #include "build/debug.h"
40 #include "build/version.h"
44 #include "common/axis.h"
45 #include "common/color.h"
46 #include "common/maths.h"
47 #include "common/printf.h"
48 #include "common/typeconversion.h"
49 #include "common/utils.h"
51 #include "config/config_eeprom.h"
52 #include "config/config_profile.h"
53 #include "config/feature.h"
54 #include "config/parameter_group.h"
55 #include "config/parameter_group_ids.h"
57 #include "drivers/accgyro.h"
58 #include "drivers/buf_writer.h"
59 #include "drivers/bus_i2c.h"
60 #include "drivers/compass.h"
61 #include "drivers/display.h"
62 #include "drivers/dma.h"
63 #include "drivers/flash.h"
64 #include "drivers/io.h"
65 #include "drivers/io_impl.h"
66 #include "drivers/rx_pwm.h"
67 #include "drivers/sdcard.h"
68 #include "drivers/sensor.h"
69 #include "drivers/serial.h"
70 #include "drivers/serial_escserial.h"
71 #include "drivers/sonar_hcsr04.h"
72 #include "drivers/stack_check.h"
73 #include "drivers/system.h"
74 #include "drivers/timer.h"
75 #include "drivers/vcd.h"
78 #include "fc/config.h"
79 #include "fc/controlrate_profile.h"
80 #include "fc/fc_core.h"
81 #include "fc/rc_adjustments.h"
82 #include "fc/rc_controls.h"
83 #include "fc/runtime_config.h"
85 #include "flight/altitudehold.h"
86 #include "flight/failsafe.h"
87 #include "flight/imu.h"
88 #include "flight/mixer.h"
89 #include "flight/navigation.h"
90 #include "flight/pid.h"
91 #include "flight/servos.h"
93 #include "io/asyncfatfs/asyncfatfs.h"
94 #include "io/beeper.h"
95 #include "io/flashfs.h"
96 #include "io/displayport_max7456.h"
97 #include "io/displayport_msp.h"
98 #include "io/gimbal.h"
100 #include "io/ledstrip.h"
102 #include "io/serial.h"
106 #include "rx/spektrum.h"
108 #include "scheduler/scheduler.h"
110 #include "sensors/acceleration.h"
111 #include "sensors/barometer.h"
112 #include "sensors/battery.h"
113 #include "sensors/boardalignment.h"
114 #include "sensors/compass.h"
115 #include "sensors/gyro.h"
116 #include "sensors/sensors.h"
118 #include "telemetry/frsky.h"
119 #include "telemetry/telemetry.h"
122 static serialPort_t
*cliPort
;
123 static bufWriter_t
*cliWriter
;
124 static uint8_t cliWriteBuffer
[sizeof(*cliWriter
) + 128];
126 static char cliBuffer
[64];
127 static uint32_t bufferIndex
= 0;
129 static const char* const emptyName
= "-";
131 #ifndef USE_QUAD_MIXER_ONLY
132 // sync this with mixerMode_e
133 static const char * const mixerNames
[] = {
134 "TRI", "QUADP", "QUADX", "BI",
135 "GIMBAL", "Y6", "HEX6",
136 "FLYING_WING", "Y4", "HEX6X", "OCTOX8", "OCTOFLATP", "OCTOFLATX",
137 "AIRPLANE", "HELI_120_CCPM", "HELI_90_DEG", "VTAIL4",
138 "HEX6H", "PPM_TO_SERVO", "DUALCOPTER", "SINGLECOPTER",
139 "ATAIL4", "CUSTOM", "CUSTOMAIRPLANE", "CUSTOMTRI", "QUADX1234", NULL
143 // sync this with features_e
144 static const char * const featureNames
[] = {
145 "RX_PPM", "VBAT", "INFLIGHT_ACC_CAL", "RX_SERIAL", "MOTOR_STOP",
146 "SERVO_TILT", "SOFTSERIAL", "GPS", "FAILSAFE",
147 "SONAR", "TELEMETRY", "CURRENT_METER", "3D", "RX_PARALLEL_PWM",
148 "RX_MSP", "RSSI_ADC", "LED_STRIP", "DISPLAY", "OSD",
149 "BLACKBOX", "CHANNEL_FORWARDING", "TRANSPONDER", "AIRMODE",
150 "SDCARD", "VTX", "RX_SPI", "SOFTSPI", "ESC_SENSOR", NULL
153 // sync this with rxFailsafeChannelMode_e
154 static const char rxFailsafeModeCharacters
[] = "ahs";
156 static const rxFailsafeChannelMode_e rxFailsafeModesTable
[RX_FAILSAFE_TYPE_COUNT
][RX_FAILSAFE_MODE_COUNT
] = {
157 { RX_FAILSAFE_MODE_AUTO
, RX_FAILSAFE_MODE_HOLD
, RX_FAILSAFE_MODE_INVALID
},
158 { RX_FAILSAFE_MODE_INVALID
, RX_FAILSAFE_MODE_HOLD
, RX_FAILSAFE_MODE_SET
}
161 // sync this with accelerationSensor_e
162 static const char * const lookupTableAccHardware
[] = {
180 // sync this with baroSensor_e
181 static const char * const lookupTableBaroHardware
[] = {
191 // sync this with magSensor_e
192 static const char * const lookupTableMagHardware
[] = {
201 #if defined(USE_SENSOR_NAMES)
202 // sync this with sensors_e
203 static const char * const sensorTypeNames
[] = {
204 "GYRO", "ACC", "BARO", "MAG", "SONAR", "GPS", "GPS+MAG", NULL
207 #define SENSOR_NAMES_MASK (SENSOR_GYRO | SENSOR_ACC | SENSOR_BARO | SENSOR_MAG)
209 static const char * const sensorHardwareNames
[4][16] = {
210 { "", "None", "MPU6050", "L3G4200D", "MPU3050", "L3GD20", "MPU6000", "MPU6500", "MPU9250", "ICM20689", "ICM20608G", "ICM20602", "BMI160", "FAKE", NULL
},
211 { "", "None", "ADXL345", "MPU6050", "MMA845x", "BMA280", "LSM303DLHC", "MPU6000", "MPU6500", "ICM20689", "MPU9250", "ICM20608G", "ICM20602", "BMI160", "FAKE", NULL
},
212 { "", "None", "BMP085", "MS5611", "BMP280", NULL
},
213 { "", "None", "HMC5883", "AK8975", "AK8963", NULL
}
215 #endif /* USE_SENSOR_NAMES */
217 static const char * const lookupTableOffOn
[] = {
221 static const char * const lookupTableUnit
[] = {
225 static const char * const lookupTableAlignment
[] = {
238 static const char * const lookupTableGPSProvider
[] = {
242 static const char * const lookupTableGPSSBASMode
[] = {
243 "AUTO", "EGNOS", "WAAS", "MSAS", "GAGAN"
247 static const char * const lookupTableCurrentSensor
[] = {
248 "NONE", "ADC", "VIRTUAL", "ESC"
251 static const char * const lookupTableBatterySensor
[] = {
256 static const char * const lookupTableGimbalMode
[] = {
262 static const char * const lookupTableBlackboxDevice
[] = {
263 "SERIAL", "SPIFLASH", "SDCARD"
268 static const char * const lookupTableSerialRX
[] = {
284 // sync with rx_spi_protocol_e
285 static const char * const lookupTableRxSpi
[] = {
297 static const char * const lookupTableGyroLpf
[] = {
308 static const char * const lookupTableDebug
[DEBUG_COUNT
] = {
328 static const char * const lookupTableOsdType
[] = {
335 static const char * const lookupTableSuperExpoYaw
[] = {
336 "OFF", "ON", "ALWAYS"
339 static const char * const lookupTablePwmProtocol
[] = {
340 "OFF", "ONESHOT125", "ONESHOT42", "MULTISHOT", "BRUSHED",
342 "DSHOT150", "DSHOT300", "DSHOT600", "DSHOT1200"
346 static const char * const lookupTableRcInterpolation
[] = {
347 "OFF", "PRESET", "AUTO", "MANUAL"
350 static const char * const lookupTableRcInterpolationChannels
[] = {
354 static const char * const lookupTableLowpassType
[] = {
355 "PT1", "BIQUAD", "FIR"
358 static const char * const lookupTableFailsafe
[] = {
362 typedef struct lookupTableEntry_s
{
363 const char * const *values
;
364 const uint8_t valueCount
;
365 } lookupTableEntry_t
;
376 TABLE_BLACKBOX_DEVICE
,
399 TABLE_MOTOR_PWM_PROTOCOL
,
400 TABLE_RC_INTERPOLATION
,
401 TABLE_RC_INTERPOLATION_CHANNELS
,
408 } lookupTableIndex_e
;
410 static const lookupTableEntry_t lookupTables
[] = {
411 { lookupTableOffOn
, sizeof(lookupTableOffOn
) / sizeof(char *) },
412 { lookupTableUnit
, sizeof(lookupTableUnit
) / sizeof(char *) },
413 { lookupTableAlignment
, sizeof(lookupTableAlignment
) / sizeof(char *) },
415 { lookupTableGPSProvider
, sizeof(lookupTableGPSProvider
) / sizeof(char *) },
416 { lookupTableGPSSBASMode
, sizeof(lookupTableGPSSBASMode
) / sizeof(char *) },
419 { lookupTableBlackboxDevice
, sizeof(lookupTableBlackboxDevice
) / sizeof(char *) },
421 { lookupTableCurrentSensor
, sizeof(lookupTableCurrentSensor
) / sizeof(char *) },
422 { lookupTableBatterySensor
, sizeof(lookupTableBatterySensor
) / sizeof(char *) },
424 { lookupTableGimbalMode
, sizeof(lookupTableGimbalMode
) / sizeof(char *) },
427 { lookupTableSerialRX
, sizeof(lookupTableSerialRX
) / sizeof(char *) },
430 { lookupTableRxSpi
, sizeof(lookupTableRxSpi
) / sizeof(char *) },
432 { lookupTableGyroLpf
, sizeof(lookupTableGyroLpf
) / sizeof(char *) },
433 { lookupTableAccHardware
, sizeof(lookupTableAccHardware
) / sizeof(char *) },
435 { lookupTableBaroHardware
, sizeof(lookupTableBaroHardware
) / sizeof(char *) },
438 { lookupTableMagHardware
, sizeof(lookupTableMagHardware
) / sizeof(char *) },
440 { lookupTableDebug
, sizeof(lookupTableDebug
) / sizeof(char *) },
441 { lookupTableSuperExpoYaw
, sizeof(lookupTableSuperExpoYaw
) / sizeof(char *) },
442 { lookupTablePwmProtocol
, sizeof(lookupTablePwmProtocol
) / sizeof(char *) },
443 { lookupTableRcInterpolation
, sizeof(lookupTableRcInterpolation
) / sizeof(char *) },
444 { lookupTableRcInterpolationChannels
, sizeof(lookupTableRcInterpolationChannels
) / sizeof(char *) },
445 { lookupTableLowpassType
, sizeof(lookupTableLowpassType
) / sizeof(char *) },
446 { lookupTableFailsafe
, sizeof(lookupTableFailsafe
) / sizeof(char *) },
448 { lookupTableOsdType
, sizeof(lookupTableOsdType
) / sizeof(char *) },
452 #define VALUE_TYPE_OFFSET 0
453 #define VALUE_SECTION_OFFSET 4
454 #define VALUE_MODE_OFFSET 6
457 // value type, bits 0-3
458 VAR_UINT8
= (0 << VALUE_TYPE_OFFSET
),
459 VAR_INT8
= (1 << VALUE_TYPE_OFFSET
),
460 VAR_UINT16
= (2 << VALUE_TYPE_OFFSET
),
461 VAR_INT16
= (3 << VALUE_TYPE_OFFSET
),
462 //VAR_UINT32 = (4 << VALUE_TYPE_OFFSET),
463 VAR_FLOAT
= (5 << VALUE_TYPE_OFFSET
), // 0x05
465 // value section, bits 4-5
466 MASTER_VALUE
= (0 << VALUE_SECTION_OFFSET
),
467 PROFILE_VALUE
= (1 << VALUE_SECTION_OFFSET
),
468 PROFILE_RATE_VALUE
= (2 << VALUE_SECTION_OFFSET
), // 0x20
470 MODE_DIRECT
= (0 << VALUE_MODE_OFFSET
), // 0x40
471 MODE_LOOKUP
= (1 << VALUE_MODE_OFFSET
) // 0x80
474 #define VALUE_TYPE_MASK (0x0F)
475 #define VALUE_SECTION_MASK (0x30)
476 #define VALUE_MODE_MASK (0xC0)
478 typedef struct cliMinMaxConfig_s
{
483 typedef struct cliLookupTableConfig_s
{
484 const lookupTableIndex_e tableIndex
;
485 } cliLookupTableConfig_t
;
488 cliLookupTableConfig_t lookup
;
489 cliMinMaxConfig_t minmax
;
492 #ifdef USE_PARAMETER_GROUPS
495 const uint8_t type
; // see cliValueFlag_e
496 const cliValueConfig_t config
;
500 } __attribute__((packed
)) clivalue_t
;
502 static const clivalue_t valueTable
[] = {
504 { "align_gyro", VAR_UINT8
| MASTER_VALUE
| MODE_LOOKUP
, .config
.lookup
= { TABLE_ALIGNMENT
}, PG_GYRO_CONFIG
, offsetof(gyroConfig_t
, gyro_align
) },
505 { "gyro_lpf", VAR_UINT8
| MASTER_VALUE
| MODE_LOOKUP
, .config
.lookup
= { TABLE_GYRO_LPF
}, PG_GYRO_CONFIG
, offsetof(gyroConfig_t
, gyro_lpf
) },
506 { "gyro_sync_denom", VAR_UINT8
| MASTER_VALUE
, .config
.minmax
= { 1, 32 }, PG_GYRO_CONFIG
, offsetof(gyroConfig_t
, gyro_sync_denom
) },
507 { "gyro_lowpass_type", VAR_UINT8
| MASTER_VALUE
| MODE_LOOKUP
, .config
.lookup
= { TABLE_LOWPASS_TYPE
}, PG_GYRO_CONFIG
, offsetof(gyroConfig_t
, gyro_soft_lpf_type
) },
508 { "gyro_lowpass", VAR_UINT8
| MASTER_VALUE
, .config
.minmax
= { 0, 255 }, PG_GYRO_CONFIG
, offsetof(gyroConfig_t
, gyro_soft_lpf_hz
) },
509 { "gyro_notch1_hz", VAR_UINT16
| MASTER_VALUE
, .config
.minmax
= { 0, 16000 }, PG_GYRO_CONFIG
, offsetof(gyroConfig_t
, gyro_soft_notch_hz_1
) },
510 { "gyro_notch1_cutoff", VAR_UINT16
| MASTER_VALUE
, .config
.minmax
= { 1, 16000 }, PG_GYRO_CONFIG
, offsetof(gyroConfig_t
, gyro_soft_notch_cutoff_1
) },
511 { "gyro_notch2_hz", VAR_UINT16
| MASTER_VALUE
, .config
.minmax
= { 0, 16000 }, PG_GYRO_CONFIG
, offsetof(gyroConfig_t
, gyro_soft_notch_hz_2
) },
512 { "gyro_notch2_cutoff", VAR_UINT16
| MASTER_VALUE
, .config
.minmax
= { 1, 16000 }, PG_GYRO_CONFIG
, offsetof(gyroConfig_t
, gyro_soft_notch_cutoff_2
) },
513 { "moron_threshold", VAR_UINT8
| MASTER_VALUE
, .config
.minmax
= { 0, 200 }, PG_GYRO_CONFIG
, offsetof(gyroConfig_t
, gyroMovementCalibrationThreshold
) },
514 #if defined(GYRO_USES_SPI)
515 #if defined(USE_GYRO_SPI_MPU6500) || defined(USE_GYRO_SPI_MPU9250) || defined(USE_GYRO_SPI_ICM20689)
516 { "gyro_use_32khz", VAR_UINT8
| MASTER_VALUE
| MODE_LOOKUP
, .config
.lookup
= { TABLE_OFF_ON
}, PG_GYRO_CONFIG
, offsetof(gyroConfig_t
, gyro_use_32khz
) },
518 #if defined(USE_MPU_DATA_READY_SIGNAL)
519 { "gyro_isr_update", VAR_UINT8
| MASTER_VALUE
| MODE_LOOKUP
, .config
.lookup
= { TABLE_OFF_ON
}, PG_GYRO_CONFIG
, offsetof(gyroConfig_t
, gyro_isr_update
) },
523 // PG_ACCELEROMETER_CONFIG
524 { "align_acc", VAR_UINT8
| MASTER_VALUE
| MODE_LOOKUP
, .config
.lookup
= { TABLE_ALIGNMENT
}, PG_ACCELEROMETER_CONFIG
, offsetof(accelerometerConfig_t
, acc_align
) },
525 { "acc_hardware", VAR_UINT8
| MASTER_VALUE
| MODE_LOOKUP
, .config
.lookup
= { TABLE_ACC_HARDWARE
}, PG_ACCELEROMETER_CONFIG
, offsetof(accelerometerConfig_t
, acc_hardware
) },
526 { "acc_lpf_hz", VAR_UINT16
| MASTER_VALUE
, .config
.minmax
= { 0, 400 }, PG_ACCELEROMETER_CONFIG
, offsetof(accelerometerConfig_t
, acc_lpf_hz
) },
527 { "acc_trim_pitch", VAR_INT16
| MASTER_VALUE
, .config
.minmax
= { -300, 300 }, PG_ACCELEROMETER_CONFIG
, offsetof(accelerometerConfig_t
, accelerometerTrims
.values
.pitch
) },
528 { "acc_trim_roll", VAR_INT16
| MASTER_VALUE
, .config
.minmax
= { -300, 300 }, PG_ACCELEROMETER_CONFIG
, offsetof(accelerometerConfig_t
, accelerometerTrims
.values
.roll
) },
532 { "align_mag", VAR_UINT8
| MASTER_VALUE
| MODE_LOOKUP
, .config
.lookup
= { TABLE_ALIGNMENT
}, PG_COMPASS_CONFIG
, offsetof(compassConfig_t
, mag_align
) },
533 { "mag_hardware", VAR_UINT8
| MASTER_VALUE
| MODE_LOOKUP
, .config
.lookup
= { TABLE_MAG_HARDWARE
}, PG_COMPASS_CONFIG
, offsetof(compassConfig_t
, mag_hardware
) },
534 { "mag_declination", VAR_INT16
| MASTER_VALUE
, .config
.minmax
= { -18000, 18000 }, PG_COMPASS_CONFIG
, offsetof(compassConfig_t
, mag_declination
) },
535 { "magzero_x", VAR_INT16
| MASTER_VALUE
, .config
.minmax
= { INT16_MIN
, INT16_MAX
}, PG_COMPASS_CONFIG
, offsetof(compassConfig_t
, magZero
.raw
[X
]) },
536 { "magzero_y", VAR_INT16
| MASTER_VALUE
, .config
.minmax
= { INT16_MIN
, INT16_MAX
}, PG_COMPASS_CONFIG
, offsetof(compassConfig_t
, magZero
.raw
[Y
]) },
537 { "magzero_z", VAR_INT16
| MASTER_VALUE
, .config
.minmax
= { INT16_MIN
, INT16_MAX
}, PG_COMPASS_CONFIG
, offsetof(compassConfig_t
, magZero
.raw
[Z
]) },
540 // PG_BAROMETER_CONFIG
542 { "baro_hardware", VAR_UINT8
| MASTER_VALUE
| MODE_LOOKUP
, .config
.lookup
= { TABLE_BARO_HARDWARE
}, PG_BAROMETER_CONFIG
, offsetof(barometerConfig_t
, baro_hardware
) },
543 { "baro_tab_size", VAR_UINT8
| MASTER_VALUE
, .config
.minmax
= { 0, BARO_SAMPLE_COUNT_MAX
}, PG_BAROMETER_CONFIG
, offsetof(barometerConfig_t
, baro_sample_count
) },
544 { "baro_noise_lpf", VAR_FLOAT
| MASTER_VALUE
, .config
.minmax
= { 0 , 1 }, PG_BAROMETER_CONFIG
, offsetof(barometerConfig_t
, baro_noise_lpf
) },
545 { "baro_cf_vel", VAR_FLOAT
| MASTER_VALUE
, .config
.minmax
= { 0 , 1 }, PG_BAROMETER_CONFIG
, offsetof(barometerConfig_t
, baro_cf_vel
) },
546 { "baro_cf_alt", VAR_FLOAT
| MASTER_VALUE
, .config
.minmax
= { 0 , 1 }, PG_BAROMETER_CONFIG
, offsetof(barometerConfig_t
, baro_cf_alt
) },
550 { "mid_rc", VAR_UINT16
| MASTER_VALUE
, .config
.minmax
= { 1200, 1700 }, PG_RX_CONFIG
, offsetof(rxConfig_t
, midrc
) },
551 { "min_check", VAR_UINT16
| MASTER_VALUE
, .config
.minmax
= { PWM_RANGE_ZERO
, PWM_RANGE_MAX
}, PG_RX_CONFIG
, offsetof(rxConfig_t
, mincheck
) },
552 { "max_check", VAR_UINT16
| MASTER_VALUE
, .config
.minmax
= { PWM_RANGE_ZERO
, PWM_RANGE_MAX
}, PG_RX_CONFIG
, offsetof(rxConfig_t
, maxcheck
) },
553 { "rssi_channel", VAR_INT8
| MASTER_VALUE
, .config
.minmax
= { 0, MAX_SUPPORTED_RC_CHANNEL_COUNT
}, PG_RX_CONFIG
, offsetof(rxConfig_t
, rssi_channel
) },
554 { "rssi_scale", VAR_UINT8
| MASTER_VALUE
, .config
.minmax
= { RSSI_SCALE_MIN
, RSSI_SCALE_MAX
}, PG_RX_CONFIG
, offsetof(rxConfig_t
, rssi_scale
) },
555 { "rssi_invert", VAR_INT8
| MASTER_VALUE
| MODE_LOOKUP
, .config
.lookup
= { TABLE_OFF_ON
}, PG_RX_CONFIG
, offsetof(rxConfig_t
, rssi_invert
) },
556 { "rc_interp", VAR_UINT8
| MASTER_VALUE
| MODE_LOOKUP
, .config
.lookup
= { TABLE_RC_INTERPOLATION
}, PG_RX_CONFIG
, offsetof(rxConfig_t
, rcInterpolation
) },
557 { "rc_interp_ch", VAR_UINT8
| MASTER_VALUE
| MODE_LOOKUP
, .config
.lookup
= { TABLE_RC_INTERPOLATION_CHANNELS
}, PG_RX_CONFIG
, offsetof(rxConfig_t
, rcInterpolationChannels
) },
558 { "rc_interp_int", VAR_UINT8
| MASTER_VALUE
, .config
.minmax
= { 1, 50 }, PG_RX_CONFIG
, offsetof(rxConfig_t
, rcInterpolationInterval
) },
559 { "fpv_mix_degrees", VAR_UINT8
| MASTER_VALUE
, .config
.minmax
= { 0, 50 }, PG_RX_CONFIG
, offsetof(rxConfig_t
, fpvCamAngleDegrees
) },
560 { "max_aux_channels", VAR_UINT8
| MASTER_VALUE
, .config
.minmax
= { 0, MAX_AUX_CHANNEL_COUNT
}, PG_RX_CONFIG
, offsetof(rxConfig_t
, max_aux_channel
) },
562 { "serialrx_provider", VAR_UINT8
| MASTER_VALUE
| MODE_LOOKUP
, .config
.lookup
= { TABLE_SERIAL_RX
}, PG_RX_CONFIG
, offsetof(rxConfig_t
, serialrx_provider
) },
563 { "sbus_inversion", VAR_UINT8
| MASTER_VALUE
| MODE_LOOKUP
, .config
.lookup
= { TABLE_OFF_ON
}, PG_RX_CONFIG
, offsetof(rxConfig_t
, sbus_inversion
) },
566 { "spektrum_sat_bind", VAR_UINT8
| MASTER_VALUE
, .config
.minmax
= { SPEKTRUM_SAT_BIND_DISABLED
, SPEKTRUM_SAT_BIND_MAX
}, PG_RX_CONFIG
, offsetof(rxConfig_t
, spektrum_sat_bind
) },
567 { "spektrum_sat_bind_autoreset",VAR_UINT8
| MASTER_VALUE
, .config
.minmax
= { 0, 1 }, PG_RX_CONFIG
, offsetof(rxConfig_t
, spektrum_sat_bind_autoreset
) },
569 { "airmode_start_throttle", VAR_UINT16
| MASTER_VALUE
, .config
.minmax
= { 1000, 2000 }, PG_RX_CONFIG
, offsetof(rxConfig_t
, airModeActivateThreshold
) },
570 { "rx_min_usec", VAR_UINT16
| MASTER_VALUE
, .config
.minmax
= { PWM_PULSE_MIN
, PWM_PULSE_MAX
}, PG_RX_CONFIG
, offsetof(rxConfig_t
, rx_min_usec
) },
571 { "rx_max_usec", VAR_UINT16
| MASTER_VALUE
, .config
.minmax
= { PWM_PULSE_MIN
, PWM_PULSE_MAX
}, PG_RX_CONFIG
, offsetof(rxConfig_t
, rx_max_usec
) },
573 { "serialrx_halfduplex", VAR_UINT8
| MASTER_VALUE
| MODE_LOOKUP
, .config
.lookup
= { TABLE_OFF_ON
}, PG_RX_CONFIG
, offsetof(rxConfig_t
, halfDuplex
) },
578 { "input_filtering_mode", VAR_INT8
| MASTER_VALUE
| MODE_LOOKUP
, .config
.lookup
= { TABLE_OFF_ON
}, PG_PWM_CONFIG
, offsetof(pwmConfig_t
, inputFilteringMode
) },
581 // PG_BLACKBOX_CONFIG
583 { "blackbox_rate_num", VAR_UINT8
| MASTER_VALUE
, .config
.minmax
= { 1, 32 }, PG_BLACKBOX_CONFIG
, offsetof(blackboxConfig_t
, rate_num
) },
584 { "blackbox_rate_denom", VAR_UINT8
| MASTER_VALUE
, .config
.minmax
= { 1, 32 }, PG_BLACKBOX_CONFIG
, offsetof(blackboxConfig_t
, rate_denom
) },
585 { "blackbox_device", VAR_UINT8
| MASTER_VALUE
| MODE_LOOKUP
, .config
.lookup
= { TABLE_BLACKBOX_DEVICE
}, PG_BLACKBOX_CONFIG
, offsetof(blackboxConfig_t
, device
) },
586 { "blackbox_on_motor_test", VAR_UINT8
| MASTER_VALUE
| MODE_LOOKUP
, .config
.lookup
= { TABLE_OFF_ON
}, PG_BLACKBOX_CONFIG
, offsetof(blackboxConfig_t
, on_motor_test
) },
590 { "min_throttle", VAR_UINT16
| MASTER_VALUE
, .config
.minmax
= { PWM_RANGE_ZERO
, PWM_RANGE_MAX
}, PG_MOTOR_CONFIG
, offsetof(motorConfig_t
, minthrottle
) },
591 { "max_throttle", VAR_UINT16
| MASTER_VALUE
, .config
.minmax
= { PWM_RANGE_ZERO
, PWM_RANGE_MAX
}, PG_MOTOR_CONFIG
, offsetof(motorConfig_t
, maxthrottle
) },
592 { "min_command", VAR_UINT16
| MASTER_VALUE
, .config
.minmax
= { PWM_RANGE_ZERO
, PWM_RANGE_MAX
}, PG_MOTOR_CONFIG
, offsetof(motorConfig_t
, mincommand
) },
594 { "digital_idle_percent", VAR_FLOAT
| MASTER_VALUE
, .config
.minmax
= { 0, 20 }, PG_MOTOR_CONFIG
, offsetof(motorConfig_t
, digitalIdleOffsetPercent
) },
596 { "use_unsynced_pwm", VAR_UINT8
| MASTER_VALUE
| MODE_LOOKUP
, .config
.lookup
= { TABLE_OFF_ON
}, PG_MOTOR_CONFIG
, offsetof(motorConfig_t
, dev
.useUnsyncedPwm
) },
597 { "motor_pwm_protocol", VAR_UINT8
| MASTER_VALUE
| MODE_LOOKUP
, .config
.lookup
= { TABLE_MOTOR_PWM_PROTOCOL
}, PG_MOTOR_CONFIG
, offsetof(motorConfig_t
, dev
.motorPwmProtocol
) },
598 { "motor_pwm_rate", VAR_UINT16
| MASTER_VALUE
, .config
.minmax
= { 200, 32000 }, PG_MOTOR_CONFIG
, offsetof(motorConfig_t
, dev
.motorPwmRate
) },
599 { "motor_pwm_inversion", VAR_UINT8
| MASTER_VALUE
| MODE_LOOKUP
, .config
.lookup
= { TABLE_OFF_ON
}, PG_MOTOR_CONFIG
, offsetof(motorConfig_t
, dev
.motorPwmInversion
) },
601 // PG_THROTTLE_CORRECTION_CONFIG
602 { "thr_corr_value", VAR_UINT8
| MASTER_VALUE
, .config
.minmax
= { 0, 150 }, PG_THROTTLE_CORRECTION_CONFIG
, offsetof(throttleCorrectionConfig_t
, throttle_correction_value
) },
603 { "thr_corr_angle", VAR_UINT16
| MASTER_VALUE
, .config
.minmax
= { 1, 900 }, PG_THROTTLE_CORRECTION_CONFIG
, offsetof(throttleCorrectionConfig_t
, throttle_correction_angle
) },
605 // PG_FAILSAFE_CONFIG
606 { "failsafe_delay", VAR_UINT8
| MASTER_VALUE
, .config
.minmax
= { 0, 200 }, PG_FAILSAFE_CONFIG
, offsetof(failsafeConfig_t
, failsafe_delay
) },
607 { "failsafe_off_delay", VAR_UINT8
| MASTER_VALUE
, .config
.minmax
= { 0, 200 }, PG_FAILSAFE_CONFIG
, offsetof(failsafeConfig_t
, failsafe_off_delay
) },
608 { "failsafe_throttle", VAR_UINT16
| MASTER_VALUE
, .config
.minmax
= { PWM_RANGE_MIN
, PWM_RANGE_MAX
}, PG_FAILSAFE_CONFIG
, offsetof(failsafeConfig_t
, failsafe_throttle
) },
609 { "failsafe_kill_switch", VAR_UINT8
| MASTER_VALUE
| MODE_LOOKUP
, .config
.lookup
= { TABLE_OFF_ON
}, PG_FAILSAFE_CONFIG
, offsetof(failsafeConfig_t
, failsafe_kill_switch
) },
610 { "failsafe_throttle_low_delay",VAR_UINT16
| MASTER_VALUE
, .config
.minmax
= { 0, 300 }, PG_FAILSAFE_CONFIG
, offsetof(failsafeConfig_t
, failsafe_throttle_low_delay
) },
611 { "failsafe_procedure", VAR_UINT8
| MASTER_VALUE
| MODE_LOOKUP
, .config
.lookup
= { TABLE_FAILSAFE
}, PG_FAILSAFE_CONFIG
, offsetof(failsafeConfig_t
, failsafe_procedure
) },
613 // PG_BOARDALIGNMENT_CONFIG
614 { "align_board_roll", VAR_INT16
| MASTER_VALUE
, .config
.minmax
= { -180, 360 }, PG_BOARD_ALIGNMENT
, offsetof(boardAlignment_t
, rollDegrees
) },
615 { "align_board_pitch", VAR_INT16
| MASTER_VALUE
, .config
.minmax
= { -180, 360 }, PG_BOARD_ALIGNMENT
, offsetof(boardAlignment_t
, pitchDegrees
) },
616 { "align_board_yaw", VAR_INT16
| MASTER_VALUE
, .config
.minmax
= { -180, 360 }, PG_BOARD_ALIGNMENT
, offsetof(boardAlignment_t
, yawDegrees
) },
620 { "gimbal_mode", VAR_UINT8
| MASTER_VALUE
| MODE_LOOKUP
, .config
.lookup
= { TABLE_GIMBAL_MODE
}, PG_GIMBAL_CONFIG
, offsetof(gimbalConfig_t
, mode
) },
624 { "bat_capacity", VAR_UINT16
| MASTER_VALUE
, .config
.minmax
= { 0, 20000 }, PG_BATTERY_CONFIG
, offsetof(batteryConfig_t
, batteryCapacity
) },
625 { "vbat_max_cell_voltage", VAR_UINT8
| MASTER_VALUE
, .config
.minmax
= { 10, 50 }, PG_BATTERY_CONFIG
, offsetof(batteryConfig_t
, vbatmaxcellvoltage
) },
626 { "vbat_min_cell_voltage", VAR_UINT8
| MASTER_VALUE
, .config
.minmax
= { 10, 50 }, PG_BATTERY_CONFIG
, offsetof(batteryConfig_t
, vbatmincellvoltage
) },
627 { "vbat_warning_cell_voltage", VAR_UINT8
| MASTER_VALUE
, .config
.minmax
= { 10, 50 }, PG_BATTERY_CONFIG
, offsetof(batteryConfig_t
, vbatwarningcellvoltage
) },
628 { "vbat_hysteresis", VAR_UINT8
| MASTER_VALUE
, .config
.minmax
= { 0, 250 }, PG_BATTERY_CONFIG
, offsetof(batteryConfig_t
, vbathysteresis
) },
629 { "current_meter", VAR_UINT8
| MASTER_VALUE
| MODE_LOOKUP
, .config
.lookup
= { TABLE_CURRENT_METER
}, PG_BATTERY_CONFIG
, offsetof(batteryConfig_t
, currentMeterSource
) },
630 { "battery_meter", VAR_UINT8
| MASTER_VALUE
| MODE_LOOKUP
, .config
.lookup
= { TABLE_VOLTAGE_METER
}, PG_BATTERY_CONFIG
, offsetof(batteryConfig_t
, voltageMeterSource
) },
631 { "bat_detect_thresh", VAR_UINT8
| MASTER_VALUE
, .config
.minmax
= { 0, 200 }, PG_BATTERY_CONFIG
, offsetof(batteryConfig_t
, batteryNotPresentLevel
) },
632 { "use_vbat_alerts", VAR_UINT8
| MASTER_VALUE
| MODE_LOOKUP
, .config
.lookup
= { TABLE_OFF_ON
}, PG_BATTERY_CONFIG
, offsetof(batteryConfig_t
, useVBatAlerts
) },
633 { "use_cbat_alerts", VAR_UINT8
| MASTER_VALUE
| MODE_LOOKUP
, .config
.lookup
= { TABLE_OFF_ON
}, PG_BATTERY_CONFIG
, offsetof(batteryConfig_t
, useConsumptionAlerts
) },
634 { "cbat_alert_percent", VAR_UINT8
| MASTER_VALUE
, .config
.minmax
= { 0, 100 }, PG_BATTERY_CONFIG
, offsetof(batteryConfig_t
, consumptionWarningPercentage
) },
636 // PG_VOLTAGE_SENSOR_ADC_CONFIG
637 { "vbat_scale", VAR_UINT8
| MASTER_VALUE
, .config
.minmax
= { VBAT_SCALE_MIN
, VBAT_SCALE_MAX
}, PG_VOLTAGE_SENSOR_ADC_CONFIG
, offsetof(voltageSensorADCConfig_t
, vbatscale
) },
639 // PG_CURRENT_SENSOR_ADC_CONFIG
640 { "ibata_scale", VAR_INT16
| MASTER_VALUE
, .config
.minmax
= { -16000, 16000 }, PG_CURRENT_SENSOR_ADC_CONFIG
, offsetof(currentSensorADCConfig_t
, scale
) },
641 { "ibata_offset", VAR_INT16
| MASTER_VALUE
, .config
.minmax
= { -16000, 16000 }, PG_CURRENT_SENSOR_ADC_CONFIG
, offsetof(currentSensorADCConfig_t
, offset
) },
642 // PG_CURRENT_SENSOR_ADC_CONFIG
643 { "ibatv_scale", VAR_INT16
| MASTER_VALUE
, .config
.minmax
= { -16000, 16000 }, PG_CURRENT_SENSOR_VIRTUAL_CONFIG
, offsetof(currentSensorVirtualConfig_t
, scale
) },
644 { "ibatv_offset", VAR_INT16
| MASTER_VALUE
, .config
.minmax
= { -16000, 16000 }, PG_CURRENT_SENSOR_VIRTUAL_CONFIG
, offsetof(currentSensorVirtualConfig_t
, offset
) },
646 // PG_BEEPER_DEV_CONFIG
648 { "beeper_inversion", VAR_UINT8
| MASTER_VALUE
| MODE_LOOKUP
, .config
.lookup
= { TABLE_OFF_ON
}, PG_BEEPER_DEV_CONFIG
, offsetof(beeperDevConfig_t
, isInverted
) },
649 { "beeper_od", VAR_UINT8
| MASTER_VALUE
| MODE_LOOKUP
, .config
.lookup
= { TABLE_OFF_ON
}, PG_BEEPER_DEV_CONFIG
, offsetof(beeperDevConfig_t
, isOpenDrain
) },
653 { "yaw_motor_direction", VAR_INT8
| MASTER_VALUE
, .config
.minmax
= { -1, 1 }, PG_MIXER_CONFIG
, offsetof(mixerConfig_t
, yaw_motor_direction
) },
655 // PG_MOTOR_3D_CONFIG
656 { "3d_deadband_low", VAR_UINT16
| MASTER_VALUE
, .config
.minmax
= { PWM_RANGE_ZERO
, PWM_RANGE_MAX
}, PG_MOTOR_3D_CONFIG
, offsetof(flight3DConfig_t
, deadband3d_low
) }, // FIXME upper limit should match code in the mixer, 1500 currently
657 { "3d_deadband_high", VAR_UINT16
| MASTER_VALUE
, .config
.minmax
= { PWM_RANGE_ZERO
, PWM_RANGE_MAX
}, PG_MOTOR_3D_CONFIG
, offsetof(flight3DConfig_t
, deadband3d_high
) }, // FIXME lower limit should match code in the mixer, 1500 currently,
658 { "3d_neutral", VAR_UINT16
| MASTER_VALUE
, .config
.minmax
= { PWM_RANGE_ZERO
, PWM_RANGE_MAX
}, PG_MOTOR_3D_CONFIG
, offsetof(flight3DConfig_t
, neutral3d
) },
659 { "3d_deadband_throttle", VAR_UINT16
| MASTER_VALUE
, .config
.minmax
= { PWM_RANGE_ZERO
, PWM_RANGE_MAX
}, PG_MOTOR_3D_CONFIG
, offsetof(flight3DConfig_t
, deadband3d_throttle
) },
663 { "servo_center_pulse", VAR_UINT16
| MASTER_VALUE
, .config
.minmax
= { PWM_RANGE_ZERO
, PWM_RANGE_MAX
}, PG_SERVO_CONFIG
, offsetof(servoConfig_t
, dev
.servoCenterPulse
) },
664 { "servo_pwm_rate", VAR_UINT16
| MASTER_VALUE
, .config
.minmax
= { 50, 498 }, PG_SERVO_CONFIG
, offsetof(servoConfig_t
, dev
.servoPwmRate
) },
665 { "servo_lowpass_hz", VAR_UINT16
| MASTER_VALUE
, .config
.minmax
= { 0, 400}, PG_SERVO_CONFIG
, offsetof(servoConfig_t
, servo_lowpass_freq
) },
666 { "tri_unarmed_servo", VAR_INT8
| MASTER_VALUE
| MODE_LOOKUP
, .config
.lookup
= { TABLE_OFF_ON
}, PG_SERVO_CONFIG
, offsetof(servoConfig_t
, tri_unarmed_servo
) },
667 { "channel_forwarding_start", VAR_UINT8
| MASTER_VALUE
, .config
.minmax
= { AUX1
, MAX_SUPPORTED_RC_CHANNEL_COUNT
}, PG_SERVO_CONFIG
, offsetof(servoConfig_t
, channelForwardingStartChannel
) },
670 // PG_CONTROLRATE_PROFILES
671 { "rc_rate", VAR_UINT8
| PROFILE_RATE_VALUE
, .config
.minmax
= { 0, 255 }, PG_CONTROL_RATE_PROFILES
, offsetof(controlRateConfig_t
, rcRate8
) },
672 { "rc_rate_yaw", VAR_UINT8
| PROFILE_RATE_VALUE
, .config
.minmax
= { 0, 255 }, PG_CONTROL_RATE_PROFILES
, offsetof(controlRateConfig_t
, rcYawRate8
) },
673 { "rc_expo", VAR_UINT8
| PROFILE_RATE_VALUE
, .config
.minmax
= { 0, 100 }, PG_CONTROL_RATE_PROFILES
, offsetof(controlRateConfig_t
, rcExpo8
) },
674 { "rc_yaw_expo", VAR_UINT8
| PROFILE_RATE_VALUE
, .config
.minmax
= { 0, 100 }, PG_CONTROL_RATE_PROFILES
, offsetof(controlRateConfig_t
, rcYawExpo8
) },
675 { "thr_mid", VAR_UINT8
| PROFILE_RATE_VALUE
, .config
.minmax
= { 0, 100 }, PG_CONTROL_RATE_PROFILES
, offsetof(controlRateConfig_t
, thrMid8
) },
676 { "thr_expo", VAR_UINT8
| PROFILE_RATE_VALUE
, .config
.minmax
= { 0, 100 }, PG_CONTROL_RATE_PROFILES
, offsetof(controlRateConfig_t
, thrExpo8
) },
677 { "roll_srate", VAR_UINT8
| PROFILE_RATE_VALUE
, .config
.minmax
= { 0, CONTROL_RATE_CONFIG_ROLL_PITCH_RATE_MAX
}, PG_CONTROL_RATE_PROFILES
, offsetof(controlRateConfig_t
, rates
[FD_ROLL
]) },
678 { "pitch_srate", VAR_UINT8
| PROFILE_RATE_VALUE
, .config
.minmax
= { 0, CONTROL_RATE_CONFIG_ROLL_PITCH_RATE_MAX
}, PG_CONTROL_RATE_PROFILES
, offsetof(controlRateConfig_t
, rates
[FD_PITCH
]) },
679 { "yaw_srate", VAR_UINT8
| PROFILE_RATE_VALUE
, .config
.minmax
= { 0, CONTROL_RATE_CONFIG_YAW_RATE_MAX
}, PG_CONTROL_RATE_PROFILES
, offsetof(controlRateConfig_t
, rates
[FD_YAW
]) },
680 { "tpa_rate", VAR_UINT8
| PROFILE_RATE_VALUE
, .config
.minmax
= { 0, CONTROL_RATE_CONFIG_TPA_MAX
}, PG_CONTROL_RATE_PROFILES
, offsetof(controlRateConfig_t
, dynThrPID
) },
681 { "tpa_breakpoint", VAR_UINT16
| PROFILE_RATE_VALUE
, .config
.minmax
= { PWM_RANGE_MIN
, PWM_RANGE_MAX
}, PG_CONTROL_RATE_PROFILES
, offsetof(controlRateConfig_t
, tpa_breakpoint
) },
684 { "reboot_character", VAR_UINT8
| MASTER_VALUE
, .config
.minmax
= { 48, 126 }, PG_SERIAL_CONFIG
, offsetof(serialConfig_t
, reboot_character
) },
685 { "serial_update_rate_hz", VAR_UINT16
| MASTER_VALUE
, .config
.minmax
= { 100, 2000 }, PG_SERIAL_CONFIG
, offsetof(serialConfig_t
, serial_update_rate_hz
) },
688 { "accxy_deadband", VAR_UINT8
| MASTER_VALUE
, .config
.minmax
= { 0, 100 }, PG_IMU_CONFIG
, offsetof(imuConfig_t
, accDeadband
.xy
) },
689 { "accz_deadband", VAR_UINT8
| MASTER_VALUE
, .config
.minmax
= { 0, 100 }, PG_IMU_CONFIG
, offsetof(imuConfig_t
, accDeadband
.z
) },
690 { "acc_unarmedcal", VAR_UINT8
| MASTER_VALUE
| MODE_LOOKUP
, .config
.lookup
= { TABLE_OFF_ON
}, PG_IMU_CONFIG
, offsetof(imuConfig_t
, acc_unarmedcal
) },
691 { "imu_dcm_kp", VAR_UINT16
| MASTER_VALUE
, .config
.minmax
= { 0, 32000 }, PG_IMU_CONFIG
, offsetof(imuConfig_t
, dcm_kp
) },
692 { "imu_dcm_ki", VAR_UINT16
| MASTER_VALUE
, .config
.minmax
= { 0, 32000 }, PG_IMU_CONFIG
, offsetof(imuConfig_t
, dcm_ki
) },
693 { "small_angle", VAR_UINT8
| MASTER_VALUE
, .config
.minmax
= { 0, 180 }, PG_IMU_CONFIG
, offsetof(imuConfig_t
, small_angle
) },
696 { "auto_disarm_delay", VAR_UINT8
| MASTER_VALUE
, .config
.minmax
= { 0, 60 }, PG_ARMING_CONFIG
, offsetof(armingConfig_t
, auto_disarm_delay
) },
697 { "disarm_kill_switch", VAR_UINT8
| MASTER_VALUE
| MODE_LOOKUP
, .config
.lookup
= { TABLE_OFF_ON
}, PG_ARMING_CONFIG
, offsetof(armingConfig_t
, disarm_kill_switch
) },
698 { "gyro_cal_on_first_arm", VAR_UINT8
| MASTER_VALUE
| MODE_LOOKUP
, .config
.lookup
= { TABLE_OFF_ON
}, PG_ARMING_CONFIG
, offsetof(armingConfig_t
, gyro_cal_on_first_arm
) },
703 { "gps_provider", VAR_UINT8
| MASTER_VALUE
| MODE_LOOKUP
, .config
.lookup
= { TABLE_GPS_PROVIDER
}, PG_GPS_CONFIG
, offsetof(gpsConfig_t
, provider
) },
704 { "gps_sbas_mode", VAR_UINT8
| MASTER_VALUE
| MODE_LOOKUP
, .config
.lookup
= { TABLE_GPS_SBAS_MODE
}, PG_GPS_CONFIG
, offsetof(gpsConfig_t
, sbasMode
) },
705 { "gps_auto_config", VAR_UINT8
| MASTER_VALUE
| MODE_LOOKUP
, .config
.lookup
= { TABLE_OFF_ON
}, PG_GPS_CONFIG
, offsetof(gpsConfig_t
, autoConfig
) },
706 { "gps_auto_baud", VAR_UINT8
| MASTER_VALUE
| MODE_LOOKUP
, .config
.lookup
= { TABLE_OFF_ON
}, PG_GPS_CONFIG
, offsetof(gpsConfig_t
, autoBaud
) },
709 // PG_NAVIGATION_CONFIG
711 { "gps_wp_radius", VAR_UINT16
| MASTER_VALUE
, .config
.minmax
= { 0, 2000 }, PG_NAVIGATION_CONFIG
, offsetof(navigationConfig_t
, gps_wp_radius
) },
712 { "nav_controls_heading", VAR_UINT8
| MASTER_VALUE
| MODE_LOOKUP
, .config
.lookup
= { TABLE_OFF_ON
}, PG_NAVIGATION_CONFIG
, offsetof(navigationConfig_t
, nav_controls_heading
) },
713 { "nav_speed_min", VAR_UINT16
| MASTER_VALUE
, .config
.minmax
= { 10, 2000 }, PG_NAVIGATION_CONFIG
, offsetof(navigationConfig_t
, nav_speed_min
) },
714 { "nav_speed_max", VAR_UINT16
| MASTER_VALUE
, .config
.minmax
= { 10, 2000 }, PG_NAVIGATION_CONFIG
, offsetof(navigationConfig_t
, nav_speed_max
) },
715 { "nav_slew_rate", VAR_UINT8
| MASTER_VALUE
, .config
.minmax
= { 0, 100 }, PG_NAVIGATION_CONFIG
, offsetof(navigationConfig_t
, nav_slew_rate
) },
718 // PG_AIRPLANE_CONFIG
719 { "fixedwing_althold_dir", VAR_INT8
| MASTER_VALUE
, .config
.minmax
= { -1, 1 }, PG_AIRPLANE_CONFIG
, offsetof(airplaneConfig_t
, fixedwing_althold_dir
) },
721 // PG_RC_CONTROLS_CONFIG
722 { "alt_hold_deadband", VAR_UINT8
| MASTER_VALUE
, .config
.minmax
= { 1, 250 }, PG_RC_CONTROLS_CONFIG
, offsetof(rcControlsConfig_t
, alt_hold_deadband
) },
723 { "alt_hold_fast_change", VAR_UINT8
| MASTER_VALUE
| MODE_LOOKUP
, .config
.lookup
= { TABLE_OFF_ON
}, PG_RC_CONTROLS_CONFIG
, offsetof(rcControlsConfig_t
, alt_hold_fast_change
) },
724 { "deadband", VAR_UINT8
| MASTER_VALUE
, .config
.minmax
= { 0, 32 }, PG_RC_CONTROLS_CONFIG
, offsetof(rcControlsConfig_t
, deadband
) },
725 { "yaw_deadband", VAR_UINT8
| MASTER_VALUE
, .config
.minmax
= { 0, 100 }, PG_RC_CONTROLS_CONFIG
, offsetof(rcControlsConfig_t
, yaw_deadband
) },
726 { "yaw_control_direction", VAR_INT8
| MASTER_VALUE
, .config
.minmax
= { -1, 1 }, PG_RC_CONTROLS_CONFIG
, offsetof(rcControlsConfig_t
, yaw_control_direction
) },
729 { "pid_process_denom", VAR_UINT8
| MASTER_VALUE
, .config
.minmax
= { 1, MAX_PID_PROCESS_DENOM
}, PG_PID_CONFIG
, offsetof(pidConfig_t
, pid_process_denom
) },
732 { "d_lowpass_type", VAR_UINT8
| PROFILE_VALUE
| MODE_LOOKUP
, .config
.lookup
= { TABLE_LOWPASS_TYPE
}, PG_PID_PROFILE
, offsetof(pidProfile_t
, dterm_filter_type
) },
733 { "d_lowpass", VAR_INT16
| PROFILE_VALUE
, .config
.minmax
= { 0, 16000 }, PG_PID_PROFILE
, offsetof(pidProfile_t
, dterm_lpf_hz
) },
734 { "d_notch_hz", VAR_UINT16
| PROFILE_VALUE
, .config
.minmax
= { 0, 16000 }, PG_PID_PROFILE
, offsetof(pidProfile_t
, dterm_notch_hz
) },
735 { "d_notch_cut", VAR_UINT16
| PROFILE_VALUE
, .config
.minmax
= { 1, 16000 }, PG_PID_PROFILE
, offsetof(pidProfile_t
, dterm_notch_cutoff
) },
736 { "vbat_pid_gain", VAR_UINT8
| PROFILE_VALUE
| MODE_LOOKUP
, .config
.lookup
= { TABLE_OFF_ON
}, PG_PID_PROFILE
, offsetof(pidProfile_t
, vbatPidCompensation
) },
737 { "pid_at_min_throttle", VAR_UINT8
| PROFILE_VALUE
| MODE_LOOKUP
, .config
.lookup
= { TABLE_OFF_ON
}, PG_PID_PROFILE
, offsetof(pidProfile_t
, pidAtMinThrottle
) },
738 { "anti_gravity_thresh", VAR_UINT16
| PROFILE_VALUE
, .config
.minmax
= { 20, 1000 }, PG_PID_PROFILE
, offsetof(pidProfile_t
, itermThrottleThreshold
) },
739 { "anti_gravity_gain", VAR_FLOAT
| PROFILE_VALUE
, .config
.minmax
= { 1, 30 }, PG_PID_PROFILE
, offsetof(pidProfile_t
, itermAcceleratorGain
) },
740 { "setpoint_relax_ratio", VAR_UINT8
| PROFILE_VALUE
, .config
.minmax
= { 0, 100 }, PG_PID_PROFILE
, offsetof(pidProfile_t
, setpointRelaxRatio
) },
741 { "d_setpoint_weight", VAR_UINT8
| PROFILE_VALUE
, .config
.minmax
= { 0, 254 }, PG_PID_PROFILE
, offsetof(pidProfile_t
, dtermSetpointWeight
) },
742 { "yaw_accel_limit", VAR_FLOAT
| PROFILE_VALUE
, .config
.minmax
= { 0.1f
, 50.0f
}, PG_PID_PROFILE
, offsetof(pidProfile_t
, yawRateAccelLimit
) },
743 { "accel_limit", VAR_FLOAT
| PROFILE_VALUE
, .config
.minmax
= { 0.1f
, 50.0f
}, PG_PID_PROFILE
, offsetof(pidProfile_t
, rateAccelLimit
) },
745 { "iterm_windup", VAR_UINT8
| PROFILE_VALUE
, .config
.minmax
= { 30, 100 }, PG_PID_PROFILE
, offsetof(pidProfile_t
, itermWindupPointPercent
) },
746 { "yaw_lowpass", VAR_UINT16
| PROFILE_VALUE
, .config
.minmax
= { 0, 500 }, PG_PID_PROFILE
, offsetof(pidProfile_t
, yaw_lpf_hz
) },
747 { "pidsum_limit", VAR_FLOAT
| PROFILE_VALUE
, .config
.minmax
= { 0.1, 1.0 }, PG_PID_PROFILE
, offsetof(pidProfile_t
, pidSumLimit
) },
748 { "pidsum_limit_yaw", VAR_FLOAT
| PROFILE_VALUE
, .config
.minmax
= { 0.1, 1.0 }, PG_PID_PROFILE
, offsetof(pidProfile_t
, pidSumLimitYaw
) },
750 { "p_pitch", VAR_UINT8
| PROFILE_VALUE
, .config
.minmax
= { 0, 200 }, PG_PID_PROFILE
, offsetof(pidProfile_t
, P8
[PITCH
]) },
751 { "i_pitch", VAR_UINT8
| PROFILE_VALUE
, .config
.minmax
= { 0, 200 }, PG_PID_PROFILE
, offsetof(pidProfile_t
, I8
[PITCH
]) },
752 { "d_pitch", VAR_UINT8
| PROFILE_VALUE
, .config
.minmax
= { 0, 200 }, PG_PID_PROFILE
, offsetof(pidProfile_t
, D8
[PITCH
]) },
753 { "p_roll", VAR_UINT8
| PROFILE_VALUE
, .config
.minmax
= { 0, 200 }, PG_PID_PROFILE
, offsetof(pidProfile_t
, P8
[ROLL
]) },
754 { "i_roll", VAR_UINT8
| PROFILE_VALUE
, .config
.minmax
= { 0, 200 }, PG_PID_PROFILE
, offsetof(pidProfile_t
, I8
[ROLL
]) },
755 { "d_roll", VAR_UINT8
| PROFILE_VALUE
, .config
.minmax
= { 0, 200 }, PG_PID_PROFILE
, offsetof(pidProfile_t
, D8
[ROLL
]) },
756 { "p_yaw", VAR_UINT8
| PROFILE_VALUE
, .config
.minmax
= { 0, 200 }, PG_PID_PROFILE
, offsetof(pidProfile_t
, P8
[YAW
]) },
757 { "i_yaw", VAR_UINT8
| PROFILE_VALUE
, .config
.minmax
= { 0, 200 }, PG_PID_PROFILE
, offsetof(pidProfile_t
, I8
[YAW
]) },
758 { "d_yaw", VAR_UINT8
| PROFILE_VALUE
, .config
.minmax
= { 0, 200 }, PG_PID_PROFILE
, offsetof(pidProfile_t
, D8
[YAW
]) },
760 { "p_alt", VAR_UINT8
| PROFILE_VALUE
, .config
.minmax
= { 0, 200 }, PG_PID_PROFILE
, offsetof(pidProfile_t
, P8
[PIDALT
]) },
761 { "i_alt", VAR_UINT8
| PROFILE_VALUE
, .config
.minmax
= { 0, 200 }, PG_PID_PROFILE
, offsetof(pidProfile_t
, I8
[PIDALT
]) },
762 { "d_alt", VAR_UINT8
| PROFILE_VALUE
, .config
.minmax
= { 0, 200 }, PG_PID_PROFILE
, offsetof(pidProfile_t
, D8
[PIDALT
]) },
764 { "p_level", VAR_UINT8
| PROFILE_VALUE
, .config
.minmax
= { 0, 200 }, PG_PID_PROFILE
, offsetof(pidProfile_t
, P8
[PIDLEVEL
]) },
765 { "i_level", VAR_UINT8
| PROFILE_VALUE
, .config
.minmax
= { 0, 200 }, PG_PID_PROFILE
, offsetof(pidProfile_t
, I8
[PIDLEVEL
]) },
766 { "d_level", VAR_UINT8
| PROFILE_VALUE
, .config
.minmax
= { 0, 200 }, PG_PID_PROFILE
, offsetof(pidProfile_t
, D8
[PIDLEVEL
]) },
768 { "p_vel", VAR_UINT8
| PROFILE_VALUE
, .config
.minmax
= { 0, 200 }, PG_PID_PROFILE
, offsetof(pidProfile_t
, P8
[PIDVEL
]) },
769 { "i_vel", VAR_UINT8
| PROFILE_VALUE
, .config
.minmax
= { 0, 200 }, PG_PID_PROFILE
, offsetof(pidProfile_t
, I8
[PIDVEL
]) },
770 { "d_vel", VAR_UINT8
| PROFILE_VALUE
, .config
.minmax
= { 0, 200 }, PG_PID_PROFILE
, offsetof(pidProfile_t
, D8
[PIDVEL
]) },
772 { "level_sensitivity", VAR_UINT8
| PROFILE_VALUE
, .config
.minmax
= { 10, 200 }, PG_PID_PROFILE
, offsetof(pidProfile_t
, levelSensitivity
) },
773 { "level_limit", VAR_UINT8
| PROFILE_VALUE
, .config
.minmax
= { 10, 120 }, PG_PID_PROFILE
, offsetof(pidProfile_t
, levelAngleLimit
) },
775 { "gps_pos_p", VAR_UINT8
| PROFILE_VALUE
, .config
.minmax
= { 0, 200 }, PG_PID_PROFILE
, offsetof(pidProfile_t
, P8
[PIDPOS
]) },
776 { "gps_pos_i", VAR_UINT8
| PROFILE_VALUE
, .config
.minmax
= { 0, 200 }, PG_PID_PROFILE
, offsetof(pidProfile_t
, I8
[PIDPOS
]) },
777 { "gps_pos_d", VAR_UINT8
| PROFILE_VALUE
, .config
.minmax
= { 0, 200 }, PG_PID_PROFILE
, offsetof(pidProfile_t
, D8
[PIDPOS
]) },
778 { "gps_posr_p", VAR_UINT8
| PROFILE_VALUE
, .config
.minmax
= { 0, 200 }, PG_PID_PROFILE
, offsetof(pidProfile_t
, P8
[PIDPOSR
]) },
779 { "gps_posr_i", VAR_UINT8
| PROFILE_VALUE
, .config
.minmax
= { 0, 200 }, PG_PID_PROFILE
, offsetof(pidProfile_t
, I8
[PIDPOSR
]) },
780 { "gps_posr_d", VAR_UINT8
| PROFILE_VALUE
, .config
.minmax
= { 0, 200 }, PG_PID_PROFILE
, offsetof(pidProfile_t
, D8
[PIDPOSR
]) },
781 { "gps_nav_p", VAR_UINT8
| PROFILE_VALUE
, .config
.minmax
= { 0, 200 }, PG_PID_PROFILE
, offsetof(pidProfile_t
, P8
[PIDNAVR
]) },
782 { "gps_nav_i", VAR_UINT8
| PROFILE_VALUE
, .config
.minmax
= { 0, 200 }, PG_PID_PROFILE
, offsetof(pidProfile_t
, I8
[PIDNAVR
]) },
783 { "gps_nav_d", VAR_UINT8
| PROFILE_VALUE
, .config
.minmax
= { 0, 200 }, PG_PID_PROFILE
, offsetof(pidProfile_t
, D8
[PIDNAVR
]) },
786 // PG_TELEMETRY_CONFIG
788 { "tlm_switch", VAR_UINT8
| MASTER_VALUE
| MODE_LOOKUP
, .config
.lookup
= { TABLE_OFF_ON
}, PG_TELEMETRY_CONFIG
, offsetof(telemetryConfig_t
, telemetry_switch
) },
789 { "tlm_inversion", VAR_UINT8
| MASTER_VALUE
| MODE_LOOKUP
, .config
.lookup
= { TABLE_OFF_ON
}, PG_TELEMETRY_CONFIG
, offsetof(telemetryConfig_t
, telemetry_inversion
) },
790 { "sport_halfduplex", VAR_UINT8
| MASTER_VALUE
| MODE_LOOKUP
, .config
.lookup
= { TABLE_OFF_ON
}, PG_TELEMETRY_CONFIG
, offsetof(telemetryConfig_t
, sportHalfDuplex
) },
791 { "frsky_default_lat", VAR_FLOAT
| MASTER_VALUE
, .config
.minmax
= { -90.0, 90.0 }, PG_TELEMETRY_CONFIG
, offsetof(telemetryConfig_t
, gpsNoFixLatitude
) },
792 { "frsky_default_long", VAR_FLOAT
| MASTER_VALUE
, .config
.minmax
= { -180.0, 180.0 }, PG_TELEMETRY_CONFIG
, offsetof(telemetryConfig_t
, gpsNoFixLongitude
) },
793 { "frsky_gps_format", VAR_UINT8
| MASTER_VALUE
, .config
.minmax
= { 0, FRSKY_FORMAT_NMEA
}, PG_TELEMETRY_CONFIG
, offsetof(telemetryConfig_t
, frsky_coordinate_format
) },
794 { "frsky_unit", VAR_UINT8
| MASTER_VALUE
| MODE_LOOKUP
, .config
.lookup
= { TABLE_UNIT
}, PG_TELEMETRY_CONFIG
, offsetof(telemetryConfig_t
, frsky_unit
) },
795 { "frsky_vfas_precision", VAR_UINT8
| MASTER_VALUE
, .config
.minmax
= { FRSKY_VFAS_PRECISION_LOW
, FRSKY_VFAS_PRECISION_HIGH
}, PG_TELEMETRY_CONFIG
, offsetof(telemetryConfig_t
, frsky_vfas_precision
) },
796 { "frsky_vfas_cell_voltage", VAR_UINT8
| MASTER_VALUE
| MODE_LOOKUP
, .config
.lookup
= { TABLE_OFF_ON
}, PG_TELEMETRY_CONFIG
, offsetof(telemetryConfig_t
, frsky_vfas_cell_voltage
) },
797 { "hott_alarm_int", VAR_UINT8
| MASTER_VALUE
, .config
.minmax
= { 0, 120 }, PG_TELEMETRY_CONFIG
, offsetof(telemetryConfig_t
, hottAlarmSoundInterval
) },
798 { "pid_in_tlm", VAR_UINT8
| MASTER_VALUE
| MODE_LOOKUP
, .config
.lookup
= {TABLE_OFF_ON
}, PG_TELEMETRY_CONFIG
, offsetof(telemetryConfig_t
, pidValuesAsTelemetry
) },
799 #if defined(TELEMETRY_IBUS)
800 { "ibus_report_cell_voltage", VAR_UINT8
| MASTER_VALUE
| MODE_LOOKUP
, .config
.lookup
= { TABLE_OFF_ON
}, PG_TELEMETRY_CONFIG
, offsetof(telemetryConfig_t
, report_cell_voltage
) },
804 // PG_LED_STRIP_CONFIG
806 { "ledstrip_visual_beeper", VAR_UINT8
| MASTER_VALUE
| MODE_LOOKUP
, .config
.lookup
= { TABLE_OFF_ON
}, PG_LED_STRIP_CONFIG
, offsetof(ledStripConfig_t
, ledstrip_visual_beeper
) },
811 { "sdcard_dma", VAR_UINT8
| MASTER_VALUE
| MODE_LOOKUP
, .config
.lookup
= { TABLE_OFF_ON
}, PG_SDCARD_CONFIG
, offsetof(sdcardConfig_t
, useDma
) },
816 { "osd_units", VAR_UINT8
| MASTER_VALUE
| MODE_LOOKUP
, .config
.lookup
= { TABLE_UNIT
}, PG_OSD_CONFIG
, offsetof(osdConfig_t
, units
) },
818 { "osd_rssi_alarm", VAR_UINT8
| MASTER_VALUE
, .config
.minmax
= { 0, 100 }, PG_OSD_CONFIG
, offsetof(osdConfig_t
, rssi_alarm
) },
819 { "osd_cap_alarm", VAR_UINT16
| MASTER_VALUE
, .config
.minmax
= { 0, 20000 }, PG_OSD_CONFIG
, offsetof(osdConfig_t
, cap_alarm
) },
820 { "osd_time_alarm", VAR_UINT16
| MASTER_VALUE
, .config
.minmax
= { 0, 60 }, PG_OSD_CONFIG
, offsetof(osdConfig_t
, time_alarm
) },
821 { "osd_alt_alarm", VAR_UINT16
| MASTER_VALUE
, .config
.minmax
= { 0, 10000 }, PG_OSD_CONFIG
, offsetof(osdConfig_t
, alt_alarm
) },
823 { "osd_vbat_pos", VAR_UINT16
| MASTER_VALUE
, .config
.minmax
= { 0, OSD_POSCFG_MAX
}, PG_OSD_CONFIG
, offsetof(osdConfig_t
, item_pos
[OSD_MAIN_BATT_VOLTAGE
]) },
824 { "osd_rssi_pos", VAR_UINT16
| MASTER_VALUE
, .config
.minmax
= { 0, OSD_POSCFG_MAX
}, PG_OSD_CONFIG
, offsetof(osdConfig_t
, item_pos
[OSD_RSSI_VALUE
]) },
825 { "osd_flytimer_pos", VAR_UINT16
| MASTER_VALUE
, .config
.minmax
= { 0, OSD_POSCFG_MAX
}, PG_OSD_CONFIG
, offsetof(osdConfig_t
, item_pos
[OSD_FLYTIME
]) },
826 { "osd_ontimer_pos", VAR_UINT16
| MASTER_VALUE
, .config
.minmax
= { 0, OSD_POSCFG_MAX
}, PG_OSD_CONFIG
, offsetof(osdConfig_t
, item_pos
[OSD_ONTIME
]) },
827 { "osd_flymode_pos", VAR_UINT16
| MASTER_VALUE
, .config
.minmax
= { 0, OSD_POSCFG_MAX
}, PG_OSD_CONFIG
, offsetof(osdConfig_t
, item_pos
[OSD_FLYMODE
]) },
828 { "osd_throttle_pos", VAR_UINT16
| MASTER_VALUE
, .config
.minmax
= { 0, OSD_POSCFG_MAX
}, PG_OSD_CONFIG
, offsetof(osdConfig_t
, item_pos
[OSD_THROTTLE_POS
]) },
829 { "osd_vtx_channel_pos", VAR_UINT16
| MASTER_VALUE
, .config
.minmax
= { 0, OSD_POSCFG_MAX
}, PG_OSD_CONFIG
, offsetof(osdConfig_t
, item_pos
[OSD_VTX_CHANNEL
]) },
830 { "osd_crosshairs", VAR_UINT16
| MASTER_VALUE
, .config
.minmax
= { 0, OSD_POSCFG_MAX
}, PG_OSD_CONFIG
, offsetof(osdConfig_t
, item_pos
[OSD_CROSSHAIRS
]) },
831 { "osd_horizon_pos", VAR_UINT16
| MASTER_VALUE
, .config
.minmax
= { 0, OSD_POSCFG_MAX
}, PG_OSD_CONFIG
, offsetof(osdConfig_t
, item_pos
[OSD_ARTIFICIAL_HORIZON
]) },
832 { "osd_current_pos", VAR_UINT16
| MASTER_VALUE
, .config
.minmax
= { 0, OSD_POSCFG_MAX
}, PG_OSD_CONFIG
, offsetof(osdConfig_t
, item_pos
[OSD_CURRENT_DRAW
]) },
833 { "osd_mah_drawn_pos", VAR_UINT16
| MASTER_VALUE
, .config
.minmax
= { 0, OSD_POSCFG_MAX
}, PG_OSD_CONFIG
, offsetof(osdConfig_t
, item_pos
[OSD_MAH_DRAWN
]) },
834 { "osd_craft_name_pos", VAR_UINT16
| MASTER_VALUE
, .config
.minmax
= { 0, OSD_POSCFG_MAX
}, PG_OSD_CONFIG
, offsetof(osdConfig_t
, item_pos
[OSD_CRAFT_NAME
]) },
835 { "osd_gps_speed_pos", VAR_UINT16
| MASTER_VALUE
, .config
.minmax
= { 0, OSD_POSCFG_MAX
}, PG_OSD_CONFIG
, offsetof(osdConfig_t
, item_pos
[OSD_GPS_SPEED
]) },
836 { "osd_gps_sats_pos", VAR_UINT16
| MASTER_VALUE
, .config
.minmax
= { 0, OSD_POSCFG_MAX
}, PG_OSD_CONFIG
, offsetof(osdConfig_t
, item_pos
[OSD_GPS_SATS
]) },
837 { "osd_altitude_pos", VAR_UINT16
| MASTER_VALUE
, .config
.minmax
= { 0, OSD_POSCFG_MAX
}, PG_OSD_CONFIG
, offsetof(osdConfig_t
, item_pos
[OSD_ALTITUDE
]) },
838 { "osd_pid_roll_pos", VAR_UINT16
| MASTER_VALUE
, .config
.minmax
= { 0, OSD_POSCFG_MAX
}, PG_OSD_CONFIG
, offsetof(osdConfig_t
, item_pos
[OSD_ROLL_PIDS
]) },
839 { "osd_pid_pitch_pos", VAR_UINT16
| MASTER_VALUE
, .config
.minmax
= { 0, OSD_POSCFG_MAX
}, PG_OSD_CONFIG
, offsetof(osdConfig_t
, item_pos
[OSD_PITCH_PIDS
]) },
840 { "osd_pid_yaw_pos", VAR_UINT16
| MASTER_VALUE
, .config
.minmax
= { 0, OSD_POSCFG_MAX
}, PG_OSD_CONFIG
, offsetof(osdConfig_t
, item_pos
[OSD_YAW_PIDS
]) },
841 { "osd_power_pos", VAR_UINT16
| MASTER_VALUE
, .config
.minmax
= { 0, OSD_POSCFG_MAX
}, PG_OSD_CONFIG
, offsetof(osdConfig_t
, item_pos
[OSD_POWER
]) },
842 { "osd_pidrate_profile_pos", VAR_UINT16
| MASTER_VALUE
, .config
.minmax
= { 0, OSD_POSCFG_MAX
}, PG_OSD_CONFIG
, offsetof(osdConfig_t
, item_pos
[OSD_PIDRATE_PROFILE
]) },
843 { "osd_battery_warning_pos", VAR_UINT16
| MASTER_VALUE
, .config
.minmax
= { 0, OSD_POSCFG_MAX
}, PG_OSD_CONFIG
, offsetof(osdConfig_t
, item_pos
[OSD_MAIN_BATT_WARNING
]) },
844 { "osd_avg_cell_voltage_pos", VAR_UINT16
| MASTER_VALUE
, .config
.minmax
= { 0, OSD_POSCFG_MAX
}, PG_OSD_CONFIG
, offsetof(osdConfig_t
, item_pos
[OSD_AVG_CELL_VOLTAGE
]) },
848 #ifndef SKIP_TASK_STATISTICS
849 { "task_statistics", VAR_INT8
| MASTER_VALUE
| MODE_LOOKUP
, .config
.lookup
= { TABLE_OFF_ON
}, PG_SYSTEM_CONFIG
, offsetof(systemConfig_t
, task_statistics
) },
851 { "debug_mode", VAR_UINT8
| MASTER_VALUE
| MODE_LOOKUP
, .config
.lookup
= { TABLE_DEBUG
}, PG_SYSTEM_CONFIG
, offsetof(systemConfig_t
, debug_mode
) },
855 { "vtx_band", VAR_UINT8
| MASTER_VALUE
, .config
.minmax
= { 1, 5 }, PG_VTX_CONFIG
, offsetof(vtxConfig_t
, vtx_band
) },
856 { "vtx_channel", VAR_UINT8
| MASTER_VALUE
, .config
.minmax
= { 1, 8 }, PG_VTX_CONFIG
, offsetof(vtxConfig_t
, vtx_channel
) },
857 { "vtx_mode", VAR_UINT8
| MASTER_VALUE
, .config
.minmax
= { 0, 2 }, PG_VTX_CONFIG
, offsetof(vtxConfig_t
, vtx_mode
) },
858 { "vtx_mhz", VAR_UINT16
| MASTER_VALUE
, .config
.minmax
= { 5600, 5950 }, PG_VTX_CONFIG
, offsetof(vtxConfig_t
, vtx_mhz
) },
860 #if defined(USE_RTC6705)
861 { "vtx_channel", VAR_UINT8
| MASTER_VALUE
, .config
.minmax
= { 0, 39 }, PG_VTX_CONFIG
, offsetof(vtxConfig_t
, vtx_channel
) },
862 { "vtx_power", VAR_UINT8
| MASTER_VALUE
, .config
.minmax
= { 0, 1 }, PG_VTX_CONFIG
, offsetof(vtxConfig_t
, vtx_power
) },
867 { "vcd_video_system", VAR_UINT8
| MASTER_VALUE
, .config
.minmax
= { 0, 2 }, PG_VCD_CONFIG
, offsetof(vcdProfile_t
, video_system
) },
868 { "vcd_h_offset", VAR_INT8
| MASTER_VALUE
, .config
.minmax
= { -32, 31 }, PG_VCD_CONFIG
, offsetof(vcdProfile_t
, h_offset
) },
869 { "vcd_v_offset", VAR_INT8
| MASTER_VALUE
, .config
.minmax
= { -15, 16 }, PG_VCD_CONFIG
, offsetof(vcdProfile_t
, v_offset
) },
872 // PG_DISPLAY_PORT_MSP_CONFIG
873 #ifdef USE_MSP_DISPLAYPORT
874 { "displayport_msp_col_adjust", VAR_INT8
| MASTER_VALUE
, .config
.minmax
= { -6, 0 }, PG_DISPLAY_PORT_MSP_CONFIG
, offsetof(displayPortProfile_t
, colAdjust
) },
875 { "displayport_msp_row_adjust", VAR_INT8
| MASTER_VALUE
, .config
.minmax
= { -3, 0 }, PG_DISPLAY_PORT_MSP_CONFIG
, offsetof(displayPortProfile_t
, rowAdjust
) },
878 // PG_DISPLAY_PORT_MSP_CONFIG
880 { "displayport_max7456_col_adjust", VAR_INT8
| MASTER_VALUE
, .config
.minmax
= { -6, 0 }, PG_DISPLAY_PORT_MSP_CONFIG
, offsetof(displayPortProfile_t
, colAdjust
) },
881 { "displayport_max7456_row_adjust", VAR_INT8
| MASTER_VALUE
, .config
.minmax
= { -3, 0 }, PG_DISPLAY_PORT_MAX7456_CONFIG
, offsetof(displayPortProfile_t
, rowAdjust
) },
885 static featureConfig_t featureConfigCopy
;
886 static gyroConfig_t gyroConfigCopy
;
887 static accelerometerConfig_t accelerometerConfigCopy
;
889 static compassConfig_t compassConfigCopy
;
892 static barometerConfig_t barometerConfigCopy
;
895 static pitotmeterConfig_t pitotmeterConfigCopy
;
897 static featureConfig_t featureConfigCopy
;
898 static rxConfig_t rxConfigCopy
;
901 static pwmConfig_t pwmConfigCopy
;
904 static blackboxConfig_t blackboxConfigCopy
;
906 static rxFailsafeChannelConfig_t rxFailsafeChannelConfigsCopy
[MAX_SUPPORTED_RC_CHANNEL_COUNT
];
907 static rxChannelRangeConfig_t rxChannelRangeConfigsCopy
[NON_AUX_CHANNEL_COUNT
];
908 static motorConfig_t motorConfigCopy
;
909 static throttleCorrectionConfig_t throttleCorrectionConfigCopy
;
910 static failsafeConfig_t failsafeConfigCopy
;
911 static boardAlignment_t boardAlignmentCopy
;
913 static servoConfig_t servoConfigCopy
;
914 static gimbalConfig_t gimbalConfigCopy
;
915 static servoMixer_t customServoMixersCopy
[MAX_SERVO_RULES
];
916 static servoParam_t servoParamsCopy
[MAX_SUPPORTED_SERVOS
];
918 static batteryConfig_t batteryConfigCopy
;
919 static voltageSensorADCConfig_t voltageSensorADCConfigCopy
[MAX_VOLTAGE_SENSOR_ADC
];
920 static currentSensorADCConfig_t currentSensorADCConfigCopy
;
921 static currentSensorVirtualConfig_t currentSensorVirtualConfigCopy
;
922 static motorMixer_t customMotorMixerCopy
[MAX_SUPPORTED_MOTORS
];
923 static mixerConfig_t mixerConfigCopy
;
924 static flight3DConfig_t flight3DConfigCopy
;
925 static serialConfig_t serialConfigCopy
;
926 static imuConfig_t imuConfigCopy
;
927 static armingConfig_t armingConfigCopy
;
928 static rcControlsConfig_t rcControlsConfigCopy
;
930 static gpsConfig_t gpsConfigCopy
;
931 static navigationConfig_t navigationConfigCopy
;
933 static airplaneConfig_t airplaneConfigCopy
;
935 static telemetryConfig_t telemetryConfigCopy
;
937 static modeActivationCondition_t modeActivationConditionsCopy
[MAX_MODE_ACTIVATION_CONDITION_COUNT
];
938 static adjustmentRange_t adjustmentRangesCopy
[MAX_ADJUSTMENT_RANGE_COUNT
];
940 static ledStripConfig_t ledStripConfigCopy
;
943 static sdcardConfig_t sdcardConfigCopy
;
946 static osdConfig_t osdConfigCopy
;
948 static systemConfig_t systemConfigCopy
;
950 static beeperDevConfig_t beeperDevConfigCopy
;
951 static beeperConfig_t beeperConfigCopy
;
953 #if defined(USE_RTC6705) || defined(VTX)
954 static vtxConfig_t vtxConfigCopy
;
957 vcdProfile_t vcdProfileCopy
;
959 #ifdef USE_MSP_DISPLAYPORT
960 displayPortProfile_t displayPortProfileMspCopy
;
963 displayPortProfile_t displayPortProfileMax7456Copy
;
965 static pidConfig_t pidConfigCopy
;
966 static controlRateConfig_t controlRateProfilesCopy
[CONTROL_RATE_PROFILE_COUNT
];
967 static pidProfile_t pidProfileCopy
[MAX_PROFILE_COUNT
];
968 #endif // USE_PARAMETER_GROUPS
970 static void cliPrint(const char *str
)
973 bufWriterAppend(cliWriter
, *str
++);
975 bufWriterFlush(cliWriter
);
979 #define cliPrintHashLine(str)
981 static void cliPrintHashLine(const char *str
)
989 static void cliPutp(void *p
, char ch
)
991 bufWriterAppend(p
, ch
);
995 DUMP_MASTER
= (1 << 0),
996 DUMP_PROFILE
= (1 << 1),
997 DUMP_RATES
= (1 << 2),
1000 SHOW_DEFAULTS
= (1 << 5),
1001 HIDE_UNUSED
= (1 << 6)
1004 static bool cliDumpPrintf(uint8_t dumpMask
, bool equalsDefault
, const char *format
, ...)
1006 if (!((dumpMask
& DO_DIFF
) && equalsDefault
)) {
1008 va_start(va
, format
);
1009 tfp_format(cliWriter
, cliPutp
, format
, va
);
1011 bufWriterFlush(cliWriter
);
1018 static void cliWrite(uint8_t ch
)
1020 bufWriterAppend(cliWriter
, ch
);
1023 static bool cliDefaultPrintf(uint8_t dumpMask
, bool equalsDefault
, const char *format
, ...)
1025 if ((dumpMask
& SHOW_DEFAULTS
) && !equalsDefault
) {
1029 va_start(va
, format
);
1030 tfp_format(cliWriter
, cliPutp
, format
, va
);
1032 bufWriterFlush(cliWriter
);
1039 static void cliPrintf(const char *format
, ...)
1042 va_start(va
, format
);
1043 tfp_format(cliWriter
, cliPutp
, format
, va
);
1045 bufWriterFlush(cliWriter
);
1048 static void printValuePointer(const clivalue_t
*var
, const void *valuePointer
, uint32_t full
)
1053 switch (var
->type
& VALUE_TYPE_MASK
) {
1055 value
= *(uint8_t *)valuePointer
;
1059 value
= *(int8_t *)valuePointer
;
1063 value
= *(uint16_t *)valuePointer
;
1067 value
= *(int16_t *)valuePointer
;
1070 /* not currently used
1072 value = *(uint32_t *)valuePointer;
1076 cliPrintf("%s", ftoa(*(float *)valuePointer
, buf
));
1077 if (full
&& (var
->type
& VALUE_MODE_MASK
) == MODE_DIRECT
) {
1078 cliPrintf(" %s", ftoa((float)var
->config
.minmax
.min
, buf
));
1079 cliPrintf(" %s", ftoa((float)var
->config
.minmax
.max
, buf
));
1081 return; // return from case for float only
1084 switch(var
->type
& VALUE_MODE_MASK
) {
1086 cliPrintf("%d", value
);
1088 cliPrintf(" %d %d", var
->config
.minmax
.min
, var
->config
.minmax
.max
);
1092 cliPrint(lookupTables
[var
->config
.lookup
.tableIndex
].values
[value
]);
1097 #ifdef USE_PARAMETER_GROUPS
1099 static bool valuePtrEqualsDefault(uint8_t type
, const void *ptr
, const void *ptrDefault
)
1101 bool result
= false;
1102 switch (type
& VALUE_TYPE_MASK
) {
1104 result
= *(uint8_t *)ptr
== *(uint8_t *)ptrDefault
;
1108 result
= *(int8_t *)ptr
== *(int8_t *)ptrDefault
;
1112 result
= *(uint16_t *)ptr
== *(uint16_t *)ptrDefault
;
1116 result
= *(int16_t *)ptr
== *(int16_t *)ptrDefault
;
1119 /* not currently used
1121 result = *(uint32_t *)ptr == *(uint32_t *)ptrDefault;
1125 result
= *(float *)ptr
== *(float *)ptrDefault
;
1131 typedef struct cliCurrentAndDefaultConfig_s
{
1132 const void *currentConfig
; // the copy
1133 const void *defaultConfig
; // the PG value as set by default
1134 } cliCurrentAndDefaultConfig_t
;
1136 static const cliCurrentAndDefaultConfig_t
*getCurrentAndDefaultConfigs(pgn_t pgn
)
1138 static cliCurrentAndDefaultConfig_t ret
;
1141 case PG_GYRO_CONFIG
:
1142 ret
.currentConfig
= &gyroConfigCopy
;
1143 ret
.defaultConfig
= gyroConfig();
1145 case PG_ACCELEROMETER_CONFIG
:
1146 ret
.currentConfig
= &accelerometerConfigCopy
;
1147 ret
.defaultConfig
= accelerometerConfig();
1150 case PG_COMPASS_CONFIG
:
1151 ret
.currentConfig
= &compassConfigCopy
;
1152 ret
.defaultConfig
= compassConfig();
1156 case PG_BAROMETER_CONFIG
:
1157 ret
.currentConfig
= &barometerConfigCopy
;
1158 ret
.defaultConfig
= barometerConfig();
1162 case PG_PITOTMETER_CONFIG
:
1163 ret
.currentConfig
= &pitotmeterConfigCopy
;
1164 ret
.defaultConfig
= pitotmeterConfig();
1167 case PG_FEATURE_CONFIG
:
1168 ret
.currentConfig
= &featureConfigCopy
;
1169 ret
.defaultConfig
= featureConfig();
1172 ret
.currentConfig
= &rxConfigCopy
;
1173 ret
.defaultConfig
= rxConfig();
1177 ret
.currentConfig
= &pwmConfigCopy
;
1178 ret
.defaultConfig
= pwmConfig();
1182 case PG_BLACKBOX_CONFIG
:
1183 ret
.currentConfig
= &blackboxConfigCopy
;
1184 ret
.defaultConfig
= blackboxConfig();
1187 case PG_MOTOR_CONFIG
:
1188 ret
.currentConfig
= &motorConfigCopy
;
1189 ret
.defaultConfig
= motorConfig();
1191 case PG_THROTTLE_CORRECTION_CONFIG
:
1192 ret
.currentConfig
= &throttleCorrectionConfigCopy
;
1193 ret
.defaultConfig
= throttleCorrectionConfig();
1195 case PG_FAILSAFE_CONFIG
:
1196 ret
.currentConfig
= &failsafeConfigCopy
;
1197 ret
.defaultConfig
= failsafeConfig();
1199 case PG_BOARD_ALIGNMENT
:
1200 ret
.currentConfig
= &boardAlignmentCopy
;
1201 ret
.defaultConfig
= boardAlignment();
1203 case PG_MIXER_CONFIG
:
1204 ret
.currentConfig
= &mixerConfigCopy
;
1205 ret
.defaultConfig
= mixerConfig();
1207 case PG_MOTOR_3D_CONFIG
:
1208 ret
.currentConfig
= &flight3DConfigCopy
;
1209 ret
.defaultConfig
= flight3DConfig();
1212 case PG_SERVO_CONFIG
:
1213 ret
.currentConfig
= &servoConfigCopy
;
1214 ret
.defaultConfig
= servoConfig();
1216 case PG_GIMBAL_CONFIG
:
1217 ret
.currentConfig
= &gimbalConfigCopy
;
1218 ret
.defaultConfig
= gimbalConfig();
1221 case PG_BATTERY_CONFIG
:
1222 ret
.currentConfig
= &batteryConfigCopy
;
1223 ret
.defaultConfig
= batteryConfig();
1225 case PG_VOLTAGE_SENSOR_ADC_CONFIG
:
1226 ret
.currentConfig
= &voltageSensorADCConfigCopy
[0];
1227 ret
.defaultConfig
= voltageSensorADCConfig(0);
1229 case PG_CURRENT_SENSOR_ADC_CONFIG
:
1230 ret
.currentConfig
= ¤tSensorADCConfigCopy
;
1231 ret
.defaultConfig
= currentSensorADCConfig();
1233 case PG_CURRENT_SENSOR_VIRTUAL_CONFIG
:
1234 ret
.currentConfig
= ¤tSensorVirtualConfigCopy
;
1235 ret
.defaultConfig
= currentSensorVirtualConfig();
1237 case PG_SERIAL_CONFIG
:
1238 ret
.currentConfig
= &serialConfigCopy
;
1239 ret
.defaultConfig
= serialConfig();
1242 ret
.currentConfig
= &imuConfigCopy
;
1243 ret
.defaultConfig
= imuConfig();
1245 case PG_RC_CONTROLS_CONFIG
:
1246 ret
.currentConfig
= &rcControlsConfigCopy
;
1247 ret
.defaultConfig
= rcControlsConfig();
1249 case PG_ARMING_CONFIG
:
1250 ret
.currentConfig
= &armingConfigCopy
;
1251 ret
.defaultConfig
= armingConfig();
1255 ret
.currentConfig
= &gpsConfigCopy
;
1256 ret
.defaultConfig
= gpsConfig();
1258 case PG_NAVIGATION_CONFIG
:
1259 ret
.currentConfig
= &navigationConfigCopy
;
1260 ret
.defaultConfig
= navigationConfig();
1263 case PG_AIRPLANE_CONFIG
:
1264 ret
.currentConfig
= &airplaneConfigCopy
;
1265 ret
.defaultConfig
= airplaneConfig();
1268 case PG_TELEMETRY_CONFIG
:
1269 ret
.currentConfig
= &telemetryConfigCopy
;
1270 ret
.defaultConfig
= telemetryConfig();
1274 case PG_LED_STRIP_CONFIG
:
1275 ret
.currentConfig
= &ledStripConfigCopy
;
1276 ret
.defaultConfig
= ledStripConfig();
1280 case PG_SDCARD_CONFIG
:
1281 ret
.currentConfig
= &sdcardConfigCopy
;
1282 ret
.defaultConfig
= sdcardConfig();
1287 ret
.currentConfig
= &osdConfigCopy
;
1288 ret
.defaultConfig
= osdConfig();
1291 case PG_SYSTEM_CONFIG
:
1292 ret
.currentConfig
= &systemConfigCopy
;
1293 ret
.defaultConfig
= systemConfig();
1295 case PG_CONTROL_RATE_PROFILES
:
1296 ret
.currentConfig
= &controlRateProfilesCopy
[0];
1297 ret
.defaultConfig
= controlRateProfiles(0);
1299 case PG_PID_PROFILE
:
1300 ret
.currentConfig
= &pidProfileCopy
[0];
1301 ret
.defaultConfig
= pidProfiles(0);
1303 case PG_RX_FAILSAFE_CHANNEL_CONFIG
:
1304 ret
.currentConfig
= &rxFailsafeChannelConfigsCopy
[0];
1305 ret
.defaultConfig
= rxFailsafeChannelConfigs(0);
1307 case PG_RX_CHANNEL_RANGE_CONFIG
:
1308 ret
.currentConfig
= &rxChannelRangeConfigsCopy
[0];
1309 ret
.defaultConfig
= rxChannelRangeConfigs(0);
1312 case PG_SERVO_MIXER
:
1313 ret
.currentConfig
= &customServoMixersCopy
[0];
1314 ret
.defaultConfig
= customServoMixers(0);
1316 case PG_SERVO_PARAMS
:
1317 ret
.currentConfig
= &servoParamsCopy
[0];
1318 ret
.defaultConfig
= servoParams(0);
1321 case PG_MOTOR_MIXER
:
1322 ret
.currentConfig
= &customMotorMixerCopy
[0];
1323 ret
.defaultConfig
= customMotorMixer(0);
1325 case PG_MODE_ACTIVATION_PROFILE
:
1326 ret
.currentConfig
= &modeActivationConditionsCopy
[0];
1327 ret
.defaultConfig
= modeActivationConditions(0);
1329 case PG_ADJUSTMENT_RANGE_CONFIG
:
1330 ret
.currentConfig
= &adjustmentRangesCopy
[0];
1331 ret
.defaultConfig
= adjustmentRanges(0);
1334 case PG_BEEPER_CONFIG
:
1335 ret
.currentConfig
= &beeperConfigCopy
;
1336 ret
.defaultConfig
= beeperConfig();
1338 case PG_BEEPER_DEV_CONFIG
:
1339 ret
.currentConfig
= &beeperDevConfigCopy
;
1340 ret
.defaultConfig
= beeperDevConfig();
1345 ret
.currentConfig
= &vtxConfigCopy
;
1346 ret
.defaultConfig
= vtxConfig();
1351 ret
.currentConfig
= &vcdProfileCopy
;
1352 ret
.defaultConfig
= vcdProfile();
1355 #ifdef USE_MSP_DISPLAYPORT
1356 case PG_DISPLAY_PORT_MSP_CONFIG
:
1357 ret
.currentConfig
= &displayPortProfileMspCopy
;
1358 ret
.defaultConfig
= displayPortProfileMsp();
1362 case PG_DISPLAY_PORT_MAX7456_CONFIG
:
1363 ret
.currentConfig
= &displayPortProfileMax7456Copy
;
1364 ret
.defaultConfig
= displayPortProfileMax7456();
1368 ret
.currentConfig
= &pidConfigCopy
;
1369 ret
.defaultConfig
= pidConfig();
1372 ret
.currentConfig
= NULL
;
1373 ret
.defaultConfig
= NULL
;
1379 static uint16_t getValueOffset(const clivalue_t
*value
)
1381 switch (value
->type
& VALUE_SECTION_MASK
) {
1383 return value
->offset
;
1385 return value
->offset
+ sizeof(pidProfile_t
) * getCurrentPidProfileIndex();
1386 case PROFILE_RATE_VALUE
:
1387 return value
->offset
+ sizeof(controlRateConfig_t
) * getCurrentControlRateProfileIndex();
1392 static void *getValuePointer(const clivalue_t
*value
)
1394 const pgRegistry_t
* rec
= pgFind(value
->pgn
);
1395 return CONST_CAST(void *, rec
->address
+ getValueOffset(value
));
1398 static void dumpPgValue(const clivalue_t
*value
, uint8_t dumpMask
)
1400 const char *format
= "set %s = ";
1401 const cliCurrentAndDefaultConfig_t
*config
= getCurrentAndDefaultConfigs(value
->pgn
);
1402 if (config
->currentConfig
== NULL
|| config
->defaultConfig
== NULL
) {
1403 // has not been set up properly
1404 cliPrintf("VALUE %s ERROR\r\n", value
->name
);
1407 const int valueOffset
= getValueOffset(value
);
1408 switch (dumpMask
& (DO_DIFF
| SHOW_DEFAULTS
)) {
1410 if (valuePtrEqualsDefault(value
->type
, (uint8_t*)config
->currentConfig
+ valueOffset
, (uint8_t*)config
->defaultConfig
+ valueOffset
)) {
1413 // drop through, since not equal to default
1416 cliPrintf(format
, value
->name
);
1417 printValuePointer(value
, (uint8_t*)config
->currentConfig
+ valueOffset
, 0);
1423 static void dumpAllValues(uint16_t valueSection
, uint8_t dumpMask
)
1425 for (uint32_t i
= 0; i
< ARRAYLEN(valueTable
); i
++) {
1426 const clivalue_t
*value
= &valueTable
[i
];
1427 bufWriterFlush(cliWriter
);
1428 if ((value
->type
& VALUE_SECTION_MASK
) == valueSection
) {
1429 dumpPgValue(value
, dumpMask
);
1436 void *getValuePointer(const clivalue_t
*value
)
1438 uint8_t *ptr
= value
->ptr
;
1440 if ((value
->type
& VALUE_SECTION_MASK
) == PROFILE_VALUE
) {
1441 ptr
+= sizeof(pidProfile_t
) * getCurrentPidProfileIndex();
1442 } else if ((value
->type
& VALUE_SECTION_MASK
) == PROFILE_RATE_VALUE
) {
1443 ptr
+= sizeof(controlRateConfig_t
) * getCurrentControlRateProfileIndex();
1449 static void *getDefaultPointer(void *valuePointer
, const master_t
*defaultConfig
)
1451 return ((uint8_t *)valuePointer
) - (uint32_t)&masterConfig
+ (uint32_t)defaultConfig
;
1454 static bool valueEqualsDefault(const clivalue_t
*value
, const master_t
*defaultConfig
)
1456 void *ptr
= getValuePointer(value
);
1458 void *ptrDefault
= getDefaultPointer(ptr
, defaultConfig
);
1460 bool result
= false;
1461 switch (value
->type
& VALUE_TYPE_MASK
) {
1463 result
= *(uint8_t *)ptr
== *(uint8_t *)ptrDefault
;
1467 result
= *(int8_t *)ptr
== *(int8_t *)ptrDefault
;
1471 result
= *(uint16_t *)ptr
== *(uint16_t *)ptrDefault
;
1475 result
= *(int16_t *)ptr
== *(int16_t *)ptrDefault
;
1478 /* not currently used
1480 result = *(uint32_t *)ptr == *(uint32_t *)ptrDefault;
1484 result
= *(float *)ptr
== *(float *)ptrDefault
;
1490 static void cliPrintVarDefault(const clivalue_t
*var
, uint32_t full
, const master_t
*defaultConfig
)
1492 void *ptr
= getValuePointer(var
);
1494 void *defaultPtr
= getDefaultPointer(ptr
, defaultConfig
);
1496 printValuePointer(var
, defaultPtr
, full
);
1499 static void dumpValues(uint16_t valueSection
, uint8_t dumpMask
, const master_t
*defaultConfig
)
1501 const clivalue_t
*value
;
1502 for (uint32_t i
= 0; i
< ARRAYLEN(valueTable
); i
++) {
1503 value
= &valueTable
[i
];
1505 if ((value
->type
& VALUE_SECTION_MASK
) != valueSection
) {
1509 const char *format
= "set %s = ";
1510 if (cliDefaultPrintf(dumpMask
, valueEqualsDefault(value
, defaultConfig
), format
, valueTable
[i
].name
)) {
1511 cliPrintVarDefault(value
, 0, defaultConfig
);
1514 if (cliDumpPrintf(dumpMask
, valueEqualsDefault(value
, defaultConfig
), format
, valueTable
[i
].name
)) {
1515 cliPrintVar(value
, 0);
1520 #endif // USE_PARAMETER_GROUPS
1522 static void cliPrintVar(const clivalue_t
*var
, uint32_t full
)
1524 const void *ptr
= getValuePointer(var
);
1526 printValuePointer(var
, ptr
, full
);
1529 static void cliPrintVarRange(const clivalue_t
*var
)
1531 switch (var
->type
& VALUE_MODE_MASK
) {
1532 case (MODE_DIRECT
): {
1533 cliPrintf("Allowed range: %d - %d\r\n", var
->config
.minmax
.min
, var
->config
.minmax
.max
);
1536 case (MODE_LOOKUP
): {
1537 const lookupTableEntry_t
*tableEntry
= &lookupTables
[var
->config
.lookup
.tableIndex
];
1538 cliPrint("Allowed values:");
1539 for (uint32_t i
= 0; i
< tableEntry
->valueCount
; i
++) {
1542 cliPrintf(" %s", tableEntry
->values
[i
]);
1553 } int_float_value_t
;
1555 static void cliSetVar(const clivalue_t
*var
, const int_float_value_t value
)
1557 void *ptr
= getValuePointer(var
);
1559 switch (var
->type
& VALUE_TYPE_MASK
) {
1562 *(int8_t *)ptr
= value
.int_value
;
1567 *(int16_t *)ptr
= value
.int_value
;
1570 /* not currently used
1572 *(uint32_t *)ptr = value.int_value;
1576 *(float *)ptr
= (float)value
.float_value
;
1582 static void cliRepeat(char ch
, uint8_t len
)
1584 for (int i
= 0; i
< len
; i
++) {
1585 bufWriterAppend(cliWriter
, ch
);
1591 static void cliPrompt(void)
1596 static void cliShowParseError(void)
1598 cliPrint("Parse error\r\n");
1601 static void cliShowArgumentRangeError(char *name
, int min
, int max
)
1603 cliPrintf("%s not between %d and %d\r\n", name
, min
, max
);
1606 static const char *nextArg(const char *currentArg
)
1608 const char *ptr
= strchr(currentArg
, ' ');
1609 while (ptr
&& *ptr
== ' ') {
1616 static const char *processChannelRangeArgs(const char *ptr
, channelRange_t
*range
, uint8_t *validArgumentCount
)
1618 for (uint32_t argIndex
= 0; argIndex
< 2; argIndex
++) {
1621 int val
= atoi(ptr
);
1622 val
= CHANNEL_VALUE_TO_STEP(val
);
1623 if (val
>= MIN_MODE_RANGE_STEP
&& val
<= MAX_MODE_RANGE_STEP
) {
1624 if (argIndex
== 0) {
1625 range
->startStep
= val
;
1627 range
->endStep
= val
;
1629 (*validArgumentCount
)++;
1637 // Check if a string's length is zero
1638 static bool isEmpty(const char *string
)
1640 return (string
== NULL
|| *string
== '\0') ? true : false;
1643 static void printRxFailsafe(uint8_t dumpMask
, const rxFailsafeChannelConfig_t
*rxFailsafeChannelConfigs
, const rxFailsafeChannelConfig_t
*defaultRxFailsafeChannelConfigs
)
1645 // print out rxConfig failsafe settings
1646 for (uint32_t channel
= 0; channel
< MAX_SUPPORTED_RC_CHANNEL_COUNT
; channel
++) {
1647 const rxFailsafeChannelConfig_t
*channelFailsafeConfig
= &rxFailsafeChannelConfigs
[channel
];
1648 const rxFailsafeChannelConfig_t
*defaultChannelFailsafeConfig
= &defaultRxFailsafeChannelConfigs
[channel
];
1649 const bool equalsDefault
= channelFailsafeConfig
->mode
== defaultChannelFailsafeConfig
->mode
1650 && channelFailsafeConfig
->step
== defaultChannelFailsafeConfig
->step
;
1651 const bool requireValue
= channelFailsafeConfig
->mode
== RX_FAILSAFE_MODE_SET
;
1653 const char *format
= "rxfail %u %c %d\r\n";
1654 cliDefaultPrintf(dumpMask
, equalsDefault
, format
,
1656 rxFailsafeModeCharacters
[defaultChannelFailsafeConfig
->mode
],
1657 RXFAIL_STEP_TO_CHANNEL_VALUE(defaultChannelFailsafeConfig
->step
)
1659 cliDumpPrintf(dumpMask
, equalsDefault
, format
,
1661 rxFailsafeModeCharacters
[channelFailsafeConfig
->mode
],
1662 RXFAIL_STEP_TO_CHANNEL_VALUE(channelFailsafeConfig
->step
)
1665 const char *format
= "rxfail %u %c\r\n";
1666 cliDefaultPrintf(dumpMask
, equalsDefault
, format
,
1668 rxFailsafeModeCharacters
[defaultChannelFailsafeConfig
->mode
]
1670 cliDumpPrintf(dumpMask
, equalsDefault
, format
,
1672 rxFailsafeModeCharacters
[channelFailsafeConfig
->mode
]
1678 static void cliRxFailsafe(char *cmdline
)
1683 if (isEmpty(cmdline
)) {
1684 // print out rxConfig failsafe settings
1685 for (channel
= 0; channel
< MAX_SUPPORTED_RC_CHANNEL_COUNT
; channel
++) {
1686 cliRxFailsafe(itoa(channel
, buf
, 10));
1689 const char *ptr
= cmdline
;
1690 channel
= atoi(ptr
++);
1691 if ((channel
< MAX_SUPPORTED_RC_CHANNEL_COUNT
)) {
1693 rxFailsafeChannelConfig_t
*channelFailsafeConfig
= rxFailsafeChannelConfigsMutable(channel
);
1695 const rxFailsafeChannelType_e type
= (channel
< NON_AUX_CHANNEL_COUNT
) ? RX_FAILSAFE_TYPE_FLIGHT
: RX_FAILSAFE_TYPE_AUX
;
1696 rxFailsafeChannelMode_e mode
= channelFailsafeConfig
->mode
;
1697 bool requireValue
= channelFailsafeConfig
->mode
== RX_FAILSAFE_MODE_SET
;
1701 const char *p
= strchr(rxFailsafeModeCharacters
, *(ptr
));
1703 const uint8_t requestedMode
= p
- rxFailsafeModeCharacters
;
1704 mode
= rxFailsafeModesTable
[type
][requestedMode
];
1706 mode
= RX_FAILSAFE_MODE_INVALID
;
1708 if (mode
== RX_FAILSAFE_MODE_INVALID
) {
1709 cliShowParseError();
1713 requireValue
= mode
== RX_FAILSAFE_MODE_SET
;
1717 if (!requireValue
) {
1718 cliShowParseError();
1721 uint16_t value
= atoi(ptr
);
1722 value
= CHANNEL_VALUE_TO_RXFAIL_STEP(value
);
1723 if (value
> MAX_RXFAIL_RANGE_STEP
) {
1724 cliPrint("Value out of range\r\n");
1728 channelFailsafeConfig
->step
= value
;
1729 } else if (requireValue
) {
1730 cliShowParseError();
1733 channelFailsafeConfig
->mode
= mode
;
1736 char modeCharacter
= rxFailsafeModeCharacters
[channelFailsafeConfig
->mode
];
1738 // double use of cliPrintf below
1739 // 1. acknowledge interpretation on command,
1740 // 2. query current setting on single item,
1743 cliPrintf("rxfail %u %c %d\r\n",
1746 RXFAIL_STEP_TO_CHANNEL_VALUE(channelFailsafeConfig
->step
)
1749 cliPrintf("rxfail %u %c\r\n",
1755 cliShowArgumentRangeError("channel", 0, MAX_SUPPORTED_RC_CHANNEL_COUNT
- 1);
1760 static void printAux(uint8_t dumpMask
, const modeActivationCondition_t
*modeActivationConditions
, const modeActivationCondition_t
*defaultModeActivationConditions
)
1762 const char *format
= "aux %u %u %u %u %u\r\n";
1763 // print out aux channel settings
1764 for (uint32_t i
= 0; i
< MAX_MODE_ACTIVATION_CONDITION_COUNT
; i
++) {
1765 const modeActivationCondition_t
*mac
= &modeActivationConditions
[i
];
1766 bool equalsDefault
= false;
1767 if (defaultModeActivationConditions
) {
1768 const modeActivationCondition_t
*macDefault
= &defaultModeActivationConditions
[i
];
1769 equalsDefault
= mac
->modeId
== macDefault
->modeId
1770 && mac
->auxChannelIndex
== macDefault
->auxChannelIndex
1771 && mac
->range
.startStep
== macDefault
->range
.startStep
1772 && mac
->range
.endStep
== macDefault
->range
.endStep
;
1773 cliDefaultPrintf(dumpMask
, equalsDefault
, format
,
1776 macDefault
->auxChannelIndex
,
1777 MODE_STEP_TO_CHANNEL_VALUE(macDefault
->range
.startStep
),
1778 MODE_STEP_TO_CHANNEL_VALUE(macDefault
->range
.endStep
)
1781 cliDumpPrintf(dumpMask
, equalsDefault
, format
,
1784 mac
->auxChannelIndex
,
1785 MODE_STEP_TO_CHANNEL_VALUE(mac
->range
.startStep
),
1786 MODE_STEP_TO_CHANNEL_VALUE(mac
->range
.endStep
)
1791 static void cliAux(char *cmdline
)
1796 if (isEmpty(cmdline
)) {
1797 printAux(DUMP_MASTER
, modeActivationConditions(0), NULL
);
1801 if (i
< MAX_MODE_ACTIVATION_CONDITION_COUNT
) {
1802 modeActivationCondition_t
*mac
= modeActivationConditionsMutable(i
);
1803 uint8_t validArgumentCount
= 0;
1807 if (val
>= 0 && val
< CHECKBOX_ITEM_COUNT
) {
1809 validArgumentCount
++;
1815 if (val
>= 0 && val
< MAX_AUX_CHANNEL_COUNT
) {
1816 mac
->auxChannelIndex
= val
;
1817 validArgumentCount
++;
1820 ptr
= processChannelRangeArgs(ptr
, &mac
->range
, &validArgumentCount
);
1822 if (validArgumentCount
!= 4) {
1823 memset(mac
, 0, sizeof(modeActivationCondition_t
));
1826 cliShowArgumentRangeError("index", 0, MAX_MODE_ACTIVATION_CONDITION_COUNT
- 1);
1831 static void printSerial(uint8_t dumpMask
, const serialConfig_t
*serialConfig
, const serialConfig_t
*serialConfigDefault
)
1833 const char *format
= "serial %d %d %ld %ld %ld %ld\r\n";
1834 for (uint32_t i
= 0; i
< SERIAL_PORT_COUNT
; i
++) {
1835 if (!serialIsPortAvailable(serialConfig
->portConfigs
[i
].identifier
)) {
1838 bool equalsDefault
= false;
1839 if (serialConfigDefault
) {
1840 equalsDefault
= serialConfig
->portConfigs
[i
].identifier
== serialConfigDefault
->portConfigs
[i
].identifier
1841 && serialConfig
->portConfigs
[i
].functionMask
== serialConfigDefault
->portConfigs
[i
].functionMask
1842 && serialConfig
->portConfigs
[i
].msp_baudrateIndex
== serialConfigDefault
->portConfigs
[i
].msp_baudrateIndex
1843 && serialConfig
->portConfigs
[i
].gps_baudrateIndex
== serialConfigDefault
->portConfigs
[i
].gps_baudrateIndex
1844 && serialConfig
->portConfigs
[i
].telemetry_baudrateIndex
== serialConfigDefault
->portConfigs
[i
].telemetry_baudrateIndex
1845 && serialConfig
->portConfigs
[i
].blackbox_baudrateIndex
== serialConfigDefault
->portConfigs
[i
].blackbox_baudrateIndex
;
1846 cliDefaultPrintf(dumpMask
, equalsDefault
, format
,
1847 serialConfigDefault
->portConfigs
[i
].identifier
,
1848 serialConfigDefault
->portConfigs
[i
].functionMask
,
1849 baudRates
[serialConfigDefault
->portConfigs
[i
].msp_baudrateIndex
],
1850 baudRates
[serialConfigDefault
->portConfigs
[i
].gps_baudrateIndex
],
1851 baudRates
[serialConfigDefault
->portConfigs
[i
].telemetry_baudrateIndex
],
1852 baudRates
[serialConfigDefault
->portConfigs
[i
].blackbox_baudrateIndex
]
1855 cliDumpPrintf(dumpMask
, equalsDefault
, format
,
1856 serialConfig
->portConfigs
[i
].identifier
,
1857 serialConfig
->portConfigs
[i
].functionMask
,
1858 baudRates
[serialConfig
->portConfigs
[i
].msp_baudrateIndex
],
1859 baudRates
[serialConfig
->portConfigs
[i
].gps_baudrateIndex
],
1860 baudRates
[serialConfig
->portConfigs
[i
].telemetry_baudrateIndex
],
1861 baudRates
[serialConfig
->portConfigs
[i
].blackbox_baudrateIndex
]
1866 static void cliSerial(char *cmdline
)
1868 if (isEmpty(cmdline
)) {
1869 printSerial(DUMP_MASTER
, serialConfig(), NULL
);
1872 serialPortConfig_t portConfig
;
1873 memset(&portConfig
, 0 , sizeof(portConfig
));
1875 serialPortConfig_t
*currentConfig
;
1877 uint8_t validArgumentCount
= 0;
1879 const char *ptr
= cmdline
;
1881 int val
= atoi(ptr
++);
1882 currentConfig
= serialFindPortConfiguration(val
);
1883 if (currentConfig
) {
1884 portConfig
.identifier
= val
;
1885 validArgumentCount
++;
1891 portConfig
.functionMask
= val
& 0xFFFF;
1892 validArgumentCount
++;
1895 for (int i
= 0; i
< 4; i
++) {
1903 uint8_t baudRateIndex
= lookupBaudRateIndex(val
);
1904 if (baudRates
[baudRateIndex
] != (uint32_t) val
) {
1910 if (baudRateIndex
< BAUD_9600
|| baudRateIndex
> BAUD_1000000
) {
1913 portConfig
.msp_baudrateIndex
= baudRateIndex
;
1916 if (baudRateIndex
< BAUD_9600
|| baudRateIndex
> BAUD_115200
) {
1919 portConfig
.gps_baudrateIndex
= baudRateIndex
;
1922 if (baudRateIndex
!= BAUD_AUTO
&& baudRateIndex
> BAUD_115200
) {
1925 portConfig
.telemetry_baudrateIndex
= baudRateIndex
;
1928 if (baudRateIndex
< BAUD_19200
|| baudRateIndex
> BAUD_250000
) {
1931 portConfig
.blackbox_baudrateIndex
= baudRateIndex
;
1935 validArgumentCount
++;
1938 if (validArgumentCount
< 6) {
1939 cliShowParseError();
1943 memcpy(currentConfig
, &portConfig
, sizeof(portConfig
));
1946 #ifndef SKIP_SERIAL_PASSTHROUGH
1947 static void cliSerialPassthrough(char *cmdline
)
1949 if (isEmpty(cmdline
)) {
1950 cliShowParseError();
1958 char* tok
= strtok_r(cmdline
, " ", &saveptr
);
1961 while (tok
!= NULL
) {
1970 if (strstr(tok
, "rx") || strstr(tok
, "RX"))
1972 if (strstr(tok
, "tx") || strstr(tok
, "TX"))
1977 tok
= strtok_r(NULL
, " ", &saveptr
);
1980 serialPort_t
*passThroughPort
;
1981 serialPortUsage_t
*passThroughPortUsage
= findSerialPortUsageByIdentifier(id
);
1982 if (!passThroughPortUsage
|| passThroughPortUsage
->serialPort
== NULL
) {
1984 printf("Port %d is closed, must specify baud.\r\n", id
);
1990 passThroughPort
= openSerialPort(id
, FUNCTION_NONE
, NULL
,
1992 SERIAL_NOT_INVERTED
);
1993 if (!passThroughPort
) {
1994 printf("Port %d could not be opened.\r\n", id
);
1997 printf("Port %d opened, baud = %d.\r\n", id
, baud
);
1999 passThroughPort
= passThroughPortUsage
->serialPort
;
2000 // If the user supplied a mode, override the port's mode, otherwise
2001 // leave the mode unchanged. serialPassthrough() handles one-way ports.
2002 printf("Port %d already open.\r\n", id
);
2003 if (mode
&& passThroughPort
->mode
!= mode
) {
2004 printf("Adjusting mode from %d to %d.\r\n",
2005 passThroughPort
->mode
, mode
);
2006 serialSetMode(passThroughPort
, mode
);
2008 // If this port has a rx callback associated we need to remove it now.
2009 // Otherwise no data will be pushed in the serial port buffer!
2010 if (passThroughPort
->rxCallback
) {
2011 printf("Removing rxCallback\r\n");
2012 passThroughPort
->rxCallback
= 0;
2016 printf("Forwarding data to %d, power cycle to exit.\r\n", id
);
2018 serialPassthrough(cliPort
, passThroughPort
, NULL
, NULL
);
2022 static void printAdjustmentRange(uint8_t dumpMask
, const adjustmentRange_t
*adjustmentRanges
, const adjustmentRange_t
*defaultAdjustmentRanges
)
2024 const char *format
= "adjrange %u %u %u %u %u %u %u\r\n";
2025 // print out adjustment ranges channel settings
2026 for (uint32_t i
= 0; i
< MAX_ADJUSTMENT_RANGE_COUNT
; i
++) {
2027 const adjustmentRange_t
*ar
= &adjustmentRanges
[i
];
2028 bool equalsDefault
= false;
2029 if (defaultAdjustmentRanges
) {
2030 const adjustmentRange_t
*arDefault
= &defaultAdjustmentRanges
[i
];
2031 equalsDefault
= ar
->auxChannelIndex
== arDefault
->auxChannelIndex
2032 && ar
->range
.startStep
== arDefault
->range
.startStep
2033 && ar
->range
.endStep
== arDefault
->range
.endStep
2034 && ar
->adjustmentFunction
== arDefault
->adjustmentFunction
2035 && ar
->auxSwitchChannelIndex
== arDefault
->auxSwitchChannelIndex
2036 && ar
->adjustmentIndex
== arDefault
->adjustmentIndex
;
2037 cliDefaultPrintf(dumpMask
, equalsDefault
, format
,
2039 arDefault
->adjustmentIndex
,
2040 arDefault
->auxChannelIndex
,
2041 MODE_STEP_TO_CHANNEL_VALUE(arDefault
->range
.startStep
),
2042 MODE_STEP_TO_CHANNEL_VALUE(arDefault
->range
.endStep
),
2043 arDefault
->adjustmentFunction
,
2044 arDefault
->auxSwitchChannelIndex
2047 cliDumpPrintf(dumpMask
, equalsDefault
, format
,
2049 ar
->adjustmentIndex
,
2050 ar
->auxChannelIndex
,
2051 MODE_STEP_TO_CHANNEL_VALUE(ar
->range
.startStep
),
2052 MODE_STEP_TO_CHANNEL_VALUE(ar
->range
.endStep
),
2053 ar
->adjustmentFunction
,
2054 ar
->auxSwitchChannelIndex
2059 static void cliAdjustmentRange(char *cmdline
)
2064 if (isEmpty(cmdline
)) {
2065 printAdjustmentRange(DUMP_MASTER
, adjustmentRanges(0), NULL
);
2069 if (i
< MAX_ADJUSTMENT_RANGE_COUNT
) {
2070 adjustmentRange_t
*ar
= adjustmentRangesMutable(i
);
2071 uint8_t validArgumentCount
= 0;
2076 if (val
>= 0 && val
< MAX_SIMULTANEOUS_ADJUSTMENT_COUNT
) {
2077 ar
->adjustmentIndex
= val
;
2078 validArgumentCount
++;
2084 if (val
>= 0 && val
< MAX_AUX_CHANNEL_COUNT
) {
2085 ar
->auxChannelIndex
= val
;
2086 validArgumentCount
++;
2090 ptr
= processChannelRangeArgs(ptr
, &ar
->range
, &validArgumentCount
);
2095 if (val
>= 0 && val
< ADJUSTMENT_FUNCTION_COUNT
) {
2096 ar
->adjustmentFunction
= val
;
2097 validArgumentCount
++;
2103 if (val
>= 0 && val
< MAX_AUX_CHANNEL_COUNT
) {
2104 ar
->auxSwitchChannelIndex
= val
;
2105 validArgumentCount
++;
2109 if (validArgumentCount
!= 6) {
2110 memset(ar
, 0, sizeof(adjustmentRange_t
));
2111 cliShowParseError();
2114 cliShowArgumentRangeError("index", 0, MAX_ADJUSTMENT_RANGE_COUNT
- 1);
2119 #ifndef USE_QUAD_MIXER_ONLY
2120 static void printMotorMix(uint8_t dumpMask
, const motorMixer_t
*customMotorMixer
, const motorMixer_t
*defaultCustomMotorMixer
)
2122 const char *format
= "mmix %d %s %s %s %s\r\n";
2127 for (uint32_t i
= 0; i
< MAX_SUPPORTED_MOTORS
; i
++) {
2128 if (customMotorMixer
[i
].throttle
== 0.0f
)
2130 const float thr
= customMotorMixer
[i
].throttle
;
2131 const float roll
= customMotorMixer
[i
].roll
;
2132 const float pitch
= customMotorMixer
[i
].pitch
;
2133 const float yaw
= customMotorMixer
[i
].yaw
;
2134 bool equalsDefault
= false;
2135 if (defaultCustomMotorMixer
) {
2136 const float thrDefault
= defaultCustomMotorMixer
[i
].throttle
;
2137 const float rollDefault
= defaultCustomMotorMixer
[i
].roll
;
2138 const float pitchDefault
= defaultCustomMotorMixer
[i
].pitch
;
2139 const float yawDefault
= defaultCustomMotorMixer
[i
].yaw
;
2140 const bool equalsDefault
= thr
== thrDefault
&& roll
== rollDefault
&& pitch
== pitchDefault
&& yaw
== yawDefault
;
2142 cliDefaultPrintf(dumpMask
, equalsDefault
, format
,
2144 ftoa(thrDefault
, buf0
),
2145 ftoa(rollDefault
, buf1
),
2146 ftoa(pitchDefault
, buf2
),
2147 ftoa(yawDefault
, buf3
));
2149 cliDumpPrintf(dumpMask
, equalsDefault
, format
,
2157 #endif // USE_QUAD_MIXER_ONLY
2159 static void cliMotorMix(char *cmdline
)
2161 #ifdef USE_QUAD_MIXER_ONLY
2168 if (isEmpty(cmdline
)) {
2169 printMotorMix(DUMP_MASTER
, customMotorMixer(0), NULL
);
2170 } else if (strncasecmp(cmdline
, "reset", 5) == 0) {
2171 // erase custom mixer
2172 for (uint32_t i
= 0; i
< MAX_SUPPORTED_MOTORS
; i
++) {
2173 customMotorMixerMutable(i
)->throttle
= 0.0f
;
2175 } else if (strncasecmp(cmdline
, "load", 4) == 0) {
2176 ptr
= nextArg(cmdline
);
2179 for (uint32_t i
= 0; ; i
++) {
2180 if (mixerNames
[i
] == NULL
) {
2181 cliPrint("Invalid name\r\n");
2184 if (strncasecmp(ptr
, mixerNames
[i
], len
) == 0) {
2185 mixerLoadMix(i
, customMotorMixerMutable(0));
2186 cliPrintf("Loaded %s\r\n", mixerNames
[i
]);
2194 uint32_t i
= atoi(ptr
); // get motor number
2195 if (i
< MAX_SUPPORTED_MOTORS
) {
2198 customMotorMixerMutable(i
)->throttle
= fastA2F(ptr
);
2203 customMotorMixerMutable(i
)->roll
= fastA2F(ptr
);
2208 customMotorMixerMutable(i
)->pitch
= fastA2F(ptr
);
2213 customMotorMixerMutable(i
)->yaw
= fastA2F(ptr
);
2217 cliShowParseError();
2219 printMotorMix(DUMP_MASTER
, customMotorMixer(0), NULL
);
2222 cliShowArgumentRangeError("index", 0, MAX_SUPPORTED_MOTORS
- 1);
2228 static void printRxRange(uint8_t dumpMask
, const rxChannelRangeConfig_t
*channelRangeConfigs
, const rxChannelRangeConfig_t
*defaultChannelRangeConfigs
)
2230 const char *format
= "rxrange %u %u %u\r\n";
2231 for (uint32_t i
= 0; i
< NON_AUX_CHANNEL_COUNT
; i
++) {
2232 bool equalsDefault
= false;
2233 if (defaultChannelRangeConfigs
) {
2234 equalsDefault
= channelRangeConfigs
[i
].min
== defaultChannelRangeConfigs
[i
].min
2235 && channelRangeConfigs
[i
].max
== defaultChannelRangeConfigs
[i
].max
;
2236 cliDefaultPrintf(dumpMask
, equalsDefault
, format
,
2238 defaultChannelRangeConfigs
[i
].min
,
2239 defaultChannelRangeConfigs
[i
].max
2242 cliDumpPrintf(dumpMask
, equalsDefault
, format
,
2244 channelRangeConfigs
[i
].min
,
2245 channelRangeConfigs
[i
].max
2250 static void cliRxRange(char *cmdline
)
2252 int i
, validArgumentCount
= 0;
2255 if (isEmpty(cmdline
)) {
2256 printRxRange(DUMP_MASTER
, rxChannelRangeConfigs(0), NULL
);
2257 } else if (strcasecmp(cmdline
, "reset") == 0) {
2258 resetAllRxChannelRangeConfigurations(rxChannelRangeConfigsMutable(0));
2262 if (i
>= 0 && i
< NON_AUX_CHANNEL_COUNT
) {
2263 int rangeMin
, rangeMax
;
2267 rangeMin
= atoi(ptr
);
2268 validArgumentCount
++;
2273 rangeMax
= atoi(ptr
);
2274 validArgumentCount
++;
2277 if (validArgumentCount
!= 2) {
2278 cliShowParseError();
2279 } else if (rangeMin
< PWM_PULSE_MIN
|| rangeMin
> PWM_PULSE_MAX
|| rangeMax
< PWM_PULSE_MIN
|| rangeMax
> PWM_PULSE_MAX
) {
2280 cliShowParseError();
2282 rxChannelRangeConfig_t
*channelRangeConfig
= rxChannelRangeConfigsMutable(i
);
2283 channelRangeConfig
->min
= rangeMin
;
2284 channelRangeConfig
->max
= rangeMax
;
2287 cliShowArgumentRangeError("channel", 0, NON_AUX_CHANNEL_COUNT
- 1);
2293 static void printLed(uint8_t dumpMask
, const ledConfig_t
*ledConfigs
, const ledConfig_t
*defaultLedConfigs
)
2295 const char *format
= "led %u %s\r\n";
2296 char ledConfigBuffer
[20];
2297 char ledConfigDefaultBuffer
[20];
2298 for (uint32_t i
= 0; i
< LED_MAX_STRIP_LENGTH
; i
++) {
2299 ledConfig_t ledConfig
= ledConfigs
[i
];
2300 generateLedConfig(&ledConfig
, ledConfigBuffer
, sizeof(ledConfigBuffer
));
2301 bool equalsDefault
= false;
2302 if (defaultLedConfigs
) {
2303 ledConfig_t ledConfigDefault
= defaultLedConfigs
[i
];
2304 equalsDefault
= ledConfig
== ledConfigDefault
;
2305 generateLedConfig(&ledConfigDefault
, ledConfigDefaultBuffer
, sizeof(ledConfigDefaultBuffer
));
2306 cliDefaultPrintf(dumpMask
, equalsDefault
, format
, i
, ledConfigDefaultBuffer
);
2308 cliDumpPrintf(dumpMask
, equalsDefault
, format
, i
, ledConfigBuffer
);
2312 static void cliLed(char *cmdline
)
2317 if (isEmpty(cmdline
)) {
2318 printLed(DUMP_MASTER
, ledStripConfig()->ledConfigs
, NULL
);
2322 if (i
< LED_MAX_STRIP_LENGTH
) {
2323 ptr
= nextArg(cmdline
);
2324 if (!parseLedStripConfig(i
, ptr
)) {
2325 cliShowParseError();
2328 cliShowArgumentRangeError("index", 0, LED_MAX_STRIP_LENGTH
- 1);
2333 static void printColor(uint8_t dumpMask
, const hsvColor_t
*colors
, const hsvColor_t
*defaultColors
)
2335 const char *format
= "color %u %d,%u,%u\r\n";
2336 for (uint32_t i
= 0; i
< LED_CONFIGURABLE_COLOR_COUNT
; i
++) {
2337 const hsvColor_t
*color
= &colors
[i
];
2338 bool equalsDefault
= false;
2339 if (defaultColors
) {
2340 const hsvColor_t
*colorDefault
= &defaultColors
[i
];
2341 equalsDefault
= color
->h
== colorDefault
->h
2342 && color
->s
== colorDefault
->s
2343 && color
->v
== colorDefault
->v
;
2344 cliDefaultPrintf(dumpMask
, equalsDefault
, format
, i
,colorDefault
->h
, colorDefault
->s
, colorDefault
->v
);
2346 cliDumpPrintf(dumpMask
, equalsDefault
, format
, i
, color
->h
, color
->s
, color
->v
);
2350 static void cliColor(char *cmdline
)
2352 if (isEmpty(cmdline
)) {
2353 printColor(DUMP_MASTER
, ledStripConfig()->colors
, NULL
);
2355 const char *ptr
= cmdline
;
2356 const int i
= atoi(ptr
);
2357 if (i
< LED_CONFIGURABLE_COLOR_COUNT
) {
2358 ptr
= nextArg(cmdline
);
2359 if (!parseColor(i
, ptr
)) {
2360 cliShowParseError();
2363 cliShowArgumentRangeError("index", 0, LED_CONFIGURABLE_COLOR_COUNT
- 1);
2368 static void printModeColor(uint8_t dumpMask
, const ledStripConfig_t
*ledStripConfig
, const ledStripConfig_t
*defaultLedStripConfig
)
2370 const char *format
= "mode_color %u %u %u\r\n";
2371 for (uint32_t i
= 0; i
< LED_MODE_COUNT
; i
++) {
2372 for (uint32_t j
= 0; j
< LED_DIRECTION_COUNT
; j
++) {
2373 int colorIndex
= ledStripConfig
->modeColors
[i
].color
[j
];
2374 bool equalsDefault
= false;
2375 if (defaultLedStripConfig
) {
2376 int colorIndexDefault
= defaultLedStripConfig
->modeColors
[i
].color
[j
];
2377 equalsDefault
= colorIndex
== colorIndexDefault
;
2378 cliDefaultPrintf(dumpMask
, equalsDefault
, format
, i
, j
, colorIndexDefault
);
2380 cliDumpPrintf(dumpMask
, equalsDefault
, format
, i
, j
, colorIndex
);
2384 for (uint32_t j
= 0; j
< LED_SPECIAL_COLOR_COUNT
; j
++) {
2385 const int colorIndex
= ledStripConfig
->specialColors
.color
[j
];
2386 bool equalsDefault
= false;
2387 if (defaultLedStripConfig
) {
2388 const int colorIndexDefault
= defaultLedStripConfig
->specialColors
.color
[j
];
2389 equalsDefault
= colorIndex
== colorIndexDefault
;
2390 cliDefaultPrintf(dumpMask
, equalsDefault
, format
, LED_SPECIAL
, j
, colorIndexDefault
);
2392 cliDumpPrintf(dumpMask
, equalsDefault
, format
, LED_SPECIAL
, j
, colorIndex
);
2395 const int ledStripAuxChannel
= ledStripConfig
->ledstrip_aux_channel
;
2396 bool equalsDefault
= false;
2397 if (defaultLedStripConfig
) {
2398 const int ledStripAuxChannelDefault
= defaultLedStripConfig
->ledstrip_aux_channel
;
2399 equalsDefault
= ledStripAuxChannel
== ledStripAuxChannelDefault
;
2400 cliDefaultPrintf(dumpMask
, equalsDefault
, format
, LED_AUX_CHANNEL
, 0, ledStripAuxChannelDefault
);
2402 cliDumpPrintf(dumpMask
, equalsDefault
, format
, LED_AUX_CHANNEL
, 0, ledStripAuxChannel
);
2405 static void cliModeColor(char *cmdline
)
2407 if (isEmpty(cmdline
)) {
2408 printModeColor(DUMP_MASTER
, ledStripConfig(), NULL
);
2410 enum {MODE
= 0, FUNCTION
, COLOR
, ARGS_COUNT
};
2411 int args
[ARGS_COUNT
];
2414 const char* ptr
= strtok_r(cmdline
, " ", &saveptr
);
2415 while (ptr
&& argNo
< ARGS_COUNT
) {
2416 args
[argNo
++] = atoi(ptr
);
2417 ptr
= strtok_r(NULL
, " ", &saveptr
);
2420 if (ptr
!= NULL
|| argNo
!= ARGS_COUNT
) {
2421 cliShowParseError();
2425 int modeIdx
= args
[MODE
];
2426 int funIdx
= args
[FUNCTION
];
2427 int color
= args
[COLOR
];
2428 if(!setModeColor(modeIdx
, funIdx
, color
)) {
2429 cliShowParseError();
2432 // values are validated
2433 cliPrintf("mode_color %u %u %u\r\n", modeIdx
, funIdx
, color
);
2439 static void printServo(uint8_t dumpMask
, const servoParam_t
*servoParams
, const servoParam_t
*defaultServoParams
)
2441 // print out servo settings
2442 const char *format
= "servo %u %d %d %d %d %d %d %d\r\n";
2443 for (uint32_t i
= 0; i
< MAX_SUPPORTED_SERVOS
; i
++) {
2444 const servoParam_t
*servoConf
= &servoParams
[i
];
2445 bool equalsDefault
= false;
2446 if (defaultServoParams
) {
2447 const servoParam_t
*defaultServoConf
= &defaultServoParams
[i
];
2448 equalsDefault
= servoConf
->min
== defaultServoConf
->min
2449 && servoConf
->max
== defaultServoConf
->max
2450 && servoConf
->middle
== defaultServoConf
->middle
2451 && servoConf
->angleAtMin
== defaultServoConf
->angleAtMax
2452 && servoConf
->rate
== defaultServoConf
->rate
2453 && servoConf
->forwardFromChannel
== defaultServoConf
->forwardFromChannel
;
2454 cliDefaultPrintf(dumpMask
, equalsDefault
, format
,
2456 defaultServoConf
->min
,
2457 defaultServoConf
->max
,
2458 defaultServoConf
->middle
,
2459 defaultServoConf
->angleAtMin
,
2460 defaultServoConf
->angleAtMax
,
2461 defaultServoConf
->rate
,
2462 defaultServoConf
->forwardFromChannel
2465 cliDumpPrintf(dumpMask
, equalsDefault
, format
,
2470 servoConf
->angleAtMin
,
2471 servoConf
->angleAtMax
,
2473 servoConf
->forwardFromChannel
2476 // print servo directions
2477 for (uint32_t i
= 0; i
< MAX_SUPPORTED_SERVOS
; i
++) {
2478 const char *format
= "smix reverse %d %d r\r\n";
2479 const servoParam_t
*servoConf
= &servoParams
[i
];
2480 const servoParam_t
*servoConfDefault
= &defaultServoParams
[i
];
2481 if (defaultServoParams
) {
2482 bool equalsDefault
= servoConf
->reversedSources
== servoConfDefault
->reversedSources
;
2483 for (uint32_t channel
= 0; channel
< INPUT_SOURCE_COUNT
; channel
++) {
2484 equalsDefault
= ~(servoConf
->reversedSources
^ servoConfDefault
->reversedSources
) & (1 << channel
);
2485 if (servoConfDefault
->reversedSources
& (1 << channel
)) {
2486 cliDefaultPrintf(dumpMask
, equalsDefault
, format
, i
, channel
);
2488 if (servoConf
->reversedSources
& (1 << channel
)) {
2489 cliDumpPrintf(dumpMask
, equalsDefault
, format
, i
, channel
);
2493 for (uint32_t channel
= 0; channel
< INPUT_SOURCE_COUNT
; channel
++) {
2494 if (servoConf
->reversedSources
& (1 << channel
)) {
2495 cliDumpPrintf(dumpMask
, true, format
, i
, channel
);
2502 static void cliServo(char *cmdline
)
2504 enum { SERVO_ARGUMENT_COUNT
= 8 };
2505 int16_t arguments
[SERVO_ARGUMENT_COUNT
];
2507 servoParam_t
*servo
;
2512 if (isEmpty(cmdline
)) {
2513 printServo(DUMP_MASTER
, servoParams(0), NULL
);
2515 int validArgumentCount
= 0;
2519 // Command line is integers (possibly negative) separated by spaces, no other characters allowed.
2521 // If command line doesn't fit the format, don't modify the config
2523 if (*ptr
== '-' || (*ptr
>= '0' && *ptr
<= '9')) {
2524 if (validArgumentCount
>= SERVO_ARGUMENT_COUNT
) {
2525 cliShowParseError();
2529 arguments
[validArgumentCount
++] = atoi(ptr
);
2533 } while (*ptr
>= '0' && *ptr
<= '9');
2534 } else if (*ptr
== ' ') {
2537 cliShowParseError();
2542 enum {INDEX
= 0, MIN
, MAX
, MIDDLE
, ANGLE_AT_MIN
, ANGLE_AT_MAX
, RATE
, FORWARD
};
2544 i
= arguments
[INDEX
];
2546 // Check we got the right number of args and the servo index is correct (don't validate the other values)
2547 if (validArgumentCount
!= SERVO_ARGUMENT_COUNT
|| i
< 0 || i
>= MAX_SUPPORTED_SERVOS
) {
2548 cliShowParseError();
2552 servo
= servoParamsMutable(i
);
2555 arguments
[MIN
] < PWM_PULSE_MIN
|| arguments
[MIN
] > PWM_PULSE_MAX
||
2556 arguments
[MAX
] < PWM_PULSE_MIN
|| arguments
[MAX
] > PWM_PULSE_MAX
||
2557 arguments
[MIDDLE
] < arguments
[MIN
] || arguments
[MIDDLE
] > arguments
[MAX
] ||
2558 arguments
[MIN
] > arguments
[MAX
] || arguments
[MAX
] < arguments
[MIN
] ||
2559 arguments
[RATE
] < -100 || arguments
[RATE
] > 100 ||
2560 arguments
[FORWARD
] >= MAX_SUPPORTED_RC_CHANNEL_COUNT
||
2561 arguments
[ANGLE_AT_MIN
] < 0 || arguments
[ANGLE_AT_MIN
] > 180 ||
2562 arguments
[ANGLE_AT_MAX
] < 0 || arguments
[ANGLE_AT_MAX
] > 180
2564 cliShowParseError();
2568 servo
->min
= arguments
[1];
2569 servo
->max
= arguments
[2];
2570 servo
->middle
= arguments
[3];
2571 servo
->angleAtMin
= arguments
[4];
2572 servo
->angleAtMax
= arguments
[5];
2573 servo
->rate
= arguments
[6];
2574 servo
->forwardFromChannel
= arguments
[7];
2580 static void printServoMix(uint8_t dumpMask
, const servoMixer_t
*customServoMixers
, const servoMixer_t
*defaultCustomServoMixers
)
2582 const char *format
= "smix %d %d %d %d %d %d %d %d\r\n";
2583 for (uint32_t i
= 0; i
< MAX_SERVO_RULES
; i
++) {
2584 const servoMixer_t customServoMixer
= customServoMixers
[i
];
2585 if (customServoMixer
.rate
== 0) {
2589 bool equalsDefault
= false;
2590 if (defaultCustomServoMixers
) {
2591 servoMixer_t customServoMixerDefault
= defaultCustomServoMixers
[i
];
2592 equalsDefault
= customServoMixer
.targetChannel
== customServoMixerDefault
.targetChannel
2593 && customServoMixer
.inputSource
== customServoMixerDefault
.inputSource
2594 && customServoMixer
.rate
== customServoMixerDefault
.rate
2595 && customServoMixer
.speed
== customServoMixerDefault
.speed
2596 && customServoMixer
.min
== customServoMixerDefault
.min
2597 && customServoMixer
.max
== customServoMixerDefault
.max
2598 && customServoMixer
.box
== customServoMixerDefault
.box
;
2600 cliDefaultPrintf(dumpMask
, equalsDefault
, format
,
2602 customServoMixerDefault
.targetChannel
,
2603 customServoMixerDefault
.inputSource
,
2604 customServoMixerDefault
.rate
,
2605 customServoMixerDefault
.speed
,
2606 customServoMixerDefault
.min
,
2607 customServoMixerDefault
.max
,
2608 customServoMixerDefault
.box
2611 cliDumpPrintf(dumpMask
, equalsDefault
, format
,
2613 customServoMixer
.targetChannel
,
2614 customServoMixer
.inputSource
,
2615 customServoMixer
.rate
,
2616 customServoMixer
.speed
,
2617 customServoMixer
.min
,
2618 customServoMixer
.max
,
2619 customServoMixer
.box
2626 static void cliServoMix(char *cmdline
)
2628 int args
[8], check
= 0;
2629 int len
= strlen(cmdline
);
2632 printServoMix(DUMP_MASTER
, customServoMixers(0), NULL
);
2633 } else if (strncasecmp(cmdline
, "reset", 5) == 0) {
2634 // erase custom mixer
2635 #ifdef USE_PARAMETER_GROUPS
2636 memset(customServoMixers_array(), 0, sizeof(*customServoMixers_array()));
2638 memset(masterConfig
.customServoMixer
, 0, sizeof(masterConfig
.customServoMixer
));
2640 for (uint32_t i
= 0; i
< MAX_SUPPORTED_SERVOS
; i
++) {
2641 servoParamsMutable(i
)->reversedSources
= 0;
2643 } else if (strncasecmp(cmdline
, "load", 4) == 0) {
2644 const char *ptr
= nextArg(cmdline
);
2647 for (uint32_t i
= 0; ; i
++) {
2648 if (mixerNames
[i
] == NULL
) {
2649 cliPrintf("Invalid name\r\n");
2652 if (strncasecmp(ptr
, mixerNames
[i
], len
) == 0) {
2653 servoMixerLoadMix(i
);
2654 cliPrintf("Loaded %s\r\n", mixerNames
[i
]);
2660 } else if (strncasecmp(cmdline
, "reverse", 7) == 0) {
2661 enum {SERVO
= 0, INPUT
, REVERSE
, ARGS_COUNT
};
2662 char *ptr
= strchr(cmdline
, ' ');
2667 for (uint32_t inputSource
= 0; inputSource
< INPUT_SOURCE_COUNT
; inputSource
++)
2668 cliPrintf("\ti%d", inputSource
);
2671 for (uint32_t servoIndex
= 0; servoIndex
< MAX_SUPPORTED_SERVOS
; servoIndex
++) {
2672 cliPrintf("%d", servoIndex
);
2673 for (uint32_t inputSource
= 0; inputSource
< INPUT_SOURCE_COUNT
; inputSource
++)
2674 cliPrintf("\t%s ", (servoParams(servoIndex
)->reversedSources
& (1 << inputSource
)) ? "r" : "n");
2681 ptr
= strtok_r(ptr
, " ", &saveptr
);
2682 while (ptr
!= NULL
&& check
< ARGS_COUNT
- 1) {
2683 args
[check
++] = atoi(ptr
);
2684 ptr
= strtok_r(NULL
, " ", &saveptr
);
2687 if (ptr
== NULL
|| check
!= ARGS_COUNT
- 1) {
2688 cliShowParseError();
2692 if (args
[SERVO
] >= 0 && args
[SERVO
] < MAX_SUPPORTED_SERVOS
2693 && args
[INPUT
] >= 0 && args
[INPUT
] < INPUT_SOURCE_COUNT
2694 && (*ptr
== 'r' || *ptr
== 'n')) {
2696 servoParamsMutable(args
[SERVO
])->reversedSources
|= 1 << args
[INPUT
];
2698 servoParamsMutable(args
[SERVO
])->reversedSources
&= ~(1 << args
[INPUT
]);
2700 cliShowParseError();
2702 cliServoMix("reverse");
2704 enum {RULE
= 0, TARGET
, INPUT
, RATE
, SPEED
, MIN
, MAX
, BOX
, ARGS_COUNT
};
2706 char *ptr
= strtok_r(cmdline
, " ", &saveptr
);
2707 while (ptr
!= NULL
&& check
< ARGS_COUNT
) {
2708 args
[check
++] = atoi(ptr
);
2709 ptr
= strtok_r(NULL
, " ", &saveptr
);
2712 if (ptr
!= NULL
|| check
!= ARGS_COUNT
) {
2713 cliShowParseError();
2717 int32_t i
= args
[RULE
];
2718 if (i
>= 0 && i
< MAX_SERVO_RULES
&&
2719 args
[TARGET
] >= 0 && args
[TARGET
] < MAX_SUPPORTED_SERVOS
&&
2720 args
[INPUT
] >= 0 && args
[INPUT
] < INPUT_SOURCE_COUNT
&&
2721 args
[RATE
] >= -100 && args
[RATE
] <= 100 &&
2722 args
[SPEED
] >= 0 && args
[SPEED
] <= MAX_SERVO_SPEED
&&
2723 args
[MIN
] >= 0 && args
[MIN
] <= 100 &&
2724 args
[MAX
] >= 0 && args
[MAX
] <= 100 && args
[MIN
] < args
[MAX
] &&
2725 args
[BOX
] >= 0 && args
[BOX
] <= MAX_SERVO_BOXES
) {
2726 customServoMixersMutable(i
)->targetChannel
= args
[TARGET
];
2727 customServoMixersMutable(i
)->inputSource
= args
[INPUT
];
2728 customServoMixersMutable(i
)->rate
= args
[RATE
];
2729 customServoMixersMutable(i
)->speed
= args
[SPEED
];
2730 customServoMixersMutable(i
)->min
= args
[MIN
];
2731 customServoMixersMutable(i
)->max
= args
[MAX
];
2732 customServoMixersMutable(i
)->box
= args
[BOX
];
2735 cliShowParseError();
2743 static void cliWriteBytes(const uint8_t *buffer
, int count
)
2752 static void cliSdInfo(char *cmdline
)
2756 cliPrint("SD card: ");
2758 if (!sdcard_isInserted()) {
2759 cliPrint("None inserted\r\n");
2763 if (!sdcard_isInitialized()) {
2764 cliPrint("Startup failed\r\n");
2768 const sdcardMetadata_t
*metadata
= sdcard_getMetadata();
2770 cliPrintf("Manufacturer 0x%x, %ukB, %02d/%04d, v%d.%d, '",
2771 metadata
->manufacturerID
,
2772 metadata
->numBlocks
/ 2, /* One block is half a kB */
2773 metadata
->productionMonth
,
2774 metadata
->productionYear
,
2775 metadata
->productRevisionMajor
,
2776 metadata
->productRevisionMinor
2779 cliWriteBytes((uint8_t*)metadata
->productName
, sizeof(metadata
->productName
));
2781 cliPrint("'\r\n" "Filesystem: ");
2783 switch (afatfs_getFilesystemState()) {
2784 case AFATFS_FILESYSTEM_STATE_READY
:
2787 case AFATFS_FILESYSTEM_STATE_INITIALIZATION
:
2788 cliPrint("Initializing");
2790 case AFATFS_FILESYSTEM_STATE_UNKNOWN
:
2791 case AFATFS_FILESYSTEM_STATE_FATAL
:
2794 switch (afatfs_getLastError()) {
2795 case AFATFS_ERROR_BAD_MBR
:
2796 cliPrint(" - no FAT MBR partitions");
2798 case AFATFS_ERROR_BAD_FILESYSTEM_HEADER
:
2799 cliPrint(" - bad FAT header");
2801 case AFATFS_ERROR_GENERIC
:
2802 case AFATFS_ERROR_NONE
:
2803 ; // Nothing more detailed to print
2815 static void cliFlashInfo(char *cmdline
)
2817 const flashGeometry_t
*layout
= flashfsGetGeometry();
2821 cliPrintf("Flash sectors=%u, sectorSize=%u, pagesPerSector=%u, pageSize=%u, totalSize=%u, usedSize=%u\r\n",
2822 layout
->sectors
, layout
->sectorSize
, layout
->pagesPerSector
, layout
->pageSize
, layout
->totalSize
, flashfsGetOffset());
2826 static void cliFlashErase(char *cmdline
)
2832 cliPrintf("Erasing, please wait ... \r\n");
2834 cliPrintf("Erasing,\r\n");
2837 bufWriterFlush(cliWriter
);
2838 flashfsEraseCompletely();
2840 while (!flashfsIsReady()) {
2848 bufWriterFlush(cliWriter
);
2852 beeper(BEEPER_BLACKBOX_ERASE
);
2853 cliPrintf("\r\nDone.\r\n");
2856 #ifdef USE_FLASH_TOOLS
2858 static void cliFlashWrite(char *cmdline
)
2860 const uint32_t address
= atoi(cmdline
);
2861 const char *text
= strchr(cmdline
, ' ');
2864 cliShowParseError();
2866 flashfsSeekAbs(address
);
2867 flashfsWrite((uint8_t*)text
, strlen(text
), true);
2870 cliPrintf("Wrote %u bytes at %u.\r\n", strlen(text
), address
);
2874 static void cliFlashRead(char *cmdline
)
2876 uint32_t address
= atoi(cmdline
);
2878 const char *nextArg
= strchr(cmdline
, ' ');
2881 cliShowParseError();
2883 uint32_t length
= atoi(nextArg
);
2885 cliPrintf("Reading %u bytes at %u:\r\n", length
, address
);
2888 while (length
> 0) {
2889 int bytesRead
= flashfsReadAbs(address
, buffer
, length
< sizeof(buffer
) ? length
: sizeof(buffer
));
2891 for (int i
= 0; i
< bytesRead
; i
++) {
2892 cliWrite(buffer
[i
]);
2895 length
-= bytesRead
;
2896 address
+= bytesRead
;
2898 if (bytesRead
== 0) {
2899 //Assume we reached the end of the volume or something fatal happened
2910 #if defined(USE_RTC6705) || defined(VTX)
2911 static void printVtx(uint8_t dumpMask
, const vtxConfig_t
*vtxConfig
, const vtxConfig_t
*vtxConfigDefault
)
2913 // print out vtx channel settings
2914 const char *format
= "vtx %u %u %u %u %u %u\r\n";
2915 bool equalsDefault
= false;
2916 for (uint32_t i
= 0; i
< MAX_CHANNEL_ACTIVATION_CONDITION_COUNT
; i
++) {
2917 const vtxChannelActivationCondition_t
*cac
= &vtxConfig
->vtxChannelActivationConditions
[i
];
2918 if (vtxConfigDefault
) {
2919 const vtxChannelActivationCondition_t
*cacDefault
= &vtxConfigDefault
->vtxChannelActivationConditions
[i
];
2920 equalsDefault
= cac
->auxChannelIndex
== cacDefault
->auxChannelIndex
2921 && cac
->band
== cacDefault
->band
2922 && cac
->channel
== cacDefault
->channel
2923 && cac
->range
.startStep
== cacDefault
->range
.startStep
2924 && cac
->range
.endStep
== cacDefault
->range
.endStep
;
2925 cliDefaultPrintf(dumpMask
, equalsDefault
, format
,
2927 cacDefault
->auxChannelIndex
,
2929 cacDefault
->channel
,
2930 MODE_STEP_TO_CHANNEL_VALUE(cacDefault
->range
.startStep
),
2931 MODE_STEP_TO_CHANNEL_VALUE(cacDefault
->range
.endStep
)
2934 cliDumpPrintf(dumpMask
, equalsDefault
, format
,
2936 cac
->auxChannelIndex
,
2939 MODE_STEP_TO_CHANNEL_VALUE(cac
->range
.startStep
),
2940 MODE_STEP_TO_CHANNEL_VALUE(cac
->range
.endStep
)
2946 static void cliVtx(char *cmdline
)
2951 if (isEmpty(cmdline
)) {
2952 printVtx(DUMP_MASTER
, vtxConfig(), NULL
);
2956 if (i
< MAX_CHANNEL_ACTIVATION_CONDITION_COUNT
) {
2957 vtxChannelActivationCondition_t
*cac
= &vtxConfigMutable()->vtxChannelActivationConditions
[i
];
2958 uint8_t validArgumentCount
= 0;
2962 if (val
>= 0 && val
< MAX_AUX_CHANNEL_COUNT
) {
2963 cac
->auxChannelIndex
= val
;
2964 validArgumentCount
++;
2970 if (val
>= VTX_BAND_MIN
&& val
<= VTX_BAND_MAX
) {
2972 validArgumentCount
++;
2978 if (val
>= VTX_CHANNEL_MIN
&& val
<= VTX_CHANNEL_MAX
) {
2980 validArgumentCount
++;
2983 ptr
= processChannelRangeArgs(ptr
, &cac
->range
, &validArgumentCount
);
2985 if (validArgumentCount
!= 5) {
2986 memset(cac
, 0, sizeof(vtxChannelActivationCondition_t
));
2989 cliShowArgumentRangeError("index", 0, MAX_CHANNEL_ACTIVATION_CONDITION_COUNT
- 1);
2996 static void printName(uint8_t dumpMask
, const systemConfig_t
*systemConfig
)
2998 const bool equalsDefault
= strlen(systemConfig
->name
) == 0;
2999 cliDumpPrintf(dumpMask
, equalsDefault
, "name %s\r\n", equalsDefault
? emptyName
: systemConfig
->name
);
3002 static void cliName(char *cmdline
)
3004 const uint32_t len
= strlen(cmdline
);
3006 memset(systemConfigMutable()->name
, 0, ARRAYLEN(systemConfig()->name
));
3007 if (strncmp(cmdline
, emptyName
, len
)) {
3008 strncpy(systemConfigMutable()->name
, cmdline
, MIN(len
, MAX_NAME_LENGTH
));
3011 printName(DUMP_MASTER
, systemConfig());
3014 static void printFeature(uint8_t dumpMask
, const featureConfig_t
*featureConfig
, const featureConfig_t
*featureConfigDefault
)
3016 const uint32_t mask
= featureConfig
->enabledFeatures
;
3017 const uint32_t defaultMask
= featureConfigDefault
->enabledFeatures
;
3018 for (uint32_t i
= 0; featureNames
[i
]; i
++) { // disable all feature first
3019 const char *format
= "feature -%s\r\n";
3020 cliDefaultPrintf(dumpMask
, (defaultMask
| ~mask
) & (1 << i
), format
, featureNames
[i
]);
3021 cliDumpPrintf(dumpMask
, (~defaultMask
| mask
) & (1 << i
), format
, featureNames
[i
]);
3023 for (uint32_t i
= 0; featureNames
[i
]; i
++) { // reenable what we want.
3024 const char *format
= "feature %s\r\n";
3025 if (defaultMask
& (1 << i
)) {
3026 cliDefaultPrintf(dumpMask
, (~defaultMask
| mask
) & (1 << i
), format
, featureNames
[i
]);
3028 if (mask
& (1 << i
)) {
3029 cliDumpPrintf(dumpMask
, (defaultMask
| ~mask
) & (1 << i
), format
, featureNames
[i
]);
3034 static void cliFeature(char *cmdline
)
3036 uint32_t len
= strlen(cmdline
);
3037 uint32_t mask
= featureMask();
3040 cliPrint("Enabled: ");
3041 for (uint32_t i
= 0; ; i
++) {
3042 if (featureNames
[i
] == NULL
)
3044 if (mask
& (1 << i
))
3045 cliPrintf("%s ", featureNames
[i
]);
3048 } else if (strncasecmp(cmdline
, "list", len
) == 0) {
3049 cliPrint("Available: ");
3050 for (uint32_t i
= 0; ; i
++) {
3051 if (featureNames
[i
] == NULL
)
3053 cliPrintf("%s ", featureNames
[i
]);
3058 bool remove
= false;
3059 if (cmdline
[0] == '-') {
3062 cmdline
++; // skip over -
3066 for (uint32_t i
= 0; ; i
++) {
3067 if (featureNames
[i
] == NULL
) {
3068 cliPrint("Invalid name\r\n");
3072 if (strncasecmp(cmdline
, featureNames
[i
], len
) == 0) {
3076 if (mask
& FEATURE_GPS
) {
3077 cliPrint("unavailable\r\n");
3082 if (mask
& FEATURE_SONAR
) {
3083 cliPrint("unavailable\r\n");
3089 cliPrint("Disabled");
3092 cliPrint("Enabled");
3094 cliPrintf(" %s\r\n", featureNames
[i
]);
3102 static void printBeeper(uint8_t dumpMask
, const beeperConfig_t
*beeperConfig
, const beeperConfig_t
*beeperConfigDefault
)
3104 const uint8_t beeperCount
= beeperTableEntryCount();
3105 const uint32_t mask
= beeperConfig
->beeper_off_flags
;
3106 const uint32_t defaultMask
= beeperConfigDefault
->beeper_off_flags
;
3107 for (int32_t i
= 0; i
< beeperCount
- 2; i
++) {
3108 const char *formatOff
= "beeper -%s\r\n";
3109 const char *formatOn
= "beeper %s\r\n";
3110 cliDefaultPrintf(dumpMask
, ~(mask
^ defaultMask
) & (1 << i
), mask
& (1 << i
) ? formatOn
: formatOff
, beeperNameForTableIndex(i
));
3111 cliDumpPrintf(dumpMask
, ~(mask
^ defaultMask
) & (1 << i
), mask
& (1 << i
) ? formatOff
: formatOn
, beeperNameForTableIndex(i
));
3115 static void cliBeeper(char *cmdline
)
3117 uint32_t len
= strlen(cmdline
);
3118 uint8_t beeperCount
= beeperTableEntryCount();
3119 uint32_t mask
= getBeeperOffMask();
3122 cliPrintf("Disabled:");
3123 for (int32_t i
= 0; ; i
++) {
3124 if (i
== beeperCount
- 2){
3129 if (mask
& (1 << i
))
3130 cliPrintf(" %s", beeperNameForTableIndex(i
));
3133 } else if (strncasecmp(cmdline
, "list", len
) == 0) {
3134 cliPrint("Available:");
3135 for (uint32_t i
= 0; i
< beeperCount
; i
++)
3136 cliPrintf(" %s", beeperNameForTableIndex(i
));
3140 bool remove
= false;
3141 if (cmdline
[0] == '-') {
3142 remove
= true; // this is for beeper OFF condition
3147 for (uint32_t i
= 0; ; i
++) {
3148 if (i
== beeperCount
) {
3149 cliPrint("Invalid name\r\n");
3152 if (strncasecmp(cmdline
, beeperNameForTableIndex(i
), len
) == 0) {
3153 if (remove
) { // beeper off
3154 if (i
== BEEPER_ALL
-1)
3155 beeperOffSetAll(beeperCount
-2);
3157 if (i
== BEEPER_PREFERENCE
-1)
3158 setBeeperOffMask(getPreferredBeeperOffMask());
3163 cliPrint("Disabled");
3166 if (i
== BEEPER_ALL
-1)
3167 beeperOffClearAll();
3169 if (i
== BEEPER_PREFERENCE
-1)
3170 setPreferredBeeperOffMask(getBeeperOffMask());
3173 beeperOffClear(mask
);
3175 cliPrint("Enabled");
3177 cliPrintf(" %s\r\n", beeperNameForTableIndex(i
));
3185 static void printMap(uint8_t dumpMask
, const rxConfig_t
*rxConfig
, const rxConfig_t
*defaultRxConfig
)
3187 bool equalsDefault
= true;
3189 char bufDefault
[16];
3191 for (i
= 0; i
< MAX_MAPPABLE_RX_INPUTS
; i
++) {
3192 buf
[rxConfig
->rcmap
[i
]] = rcChannelLetters
[i
];
3193 if (defaultRxConfig
) {
3194 bufDefault
[defaultRxConfig
->rcmap
[i
]] = rcChannelLetters
[i
];
3195 equalsDefault
= equalsDefault
&& (rxConfig
->rcmap
[i
] == defaultRxConfig
->rcmap
[i
]);
3200 const char *formatMap
= "map %s\r\n";
3201 cliDefaultPrintf(dumpMask
, equalsDefault
, formatMap
, bufDefault
);
3202 cliDumpPrintf(dumpMask
, equalsDefault
, formatMap
, buf
);
3205 static void cliMap(char *cmdline
)
3210 len
= strlen(cmdline
);
3214 for (uint32_t i
= 0; i
< 8; i
++)
3215 cmdline
[i
] = toupper((unsigned char)cmdline
[i
]);
3216 for (uint32_t i
= 0; i
< 8; i
++) {
3217 if (strchr(rcChannelLetters
, cmdline
[i
]) && !strchr(cmdline
+ i
+ 1, cmdline
[i
]))
3219 cliShowParseError();
3222 parseRcChannels(cmdline
, rxConfigMutable());
3226 for (i
= 0; i
< 8; i
++)
3227 out
[rxConfig()->rcmap
[i
]] = rcChannelLetters
[i
];
3229 cliPrintf("%s\r\n", out
);
3232 static char *checkCommand(char *cmdLine
, const char *command
)
3234 if(!strncasecmp(cmdLine
, command
, strlen(command
)) // command names match
3235 && (isspace((unsigned)cmdLine
[strlen(command
)]) || cmdLine
[strlen(command
)] == 0)) {
3236 return cmdLine
+ strlen(command
) + 1;
3242 static void cliRebootEx(bool bootLoader
)
3244 cliPrint("\r\nRebooting");
3245 bufWriterFlush(cliWriter
);
3246 waitForSerialPortToFinishTransmitting(cliPort
);
3249 systemResetToBootloader();
3255 static void cliReboot(void)
3260 static void cliBootloader(char *cmdLine
)
3264 cliPrintHashLine("restarting in bootloader mode");
3268 static void cliExit(char *cmdline
)
3272 cliPrintHashLine("leaving CLI mode, unsaved changes lost");
3273 bufWriterFlush(cliWriter
);
3278 // incase a motor was left running during motortest, clear it here
3279 mixerResetDisarmedMotors();
3286 static void cliGpsPassthrough(char *cmdline
)
3290 gpsEnablePassthrough(cliPort
);
3294 #if defined(USE_ESCSERIAL) || defined(USE_DSHOT)
3297 #define ALL_ESCS 255
3300 static int parseEscNumber(char *pch
, bool allowAllEscs
) {
3301 int escNumber
= atoi(pch
);
3302 if ((escNumber
>= 0) && (escNumber
< getMotorCount())) {
3303 printf("Programming on ESC %d.\r\n", escNumber
);
3304 } else if (allowAllEscs
&& escNumber
== ALL_ESCS
) {
3305 printf("Programming on all ESCs.\r\n");
3307 printf("Invalid ESC number, range: 0 to %d.\r\n", getMotorCount() - 1);
3317 static void cliDshotProg(char *cmdline
)
3319 if (isEmpty(cmdline
) || motorConfig()->dev
.motorPwmProtocol
< PWM_TYPE_DSHOT150
) {
3320 cliShowParseError();
3326 char *pch
= strtok_r(cmdline
, " ", &saveptr
);
3329 while (pch
!= NULL
) {
3332 escNumber
= parseEscNumber(pch
, true);
3333 if (escNumber
== -1) {
3339 motorControlEnable
= false;
3341 int command
= atoi(pch
);
3342 if (command
>= 0 && command
< DSHOT_MIN_THROTTLE
) {
3343 if (escNumber
== ALL_ESCS
) {
3344 for (unsigned i
= 0; i
< getMotorCount(); i
++) {
3345 pwmWriteDshotCommand(i
, command
);
3348 pwmWriteDshotCommand(escNumber
, command
);
3352 delay(10); // wait for sound output to finish
3355 printf("Command %d written.\r\n", command
);
3357 printf("Invalid command, range 1 to %d.\r\n", DSHOT_MIN_THROTTLE
- 1);
3364 pch
= strtok_r(NULL
, " ", &saveptr
);
3367 motorControlEnable
= true;
3371 #ifdef USE_ESCSERIAL
3372 static void cliEscPassthrough(char *cmdline
)
3374 if (isEmpty(cmdline
)) {
3375 cliShowParseError();
3381 char *pch
= strtok_r(cmdline
, " ", &saveptr
);
3385 while (pch
!= NULL
) {
3388 if(strncasecmp(pch
, "sk", strlen(pch
)) == 0) {
3389 mode
= PROTOCOL_SIMONK
;
3390 } else if(strncasecmp(pch
, "bl", strlen(pch
)) == 0) {
3391 mode
= PROTOCOL_BLHELI
;
3392 } else if(strncasecmp(pch
, "ki", strlen(pch
)) == 0) {
3393 mode
= PROTOCOL_KISS
;
3394 } else if(strncasecmp(pch
, "cc", strlen(pch
)) == 0) {
3395 mode
= PROTOCOL_KISSALL
;
3397 cliShowParseError();
3403 escNumber
= parseEscNumber(pch
, mode
== PROTOCOL_KISS
);
3404 if (escNumber
== -1) {
3410 cliShowParseError();
3418 pch
= strtok_r(NULL
, " ", &saveptr
);
3421 escEnablePassthrough(cliPort
, escNumber
, mode
);
3425 #ifndef USE_QUAD_MIXER_ONLY
3426 static void cliMixer(char *cmdline
)
3430 len
= strlen(cmdline
);
3433 cliPrintf("Mixer: %s\r\n", mixerNames
[mixerConfig()->mixerMode
- 1]);
3435 } else if (strncasecmp(cmdline
, "list", len
) == 0) {
3436 cliPrint("Available mixers: ");
3437 for (uint32_t i
= 0; ; i
++) {
3438 if (mixerNames
[i
] == NULL
)
3440 cliPrintf("%s ", mixerNames
[i
]);
3446 for (uint32_t i
= 0; ; i
++) {
3447 if (mixerNames
[i
] == NULL
) {
3448 cliPrint("Invalid name\r\n");
3451 if (strncasecmp(cmdline
, mixerNames
[i
], len
) == 0) {
3452 mixerConfigMutable()->mixerMode
= i
+ 1;
3461 static void cliMotor(char *cmdline
)
3463 int motor_index
= 0;
3464 int motor_value
= 0;
3469 if (isEmpty(cmdline
)) {
3470 cliShowParseError();
3474 pch
= strtok_r(cmdline
, " ", &saveptr
);
3475 while (pch
!= NULL
) {
3478 motor_index
= atoi(pch
);
3481 motor_value
= atoi(pch
);
3485 pch
= strtok_r(NULL
, " ", &saveptr
);
3488 if (motor_index
< 0 || motor_index
>= MAX_SUPPORTED_MOTORS
) {
3489 cliShowArgumentRangeError("index", 0, MAX_SUPPORTED_MOTORS
- 1);
3494 if (motor_value
< PWM_RANGE_MIN
|| motor_value
> PWM_RANGE_MAX
) {
3495 cliShowArgumentRangeError("value", 1000, 2000);
3497 motor_disarmed
[motor_index
] = convertExternalToMotor(motor_value
);
3499 cliPrintf("motor %d: %d\r\n", motor_index
, convertMotorToExternal(motor_disarmed
[motor_index
]));
3506 static void cliPlaySound(char *cmdline
)
3510 static int lastSoundIdx
= -1;
3512 if (isEmpty(cmdline
)) {
3513 i
= lastSoundIdx
+ 1; //next sound index
3514 if ((name
=beeperNameForTableIndex(i
)) == NULL
) {
3515 while (true) { //no name for index; try next one
3516 if (++i
>= beeperTableEntryCount())
3517 i
= 0; //if end then wrap around to first entry
3518 if ((name
=beeperNameForTableIndex(i
)) != NULL
)
3519 break; //if name OK then play sound below
3520 if (i
== lastSoundIdx
+ 1) { //prevent infinite loop
3521 cliPrintf("Error playing sound\r\n");
3526 } else { //index value was given
3528 if ((name
=beeperNameForTableIndex(i
)) == NULL
) {
3529 cliPrintf("No sound for index %d\r\n", i
);
3535 cliPrintf("Playing sound %d: %s\r\n", i
, name
);
3536 beeper(beeperModeForTableIndex(i
));
3540 static void cliProfile(char *cmdline
)
3542 if (isEmpty(cmdline
)) {
3543 cliPrintf("profile %d\r\n", getCurrentPidProfileIndex());
3546 const int i
= atoi(cmdline
);
3547 if (i
>= 0 && i
< MAX_PROFILE_COUNT
) {
3548 systemConfigMutable()->pidProfileIndex
= i
;
3554 static void cliRateProfile(char *cmdline
)
3556 if (isEmpty(cmdline
)) {
3557 cliPrintf("rateprofile %d\r\n", getCurrentControlRateProfileIndex());
3560 const int i
= atoi(cmdline
);
3561 if (i
>= 0 && i
< CONTROL_RATE_PROFILE_COUNT
) {
3562 changeControlRateProfile(i
);
3568 #ifdef USE_PARAMETER_GROUPS
3569 static void cliDumpPidProfile(uint8_t pidProfileIndex
, uint8_t dumpMask
)
3571 if (pidProfileIndex
>= MAX_PROFILE_COUNT
) {
3575 changePidProfile(pidProfileIndex
);
3576 cliPrintHashLine("profile");
3579 dumpAllValues(PROFILE_VALUE
, dumpMask
);
3582 static void cliDumpRateProfile(uint8_t rateProfileIndex
, uint8_t dumpMask
)
3584 if (rateProfileIndex
>= CONTROL_RATE_PROFILE_COUNT
) {
3588 changeControlRateProfile(rateProfileIndex
);
3589 cliPrintHashLine("rateprofile");
3592 dumpAllValues(PROFILE_RATE_VALUE
, dumpMask
);
3595 static void cliDumpPidProfile(uint8_t pidProfileIndex
, uint8_t dumpMask
, const master_t
*defaultConfig
)
3597 if (pidProfileIndex
>= MAX_PROFILE_COUNT
) {
3601 changePidProfile(pidProfileIndex
);
3602 cliPrintHashLine("profile");
3605 dumpValues(PROFILE_VALUE
, dumpMask
, defaultConfig
);
3608 static void cliDumpRateProfile(uint8_t rateProfileIndex
, uint8_t dumpMask
, const master_t
*defaultConfig
)
3610 if (rateProfileIndex
>= CONTROL_RATE_PROFILE_COUNT
) {
3614 changeControlRateProfile(rateProfileIndex
);
3615 cliPrintHashLine("rateprofile");
3618 dumpValues(PROFILE_RATE_VALUE
, dumpMask
, defaultConfig
);
3622 static void cliSave(char *cmdline
)
3626 cliPrintHashLine("saving");
3631 static void cliDefaults(char *cmdline
)
3635 cliPrintHashLine("resetting to defaults");
3640 static void cliGet(char *cmdline
)
3642 const clivalue_t
*val
;
3643 int matchedCommands
= 0;
3645 for (uint32_t i
= 0; i
< ARRAYLEN(valueTable
); i
++) {
3646 if (strstr(valueTable
[i
].name
, cmdline
)) {
3647 val
= &valueTable
[i
];
3648 cliPrintf("%s = ", valueTable
[i
].name
);
3649 cliPrintVar(val
, 0);
3651 cliPrintVarRange(val
);
3659 if (matchedCommands
) {
3663 cliPrint("Invalid name\r\n");
3666 static void cliSet(char *cmdline
)
3669 const clivalue_t
*val
;
3672 len
= strlen(cmdline
);
3674 if (len
== 0 || (len
== 1 && cmdline
[0] == '*')) {
3675 cliPrint("Current settings: \r\n");
3676 for (uint32_t i
= 0; i
< ARRAYLEN(valueTable
); i
++) {
3677 val
= &valueTable
[i
];
3678 cliPrintf("%s = ", valueTable
[i
].name
);
3679 cliPrintVar(val
, len
); // when len is 1 (when * is passed as argument), it will print min/max values as well, for gui
3682 } else if ((eqptr
= strstr(cmdline
, "=")) != NULL
) {
3685 char *lastNonSpaceCharacter
= eqptr
;
3686 while (*(lastNonSpaceCharacter
- 1) == ' ') {
3687 lastNonSpaceCharacter
--;
3689 uint8_t variableNameLength
= lastNonSpaceCharacter
- cmdline
;
3691 // skip the '=' and any ' ' characters
3693 while (*(eqptr
) == ' ') {
3697 for (uint32_t i
= 0; i
< ARRAYLEN(valueTable
); i
++) {
3698 val
= &valueTable
[i
];
3699 // ensure exact match when setting to prevent setting variables with shorter names
3700 if (strncasecmp(cmdline
, valueTable
[i
].name
, strlen(valueTable
[i
].name
)) == 0 && variableNameLength
== strlen(valueTable
[i
].name
)) {
3702 bool changeValue
= false;
3703 int_float_value_t tmp
= { 0 };
3704 switch (valueTable
[i
].type
& VALUE_MODE_MASK
) {
3709 value
= atoi(eqptr
);
3710 valuef
= fastA2F(eqptr
);
3712 if (valuef
>= valueTable
[i
].config
.minmax
.min
&& valuef
<= valueTable
[i
].config
.minmax
.max
) { // note: compare float value
3714 if ((valueTable
[i
].type
& VALUE_TYPE_MASK
) == VAR_FLOAT
)
3715 tmp
.float_value
= valuef
;
3717 tmp
.int_value
= value
;
3724 const lookupTableEntry_t
*tableEntry
= &lookupTables
[valueTable
[i
].config
.lookup
.tableIndex
];
3725 bool matched
= false;
3726 for (uint32_t tableValueIndex
= 0; tableValueIndex
< tableEntry
->valueCount
&& !matched
; tableValueIndex
++) {
3727 matched
= strcasecmp(tableEntry
->values
[tableValueIndex
], eqptr
) == 0;
3730 tmp
.int_value
= tableValueIndex
;
3739 cliSetVar(val
, tmp
);
3741 cliPrintf("%s set to ", valueTable
[i
].name
);
3742 cliPrintVar(val
, 0);
3744 cliPrint("Invalid value\r\n");
3745 cliPrintVarRange(val
);
3751 cliPrint("Invalid name\r\n");
3753 // no equals, check for matching variables.
3758 static void cliStatus(char *cmdline
)
3762 cliPrintf("System Uptime: %d seconds\r\n", millis() / 1000);
3763 cliPrintf("Voltage: %d * 0.1V (%dS battery - %s)\r\n", getBatteryVoltage(), getBatteryCellCount(), getBatteryStateString());
3765 cliPrintf("CPU Clock=%dMHz", (SystemCoreClock
/ 1000000));
3767 #if defined(USE_SENSOR_NAMES)
3768 const uint32_t detectedSensorsMask
= sensorsMask();
3769 for (uint32_t i
= 0; ; i
++) {
3770 if (sensorTypeNames
[i
] == NULL
) {
3773 const uint32_t mask
= (1 << i
);
3774 if ((detectedSensorsMask
& mask
) && (mask
& SENSOR_NAMES_MASK
)) {
3775 const uint8_t sensorHardwareIndex
= detectedSensors
[i
];
3776 const char *sensorHardware
= sensorHardwareNames
[i
][sensorHardwareIndex
];
3777 cliPrintf(", %s=%s", sensorTypeNames
[i
], sensorHardware
);
3778 if (mask
== SENSOR_ACC
&& acc
.dev
.revisionCode
) {
3779 cliPrintf(".%c", acc
.dev
.revisionCode
);
3783 #endif /* USE_SENSOR_NAMES */
3791 const uint16_t i2cErrorCounter
= i2cGetErrorCounter();
3793 const uint16_t i2cErrorCounter
= 0;
3797 cliPrintf("Stack used: %d, ", stackUsedSize());
3799 cliPrintf("Stack size: %d, Stack address: 0x%x\r\n", stackTotalSize(), stackHighMem());
3801 #ifdef USE_PARAMETER_GROUPS
3802 cliPrintf("I2C Errors: %d, config size: %d, max available config: %d\r\n", i2cErrorCounter
, getEEPROMConfigSize(), &__config_end
- &__config_start
);
3804 cliPrintf("I2C Errors: %d, config size: %d\r\n", i2cErrorCounter
, sizeof(master_t
));
3807 const int gyroRate
= getTaskDeltaTime(TASK_GYROPID
) == 0 ? 0 : (int)(1000000.0f
/ ((float)getTaskDeltaTime(TASK_GYROPID
)));
3808 const int rxRate
= getTaskDeltaTime(TASK_RX
) == 0 ? 0 : (int)(1000000.0f
/ ((float)getTaskDeltaTime(TASK_RX
)));
3809 const int systemRate
= getTaskDeltaTime(TASK_SYSTEM
) == 0 ? 0 : (int)(1000000.0f
/ ((float)getTaskDeltaTime(TASK_SYSTEM
)));
3810 cliPrintf("CPU:%d%%, cycle time: %d, GYRO rate: %d, RX rate: %d, System rate: %d\r\n",
3811 constrain(averageSystemLoadPercent
, 0, 100), getTaskDeltaTime(TASK_GYROPID
), gyroRate
, rxRate
, systemRate
);
3815 #ifndef SKIP_TASK_STATISTICS
3816 static void cliTasks(char *cmdline
)
3820 int averageLoadSum
= 0;
3823 if (systemConfig()->task_statistics
) {
3824 cliPrintf("Task list rate/hz max/us avg/us maxload avgload total/ms\r\n");
3826 cliPrintf("Task list\r\n");
3829 for (cfTaskId_e taskId
= 0; taskId
< TASK_COUNT
; taskId
++) {
3830 cfTaskInfo_t taskInfo
;
3831 getTaskInfo(taskId
, &taskInfo
);
3832 if (taskInfo
.isEnabled
) {
3834 int subTaskFrequency
;
3835 if (taskId
== TASK_GYROPID
) {
3836 subTaskFrequency
= taskInfo
.latestDeltaTime
== 0 ? 0 : (int)(1000000.0f
/ ((float)taskInfo
.latestDeltaTime
));
3837 taskFrequency
= subTaskFrequency
/ pidConfig()->pid_process_denom
;
3838 if (pidConfig()->pid_process_denom
> 1) {
3839 cliPrintf("%02d - (%15s) ", taskId
, taskInfo
.taskName
);
3841 taskFrequency
= subTaskFrequency
;
3842 cliPrintf("%02d - (%11s/%3s) ", taskId
, taskInfo
.subTaskName
, taskInfo
.taskName
);
3845 taskFrequency
= taskInfo
.latestDeltaTime
== 0 ? 0 : (int)(1000000.0f
/ ((float)taskInfo
.latestDeltaTime
));
3846 cliPrintf("%02d - (%15s) ", taskId
, taskInfo
.taskName
);
3848 const int maxLoad
= taskInfo
.maxExecutionTime
== 0 ? 0 :(taskInfo
.maxExecutionTime
* taskFrequency
+ 5000) / 1000;
3849 const int averageLoad
= taskInfo
.averageExecutionTime
== 0 ? 0 : (taskInfo
.averageExecutionTime
* taskFrequency
+ 5000) / 1000;
3850 if (taskId
!= TASK_SERIAL
) {
3851 maxLoadSum
+= maxLoad
;
3852 averageLoadSum
+= averageLoad
;
3854 if (systemConfig()->task_statistics
) {
3855 cliPrintf("%6d %7d %7d %4d.%1d%% %4d.%1d%% %9d\r\n",
3856 taskFrequency
, taskInfo
.maxExecutionTime
, taskInfo
.averageExecutionTime
,
3857 maxLoad
/10, maxLoad
%10, averageLoad
/10, averageLoad
%10, taskInfo
.totalExecutionTime
/ 1000);
3859 cliPrintf("%6d\r\n", taskFrequency
);
3861 if (taskId
== TASK_GYROPID
&& pidConfig()->pid_process_denom
> 1) {
3862 cliPrintf(" - (%15s) %6d\r\n", taskInfo
.subTaskName
, subTaskFrequency
);
3866 if (systemConfig()->task_statistics
) {
3867 cfCheckFuncInfo_t checkFuncInfo
;
3868 getCheckFuncInfo(&checkFuncInfo
);
3869 cliPrintf("RX Check Function %17d %7d %25d\r\n", checkFuncInfo
.maxExecutionTime
, checkFuncInfo
.averageExecutionTime
, checkFuncInfo
.totalExecutionTime
/ 1000);
3870 cliPrintf("Total (excluding SERIAL) %23d.%1d%% %4d.%1d%%\r\n", maxLoadSum
/10, maxLoadSum
%10, averageLoadSum
/10, averageLoadSum
%10);
3875 static void cliVersion(char *cmdline
)
3879 cliPrintf("# %s / %s %s %s / %s (%s)\r\n",
3889 #if defined(USE_RESOURCE_MGMT)
3891 #define MAX_RESOURCE_INDEX(x) ((x) == 0 ? 1 : (x))
3894 const uint8_t owner
;
3897 const uint8_t maxIndex
;
3898 } cliResourceValue_t
;
3900 const cliResourceValue_t resourceTable
[] = {
3902 { OWNER_BEEPER
, PG_BEEPER_DEV_CONFIG
, offsetof(beeperDevConfig_t
, ioTag
), 0 },
3904 { OWNER_MOTOR
, PG_MOTOR_CONFIG
, offsetof(motorConfig_t
, dev
.ioTags
[0]), MAX_SUPPORTED_MOTORS
},
3906 { OWNER_SERVO
, PG_SERVO_CONFIG
, offsetof(servoConfig_t
, dev
.ioTags
[0]), MAX_SUPPORTED_SERVOS
},
3908 #if defined(USE_PWM) || defined(USE_PPM)
3909 { OWNER_PPMINPUT
, PG_PPM_CONFIG
, offsetof(ppmConfig_t
, ioTag
), 0 },
3910 { OWNER_PWMINPUT
, PG_PWM_CONFIG
, offsetof(pwmConfig_t
, ioTags
[0]), PWM_INPUT_PORT_COUNT
},
3913 { OWNER_SONAR_TRIGGER
, PG_SONAR_CONFIG
, offsetof(sonarConfig_t
, triggerTag
), 0 },
3914 { OWNER_SONAR_ECHO
, PG_SERIAL_CONFIG
, offsetof(sonarConfig_t
, echoTag
), 0 },
3917 { OWNER_LED_STRIP
, PG_LED_STRIP_CONFIG
, offsetof(ledStripConfig_t
, ioTag
), 0 },
3919 { OWNER_SERIAL_TX
, PG_SERIAL_CONFIG
, offsetof(serialPinConfig_t
, ioTagTx
[0]), SERIAL_PORT_MAX_INDEX
},
3920 { OWNER_SERIAL_RX
, PG_SERIAL_CONFIG
, offsetof(serialPinConfig_t
, ioTagRx
[0]), SERIAL_PORT_MAX_INDEX
},
3923 static ioTag_t
*getIoTag(const cliResourceValue_t value
, uint8_t index
)
3925 const pgRegistry_t
* rec
= pgFind(value
.pgn
);
3926 return CONST_CAST(ioTag_t
*, rec
->address
+ value
.offset
+ index
);
3929 static void printResource(uint8_t dumpMask
)
3931 for (unsigned int i
= 0; i
< ARRAYLEN(resourceTable
); i
++) {
3932 const char* owner
= ownerNames
[resourceTable
[i
].owner
];
3933 const void *currentConfig
;
3934 const void *defaultConfig
;
3935 if (dumpMask
& DO_DIFF
|| dumpMask
& SHOW_DEFAULTS
) {
3936 const cliCurrentAndDefaultConfig_t
*config
= getCurrentAndDefaultConfigs(resourceTable
[i
].pgn
);
3937 currentConfig
= config
->currentConfig
;
3938 defaultConfig
= config
->defaultConfig
;
3939 } else { // Not guaranteed to have initialised default configs in this case
3940 currentConfig
= pgFind(resourceTable
[i
].pgn
)->address
;
3941 defaultConfig
= currentConfig
;
3944 for (int index
= 0; index
< MAX_RESOURCE_INDEX(resourceTable
[i
].maxIndex
); index
++) {
3945 const ioTag_t ioTag
= *((const ioTag_t
*)currentConfig
+ resourceTable
[i
].offset
+ index
);
3946 const ioTag_t ioTagDefault
= *((const ioTag_t
*)defaultConfig
+ resourceTable
[i
].offset
+ index
);
3948 bool equalsDefault
= ioTag
== ioTagDefault
;
3949 const char *format
= "resource %s %d %c%02d\r\n";
3950 const char *formatUnassigned
= "resource %s %d NONE\r\n";
3951 if (!ioTagDefault
) {
3952 cliDefaultPrintf(dumpMask
, equalsDefault
, formatUnassigned
, owner
, RESOURCE_INDEX(index
));
3954 cliDefaultPrintf(dumpMask
, equalsDefault
, format
, owner
, RESOURCE_INDEX(index
), IO_GPIOPortIdxByTag(ioTagDefault
) + 'A', IO_GPIOPinIdxByTag(ioTagDefault
));
3957 if (!(dumpMask
& HIDE_UNUSED
)) {
3958 cliDumpPrintf(dumpMask
, equalsDefault
, formatUnassigned
, owner
, RESOURCE_INDEX(index
));
3961 cliDumpPrintf(dumpMask
, equalsDefault
, format
, owner
, RESOURCE_INDEX(index
), IO_GPIOPortIdxByTag(ioTag
) + 'A', IO_GPIOPinIdxByTag(ioTag
));
3967 static void printResourceOwner(uint8_t owner
, uint8_t index
)
3969 cliPrintf("%s", ownerNames
[resourceTable
[owner
].owner
]);
3971 if (resourceTable
[owner
].maxIndex
> 0) {
3972 cliPrintf(" %d", RESOURCE_INDEX(index
));
3976 static void resourceCheck(uint8_t resourceIndex
, uint8_t index
, ioTag_t newTag
)
3982 const char * format
= "\r\nNOTE: %c%02d already assigned to ";
3983 for (int r
= 0; r
< (int)ARRAYLEN(resourceTable
); r
++) {
3984 for (int i
= 0; i
< MAX_RESOURCE_INDEX(resourceTable
[r
].maxIndex
); i
++) {
3985 ioTag_t
*tag
= getIoTag(resourceTable
[r
], i
);
3986 if (*tag
== newTag
) {
3987 bool cleared
= false;
3988 if (r
== resourceIndex
) {
3996 cliPrintf(format
, DEFIO_TAG_GPIOID(newTag
) + 'A', DEFIO_TAG_PIN(newTag
));
3998 printResourceOwner(r
, i
);
4002 printResourceOwner(r
, i
);
4003 cliPrintf(" disabled");
4012 static void cliResource(char *cmdline
)
4014 int len
= strlen(cmdline
);
4017 printResource(DUMP_MASTER
| HIDE_UNUSED
);
4020 } else if (strncasecmp(cmdline
, "list", len
) == 0) {
4022 cliPrintf("IO\r\n");
4024 cliPrintf("Currently active IO resource assignments:\r\n(reboot to update)\r\n");
4027 for (int i
= 0; i
< DEFIO_IO_USED_COUNT
; i
++) {
4029 owner
= ownerNames
[ioRecs
[i
].owner
];
4031 cliPrintf("%c%02d: %s ", IO_GPIOPortIdx(ioRecs
+ i
) + 'A', IO_GPIOPinIdx(ioRecs
+ i
), owner
);
4032 if (ioRecs
[i
].index
> 0) {
4033 cliPrintf("%d", ioRecs
[i
].index
);
4038 cliPrintf("\r\n\r\n");
4040 cliPrintf("DMA:\r\n");
4042 cliPrintf("Currently active DMA:\r\n");
4045 for (int i
= 0; i
< DMA_MAX_DESCRIPTORS
; i
++) {
4047 owner
= ownerNames
[dmaGetOwner(i
)];
4049 cliPrintf(DMA_OUTPUT_STRING
, i
/ DMA_MOD_VALUE
+ 1, (i
% DMA_MOD_VALUE
) + DMA_MOD_OFFSET
);
4050 uint8_t resourceIndex
= dmaGetResourceIndex(i
);
4051 if (resourceIndex
> 0) {
4052 cliPrintf(" %s %d\r\n", owner
, resourceIndex
);
4054 cliPrintf(" %s\r\n", owner
);
4059 cliPrintf("\r\nUse: 'resource' to see how to change resources.\r\n");
4065 uint8_t resourceIndex
= 0;
4070 pch
= strtok_r(cmdline
, " ", &saveptr
);
4071 for (resourceIndex
= 0; ; resourceIndex
++) {
4072 if (resourceIndex
>= ARRAYLEN(resourceTable
)) {
4073 cliPrint("Invalid\r\n");
4077 if (strncasecmp(pch
, ownerNames
[resourceTable
[resourceIndex
].owner
], len
) == 0) {
4082 pch
= strtok_r(NULL
, " ", &saveptr
);
4085 if (resourceTable
[resourceIndex
].maxIndex
> 0 || index
> 0) {
4086 if (index
<= 0 || index
> MAX_RESOURCE_INDEX(resourceTable
[resourceIndex
].maxIndex
)) {
4087 cliShowArgumentRangeError("index", 1, MAX_RESOURCE_INDEX(resourceTable
[resourceIndex
].maxIndex
));
4092 pch
= strtok_r(NULL
, " ", &saveptr
);
4095 ioTag_t
*tag
= getIoTag(resourceTable
[resourceIndex
], index
);
4098 if (strlen(pch
) > 0) {
4099 if (strcasecmp(pch
, "NONE") == 0) {
4102 cliPrintf("Freed\r\n");
4104 cliPrintf("Resource is freed\r\n");
4108 uint8_t port
= (*pch
) - 'A';
4110 port
= (*pch
) - 'a';
4117 ioRec_t
*rec
= IO_Rec(IOGetByTag(DEFIO_TAG_MAKE(port
, pin
)));
4119 resourceCheck(resourceIndex
, index
, DEFIO_TAG_MAKE(port
, pin
));
4121 cliPrintf(" %c%02d set\r\n", port
+ 'A', pin
);
4123 cliPrintf("\r\nResource is set to %c%02d!\r\n", port
+ 'A', pin
);
4125 *tag
= DEFIO_TAG_MAKE(port
, pin
);
4127 cliShowParseError();
4135 cliShowParseError();
4137 #endif /* USE_RESOURCE_MGMT */
4139 #ifdef USE_PARAMETER_GROUPS
4140 static void backupConfigs(void)
4142 // make copies of configs to do differencing
4144 // currentConfig is the copy
4145 const cliCurrentAndDefaultConfig_t
*cliCurrentAndDefaultConfig
= getCurrentAndDefaultConfigs(pgN(reg
));
4146 if (cliCurrentAndDefaultConfig
->currentConfig
) {
4147 if (pgIsProfile(reg
)) {
4148 //memcpy((uint8_t *)cliCurrentAndDefaultConfig->currentConfig, reg->address, reg->size * MAX_PROFILE_COUNT);
4150 memcpy((uint8_t *)cliCurrentAndDefaultConfig
->currentConfig
, reg
->address
, reg
->size
);
4152 #ifdef SERIAL_CLI_DEBUG
4154 cliPrintf("BACKUP %d SET UP INCORRECTLY\r\n", pgN(reg
));
4160 static void restoreConfigs(void)
4163 // currentConfig is the copy
4164 const cliCurrentAndDefaultConfig_t
*cliCurrentAndDefaultConfig
= getCurrentAndDefaultConfigs(pgN(reg
));
4165 if (cliCurrentAndDefaultConfig
->currentConfig
) {
4166 if (pgIsProfile(reg
)) {
4167 //memcpy(reg->address, (uint8_t *)cliCurrentAndDefaultConfig->currentConfig, reg->size * MAX_PROFILE_COUNT);
4169 memcpy(reg
->address
, (uint8_t *)cliCurrentAndDefaultConfig
->currentConfig
, reg
->size
);
4171 #ifdef SERIAL_CLI_DEBUG
4173 cliPrintf("RESTORE %d SET UP INCORRECTLY\r\n", pgN(reg
));
4180 static void printConfig(char *cmdline
, bool doDiff
)
4182 uint8_t dumpMask
= DUMP_MASTER
;
4184 if ((options
= checkCommand(cmdline
, "master"))) {
4185 dumpMask
= DUMP_MASTER
; // only
4186 } else if ((options
= checkCommand(cmdline
, "profile"))) {
4187 dumpMask
= DUMP_PROFILE
; // only
4188 } else if ((options
= checkCommand(cmdline
, "rates"))) {
4189 dumpMask
= DUMP_RATES
; // only
4190 } else if ((options
= checkCommand(cmdline
, "all"))) {
4191 dumpMask
= DUMP_ALL
; // all profiles and rates
4197 dumpMask
= dumpMask
| DO_DIFF
;
4201 // reset all configs to defaults to do differencing
4204 #if defined(TARGET_CONFIG)
4205 targetConfiguration();
4207 if (checkCommand(options
, "showdefaults")) {
4208 dumpMask
= dumpMask
| SHOW_DEFAULTS
; // add default values as comments for changed values
4211 if ((dumpMask
& DUMP_MASTER
) || (dumpMask
& DUMP_ALL
)) {
4212 cliPrintHashLine("version");
4215 if ((dumpMask
& (DUMP_ALL
| DO_DIFF
)) == (DUMP_ALL
| DO_DIFF
)) {
4216 cliPrintHashLine("reset configuration to default settings");
4217 cliPrint("defaults\r\n");
4220 cliPrintHashLine("name");
4221 printName(dumpMask
, &systemConfigCopy
);
4223 #ifdef USE_RESOURCE_MGMT
4224 cliPrintHashLine("resources");
4225 printResource(dumpMask
);
4228 #ifndef USE_QUAD_MIXER_ONLY
4229 cliPrintHashLine("mixer");
4230 const bool equalsDefault
= mixerConfigCopy
.mixerMode
== mixerConfig()->mixerMode
;
4231 const char *formatMixer
= "mixer %s\r\n";
4232 cliDefaultPrintf(dumpMask
, equalsDefault
, formatMixer
, mixerNames
[mixerConfig()->mixerMode
- 1]);
4233 cliDumpPrintf(dumpMask
, equalsDefault
, formatMixer
, mixerNames
[mixerConfigCopy
.mixerMode
- 1]);
4235 cliDumpPrintf(dumpMask
, customMotorMixer(0)->throttle
== 0.0f
, "\r\nmmix reset\r\n\r\n");
4237 printMotorMix(dumpMask
, customMotorMixerCopy
, customMotorMixer(0));
4240 cliPrintHashLine("servo");
4241 printServo(dumpMask
, servoParamsCopy
, servoParams(0));
4243 cliPrintHashLine("servo mix");
4244 // print custom servo mixer if exists
4245 cliDumpPrintf(dumpMask
, customServoMixers(0)->rate
== 0, "smix reset\r\n\r\n");
4246 printServoMix(dumpMask
, customServoMixersCopy
, customServoMixers(0));
4250 cliPrintHashLine("feature");
4251 printFeature(dumpMask
, &featureConfigCopy
, featureConfig());
4254 cliPrintHashLine("beeper");
4255 printBeeper(dumpMask
, &beeperConfigCopy
, beeperConfig());
4258 cliPrintHashLine("map");
4259 printMap(dumpMask
, &rxConfigCopy
, rxConfig());
4261 cliPrintHashLine("serial");
4262 printSerial(dumpMask
, &serialConfigCopy
, serialConfig());
4265 cliPrintHashLine("led");
4266 printLed(dumpMask
, ledStripConfigCopy
.ledConfigs
, ledStripConfig()->ledConfigs
);
4268 cliPrintHashLine("color");
4269 printColor(dumpMask
, ledStripConfigCopy
.colors
, ledStripConfig()->colors
);
4271 cliPrintHashLine("mode_color");
4272 printModeColor(dumpMask
, &ledStripConfigCopy
, ledStripConfig());
4275 cliPrintHashLine("aux");
4276 printAux(dumpMask
, modeActivationConditionsCopy
, modeActivationConditions(0));
4278 cliPrintHashLine("adjrange");
4279 printAdjustmentRange(dumpMask
, adjustmentRangesCopy
, adjustmentRanges(0));
4281 cliPrintHashLine("rxrange");
4282 printRxRange(dumpMask
, rxChannelRangeConfigsCopy
, rxChannelRangeConfigs(0));
4284 #if defined(USE_RTC6705) || defined(VTX)
4285 cliPrintHashLine("vtx");
4286 printVtx(dumpMask
, &vtxConfigCopy
, vtxConfig());
4289 cliPrintHashLine("rxfail");
4290 printRxFailsafe(dumpMask
, rxFailsafeChannelConfigsCopy
, rxFailsafeChannelConfigs(0));
4292 cliPrintHashLine("master");
4293 dumpAllValues(MASTER_VALUE
, dumpMask
);
4295 if (dumpMask
& DUMP_ALL
) {
4296 const uint8_t pidProfileIndexSave
= systemConfigCopy
.pidProfileIndex
;
4297 for (uint32_t pidProfileIndex
= 0; pidProfileIndex
< MAX_PROFILE_COUNT
; pidProfileIndex
++) {
4298 cliDumpPidProfile(pidProfileIndex
, dumpMask
);
4300 changePidProfile(pidProfileIndexSave
);
4301 cliPrintHashLine("restore original profile selection");
4304 const uint8_t controlRateProfileIndexSave
= systemConfigCopy
.activeRateProfile
;
4305 for (uint32_t rateIndex
= 0; rateIndex
< CONTROL_RATE_PROFILE_COUNT
; rateIndex
++) {
4306 cliDumpRateProfile(rateIndex
, dumpMask
);
4308 changeControlRateProfile(controlRateProfileIndexSave
);
4309 cliPrintHashLine("restore original rateprofile selection");
4312 cliPrintHashLine("save configuration");
4315 cliDumpPidProfile(systemConfigCopy
.pidProfileIndex
, dumpMask
);
4317 cliDumpRateProfile(systemConfigCopy
.activeRateProfile
, dumpMask
);
4321 if (dumpMask
& DUMP_PROFILE
) {
4322 cliDumpPidProfile(systemConfigCopy
.pidProfileIndex
, dumpMask
);
4325 if (dumpMask
& DUMP_RATES
) {
4326 cliDumpRateProfile(systemConfigCopy
.activeRateProfile
, dumpMask
);
4328 #ifdef USE_PARAMETER_GROUPS
4329 // restore configs from copies
4334 static void cliDump(char *cmdline
)
4336 printConfig(cmdline
, false);
4339 static void cliDiff(char *cmdline
)
4341 printConfig(cmdline
, true);
4347 const char *description
;
4350 void (*func
)(char *cmdline
);
4354 #define CLI_COMMAND_DEF(name, description, args, method) \
4362 #define CLI_COMMAND_DEF(name, description, args, method) \
4369 static void cliHelp(char *cmdline
);
4371 // should be sorted a..z for bsearch()
4372 const clicmd_t cmdTable
[] = {
4373 CLI_COMMAND_DEF("adjrange", "configure adjustment ranges", NULL
, cliAdjustmentRange
),
4374 CLI_COMMAND_DEF("aux", "configure modes", NULL
, cliAux
),
4376 CLI_COMMAND_DEF("beeper", "turn on/off beeper", "list\r\n"
4377 "\t<+|->[name]", cliBeeper
),
4380 CLI_COMMAND_DEF("color", "configure colors", NULL
, cliColor
),
4382 CLI_COMMAND_DEF("defaults", "reset to defaults and reboot", NULL
, cliDefaults
),
4383 CLI_COMMAND_DEF("bl", "reboot into bootloader", NULL
, cliBootloader
),
4384 CLI_COMMAND_DEF("diff", "list configuration changes from default",
4385 "[master|profile|rates|all] {showdefaults}", cliDiff
),
4387 CLI_COMMAND_DEF("dshotprog", "program DShot ESC(s)", "<index> <command>+", cliDshotProg
),
4389 CLI_COMMAND_DEF("dump", "dump configuration",
4390 "[master|profile|rates|all] {showdefaults}", cliDump
),
4391 #ifdef USE_ESCSERIAL
4392 CLI_COMMAND_DEF("escprog", "passthrough esc to serial", "<mode [sk/bl/ki/cc]> <index>", cliEscPassthrough
),
4394 CLI_COMMAND_DEF("exit", NULL
, NULL
, cliExit
),
4395 CLI_COMMAND_DEF("feature", "configure features",
4397 "\t<+|->[name]", cliFeature
),
4399 CLI_COMMAND_DEF("flash_erase", "erase flash chip", NULL
, cliFlashErase
),
4400 CLI_COMMAND_DEF("flash_info", "show flash chip info", NULL
, cliFlashInfo
),
4401 #ifdef USE_FLASH_TOOLS
4402 CLI_COMMAND_DEF("flash_read", NULL
, "<length> <address>", cliFlashRead
),
4403 CLI_COMMAND_DEF("flash_write", NULL
, "<address> <message>", cliFlashWrite
),
4406 CLI_COMMAND_DEF("get", "get variable value", "[name]", cliGet
),
4408 CLI_COMMAND_DEF("gpspassthrough", "passthrough gps to serial", NULL
, cliGpsPassthrough
),
4410 CLI_COMMAND_DEF("help", NULL
, NULL
, cliHelp
),
4412 CLI_COMMAND_DEF("led", "configure leds", NULL
, cliLed
),
4414 CLI_COMMAND_DEF("map", "configure rc channel order", "[<map>]", cliMap
),
4415 #ifndef USE_QUAD_MIXER_ONLY
4416 CLI_COMMAND_DEF("mixer", "configure mixer", "list\r\n\t<name>", cliMixer
),
4418 CLI_COMMAND_DEF("mmix", "custom motor mixer", NULL
, cliMotorMix
),
4420 CLI_COMMAND_DEF("mode_color", "configure mode and special colors", NULL
, cliModeColor
),
4422 CLI_COMMAND_DEF("motor", "get/set motor", "<index> [<value>]", cliMotor
),
4423 CLI_COMMAND_DEF("name", "name of craft", NULL
, cliName
),
4425 CLI_COMMAND_DEF("play_sound", NULL
, "[<index>]", cliPlaySound
),
4427 CLI_COMMAND_DEF("profile", "change profile", "[<index>]", cliProfile
),
4428 CLI_COMMAND_DEF("rateprofile", "change rate profile", "[<index>]", cliRateProfile
),
4429 #if defined(USE_RESOURCE_MGMT)
4430 CLI_COMMAND_DEF("resource", "show/set resources", NULL
, cliResource
),
4432 CLI_COMMAND_DEF("rxfail", "show/set rx failsafe settings", NULL
, cliRxFailsafe
),
4433 CLI_COMMAND_DEF("rxrange", "configure rx channel ranges", NULL
, cliRxRange
),
4434 CLI_COMMAND_DEF("save", "save and reboot", NULL
, cliSave
),
4436 CLI_COMMAND_DEF("sd_info", "sdcard info", NULL
, cliSdInfo
),
4438 CLI_COMMAND_DEF("serial", "configure serial ports", NULL
, cliSerial
),
4439 #ifndef SKIP_SERIAL_PASSTHROUGH
4440 CLI_COMMAND_DEF("serialpassthrough", "passthrough serial data to port", "<id> [baud] [mode] : passthrough to serial", cliSerialPassthrough
),
4443 CLI_COMMAND_DEF("servo", "configure servos", NULL
, cliServo
),
4445 CLI_COMMAND_DEF("set", "change setting", "[<name>=<value>]", cliSet
),
4447 CLI_COMMAND_DEF("smix", "servo mixer", "<rule> <servo> <source> <rate> <speed> <min> <max> <box>\r\n"
4449 "\tload <mixer>\r\n"
4450 "\treverse <servo> <source> r|n", cliServoMix
),
4452 CLI_COMMAND_DEF("status", "show status", NULL
, cliStatus
),
4453 #ifndef SKIP_TASK_STATISTICS
4454 CLI_COMMAND_DEF("tasks", "show task stats", NULL
, cliTasks
),
4456 CLI_COMMAND_DEF("version", "show version", NULL
, cliVersion
),
4458 CLI_COMMAND_DEF("vtx", "vtx channels on switch", NULL
, cliVtx
),
4461 static void cliHelp(char *cmdline
)
4465 for (uint32_t i
= 0; i
< ARRAYLEN(cmdTable
); i
++) {
4466 cliPrint(cmdTable
[i
].name
);
4468 if (cmdTable
[i
].description
) {
4469 cliPrintf(" - %s", cmdTable
[i
].description
);
4471 if (cmdTable
[i
].args
) {
4472 cliPrintf("\r\n\t%s", cmdTable
[i
].args
);
4479 void cliProcess(void)
4485 // Be a little bit tricky. Flush the last inputs buffer, if any.
4486 bufWriterFlush(cliWriter
);
4488 while (serialRxBytesWaiting(cliPort
)) {
4489 uint8_t c
= serialRead(cliPort
);
4490 if (c
== '\t' || c
== '?') {
4491 // do tab completion
4492 const clicmd_t
*cmd
, *pstart
= NULL
, *pend
= NULL
;
4493 uint32_t i
= bufferIndex
;
4494 for (cmd
= cmdTable
; cmd
< cmdTable
+ ARRAYLEN(cmdTable
); cmd
++) {
4495 if (bufferIndex
&& (strncasecmp(cliBuffer
, cmd
->name
, bufferIndex
) != 0))
4501 if (pstart
) { /* Buffer matches one or more commands */
4502 for (; ; bufferIndex
++) {
4503 if (pstart
->name
[bufferIndex
] != pend
->name
[bufferIndex
])
4505 if (!pstart
->name
[bufferIndex
] && bufferIndex
< sizeof(cliBuffer
) - 2) {
4506 /* Unambiguous -- append a space */
4507 cliBuffer
[bufferIndex
++] = ' ';
4508 cliBuffer
[bufferIndex
] = '\0';
4511 cliBuffer
[bufferIndex
] = pstart
->name
[bufferIndex
];
4514 if (!bufferIndex
|| pstart
!= pend
) {
4515 /* Print list of ambiguous matches */
4516 cliPrint("\r\033[K");
4517 for (cmd
= pstart
; cmd
<= pend
; cmd
++) {
4518 cliPrint(cmd
->name
);
4522 i
= 0; /* Redraw prompt */
4524 for (; i
< bufferIndex
; i
++)
4525 cliWrite(cliBuffer
[i
]);
4526 } else if (!bufferIndex
&& c
== 4) { // CTRL-D
4529 } else if (c
== 12) { // NewPage / CTRL-L
4531 cliPrint("\033[2J\033[1;1H");
4533 } else if (bufferIndex
&& (c
== '\n' || c
== '\r')) {
4537 // Strip comment starting with # from line
4538 char *p
= cliBuffer
;
4541 bufferIndex
= (uint32_t)(p
- cliBuffer
);
4544 // Strip trailing whitespace
4545 while (bufferIndex
> 0 && cliBuffer
[bufferIndex
- 1] == ' ') {
4549 // Process non-empty lines
4550 if (bufferIndex
> 0) {
4551 cliBuffer
[bufferIndex
] = 0; // null terminate
4553 const clicmd_t
*cmd
;
4555 for (cmd
= cmdTable
; cmd
< cmdTable
+ ARRAYLEN(cmdTable
); cmd
++) {
4556 if ((options
= checkCommand(cliBuffer
, cmd
->name
))) {
4560 if(cmd
< cmdTable
+ ARRAYLEN(cmdTable
))
4563 cliPrint("Unknown command, try 'help'");
4567 memset(cliBuffer
, 0, sizeof(cliBuffer
));
4569 // 'exit' will reset this flag, so we don't need to print prompt again
4574 } else if (c
== 127) {
4577 cliBuffer
[--bufferIndex
] = 0;
4578 cliPrint("\010 \010");
4580 } else if (bufferIndex
< sizeof(cliBuffer
) && c
>= 32 && c
<= 126) {
4581 if (!bufferIndex
&& c
== ' ')
4582 continue; // Ignore leading spaces
4583 cliBuffer
[bufferIndex
++] = c
;
4589 void cliEnter(serialPort_t
*serialPort
)
4592 cliPort
= serialPort
;
4593 setPrintfSerialPort(cliPort
);
4594 cliWriter
= bufWriterInit(cliWriteBuffer
, sizeof(cliWriteBuffer
), (bufWrite_t
)serialWriteBufShim
, serialPort
);
4596 schedulerSetCalulateTaskStatistics(systemConfig()->task_statistics
);
4599 cliPrint("\r\nEntering CLI Mode, type 'exit' to return, or 'help'\r\n");
4601 cliPrint("\r\nCLI\r\n");
4605 ENABLE_ARMING_FLAG(PREVENT_ARMING
);
4608 void cliInit(const serialConfig_t
*serialConfig
)
4610 UNUSED(serialConfig
);
4611 BUILD_BUG_ON(LOOKUP_TABLE_COUNT
!= ARRAYLEN(lookupTables
));