diff --git a/SPEC README.txt b/SPEC README.txt new file mode 100644 index 0000000000..7ac4d88100 --- /dev/null +++ b/SPEC README.txt @@ -0,0 +1,44 @@ +Disclaimer: + Street League Spec, its partners, and the authors of this software are not responsible for injury or damage to property resulting from the use of this software. Use at your own risk + +What is the RPM limiter? + The RPM limiter seeks to better equalize the performance of drone motors for spec racing purposes. A PID loop is used to limit the drone's maximum rpm to 13k rpm at 100% throttle. The limiter also linearizes the rpm of the motors such that, for example, 50% throttle will result in 50% of 13k rpm, 25% will result in 25% of the max throttle, etc. This means that battery sag will have very little effect on the rpm of the motors throughout the flight, and you will not feel it the same way you typically do. Please ensure voltage alarms configured on your drone. + +IMPORTANT - Before/after flashing: + Before flashing, click auto-detect in the firmware tab in betaflight or select your FC from the dropdown + - This tells Betaflight which custom defaults it should apply to your quad after flashing. + When you connect for the first time, you should be prompted to apply custom defaults. You MUST do this or your FC may not work properly. + If you are not prompted to apply custom defaults, use the custom defaults found on the Betaflight's github + - https://github.com/betaflight/unified-targets/tree/master/configs/default + +Which HEX do I use? + - connect your fc to betaflight + - open the cli tab and type "version" + - look for board_name: (YOUR BOARD NAME HERE) + - this will show you which board you should flash + - if in doubt, check what target your FC's manufacterur recommends in their user manual + - If you have any questions, please reach out on our discord: https://discord.gg/C4HHYccaqk + +How do I activate the boost? + - We've hijacked the beeper mode in betaflight in order to enable boost without any complicated cli commands + - Simply open the modes tab and apply an aux channel and range to the beeper mode + - We also recommend enabling the boost bar in your OSD so you can keep track of your boost usage + - Open the OSD tab and enable "Battery usage" and leave the drop down on "Graphical remaining" + +Optional CLI Commands: + - if you are having issues launching your drone (E.G. low power when you try to take off), consider lowering your moter idle speed. + - You can also try the cli command "set rpm_limiter_idle_rpm = 14" + - This sets the expected idle rpm / 100. For street league a good starting place is 1400 RPM, which is `set rpm_limiter_idle_rpm = 14`. + THE QUAD WILL SPIN UP TO THIS RPM WHEN ARMED, SO DO NOT SET TO ANY VALUE ABOVE 50! + + +Blackbox setup: + You can view the rpm averaged accross the 4 motors as well as the P term, I term and D term in the blackbox. Simply `set debug_mode = RPM_LIMITER` in the cli. The values logged as a result are as follows in this order + Debug 0. Average RPM + Debug 1. RPM error: Difference between desired RPM limit and smoothed average rpm. RPM limit is rpm_limiter_rpm_limit for linearization off and rpm_limiter_rpm_limit*throttle for linearization on. Positive means overspeed, negative means underspeed + Debug 2. I term (positive means term is pulling the throttle down) + Debug 3. D term (positive means term is pulling the throttle down) + +Am I spec yet? + Once you have flashed the HEX, your FC is spec! If you have any issues, please verify they don't also exist on stock betaflight. Then report them here. + - https://github.com/StreetLeagueSpec/betaflight \ No newline at end of file diff --git a/build.sh b/build.sh new file mode 100644 index 0000000000..33a3502d99 --- /dev/null +++ b/build.sh @@ -0,0 +1,26 @@ +#!/bin/bash + +# Check if the input file was provided +if [ -z dos2unix "$1" ]; then + echo "Usage: $0 " + exit 1 +fi + +# Convert line endings of the script file to LF +dos2unix "$0" >/dev/null 2>&1 + +# Loop through each line in the input file +while read -r line; do + # Extract the device name from the current line + device=$(echo "$line" | cut -d',' -f1) + + # Skip the header row + if [ "$device" == "DEVICE_NAME" ]; then + continue + fi + + # Run the make command for the current device + echo "Building firmware for $device" + make "$device" EXTRA_FLAGS="-D'RELEASE_NAME=4.4.1' -DCLOUD_BUILD -DUSE_DSHOT -DUSE_GPS -DUSE_GPS_PLUS_CODES -DUSE_LED_STRIP -DUSE_OSD -DUSE_OSD_HD -DUSE_OSD_SD -DUSE_PINIO -DUSE_SERIALRX -DUSE_SERIALRX_CRSF -DUSE_SERIALRX_DEFAULT -DUSE_SERIALRX_GHST -DUSE_SERIALRX_SBUS -DUSE_SERIALRX_SRXL2 -DUSE_SERIALRX_SPEKTRUM -DUSE_TELEMETRY -DUSE_TELEMETRY_CRSF -DUSE_TELEMETRY_GHST -DUSE_VTX" +#make JHEF411 EXTRA_FLAGS="-D'RELEASE_NAME=4.4.1' -DCLOUD_BUILD -DUSE_DSHOT -DUSE_GPS -DUSE_GPS_PLUS_CODES -DUSE_LED_STRIP -DUSE_OSD -DUSE_OSD_HD -DUSE_OSD_SD -DUSE_PINIO -DUSE_SERIALRX -DUSE_SERIALRX_CRSF -DUSE_SERIALRX_DEFAULT -DUSE_SERIALRX_GHST -DUSE_SERIALRX_SBUS -DUSE_SERIALRX_SRXL2 -DUSE_SERIALRX_SPEKTRUM -DUSE_TELEMETRY -DUSE_TELEMETRY_CRSF -DUSE_TELEMETRY_GHST -DUSE_VTX +done < "$1" \ No newline at end of file diff --git a/common-devices.csv b/common-devices.csv new file mode 100644 index 0000000000..7b258bde2e --- /dev/null +++ b/common-devices.csv @@ -0,0 +1,7 @@ +OMNIBUSF4SD +AIKONF4 +AIKONF7 +HOBBYWING_XROTORF4G3 +HOBBYWING_XROTORF7CONV +BETAFPVF411 +JHEF411 diff --git a/device.csv b/device.csv new file mode 100644 index 0000000000..6f452b8dde --- /dev/null +++ b/device.csv @@ -0,0 +1 @@ +BetafpvF411RX diff --git a/devices.csv b/devices.csv new file mode 100644 index 0000000000..0d6b17fdfa --- /dev/null +++ b/devices.csv @@ -0,0 +1,365 @@ +ALIENFLIGHTF4 +ALIENFLIGHTNGF7 +ALIENFLIGHTNGF7_DUAL +ALIENFLIGHTNGF7_ELRS +ALIENFLIGHTNGF7_RX +AIKONF4 +AIKONF7 +AG3XF4 +AG3XF7 +AIRBOTF4 +AIRBOTF4SD +AIRBOTF4V2 +AIRBOTF7 +AIRBOTF7HDV +HELSEL_STRIKERF7 +NOX +OMNIBUSF4 +OMNIBUSF4FW +OMNIBUSF4NANOV7 +OMNIBUSF4SD +OMNIBUSF4V6 +OMNIBUSF7 +OMNIBUSF7NANOV7 +OMNIBUSF7V2 +AIRB-OMNINXT4 +AIRB-OMNINXT7 +ALIENWHOOPF4 +MERCURYG4 +APEXF7 +AXISFLYINGF7 +AXISFLYINGF7PRO +BETAFPVF405 +BETAFPVF411 +BETAFPVF411RX +BETAFPVF4SX1280 +BETAFPVF722 +BETAFPVH743 +BLUEJAYF4 +NERO +MATEKF405STD_CLONE +CLRACINGF4 +CLRACINGF7 +RUSRACE_F4 +RUSRACE_F7 +SIRMIXALOT +CYCLONEF405_PRO +CYCLONEF722_PRO +DARWINF411 +DARWINF4SX1280HD +DARWINF722HD +DAKEFPVF405 +DAKEFPVF411 +DAKEFPVF722 +DALRCF405 +DALRCF722DUAL +SOULF4 +DFR_F722_DUAL_HD +FURYF4 +FURYF4OSD +MAMBAF405US +MAMBAF405US_I2C +MAMBAF405_2022A +MAMBAF405_2022B +MAMBAF411 +MAMBAF722 +MAMBAF722_2022A +MAMBAF722_2022B +MAMBAF722_I2C +MAMBAF722_X8 +MAMBAG4 +MAMBAH743 +MAMBAH743_2022B +ELINF405 +ELINF722 +DYSF44530D +DYSF4PRO +EACHINEF411_AIO +EACHINEF722 +EACHINEF722_AIO +EMAX_BABYHAWK_II_HD +EMAX_TINYHAWKF4SX1280 +EMAX_TINYHAWK_F411RX +EXF722DUAL +EXUAVF4PRO +FF_FORTINIF4 +FF_FORTINIF4_REV03 +FF_PIKOF4 +FF_PIKOF4OSD +FF_RACEPIT +FF_RACEPITF7 +FF_RACEPITF7_MINI +FF_RACEPIT_MINI +FLYCOLORF7 +FLYCOLORF7_AIO +KIWIF4 +KIWIF4V2 +PLUMF4 +GRAVITYF7 +REVOLT +REVOLTOSD +FLYWOOF405 +FLYWOOF405NANO +FLYWOOF405PRO +FLYWOOF405S_AIO +FLYWOOF411 +FLYWOOF411EVO_HD +FLYWOOF411HEX +FLYWOOF411V2 +FLYWOOF411_5IN1_AIO +FLYWOOF745 +FLYWOOF745NANO +FLYWOOF7DUAL +ANYFCF7 +ANYFCM7 +DRONIUSF7 +F4BY +FENIX_F405 +SPARKY2 +WORMFC +FOXEERF405 +FOXEERF722DUAL +FOXEERF722V2 +FOXEERF722V3 +FOXEERF722V4 +FOXEERF745V2_AIO +FOXEERF745V3_AIO +FOXEERF745V4_AIO +FOXEERF745_AIO +FOXEERH743 +BETAFLIGHTF4 +FPVM_BETAFLIGHTF7 +XRACERF4 +FRSKYF4 +GEELANGF411 +XILOF4 +GEPRCF405 +GEPRCF411 +GEPRCF411SX1280 +GEPRCF411_AIO +GEPRCF411_PRO +GEPRCF722 +GEPRCF722BT +GEPRCF722_BT_HD +GEPRCF745_BT_HD +GEPRCG4AIO +GEPRC_F722_AIO +GEP_F405_VTX_V3 +GEMEF411 +GEMEF722 +CRAZYBEEF4DX +CRAZYBEEF4DXS +CRAZYBEEF4FR +CRAZYBEEF4FS +CRAZYBEEF4SX1280 +HAKRCF405 +HAKRCF405D +HAKRCF405V2 +HAKRCF411 +HAKRCF411D +HAKRCF722 +HAKRCF722D +HAKRCF722MINI +HAKRCF722V2 +HAKRCF7230D +KD722 +KAKUTEF4 +KAKUTEF4V2 +KAKUTEF7 +KAKUTEF7HDV +KAKUTEF7MINI +KAKUTEF7MINIV3 +KAKUTEF7V2 +KAKUTEH7 +KAKUTEH7MINI +KAKUTEH7V2 +TALONF4V2 +TALONF7DJIHD +TALONF7FUSION +TALONF7V2 +HIFIONRCF7 +HGLRCF405 +HGLRCF405AS +HGLRCF411 +HGLRCF411SX1280 +HGLRCF411SX1280_15A +HGLRCF722 +HGLRCF722E +HGLRCF745 +ZEUSF4EVO +ZEUSF4FR +ZEUSF722_AIO +NIDICI_F4 +HOBBYWING_XROTORF4G3 +HOBBYWING_XROTORF7CONV +IFLIGHT_BLITZ_F405 +IFLIGHT_BLITZ_F411RX +IFLIGHT_BLITZ_F722 +IFLIGHT_BLITZ_F722_X1 +IFLIGHT_BLITZ_F7_AIO +IFLIGHT_BLITZ_F7_PRO +IFLIGHT_F405_AIO +IFLIGHT_F405_TWING +IFLIGHT_F411_AIO32 +IFLIGHT_F411_PRO +IFLIGHT_F4SX1280 +IFLIGHT_F722_TWING +IFLIGHT_F745_AIO +IFLIGHT_F745_AIO_V2 +IFLIGHT_H743_AIO_V2 +IFLIGHT_H7_TWING +IFLIGHT_SUCCEX_E_F4 +IFLIGHT_SUCCEX_E_F7 +JBF7 +JBF7_DJI +JBF7_V2 +JHEF405 +JHEF405PRO +JHEF411 +JHEF745 +JHEF7DUAL +JHEH743_AIO +SYNERGYF4 +LDARC_F411 +ELLE0 +FISHDRONEF4 +KROOZX +PIRXF4 +PODIUMF4 +STACKX +VRRACE +LUXAIO +LUXF4OSD +LUXF7HDV +LUXMINIF7 +MERAKRCF405 +MERAKRCF722 +MLTEMPF4 +MLTYPHF4 +MATEKF405AIO +MATEKF405CTR +MATEKF405MINI +MATEKF405SE +MATEKF405STD +MATEKF405TE +MATEKF405TE_SD +MATEKF411 +MATEKF411RX +MATEKF411SE +MATEKF722 +MATEKF722HD +MATEKF722MINI +MATEKF722SE +MATEKH743 +NBD_CRICKETF7 +NBD_CRICKETF7V2 +NBD_GALAXYAIO255 +NBD_INFINITY200RS +NBD_INFINITYAIO +NBD_INFINITYAIOV2 +NBD_INFINITYAIOV2PRO +NBD_INFINITYF4 +FLOWBOX +FLOWBOXV2 +HYBRIDG4 +NEUTRONRCF407 +NEUTRONRCF411AIO +NEUTRONRCF411SX1280 +NEUTRONRCF722AIO +NEUTRONRCF7AIO +NEUTRONRCG4AIO +NEUTRONRCH743AIO +NEUTRONRCH7BT +UAVPNG030MINI +REVO +REVONANO +PYRODRONEF4 +PYRODRONEF4PDB +PYRODRONEF7 +AIRF7 +MINI_H743_HD +ARESF7 +BEEROTORF4 +BLADE_F7 +BLADE_F7_HD +RUSHCORE7 +RUSHF7AIO +AOCODAF405 +AOCODAF405V2MPU6000 +AOCODAF405V2MPU6500 +AOCODAF722BLE +AOCODAF722MINI +AOCODARCF411_AIO +AOCODARCF722_AIO +AOCODARCF7DUAL +AOCODARCH7DUAL +SKYSTARSF405 +SKYSTARSF405AIO +SKYSTARSF411 +SKYSTARSF7HD +SKYSTARSF7HDPRO +SKYSTARSH7HD +ATOMRCF405 +ATOMRCF411 +ATOMRCF722 +SKYZONEF405 +SPEEDYBEEF4 +SPEEDYBEEF405V3 +SPEEDYBEEF7 +SPEEDYBEEF7MINI +SPEEDYBEEF7MINIV2 +SPEEDYBEEF7V2 +SPEEDYBEEF7V3 +SPEEDYBEE_F745_AIO +SPCMAKERF411 +SPEDIXF4 +SPRACINGF4EVO +SPRACINGF4NEO +SPRACINGF7DUAL +SPRACINGH7EXTREME +SPRACINGH7NANO +SPRACINGH7RF +NUCLEOF722 +STM32F411DISCOVERY +STM32F4DISCOVERY +SUB250F411 +MODULARF4 +TCMMF411 +TCMMF7 +COLIBRI +PODRACERAIO +TMH7 +TMOTORF4 +TMOTORF411 +TMOTORF4SX1280 +TMOTORF7 +TMOTORF722SE +TMOTORF7V2 +TMOTORF7_AIO +TMOTORH743 +TMOTORVELOXF7V2 +TMPACERF7 +TMPACERF7MINI +TMVELOXF411 +TMVELOXF7 +TRANSTECF405HD +TRANSTECF411 +TRANSTECF411AIO +TRANSTECF411HD +TRANSTECF7 +TRANSTECF7HD +FPVCYCLEF401 +TUNERCF405 +VGOODF722DUAL +VGOODRCF4 +VGOODRCF405_DJI +VGOODRCF411_DJI +VGOODRCF722_DJI +VIVAF4AIO +WIZZF7HD +YUPIF4 +YUPIF7 +ZEEZF7 +ZEEZF7V2 +ZEEZF7V3 +ZEEZWHOOP \ No newline at end of file diff --git a/my-devices.csv b/my-devices.csv new file mode 100644 index 0000000000..f20c6733bd --- /dev/null +++ b/my-devices.csv @@ -0,0 +1,3 @@ +HOBBYWING_XROTORF7CONV +CRAZYBEEF4FR + diff --git a/src/main/build/debug.c b/src/main/build/debug.c index deb5a9dbb2..6c7dcd5c21 100644 --- a/src/main/build/debug.c +++ b/src/main/build/debug.c @@ -108,5 +108,6 @@ const char * const debugModeNames[DEBUG_COUNT] = { "VTX_MSP", "GPS_DOP", "FAILSAFE", - "DSHOT_TELEMETRY_COUNTS" + "DSHOT_TELEMETRY_COUNTS", + "RPM_LIMITER" }; diff --git a/src/main/build/debug.h b/src/main/build/debug.h index 1413badcb4..54e9be8359 100644 --- a/src/main/build/debug.h +++ b/src/main/build/debug.h @@ -107,6 +107,7 @@ typedef enum { DEBUG_GPS_DOP, DEBUG_FAILSAFE, DEBUG_DSHOT_TELEMETRY_COUNTS, + DEBUG_RPM_LIMITER, DEBUG_COUNT } debugType_e; diff --git a/src/main/build/version.h b/src/main/build/version.h index 53239cdf8e..f9f1d7254a 100644 --- a/src/main/build/version.h +++ b/src/main/build/version.h @@ -28,6 +28,8 @@ #define FC_VERSION_MINOR 4 // increment when a minor release is made (small new feature, change etc) #define FC_VERSION_PATCH_LEVEL 4 // increment when a bug is fixed +#define SPEC_VERSION "SL-1.3.0-locked" + #define FC_VERSION_STRING STR(FC_VERSION_MAJOR) "." STR(FC_VERSION_MINOR) "." STR(FC_VERSION_PATCH_LEVEL) extern const char* const targetName; diff --git a/src/main/cli/cli.c b/src/main/cli/cli.c index 9c3c292152..5265a6d3a8 100644 --- a/src/main/cli/cli.c +++ b/src/main/cli/cli.c @@ -4951,6 +4951,7 @@ static void printVersion(const char *cmdName, bool printBoardInfo) cliPrintf("# %s / %s (%s) %s %s / %s (%s) MSP API: %s", FC_FIRMWARE_NAME, + SPEC_VERSION, targetName, systemConfig()->boardIdentifier, FC_VERSION_STRING, diff --git a/src/main/cli/settings.c b/src/main/cli/settings.c index 1aaba47b69..bc3b98d279 100644 --- a/src/main/cli/settings.c +++ b/src/main/cli/settings.c @@ -931,6 +931,52 @@ const clivalue_t valueTable[] = { { "beeper_dshot_beacon_tone", VAR_UINT8 | MASTER_VALUE, .config.minmaxUnsigned = {1, DSHOT_CMD_BEACON5 }, PG_BEEPER_CONFIG, offsetof(beeperConfig_t, dshotBeaconTone) }, #endif #endif // USE_BEEPER +// PG_MIXER_CONFIG + { "yaw_motors_reversed", VAR_INT8 | MASTER_VALUE | MODE_LOOKUP, .config.lookup = { TABLE_OFF_ON }, PG_MIXER_CONFIG, offsetof(mixerConfig_t, yaw_motors_reversed) }, + { "mixer_type", VAR_UINT8 | MASTER_VALUE | MODE_LOOKUP, .config.lookup = { TABLE_MIXER_TYPE }, PG_MIXER_CONFIG, offsetof(mixerConfig_t, mixer_type) }, + { "crashflip_motor_percent", VAR_UINT8 | MASTER_VALUE, .config.minmaxUnsigned = { 0, 100 }, PG_MIXER_CONFIG, offsetof(mixerConfig_t, crashflip_motor_percent) }, + { "crashflip_expo", VAR_UINT8 | MASTER_VALUE, .config.minmaxUnsigned = { 0, 100 }, PG_MIXER_CONFIG, offsetof(mixerConfig_t, crashflip_expo) }, + //Street League customization + { "rpm_limiter_idle_rpm", VAR_UINT16 | MASTER_VALUE, .config.minmaxUnsigned = { 0, 999 }, PG_MIXER_CONFIG, offsetof(mixerConfig_t, govenor_idle_rpm) }, + + //street league locked + { "rpm_limiter", VAR_INT8 | MASTER_VALUE | MODE_LOOKUP, .config.lookup = { TABLE_OFF_ON }, PG_MIXER_CONFIG, offsetof(mixerConfig_t, govenor) }, + { "rpm_limiter_p", VAR_UINT16 | MASTER_VALUE, .config.minmaxUnsigned = { 20, 20 }, PG_MIXER_CONFIG, offsetof(mixerConfig_t, govenor_p) }, + { "rpm_limiter_i", VAR_UINT16 | MASTER_VALUE, .config.minmaxUnsigned = { 15, 15 }, PG_MIXER_CONFIG, offsetof(mixerConfig_t, govenor_i) }, + { "rpm_limiter_d", VAR_UINT16 | MASTER_VALUE, .config.minmaxUnsigned = { 10, 10 }, PG_MIXER_CONFIG, offsetof(mixerConfig_t, govenor_d) }, + { "rpm_limiter_rpm_limit", VAR_UINT16 | MASTER_VALUE, .config.minmaxUnsigned = { 130, 130 }, PG_MIXER_CONFIG, offsetof(mixerConfig_t, govenor_rpm_limit) }, + { "rpm_limiter_afterburner", VAR_UINT16 | MASTER_VALUE, .config.minmaxUnsigned = { 16, 16 }, PG_MIXER_CONFIG, offsetof(mixerConfig_t, govenor_rpm_afterburner) }, + { "rpm_limiter_afterburner_duration", VAR_UINT8 | MASTER_VALUE, .config.minmaxUnsigned = { 5, 5 }, PG_MIXER_CONFIG, offsetof(mixerConfig_t, govenor_rpm_afterburner_duration) }, + { "rpm_limiter_afterburner_hold_to_use", VAR_INT8 | MASTER_VALUE | MODE_LOOKUP, .config.lookup = { TABLE_OFF_ON }, PG_MIXER_CONFIG, offsetof(mixerConfig_t, govenor_rpm_afterburner_hold_to_use) }, + { "rpm_limiter_afterburner_tank_count", VAR_UINT8 | MASTER_VALUE, .config.minmaxUnsigned = { 3, 3 }, PG_MIXER_CONFIG, offsetof(mixerConfig_t, govenor_rpm_afterburner_tank_count) }, + { "rpm_limiter_afterburner_reset", VAR_INT8 | MASTER_VALUE | MODE_LOOKUP, .config.lookup = { TABLE_OFF_ON }, PG_MIXER_CONFIG, offsetof(mixerConfig_t, govenor_rpm_afterburner_reset) }, + { "rpm_limiter_acceleration_limit", VAR_UINT16 | MASTER_VALUE, .config.minmaxUnsigned = { 60, 60 }, PG_MIXER_CONFIG, offsetof(mixerConfig_t, govenor_acceleration_limit) }, + { "rpm_limiter_deceleration_limit", VAR_UINT16 | MASTER_VALUE, .config.minmaxUnsigned = { 60, 60 }, PG_MIXER_CONFIG, offsetof(mixerConfig_t, govenor_deceleration_limit) }, + { "rpm_limiter_k_factor", VAR_UINT16 | MASTER_VALUE, .config.minmaxUnsigned = { 1000, 1000 }, PG_MIXER_CONFIG, offsetof(mixerConfig_t, govenor_k_factor) }, + { "rpm_limiter_full_linearization", VAR_INT8 | MASTER_VALUE | MODE_LOOKUP, .config.lookup = { TABLE_OFF_ON }, PG_MIXER_CONFIG, offsetof(mixerConfig_t, govenor_rpm_linearization) }, + + //street league unlocked + // { "rpm_limiter", VAR_INT8 | MASTER_VALUE | MODE_LOOKUP, .config.lookup = { TABLE_OFF_ON }, PG_MIXER_CONFIG, offsetof(mixerConfig_t, govenor) }, + // { "rpm_limiter_p", VAR_UINT16 | MASTER_VALUE, .config.minmaxUnsigned = { 0, 10000 }, PG_MIXER_CONFIG, offsetof(mixerConfig_t, govenor_p) }, + // { "rpm_limiter_i", VAR_UINT16 | MASTER_VALUE, .config.minmaxUnsigned = { 0, 10000 }, PG_MIXER_CONFIG, offsetof(mixerConfig_t, govenor_i) }, + // { "rpm_limiter_d", VAR_UINT16 | MASTER_VALUE, .config.minmaxUnsigned = { 0, 10000 }, PG_MIXER_CONFIG, offsetof(mixerConfig_t, govenor_d) }, + // { "rpm_limiter_rpm_limit", VAR_UINT16 | MASTER_VALUE, .config.minmaxUnsigned = { 0, 999 }, PG_MIXER_CONFIG, offsetof(mixerConfig_t, govenor_rpm_limit) }, + // { "rpm_limiter_afterburner", VAR_UINT16 | MASTER_VALUE, .config.minmaxUnsigned = { 0, 999 }, PG_MIXER_CONFIG, offsetof(mixerConfig_t, govenor_rpm_afterburner) }, + // { "rpm_limiter_afterburner_duration", VAR_UINT8 | MASTER_VALUE, .config.minmaxUnsigned = { 0, 255 }, PG_MIXER_CONFIG, offsetof(mixerConfig_t, govenor_rpm_afterburner_duration) }, + // { "rpm_limiter_afterburner_hold_to_use", VAR_INT8 | MASTER_VALUE | MODE_LOOKUP, .config.lookup = { TABLE_OFF_ON }, PG_MIXER_CONFIG, offsetof(mixerConfig_t, govenor_rpm_afterburner_hold_to_use) }, + // { "rpm_limiter_afterburner_tank_count", VAR_UINT8 | MASTER_VALUE, .config.minmaxUnsigned = { 0, 255 }, PG_MIXER_CONFIG, offsetof(mixerConfig_t, govenor_rpm_afterburner_tank_count) }, + // { "rpm_limiter_afterburner_reset", VAR_INT8 | MASTER_VALUE | MODE_LOOKUP, .config.lookup = { TABLE_OFF_ON }, PG_MIXER_CONFIG, offsetof(mixerConfig_t, govenor_rpm_afterburner_reset) }, + // { "rpm_limiter_acceleration_limit", VAR_UINT16 | MASTER_VALUE, .config.minmaxUnsigned = { 0, 50000 }, PG_MIXER_CONFIG, offsetof(mixerConfig_t, govenor_acceleration_limit) }, + // { "rpm_limiter_deceleration_limit", VAR_UINT16 | MASTER_VALUE, .config.minmaxUnsigned = { 0, 50000 }, PG_MIXER_CONFIG, offsetof(mixerConfig_t, govenor_deceleration_limit) }, + // { "rpm_limiter_k_factor", VAR_UINT16 | MASTER_VALUE, .config.minmaxUnsigned = { 1, 10000 }, PG_MIXER_CONFIG, offsetof(mixerConfig_t, govenor_k_factor) }, + // { "rpm_limiter_full_linearization", VAR_INT8 | MASTER_VALUE | MODE_LOOKUP, .config.lookup = { TABLE_OFF_ON }, PG_MIXER_CONFIG, offsetof(mixerConfig_t, govenor_rpm_linearization) }, + + { "3d_deadband_high", VAR_UINT16 | MASTER_VALUE, .config.minmaxUnsigned = { PWM_RANGE_MIDDLE, PWM_PULSE_MAX }, PG_MOTOR_3D_CONFIG, offsetof(flight3DConfig_t, deadband3d_high) }, + { "3d_neutral", VAR_UINT16 | MASTER_VALUE, .config.minmaxUnsigned = { PWM_PULSE_MIN, PWM_PULSE_MAX }, PG_MOTOR_3D_CONFIG, offsetof(flight3DConfig_t, neutral3d) }, + { "3d_deadband_throttle", VAR_UINT16 | MASTER_VALUE, .config.minmaxUnsigned = { 1, 100 }, PG_MOTOR_3D_CONFIG, offsetof(flight3DConfig_t, deadband3d_throttle) }, + { "3d_limit_low", VAR_UINT16 | MASTER_VALUE, .config.minmaxUnsigned = { PWM_PULSE_MIN, PWM_RANGE_MIDDLE }, PG_MOTOR_3D_CONFIG, offsetof(flight3DConfig_t, limit3d_low) }, + { "3d_limit_high", VAR_UINT16 | MASTER_VALUE, .config.minmaxUnsigned = { PWM_RANGE_MIDDLE, PWM_PULSE_MAX }, PG_MOTOR_3D_CONFIG, offsetof(flight3DConfig_t, limit3d_high) }, + { "3d_switched_mode", VAR_UINT8 | MASTER_VALUE | MODE_LOOKUP, .config.lookup = { TABLE_OFF_ON }, PG_MOTOR_3D_CONFIG, offsetof(flight3DConfig_t, switched_mode3d) }, // PG_MIXER_CONFIG { "yaw_motors_reversed", VAR_INT8 | MASTER_VALUE | MODE_LOOKUP, .config.lookup = { TABLE_OFF_ON }, PG_MIXER_CONFIG, offsetof(mixerConfig_t, yaw_motors_reversed) }, @@ -1699,6 +1745,7 @@ const clivalue_t valueTable[] = { #endif #endif { "craft_name", VAR_UINT8 | MASTER_VALUE | MODE_STRING, .config.string = { 1, MAX_NAME_LENGTH, STRING_FLAGS_NONE }, PG_PILOT_CONFIG, offsetof(pilotConfig_t, craftName) }, + { "drone_id", VAR_UINT8 | MASTER_VALUE | MODE_STRING, .config.string = { 1, MAX_NAME_LENGTH, STRING_FLAGS_NONE }, PG_PILOT_CONFIG, offsetof(pilotConfig_t, droneId) }, #ifdef USE_OSD { "pilot_name", VAR_UINT8 | MASTER_VALUE | MODE_STRING, .config.string = { 1, MAX_NAME_LENGTH, STRING_FLAGS_NONE }, PG_PILOT_CONFIG, offsetof(pilotConfig_t, pilotName) }, #endif diff --git a/src/main/config/config.c b/src/main/config/config.c index 1aa4e9220e..56c045d882 100644 --- a/src/main/config/config.c +++ b/src/main/config/config.c @@ -111,6 +111,7 @@ PG_REGISTER_WITH_RESET_TEMPLATE(pilotConfig_t, pilotConfig, PG_PILOT_CONFIG, 2); PG_RESET_TEMPLATE(pilotConfig_t, pilotConfig, .craftName = { 0 }, .pilotName = { 0 }, + .droneId = { 0 }, ); PG_REGISTER_WITH_RESET_TEMPLATE(systemConfig_t, systemConfig, PG_SYSTEM_CONFIG, 3); diff --git a/src/main/config/config.h b/src/main/config/config.h index af947a50eb..be12eb8f6d 100644 --- a/src/main/config/config.h +++ b/src/main/config/config.h @@ -36,6 +36,7 @@ typedef enum { typedef struct pilotConfig_s { char craftName[MAX_NAME_LENGTH + 1]; char pilotName[MAX_NAME_LENGTH + 1]; + char droneId[MAX_NAME_LENGTH + 1]; } pilotConfig_t; PG_DECLARE(pilotConfig_t, pilotConfig); diff --git a/src/main/fc/rc_modes.h b/src/main/fc/rc_modes.h index 705683f637..1162cca4db 100644 --- a/src/main/fc/rc_modes.h +++ b/src/main/fc/rc_modes.h @@ -78,7 +78,9 @@ typedef enum { BOXSTICKCOMMANDDISABLE, BOXBEEPERMUTE, BOXREADY, + BOXBOOST, CHECKBOX_ITEM_COUNT + } boxId_e; typedef enum { diff --git a/src/main/flight/mixer.c b/src/main/flight/mixer.c index a02f78ee7b..2fe49745a5 100644 --- a/src/main/flight/mixer.c +++ b/src/main/flight/mixer.c @@ -20,9 +20,7 @@ #include #include -#include #include -#include #include "platform.h" @@ -109,113 +107,114 @@ static FAST_DATA_ZERO_INIT int8_t motorOutputMixSign; static void calculateThrottleAndCurrentMotorEndpoints(timeUs_t currentTimeUs) { - static uint16_t rcThrottlePrevious = 0; // Store the last throttle direction for deadband transitions - static timeUs_t reversalTimeUs = 0; // time when motors last reversed in 3D mode + UNUSED(currentTimeUs); + // static uint16_t rcThrottlePrevious = 0; // Store the last throttle direction for deadband transitions + // static timeUs_t reversalTimeUs = 0; // time when motors last reversed in 3D mode static float motorRangeMinIncrease = 0; float currentThrottleInputRange = 0; - if (mixerRuntime.feature3dEnabled) { - uint16_t rcCommand3dDeadBandLow; - uint16_t rcCommand3dDeadBandHigh; +// if (mixerRuntime.feature3dEnabled) { +// uint16_t rcCommand3dDeadBandLow; +// uint16_t rcCommand3dDeadBandHigh; - if (!ARMING_FLAG(ARMED)) { - rcThrottlePrevious = rxConfig()->midrc; // When disarmed set to mid_rc. It always results in positive direction after arming. - } +// if (!ARMING_FLAG(ARMED)) { +// rcThrottlePrevious = rxConfig()->midrc; // When disarmed set to mid_rc. It always results in positive direction after arming. +// } - if (IS_RC_MODE_ACTIVE(BOX3D) || flight3DConfig()->switched_mode3d) { - // The min_check range is halved because the output throttle is scaled to 500us. - // So by using half of min_check we maintain the same low-throttle deadband - // stick travel as normal non-3D mode. - const int mincheckOffset = (rxConfig()->mincheck - PWM_RANGE_MIN) / 2; - rcCommand3dDeadBandLow = rxConfig()->midrc - mincheckOffset; - rcCommand3dDeadBandHigh = rxConfig()->midrc + mincheckOffset; - } else { - rcCommand3dDeadBandLow = rxConfig()->midrc - flight3DConfig()->deadband3d_throttle; - rcCommand3dDeadBandHigh = rxConfig()->midrc + flight3DConfig()->deadband3d_throttle; - } +// if (IS_RC_MODE_ACTIVE(BOX3D) || flight3DConfig()->switched_mode3d) { +// // The min_check range is halved because the output throttle is scaled to 500us. +// // So by using half of min_check we maintain the same low-throttle deadband +// // stick travel as normal non-3D mode. +// const int mincheckOffset = (rxConfig()->mincheck - PWM_RANGE_MIN) / 2; +// rcCommand3dDeadBandLow = rxConfig()->midrc - mincheckOffset; +// rcCommand3dDeadBandHigh = rxConfig()->midrc + mincheckOffset; +// } else { +// rcCommand3dDeadBandLow = rxConfig()->midrc - flight3DConfig()->deadband3d_throttle; +// rcCommand3dDeadBandHigh = rxConfig()->midrc + flight3DConfig()->deadband3d_throttle; +// } - const float rcCommandThrottleRange3dLow = rcCommand3dDeadBandLow - PWM_RANGE_MIN; - const float rcCommandThrottleRange3dHigh = PWM_RANGE_MAX - rcCommand3dDeadBandHigh; +// const float rcCommandThrottleRange3dLow = rcCommand3dDeadBandLow - PWM_RANGE_MIN; +// const float rcCommandThrottleRange3dHigh = PWM_RANGE_MAX - rcCommand3dDeadBandHigh; - if (rcCommand[THROTTLE] <= rcCommand3dDeadBandLow || isFlipOverAfterCrashActive()) { - // INVERTED - motorRangeMin = mixerRuntime.motorOutputLow; - motorRangeMax = mixerRuntime.deadbandMotor3dLow; -#ifdef USE_DSHOT - if (isMotorProtocolDshot()) { - motorOutputMin = mixerRuntime.motorOutputLow; - motorOutputRange = mixerRuntime.deadbandMotor3dLow - mixerRuntime.motorOutputLow; - } else -#endif - { - motorOutputMin = mixerRuntime.deadbandMotor3dLow; - motorOutputRange = mixerRuntime.motorOutputLow - mixerRuntime.deadbandMotor3dLow; - } +// if (rcCommand[THROTTLE] <= rcCommand3dDeadBandLow || isFlipOverAfterCrashActive()) { +// // INVERTED +// motorRangeMin = mixerRuntime.motorOutputLow; +// motorRangeMax = mixerRuntime.deadbandMotor3dLow; +// #ifdef USE_DSHOT +// if (isMotorProtocolDshot()) { +// motorOutputMin = mixerRuntime.motorOutputLow; +// motorOutputRange = mixerRuntime.deadbandMotor3dLow - mixerRuntime.motorOutputLow; +// } else +// #endif +// { +// motorOutputMin = mixerRuntime.deadbandMotor3dLow; +// motorOutputRange = mixerRuntime.motorOutputLow - mixerRuntime.deadbandMotor3dLow; +// } - if (motorOutputMixSign != -1) { - reversalTimeUs = currentTimeUs; - } - motorOutputMixSign = -1; +// if (motorOutputMixSign != -1) { +// reversalTimeUs = currentTimeUs; +// } +// motorOutputMixSign = -1; - rcThrottlePrevious = rcCommand[THROTTLE]; - throttle = rcCommand3dDeadBandLow - rcCommand[THROTTLE]; - currentThrottleInputRange = rcCommandThrottleRange3dLow; - } else if (rcCommand[THROTTLE] >= rcCommand3dDeadBandHigh) { - // NORMAL - motorRangeMin = mixerRuntime.deadbandMotor3dHigh; - motorRangeMax = mixerRuntime.motorOutputHigh; - motorOutputMin = mixerRuntime.deadbandMotor3dHigh; - motorOutputRange = mixerRuntime.motorOutputHigh - mixerRuntime.deadbandMotor3dHigh; - if (motorOutputMixSign != 1) { - reversalTimeUs = currentTimeUs; - } - motorOutputMixSign = 1; - rcThrottlePrevious = rcCommand[THROTTLE]; - throttle = rcCommand[THROTTLE] - rcCommand3dDeadBandHigh; - currentThrottleInputRange = rcCommandThrottleRange3dHigh; - } else if ((rcThrottlePrevious <= rcCommand3dDeadBandLow && - !flight3DConfigMutable()->switched_mode3d) || - isMotorsReversed()) { - // INVERTED_TO_DEADBAND - motorRangeMin = mixerRuntime.motorOutputLow; - motorRangeMax = mixerRuntime.deadbandMotor3dLow; +// rcThrottlePrevious = rcCommand[THROTTLE]; +// throttle = rcCommand3dDeadBandLow - rcCommand[THROTTLE]; +// currentThrottleInputRange = rcCommandThrottleRange3dLow; +// } else if (rcCommand[THROTTLE] >= rcCommand3dDeadBandHigh) { +// // NORMAL +// motorRangeMin = mixerRuntime.deadbandMotor3dHigh; +// motorRangeMax = mixerRuntime.motorOutputHigh; +// motorOutputMin = mixerRuntime.deadbandMotor3dHigh; +// motorOutputRange = mixerRuntime.motorOutputHigh - mixerRuntime.deadbandMotor3dHigh; +// if (motorOutputMixSign != 1) { +// reversalTimeUs = currentTimeUs; +// } +// motorOutputMixSign = 1; +// rcThrottlePrevious = rcCommand[THROTTLE]; +// throttle = rcCommand[THROTTLE] - rcCommand3dDeadBandHigh; +// currentThrottleInputRange = rcCommandThrottleRange3dHigh; +// } else if ((rcThrottlePrevious <= rcCommand3dDeadBandLow && +// !flight3DConfigMutable()->switched_mode3d) || +// isMotorsReversed()) { +// // INVERTED_TO_DEADBAND +// motorRangeMin = mixerRuntime.motorOutputLow; +// motorRangeMax = mixerRuntime.deadbandMotor3dLow; -#ifdef USE_DSHOT - if (isMotorProtocolDshot()) { - motorOutputMin = mixerRuntime.motorOutputLow; - motorOutputRange = mixerRuntime.deadbandMotor3dLow - mixerRuntime.motorOutputLow; - } else -#endif - { - motorOutputMin = mixerRuntime.deadbandMotor3dLow; - motorOutputRange = mixerRuntime.motorOutputLow - mixerRuntime.deadbandMotor3dLow; - } +// #ifdef USE_DSHOT +// if (isMotorProtocolDshot()) { +// motorOutputMin = mixerRuntime.motorOutputLow; +// motorOutputRange = mixerRuntime.deadbandMotor3dLow - mixerRuntime.motorOutputLow; +// } else +// #endif +// { +// motorOutputMin = mixerRuntime.deadbandMotor3dLow; +// motorOutputRange = mixerRuntime.motorOutputLow - mixerRuntime.deadbandMotor3dLow; +// } - if (motorOutputMixSign != -1) { - reversalTimeUs = currentTimeUs; - } - motorOutputMixSign = -1; +// if (motorOutputMixSign != -1) { +// reversalTimeUs = currentTimeUs; +// } +// motorOutputMixSign = -1; - throttle = 0; - currentThrottleInputRange = rcCommandThrottleRange3dLow; - } else { - // NORMAL_TO_DEADBAND - motorRangeMin = mixerRuntime.deadbandMotor3dHigh; - motorRangeMax = mixerRuntime.motorOutputHigh; - motorOutputMin = mixerRuntime.deadbandMotor3dHigh; - motorOutputRange = mixerRuntime.motorOutputHigh - mixerRuntime.deadbandMotor3dHigh; - if (motorOutputMixSign != 1) { - reversalTimeUs = currentTimeUs; - } - motorOutputMixSign = 1; - throttle = 0; - currentThrottleInputRange = rcCommandThrottleRange3dHigh; - } - if (currentTimeUs - reversalTimeUs < 250000) { - // keep iterm zero for 250ms after motor reversal - pidResetIterm(); - } - } else { +// throttle = 0; +// currentThrottleInputRange = rcCommandThrottleRange3dLow; +// } else { +// // NORMAL_TO_DEADBAND +// motorRangeMin = mixerRuntime.deadbandMotor3dHigh; +// motorRangeMax = mixerRuntime.motorOutputHigh; +// motorOutputMin = mixerRuntime.deadbandMotor3dHigh; +// motorOutputRange = mixerRuntime.motorOutputHigh - mixerRuntime.deadbandMotor3dHigh; +// if (motorOutputMixSign != 1) { +// reversalTimeUs = currentTimeUs; +// } +// motorOutputMixSign = 1; +// throttle = 0; +// currentThrottleInputRange = rcCommandThrottleRange3dHigh; +// } +// if (currentTimeUs - reversalTimeUs < 250000) { +// // keep iterm zero for 250ms after motor reversal +// pidResetIterm(); +// } + // } else { throttle = rcCommand[THROTTLE] - PWM_RANGE_MIN + throttleAngleCorrection; currentThrottleInputRange = PWM_RANGE; @@ -245,13 +244,13 @@ static void calculateThrottleAndCurrentMotorEndpoints(timeUs_t currentTimeUs) #if defined(USE_BATTERY_VOLTAGE_SAG_COMPENSATION) float motorRangeAttenuationFactor = 0; // reduce motorRangeMax when battery is full - if (mixerRuntime.vbatSagCompensationFactor > 0.0f) { + if (!mixerConfig()->govenor && mixerRuntime.vbatSagCompensationFactor > 0.0f) { const uint16_t currentCellVoltage = getBatterySagCellVoltage(); // batteryGoodness = 1 when voltage is above vbatFull, and 0 when voltage is below vbatLow float batteryGoodness = 1.0f - constrainf((mixerRuntime.vbatFull - currentCellVoltage) / mixerRuntime.vbatRangeToCompensate, 0.0f, 1.0f); motorRangeAttenuationFactor = (mixerRuntime.vbatRangeToCompensate / mixerRuntime.vbatFull) * batteryGoodness * mixerRuntime.vbatSagCompensationFactor; - DEBUG_SET(DEBUG_BATTERY, 2, lrintf(batteryGoodness * 100)); - DEBUG_SET(DEBUG_BATTERY, 3, lrintf(motorRangeAttenuationFactor * 1000)); + DEBUG_SET(DEBUG_BATTERY, 2, batteryGoodness * 100); + DEBUG_SET(DEBUG_BATTERY, 3, motorRangeAttenuationFactor * 1000); } motorRangeMax = isFlipOverAfterCrashActive() ? mixerRuntime.motorOutputHigh : mixerRuntime.motorOutputHigh - motorRangeAttenuationFactor * (mixerRuntime.motorOutputHigh - mixerRuntime.motorOutputLow); #else @@ -262,8 +261,7 @@ static void calculateThrottleAndCurrentMotorEndpoints(timeUs_t currentTimeUs) motorOutputMin = motorRangeMin; motorOutputRange = motorRangeMax - motorRangeMin; motorOutputMixSign = 1; - } - + // } throttle = constrainf(throttle / currentThrottleInputRange, 0.0f, 1.0f); } @@ -346,17 +344,212 @@ static void applyFlipOverAfterCrashModeToMotors(void) } } +uint8_t getAfterburnerTanksRemaining(void) +{ + return mixerRuntime.afterburnerTanksRemaining; +} + +float getAfterburnerTankPercent(void) +{ + return mixerRuntime.afterburnerTankPercent; +} + +static void applyRPMLimiter(void) +{ + if (mixerRuntime.govenorEnabled) { + float RPM_GOVENOR_LIMIT = 0.0f; + float averageRPM = 0.0f; + float averageRPM_smoothed = 0.0f; + float PIDOutput = 0.0f; + float rcCommandThrottle = (rcCommand[THROTTLE]-1000)/1000.0f; + float maxRPMLimit = 0.0f; + + maxRPMLimit = mixerRuntime.RPMLimit; + + //afterburner code + //if drone is armed + if (ARMING_FLAG(ARMED)) { + //if the afterburner switch is engaged + if(IS_RC_MODE_ACTIVE(BOXBOOST)) { + //if the afterburner isn't initiated + if(mixerRuntime.afterburnerInitiated == false) { + //if there's charge in the tank + if(mixerRuntime.afterburnerTankPercent>0.0f) { + //if we have another tank left + if(mixerRuntime.afterburnerTanksRemaining>0) { + mixerRuntime.afterburnerInitiated = true; + } + } + + } + //if the afterburner switch is NOT engaged + } else { + //if hold to boost is enabled + if(mixerRuntime.afterburnerHoldToBoost) { + mixerRuntime.afterburnerInitiated = false; + } + } + + //use afterburner + if(mixerRuntime.afterburnerInitiated) { + //if the tank is empty, reset + if(mixerRuntime.afterburnerTankPercent<=0.0f) { + mixerRuntime.afterburnerInitiated = false; + //only refil the tank if we have one remaining + if(mixerRuntime.afterburnerTanksRemaining>0) { + mixerRuntime.afterburnerTanksRemaining -= 1; + } + if(mixerRuntime.afterburnerTanksRemaining>0) { + mixerRuntime.afterburnerTankPercent = 100.0f; + } + + } + //tank percent decreases linearly + mixerRuntime.afterburnerTankPercent -= (pidGetDT()/(mixerRuntime.afterburnerDuration))*100.0f; + //tank percent can never be above 100% + mixerRuntime.afterburnerTankPercent = MIN(mixerRuntime.afterburnerTankPercent,100.0f); + //tank percent can never be below 0% + mixerRuntime.afterburnerTankPercent = MAX(mixerRuntime.afterburnerTankPercent,0.0f); + //increase the rpm limit + maxRPMLimit = mixerRuntime.RPMLimit+(mixerRuntime.afterburnerRPM*mixerRuntime.afterburnerTankPercent*0.01f); + } + }else{ + if(mixerRuntime.afterburnerReset) { + mixerRuntime.afterburnerTankPercent = 100.0f; + mixerRuntime.afterburnerTanksRemaining = mixerConfig()->govenor_rpm_afterburner_tank_count; + } + } + + //Street League customization + if (mixerRuntime.rpmLinearization) { + //scales rpm setpoint between idle rpm and max rpm limit based on throttle percent + RPM_GOVENOR_LIMIT = ((maxRPMLimit - mixerConfig()->govenor_idle_rpm))*100.0f*(rcCommandThrottle) + mixerConfig()->govenor_idle_rpm * 100.0f; + + //limit the speed with which the rpm setpoint can change based on the rpm_limiter_acceleration_limit cli command + float acceleration = RPM_GOVENOR_LIMIT - mixerRuntime.govenorPreviousRPMLimit; + if(acceleration > 0) { + acceleration = MIN(acceleration, mixerRuntime.govenorAccelerationLimit); + RPM_GOVENOR_LIMIT = mixerRuntime.govenorPreviousRPMLimit + acceleration; + } + else if(acceleration < 0) { + acceleration = MAX(acceleration, -mixerRuntime.govenorDecelerationLimit); + RPM_GOVENOR_LIMIT = mixerRuntime.govenorPreviousRPMLimit + acceleration; + } + } else { + throttle = throttle * mixerRuntime.govenorExpectedThrottleLimit; + RPM_GOVENOR_LIMIT = ((maxRPMLimit))*100.0f; + } + + //get the rpm averaged across the motors + bool motorsSaturated = false; + bool motorsDesaturated = false; + for (int i = 0; i < getMotorCount(); i++) { + averageRPM += (float)getDshotTelemetry(i); + if (motor[i] >= motorConfig()->maxthrottle) { + motorsSaturated = true; + } + if (motor[i] <= motorConfig()->minthrottle) { + motorsDesaturated = false; + } + } + averageRPM = 100.0f * averageRPM / (float)(getMotorCount()*mixerRuntime.motorPoleCount/2.0f); + + //get the smoothed rpm to avoid d term noise + averageRPM_smoothed = mixerRuntime.govenorPreviousSmoothedRPM + (float)mixerRuntime.govenorDelayK * (averageRPM - mixerRuntime.govenorPreviousSmoothedRPM); //kinda braindead to convert to rps then back + + float smoothedRPMError = averageRPM_smoothed - RPM_GOVENOR_LIMIT; + float govenorP = smoothedRPMError * mixerRuntime.govenorPGain; //+ when overspped + float govenorD = (smoothedRPMError-mixerRuntime.govenorPreviousSmoothedRPMError) * mixerRuntime.govenorDGain; // + when quickly going overspeed + + if (mixerRuntime.rpmLinearization) { + /* + //don't let I term wind up if throttle is below the motor idle + if (rcCommandThrottle < motorConfig()->digitalIdleOffsetValue / 10000.0f) { + mixerRuntime.govenorI *= 1.0f/(1.0f+(pidGetDT()*10.0f)); //slowly ramp down i term instead of resetting to avoid throttle pulsing cheats + } else { + //don't let I term wind up if motors are saturated. Otherwise, motors may stay at high throttle even after low throttle is commanded + if(!motorsSaturated) + { + mixerRuntime.govenorI += smoothedRPMError * mixerRuntime.govenorIGain; // + when overspeed + } + }*/ + + //don't let I term wind up if throttle is below the motor idle + if (rcCommandThrottle < motorConfig()->digitalIdleOffsetValue / 10000.0f) { + mixerRuntime.govenorI *= 1.0f/(1.0f+(pidGetDT()*10.0f)); //slowly ramp down i term + //mixerRuntime.govenorI = 0.0f; + } else { + //don't let I term wind up if motors are saturated. Otherwise, motors may stay at high throttle even after low throttle is commanded + if(!motorsSaturated && !motorsDesaturated) + { + mixerRuntime.govenorI += smoothedRPMError * mixerRuntime.govenorIGain; // + when overspeed + }else{ + mixerRuntime.govenorI *= 1.0f/(1.0f+(pidGetDT()*10.0f)); //slowly ramp down i term + } + } + + //sum our pid terms + PIDOutput = govenorP + mixerRuntime.govenorI + govenorD; //more + when overspeed, should be subtracted from throttle + + } else { + throttle = throttle * mixerRuntime.govenorExpectedThrottleLimit; + mixerRuntime.govenorI += smoothedRPMError * mixerRuntime.govenorIGain; // + when overspeed + mixerRuntime.govenorI = MAX(mixerRuntime.govenorI, 0.0f); + PIDOutput = govenorP + mixerRuntime.govenorI + govenorD; //more + when overspeed, should be subtracted from throttle + if (PIDOutput > 0.05) { + mixerRuntime.govenorExpectedThrottleLimit = 0.9994 * mixerRuntime.govenorExpectedThrottleLimit; + } + if (PIDOutput < -0.05 && rcCommand[THROTTLE] > 1950 && !motorsSaturated) { + mixerRuntime.govenorExpectedThrottleLimit = (1+1-0.9994) * mixerRuntime.govenorExpectedThrottleLimit; + mixerRuntime.govenorExpectedThrottleLimit = MAX(mixerRuntime.govenorExpectedThrottleLimit, 1.0f); + } + + PIDOutput = MAX(PIDOutput,0.0f); + + } + if (motorConfig()->dev.useDshotTelemetry) { + if (mixerRuntime.govenor_init) { + if (mixerRuntime.rpmLinearization) { + throttle = constrainf(-PIDOutput, 0.0f, 1.0f); + } else { + throttle = constrainf(throttle-PIDOutput, 0.0f, 1.0f); + } + } + } else { //if dshot telemetry isn't enabled. Keep the throttle at zero + throttle = 0.0f; + } + + mixerRuntime.govenor_init = true; + + //update previous values for next loop + mixerRuntime.prevAverageRPM = averageRPM; + mixerRuntime.govenorPreviousSmoothedRPM = averageRPM_smoothed; + mixerRuntime.govenorPreviousSmoothedRPMError = smoothedRPMError; + mixerRuntime.govenorPreviousRPMLimit = RPM_GOVENOR_LIMIT; + + DEBUG_SET(DEBUG_RPM_LIMITER, 0, averageRPM);//unfiltered average rpm + DEBUG_SET(DEBUG_RPM_LIMITER, 1, averageRPM_smoothed); //filtered average rpm + DEBUG_SET(DEBUG_RPM_LIMITER, 2, smoothedRPMError); //P term + DEBUG_SET(DEBUG_RPM_LIMITER, 3, mixerRuntime.govenorI*100.0f); // I term + DEBUG_SET(DEBUG_RPM_LIMITER, 4, govenorD*10000.0f); // D term + + /*DEBUG_SET(DEBUG_RPM_LIMITER, 0, mixerRuntime.afterburnerInitiated); + DEBUG_SET(DEBUG_RPM_LIMITER, 1, mixerRuntime.afterburnerTankPercent); + DEBUG_SET(DEBUG_RPM_LIMITER, 2, mixerRuntime.afterburnerTanksRemaining); + DEBUG_SET(DEBUG_RPM_LIMITER, 3, maxRPMLimit);*/ + } +} + static void applyMixToMotors(float motorMix[MAX_SUPPORTED_MOTORS], motorMixer_t *activeMixer) { - // Now add in the desired throttle, but keep in a range that doesn't clip adjusted - // roll/pitch/yaw. This could move throttle down, but also up for those low throttle flips. for (int i = 0; i < mixerRuntime.motorCount; i++) { float motorOutput = motorOutputMixSign * motorMix[i] + throttle * activeMixer[i].throttle; -#ifdef USE_THRUST_LINEARIZATION - motorOutput = pidApplyThrustLinearization(motorOutput); -#endif + if (!mixerConfig()->govenor) { + #ifdef USE_THRUST_LINEARIZATION + motorOutput = pidApplyThrustLinearization(motorOutput); + #endif + } motorOutput = motorOutputMin + motorOutputRange * motorOutput; - #ifdef USE_SERVOS if (mixerIsTricopter()) { motorOutput += mixerTricopterMotorCorrection(i); @@ -377,6 +570,8 @@ static void applyMixToMotors(float motorMix[MAX_SUPPORTED_MOTORS], motorMixer_t // Disarmed mode if (!ARMING_FLAG(ARMED)) { + mixerRuntime.govenorI = 0; + mixerRuntime.govenor_init = false; for (int i = 0; i < mixerRuntime.motorCount; i++) { motor[i] = motor_disarmed[i]; } @@ -425,68 +620,47 @@ static void updateDynLpfCutoffs(timeUs_t currentTimeUs, float throttle) } #endif -static void applyMixerAdjustmentLinear(float *motorMix, const bool airmodeEnabled) -{ - float airmodeTransitionPercent = 1.0f; - float motorDeltaScale = 0.5f; - - if (!airmodeEnabled && throttle < 0.5f) { - // this scales the motor mix authority to be 0.5 at 0 throttle, and 1.0 at 0.5 throttle as airmode off intended for things to work. - // also lays the groundwork for how an airmode percent would work. - airmodeTransitionPercent = scaleRangef(throttle, 0.0f, 0.5f, 0.5f, 1.0f); // 0.5 throttle is full transition, and 0.0 throttle is 50% airmodeTransitionPercent - motorDeltaScale *= airmodeTransitionPercent; // this should be half of the motor authority allowed - } - - const float motorMixNormalizationFactor = motorMixRange > 1.0f ? airmodeTransitionPercent / motorMixRange : airmodeTransitionPercent; - - const float motorMixDelta = motorDeltaScale * motorMixRange; - - float minMotor = FLT_MAX; - float maxMotor = FLT_MIN; +static void applyMixerAdjustmentLinear(float *motorMix, const bool airmodeEnabled) { + const float motorMixNormalizationFactor = motorMixRange > 1.0f ? motorMixRange : 1.0f; + const float motorMixDelta = 0.5f * motorMixRange; for (int i = 0; i < mixerRuntime.motorCount; ++i) { - if (mixerConfig()->mixer_type == MIXER_LINEAR) { - motorMix[i] = scaleRangef(throttle, 0.0f, 1.0f, motorMix[i] + motorMixDelta, motorMix[i] - motorMixDelta); - } else { - motorMix[i] = scaleRangef(throttle, 0.0f, 1.0f, motorMix[i] + fabsf(motorMix[i]), motorMix[i] - fabsf(motorMix[i])); + if (airmodeEnabled || throttle > 0.5f) { + if (mixerConfig()->mixer_type == MIXER_LINEAR) { + motorMix[i] = scaleRangef(throttle, 0.0f, 1.0f, motorMix[i] + motorMixDelta, motorMix[i] - motorMixDelta); + } else { + motorMix[i] = scaleRangef(throttle, 0.0f, 1.0f, motorMix[i] + ABS(motorMix[i]), motorMix[i] - ABS(motorMix[i])); + } } - motorMix[i] *= motorMixNormalizationFactor; - - maxMotor = MAX(motorMix[i], maxMotor); - minMotor = MIN(motorMix[i], minMotor); + motorMix[i] /= motorMixNormalizationFactor; } - - // constrain throttle so it won't clip any outputs - throttle = constrainf(throttle, -minMotor, 1.0f - maxMotor); } -static void applyMixerAdjustment(float *motorMix, const float motorMixMin, const float motorMixMax, const bool airmodeEnabled) -{ +static void applyMixerAdjustment(float *motorMix, const float motorMixMin, const float motorMixMax, const bool airmodeEnabled) { #ifdef USE_AIRMODE_LPF const float unadjustedThrottle = throttle; throttle += pidGetAirmodeThrottleOffset(); - float airmodeThrottleChange = 0.0f; + float airmodeThrottleChange = 0; #endif - float airmodeTransitionPercent = 1.0f; - if (!airmodeEnabled && throttle < 0.5f) { - // this scales the motor mix authority to be 0.5 at 0 throttle, and 1.0 at 0.5 throttle as airmode off intended for things to work. - // also lays the groundwork for how an airmode percent would work. - airmodeTransitionPercent = scaleRangef(throttle, 0.0f, 0.5f, 0.5f, 1.0f); // 0.5 throttle is full transition, and 0.0 throttle is 50% airmodeTransitionPercent + if (motorMixRange > 1.0f) { + for (int i = 0; i < mixerRuntime.motorCount; i++) { + motorMix[i] /= motorMixRange; + } + // Get the maximum correction by setting offset to center when airmode enabled + if (airmodeEnabled) { + throttle = 0.5f; + } + } else { + if (airmodeEnabled || throttle > 0.5f) { + throttle = constrainf(throttle, -motorMixMin, 1.0f - motorMixMax); +#ifdef USE_AIRMODE_LPF + airmodeThrottleChange = constrainf(unadjustedThrottle, -motorMixMin, 1.0f - motorMixMax) - unadjustedThrottle; +#endif + } } - const float motorMixNormalizationFactor = motorMixRange > 1.0f ? airmodeTransitionPercent / motorMixRange : airmodeTransitionPercent; - - for (int i = 0; i < mixerRuntime.motorCount; i++) { - motorMix[i] *= motorMixNormalizationFactor; - } - - const float normalizedMotorMixMin = motorMixMin * motorMixNormalizationFactor; - const float normalizedMotorMixMax = motorMixMax * motorMixNormalizationFactor; - throttle = constrainf(throttle, -normalizedMotorMixMin, 1.0f - normalizedMotorMixMax); - #ifdef USE_AIRMODE_LPF - airmodeThrottleChange = constrainf(unadjustedThrottle, -normalizedMotorMixMin, 1.0f - normalizedMotorMixMax) - unadjustedThrottle; pidUpdateAirmodeLpf(airmodeThrottleChange); #endif } @@ -557,7 +731,7 @@ FAST_CODE_NOINLINE void mixTable(timeUs_t currentTimeUs) } #endif - // send throttle value to blackbox, including scaling and throttle boost, but not TL compensation, dyn idle or airmode + // send throttle value to blackbox, including scaling and throttle boost, but not TL compensation, dyn idle or airmode mixerThrottle = throttle; #ifdef USE_DYN_IDLE @@ -571,13 +745,12 @@ FAST_CODE_NOINLINE void mixTable(timeUs_t currentTimeUs) // reduce throttle to offset additional motor output throttle = pidCompensateThrustLinearization(throttle); #endif - + applyRPMLimiter(); // Find roll/pitch/yaw desired output // ??? Where is the optimal location for this code? float motorMix[MAX_SUPPORTED_MOTORS]; float motorMixMax = 0, motorMixMin = 0; for (int i = 0; i < mixerRuntime.motorCount; i++) { - float mix = scaledAxisPidRoll * activeMixer[i].roll + scaledAxisPidPitch * activeMixer[i].pitch + @@ -647,4 +820,4 @@ void mixerSetThrottleAngleCorrection(int correctionValue) float mixerGetThrottle(void) { return mixerThrottle; -} +} \ No newline at end of file diff --git a/src/main/flight/mixer.h b/src/main/flight/mixer.h index 9e7ea6712c..a4d431c966 100644 --- a/src/main/flight/mixer.h +++ b/src/main/flight/mixer.h @@ -89,6 +89,22 @@ typedef struct mixerConfig_s { bool yaw_motors_reversed; uint8_t crashflip_motor_percent; uint8_t crashflip_expo; + bool govenor; + uint16_t govenor_p; + uint16_t govenor_i; + uint16_t govenor_d; + uint16_t govenor_rpm_limit; + uint16_t govenor_rpm_afterburner; + uint8_t govenor_rpm_afterburner_duration; + bool govenor_rpm_afterburner_reset; + bool govenor_rpm_afterburner_hold_to_use; + uint8_t govenor_rpm_afterburner_tank_count; + uint16_t govenor_acceleration_limit; + uint16_t govenor_deceleration_limit; + uint16_t govenor_k_factor; + bool govenor_rpm_linearization; + uint16_t govenorThrottleLimitLearningTimeMS; + uint16_t govenor_idle_rpm; uint8_t mixer_type; } mixerConfig_t; @@ -125,3 +141,6 @@ bool isFixedWing(void); float getMotorOutputLow(void); float getMotorOutputHigh(void); + +uint8_t getAfterburnerTanksRemaining(void); +float getAfterburnerTankPercent(void); \ No newline at end of file diff --git a/src/main/flight/mixer_init.c b/src/main/flight/mixer_init.c index 41cc172d87..771d8523cb 100644 --- a/src/main/flight/mixer_init.c +++ b/src/main/flight/mixer_init.c @@ -50,7 +50,22 @@ PG_RESET_TEMPLATE(mixerConfig_t, mixerConfig, .mixerMode = DEFAULT_MIXER, .yaw_motors_reversed = false, .crashflip_motor_percent = 0, - .crashflip_expo = 35, + .crashflip_expo = 0, + .govenor = true, + .govenor_p = 20.0f, + .govenor_i = 15.0f, + .govenor_d = 10.0f, + .govenor_rpm_linearization = true, + .govenor_idle_rpm = 17, + .govenor_acceleration_limit = 60, + .govenor_deceleration_limit = 60, + .govenor_k_factor = 1000, + .govenor_rpm_limit = 130.0f, + .govenor_rpm_afterburner = 16, + .govenor_rpm_afterburner_duration = 5, + .govenor_rpm_afterburner_reset = false, + .govenor_rpm_afterburner_hold_to_use = false, + .govenor_rpm_afterburner_tank_count = 3, .mixer_type = MIXER_LEGACY, ); @@ -310,6 +325,43 @@ void mixerInitProfile(void) mixerRuntime.motorOutputLow = DSHOT_MIN_THROTTLE; // Override value set by initEscEndpoints to allow zero motor drive } #endif +mixerRuntime.govenorExpectedThrottleLimit = 1.0f; + +//Street League spec settings + +//Locked rpm settings +mixerRuntime.govenorEnabled = true; +mixerRuntime.rpmLinearization = true; +mixerRuntime.motorPoleCount = 14; +mixerRuntime.afterburnerReset = false; +mixerRuntime.afterburnerHoldToBoost = false; + +//Unlocked rpm settings +// mixerRuntime.govenorEnabled = mixerConfig()->govenor; +// mixerRuntime.rpmLinearization = mixerConfig()->govenor_rpm_linearization; +// mixerRuntime.motorPoleCount = motorConfig()->motorPoleCount; +// mixerRuntime.afterburnerReset = mixerConfig()->govenor_rpm_afterburner_reset; +// mixerRuntime.afterburnerHoldToBoost = mixerConfig()->govenor_rpm_afterburner_hold_to_use; + + +mixerRuntime.govenorPGain = mixerConfig()->govenor_p * 0.0000015f; +mixerRuntime.govenorIGain = mixerConfig()->govenor_i * 0.0001f * pidGetDT(); +mixerRuntime.govenorDGain = mixerConfig()->govenor_d * 0.00000003f * pidGetPidFrequency(); +mixerRuntime.govenorAccelerationLimit = mixerConfig()->govenor_acceleration_limit * 1000.0f * pidGetDT(); +mixerRuntime.govenorDecelerationLimit = mixerConfig()->govenor_deceleration_limit * 1000.0f * pidGetDT(); +mixerRuntime.govenorKFactor = mixerConfig()->govenor_k_factor; +mixerRuntime.afterburnerRPM = mixerConfig()->govenor_rpm_afterburner; +mixerRuntime.afterburnerDuration = mixerConfig()->govenor_rpm_afterburner_duration; +mixerRuntime.afterburnerTanksRemaining = mixerConfig()->govenor_rpm_afterburner_tank_count; +mixerRuntime.RPMLimit = mixerConfig()->govenor_rpm_limit; + +mixerRuntime.govenorI = 0; +mixerRuntime.afterburnerTankPercent = 100.0f; +mixerRuntime.afterburnerInitiated = false; +mixerRuntime.govenorPreviousSmoothedRPMError = 0; +mixerRuntime.govenorDelayK = mixerRuntime.govenorKFactor * pidGetDT() / 20.0f; +mixerRuntime.govenorLearningThrottleK = 0.5 / (pidGetPidFrequency() * mixerConfig()->govenorThrottleLimitLearningTimeMS / 1000); // 0.5 = value ^ (4000 * time) 0.99^(4000*(20/1000)) +mixerRuntime.govenor_init = false; #if defined(USE_BATTERY_VOLTAGE_SAG_COMPENSATION) mixerRuntime.vbatSagCompensationFactor = 0.0f; @@ -346,7 +398,7 @@ void loadLaunchControlMixer(void) static void mixerConfigureOutput(void) { mixerRuntime.motorCount = 0; - + if (currentMixerMode == MIXER_CUSTOM || currentMixerMode == MIXER_CUSTOM_TRI || currentMixerMode == MIXER_CUSTOM_AIRPLANE) { // load custom mixer into currentMixer for (int i = 0; i < MAX_SUPPORTED_MOTORS; i++) { @@ -410,6 +462,7 @@ void mixerInit(mixerMode_e mixerMode) mixerRuntime.feature3dEnabled = featureIsEnabled(FEATURE_3D); initEscEndpoints(); + #ifdef USE_SERVOS if (mixerIsTricopter()) { mixerTricopterInit(); @@ -445,9 +498,11 @@ bool mixerModeIsFixedWing(mixerMode_e mixerMode) case MIXER_AIRPLANE: case MIXER_CUSTOM_AIRPLANE: return true; + break; default: return false; + break; } } @@ -465,4 +520,4 @@ float getMotorOutputLow(void) float getMotorOutputHigh(void) { return mixerRuntime.motorOutputHigh; -} +} \ No newline at end of file diff --git a/src/main/flight/mixer_init.h b/src/main/flight/mixer_init.h index dbbcf45fd6..2122c915ef 100644 --- a/src/main/flight/mixer_init.h +++ b/src/main/flight/mixer_init.h @@ -53,6 +53,33 @@ typedef struct mixerRuntime_s { float vbatFull; float vbatRangeToCompensate; #endif + float govenorExpectedThrottleLimit; + float govenorAccelerationLimit; + float govenorDecelerationLimit; + float govenorKFactor; + float prevAverageRPM; + float govenorPreviousSmoothedRPMError; + float minRPMDelayK; + float afterburnerRPM; + bool afterburnerReset; + bool afterburnerHoldToBoost; + float afterburnerDuration; + float afterburnerTankPercent; + bool afterburnerInitiated; + uint8_t afterburnerTanksRemaining; + bool rpmLinearization; + float RPMLimit; + int motorPoleCount; + bool govenorEnabled; + float govenorI; + float govenorPGain; + float govenorIGain; + float govenorDGain; + float govenorPreviousSmoothedRPM; + float govenorPreviousRPMLimit; + float govenorDelayK; + float govenorLearningThrottleK; + bool govenor_init; } mixerRuntime_t; -extern mixerRuntime_t mixerRuntime; +extern mixerRuntime_t mixerRuntime; \ No newline at end of file diff --git a/src/main/msp/msp_box.c b/src/main/msp/msp_box.c index d1942a970b..f64eab9ed2 100644 --- a/src/main/msp/msp_box.c +++ b/src/main/msp/msp_box.c @@ -100,7 +100,8 @@ static const box_t boxes[CHECKBOX_ITEM_COUNT] = { { .boxId = BOXMSPOVERRIDE, .boxName = "MSP OVERRIDE", .permanentId = 50}, { .boxId = BOXSTICKCOMMANDDISABLE, .boxName = "STICK COMMANDS DISABLE", .permanentId = 51}, { .boxId = BOXBEEPERMUTE, .boxName = "BEEPER MUTE", .permanentId = 52}, - { .boxId = BOXREADY, .boxName = "READY", .permanentId = 53} + { .boxId = BOXREADY, .boxName = "READY", .permanentId = 53}, + { .boxId = BOXBOOST, .boxName = "BOOST", .permanentId = 54}, }; // mask of enabled IDs, calculated on startup based on enabled features. boxId_e is used as bit index @@ -187,6 +188,7 @@ void initActiveBoxIds(void) #define BME(boxId) do { bitArraySet(&ena, boxId); } while (0) BME(BOXARM); BME(BOXPREARM); + BME(BOXBOOST); if (!featureIsEnabled(FEATURE_AIRMODE)) { BME(BOXAIRMODE); } diff --git a/src/main/osd/osd.h b/src/main/osd/osd.h index c7c53f9ced..ed08de0b10 100644 --- a/src/main/osd/osd.h +++ b/src/main/osd/osd.h @@ -273,6 +273,7 @@ typedef enum { OSD_WARNING_RSSI_DBM, OSD_WARNING_OVER_CAP, OSD_WARNING_RSNR, + OSD_WARNING_BOOST_ENGAGED, OSD_WARNING_COUNT // MUST BE LAST } osdWarningsFlags_e; diff --git a/src/main/osd/osd_elements.c b/src/main/osd/osd_elements.c index e5f022791f..b952ca8913 100644 --- a/src/main/osd/osd_elements.c +++ b/src/main/osd/osd_elements.c @@ -146,6 +146,7 @@ #include "flight/position.h" #include "flight/imu.h" #include "flight/mixer.h" +#include "flight/mixer_init.h" #include "flight/pid.h" #include "io/gps.h" @@ -1270,10 +1271,13 @@ static void osdElementMainBatteryUsage(osdElementParms_t *element) { uint8_t remainingCapacityBars = 0; - if (batteryConfig()->batteryCapacity) { - const float batteryRemaining = constrain(batteryConfig()->batteryCapacity - displayBasis, 0, batteryConfig()->batteryCapacity); - remainingCapacityBars = ceilf((batteryRemaining / (batteryConfig()->batteryCapacity / MAIN_BATT_USAGE_STEPS))); - } + //if (batteryConfig()->batteryCapacity) { + //const float batteryRemaining = constrain(batteryConfig()->batteryCapacity - displayBasis, 0, batteryConfig()->batteryCapacity); + //remainingCapacityBars = ceilf((batteryRemaining / (batteryConfig()->batteryCapacity / MAIN_BATT_USAGE_STEPS))); + const float maxBoost = 100.0f; + const float batteryRemaining = constrainf(getAfterburnerTankPercent(), 0, maxBoost); + remainingCapacityBars = ceilf((batteryRemaining / (maxBoost / MAIN_BATT_USAGE_STEPS))); + //} // Create empty battery indicator bar element->buff[0] = SYM_PB_START; @@ -1284,7 +1288,12 @@ static void osdElementMainBatteryUsage(osdElementParms_t *element) if (remainingCapacityBars > 0 && remainingCapacityBars < MAIN_BATT_USAGE_STEPS) { element->buff[1 + remainingCapacityBars] = SYM_PB_END; } - element->buff[MAIN_BATT_USAGE_STEPS+2] = '\0'; + //tfp_sprintf(element->buff, element->buff+"%d", mixerRuntime.afterburnerTanksRemaining); + char tanksRemainingStr[1]; + tfp_sprintf(tanksRemainingStr, "%d", getAfterburnerTanksRemaining()); + element->buff[MAIN_BATT_USAGE_STEPS+2] = tanksRemainingStr[0]; + element->buff[MAIN_BATT_USAGE_STEPS+3] = '\0'; + break; } } diff --git a/src/main/osd/osd_warnings.c b/src/main/osd/osd_warnings.c index a1dbcce2b9..381ef9ae92 100644 --- a/src/main/osd/osd_warnings.c +++ b/src/main/osd/osd_warnings.c @@ -49,6 +49,7 @@ #include "flight/gps_rescue.h" #include "flight/imu.h" #include "flight/mixer.h" +#include "flight/mixer_init.h" #include "flight/pid.h" #include "io/beeper.h" @@ -369,7 +370,12 @@ void renderOsdWarning(char *warningText, bool *blinking, uint8_t *displayAttr) } } #endif - + if (mixerRuntime.afterburnerInitiated) { + tfp_sprintf(warningText, "BOOST ENGAGED"); + *displayAttr = DISPLAYPORT_SEVERITY_WARNING; + *blinking = true; + return; + } if (osdWarnGetState(OSD_WARNING_BATTERY_WARNING) && batteryState == BATTERY_WARNING) { tfp_sprintf(warningText, "LOW BATTERY"); *displayAttr = DISPLAYPORT_SEVERITY_WARNING; @@ -377,6 +383,7 @@ void renderOsdWarning(char *warningText, bool *blinking, uint8_t *displayAttr) return; } + #ifdef USE_RC_SMOOTHING_FILTER // Show warning if rc smoothing hasn't initialized the filters if (osdWarnGetState(OSD_WARNING_RC_SMOOTHING) && ARMING_FLAG(ARMED) && !rcSmoothingInitializationComplete()) { @@ -420,6 +427,13 @@ void renderOsdWarning(char *warningText, bool *blinking, uint8_t *displayAttr) return; } + // Boost mode + if (IS_RC_MODE_ACTIVE(BOXBOOST)) { + tfp_sprintf(warningText, "BOOST ENGAGED"); + *displayAttr = DISPLAYPORT_SEVERITY_INFO; + return; + } + } #endif // USE_OSD