1
0
Fork 0
mirror of https://github.com/betaflight/betaflight.git synced 2025-07-13 11:29:58 +03:00
This commit is contained in:
Ivan Efimov 2025-07-03 03:41:51 -05:00 committed by GitHub
commit 9b2beabc9b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
22 changed files with 976 additions and 173 deletions

44
SPEC README.txt Normal file
View file

@ -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

26
build.sh Normal file
View file

@ -0,0 +1,26 @@
#!/bin/bash
# Check if the input file was provided
if [ -z dos2unix "$1" ]; then
echo "Usage: $0 <input-file>"
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"

7
common-devices.csv Normal file
View file

@ -0,0 +1,7 @@
OMNIBUSF4SD
AIKONF4
AIKONF7
HOBBYWING_XROTORF4G3
HOBBYWING_XROTORF7CONV
BETAFPVF411
JHEF411
1 OMNIBUSF4SD
2 AIKONF4
3 AIKONF7
4 HOBBYWING_XROTORF4G3
5 HOBBYWING_XROTORF7CONV
6 BETAFPVF411
7 JHEF411

1
device.csv Normal file
View file

@ -0,0 +1 @@
BetafpvF411RX
1 BetafpvF411RX

365
devices.csv Normal file
View file

@ -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
1 ALIENFLIGHTF4
2 ALIENFLIGHTNGF7
3 ALIENFLIGHTNGF7_DUAL
4 ALIENFLIGHTNGF7_ELRS
5 ALIENFLIGHTNGF7_RX
6 AIKONF4
7 AIKONF7
8 AG3XF4
9 AG3XF7
10 AIRBOTF4
11 AIRBOTF4SD
12 AIRBOTF4V2
13 AIRBOTF7
14 AIRBOTF7HDV
15 HELSEL_STRIKERF7
16 NOX
17 OMNIBUSF4
18 OMNIBUSF4FW
19 OMNIBUSF4NANOV7
20 OMNIBUSF4SD
21 OMNIBUSF4V6
22 OMNIBUSF7
23 OMNIBUSF7NANOV7
24 OMNIBUSF7V2
25 AIRB-OMNINXT4
26 AIRB-OMNINXT7
27 ALIENWHOOPF4
28 MERCURYG4
29 APEXF7
30 AXISFLYINGF7
31 AXISFLYINGF7PRO
32 BETAFPVF405
33 BETAFPVF411
34 BETAFPVF411RX
35 BETAFPVF4SX1280
36 BETAFPVF722
37 BETAFPVH743
38 BLUEJAYF4
39 NERO
40 MATEKF405STD_CLONE
41 CLRACINGF4
42 CLRACINGF7
43 RUSRACE_F4
44 RUSRACE_F7
45 SIRMIXALOT
46 CYCLONEF405_PRO
47 CYCLONEF722_PRO
48 DARWINF411
49 DARWINF4SX1280HD
50 DARWINF722HD
51 DAKEFPVF405
52 DAKEFPVF411
53 DAKEFPVF722
54 DALRCF405
55 DALRCF722DUAL
56 SOULF4
57 DFR_F722_DUAL_HD
58 FURYF4
59 FURYF4OSD
60 MAMBAF405US
61 MAMBAF405US_I2C
62 MAMBAF405_2022A
63 MAMBAF405_2022B
64 MAMBAF411
65 MAMBAF722
66 MAMBAF722_2022A
67 MAMBAF722_2022B
68 MAMBAF722_I2C
69 MAMBAF722_X8
70 MAMBAG4
71 MAMBAH743
72 MAMBAH743_2022B
73 ELINF405
74 ELINF722
75 DYSF44530D
76 DYSF4PRO
77 EACHINEF411_AIO
78 EACHINEF722
79 EACHINEF722_AIO
80 EMAX_BABYHAWK_II_HD
81 EMAX_TINYHAWKF4SX1280
82 EMAX_TINYHAWK_F411RX
83 EXF722DUAL
84 EXUAVF4PRO
85 FF_FORTINIF4
86 FF_FORTINIF4_REV03
87 FF_PIKOF4
88 FF_PIKOF4OSD
89 FF_RACEPIT
90 FF_RACEPITF7
91 FF_RACEPITF7_MINI
92 FF_RACEPIT_MINI
93 FLYCOLORF7
94 FLYCOLORF7_AIO
95 KIWIF4
96 KIWIF4V2
97 PLUMF4
98 GRAVITYF7
99 REVOLT
100 REVOLTOSD
101 FLYWOOF405
102 FLYWOOF405NANO
103 FLYWOOF405PRO
104 FLYWOOF405S_AIO
105 FLYWOOF411
106 FLYWOOF411EVO_HD
107 FLYWOOF411HEX
108 FLYWOOF411V2
109 FLYWOOF411_5IN1_AIO
110 FLYWOOF745
111 FLYWOOF745NANO
112 FLYWOOF7DUAL
113 ANYFCF7
114 ANYFCM7
115 DRONIUSF7
116 F4BY
117 FENIX_F405
118 SPARKY2
119 WORMFC
120 FOXEERF405
121 FOXEERF722DUAL
122 FOXEERF722V2
123 FOXEERF722V3
124 FOXEERF722V4
125 FOXEERF745V2_AIO
126 FOXEERF745V3_AIO
127 FOXEERF745V4_AIO
128 FOXEERF745_AIO
129 FOXEERH743
130 BETAFLIGHTF4
131 FPVM_BETAFLIGHTF7
132 XRACERF4
133 FRSKYF4
134 GEELANGF411
135 XILOF4
136 GEPRCF405
137 GEPRCF411
138 GEPRCF411SX1280
139 GEPRCF411_AIO
140 GEPRCF411_PRO
141 GEPRCF722
142 GEPRCF722BT
143 GEPRCF722_BT_HD
144 GEPRCF745_BT_HD
145 GEPRCG4AIO
146 GEPRC_F722_AIO
147 GEP_F405_VTX_V3
148 GEMEF411
149 GEMEF722
150 CRAZYBEEF4DX
151 CRAZYBEEF4DXS
152 CRAZYBEEF4FR
153 CRAZYBEEF4FS
154 CRAZYBEEF4SX1280
155 HAKRCF405
156 HAKRCF405D
157 HAKRCF405V2
158 HAKRCF411
159 HAKRCF411D
160 HAKRCF722
161 HAKRCF722D
162 HAKRCF722MINI
163 HAKRCF722V2
164 HAKRCF7230D
165 KD722
166 KAKUTEF4
167 KAKUTEF4V2
168 KAKUTEF7
169 KAKUTEF7HDV
170 KAKUTEF7MINI
171 KAKUTEF7MINIV3
172 KAKUTEF7V2
173 KAKUTEH7
174 KAKUTEH7MINI
175 KAKUTEH7V2
176 TALONF4V2
177 TALONF7DJIHD
178 TALONF7FUSION
179 TALONF7V2
180 HIFIONRCF7
181 HGLRCF405
182 HGLRCF405AS
183 HGLRCF411
184 HGLRCF411SX1280
185 HGLRCF411SX1280_15A
186 HGLRCF722
187 HGLRCF722E
188 HGLRCF745
189 ZEUSF4EVO
190 ZEUSF4FR
191 ZEUSF722_AIO
192 NIDICI_F4
193 HOBBYWING_XROTORF4G3
194 HOBBYWING_XROTORF7CONV
195 IFLIGHT_BLITZ_F405
196 IFLIGHT_BLITZ_F411RX
197 IFLIGHT_BLITZ_F722
198 IFLIGHT_BLITZ_F722_X1
199 IFLIGHT_BLITZ_F7_AIO
200 IFLIGHT_BLITZ_F7_PRO
201 IFLIGHT_F405_AIO
202 IFLIGHT_F405_TWING
203 IFLIGHT_F411_AIO32
204 IFLIGHT_F411_PRO
205 IFLIGHT_F4SX1280
206 IFLIGHT_F722_TWING
207 IFLIGHT_F745_AIO
208 IFLIGHT_F745_AIO_V2
209 IFLIGHT_H743_AIO_V2
210 IFLIGHT_H7_TWING
211 IFLIGHT_SUCCEX_E_F4
212 IFLIGHT_SUCCEX_E_F7
213 JBF7
214 JBF7_DJI
215 JBF7_V2
216 JHEF405
217 JHEF405PRO
218 JHEF411
219 JHEF745
220 JHEF7DUAL
221 JHEH743_AIO
222 SYNERGYF4
223 LDARC_F411
224 ELLE0
225 FISHDRONEF4
226 KROOZX
227 PIRXF4
228 PODIUMF4
229 STACKX
230 VRRACE
231 LUXAIO
232 LUXF4OSD
233 LUXF7HDV
234 LUXMINIF7
235 MERAKRCF405
236 MERAKRCF722
237 MLTEMPF4
238 MLTYPHF4
239 MATEKF405AIO
240 MATEKF405CTR
241 MATEKF405MINI
242 MATEKF405SE
243 MATEKF405STD
244 MATEKF405TE
245 MATEKF405TE_SD
246 MATEKF411
247 MATEKF411RX
248 MATEKF411SE
249 MATEKF722
250 MATEKF722HD
251 MATEKF722MINI
252 MATEKF722SE
253 MATEKH743
254 NBD_CRICKETF7
255 NBD_CRICKETF7V2
256 NBD_GALAXYAIO255
257 NBD_INFINITY200RS
258 NBD_INFINITYAIO
259 NBD_INFINITYAIOV2
260 NBD_INFINITYAIOV2PRO
261 NBD_INFINITYF4
262 FLOWBOX
263 FLOWBOXV2
264 HYBRIDG4
265 NEUTRONRCF407
266 NEUTRONRCF411AIO
267 NEUTRONRCF411SX1280
268 NEUTRONRCF722AIO
269 NEUTRONRCF7AIO
270 NEUTRONRCG4AIO
271 NEUTRONRCH743AIO
272 NEUTRONRCH7BT
273 UAVPNG030MINI
274 REVO
275 REVONANO
276 PYRODRONEF4
277 PYRODRONEF4PDB
278 PYRODRONEF7
279 AIRF7
280 MINI_H743_HD
281 ARESF7
282 BEEROTORF4
283 BLADE_F7
284 BLADE_F7_HD
285 RUSHCORE7
286 RUSHF7AIO
287 AOCODAF405
288 AOCODAF405V2MPU6000
289 AOCODAF405V2MPU6500
290 AOCODAF722BLE
291 AOCODAF722MINI
292 AOCODARCF411_AIO
293 AOCODARCF722_AIO
294 AOCODARCF7DUAL
295 AOCODARCH7DUAL
296 SKYSTARSF405
297 SKYSTARSF405AIO
298 SKYSTARSF411
299 SKYSTARSF7HD
300 SKYSTARSF7HDPRO
301 SKYSTARSH7HD
302 ATOMRCF405
303 ATOMRCF411
304 ATOMRCF722
305 SKYZONEF405
306 SPEEDYBEEF4
307 SPEEDYBEEF405V3
308 SPEEDYBEEF7
309 SPEEDYBEEF7MINI
310 SPEEDYBEEF7MINIV2
311 SPEEDYBEEF7V2
312 SPEEDYBEEF7V3
313 SPEEDYBEE_F745_AIO
314 SPCMAKERF411
315 SPEDIXF4
316 SPRACINGF4EVO
317 SPRACINGF4NEO
318 SPRACINGF7DUAL
319 SPRACINGH7EXTREME
320 SPRACINGH7NANO
321 SPRACINGH7RF
322 NUCLEOF722
323 STM32F411DISCOVERY
324 STM32F4DISCOVERY
325 SUB250F411
326 MODULARF4
327 TCMMF411
328 TCMMF7
329 COLIBRI
330 PODRACERAIO
331 TMH7
332 TMOTORF4
333 TMOTORF411
334 TMOTORF4SX1280
335 TMOTORF7
336 TMOTORF722SE
337 TMOTORF7V2
338 TMOTORF7_AIO
339 TMOTORH743
340 TMOTORVELOXF7V2
341 TMPACERF7
342 TMPACERF7MINI
343 TMVELOXF411
344 TMVELOXF7
345 TRANSTECF405HD
346 TRANSTECF411
347 TRANSTECF411AIO
348 TRANSTECF411HD
349 TRANSTECF7
350 TRANSTECF7HD
351 FPVCYCLEF401
352 TUNERCF405
353 VGOODF722DUAL
354 VGOODRCF4
355 VGOODRCF405_DJI
356 VGOODRCF411_DJI
357 VGOODRCF722_DJI
358 VIVAF4AIO
359 WIZZF7HD
360 YUPIF4
361 YUPIF7
362 ZEEZF7
363 ZEEZF7V2
364 ZEEZF7V3
365 ZEEZWHOOP

3
my-devices.csv Normal file
View file

@ -0,0 +1,3 @@
HOBBYWING_XROTORF7CONV
CRAZYBEEF4FR
1 HOBBYWING_XROTORF7CONV
2 CRAZYBEEF4FR

View file

@ -108,5 +108,6 @@ const char * const debugModeNames[DEBUG_COUNT] = {
"VTX_MSP",
"GPS_DOP",
"FAILSAFE",
"DSHOT_TELEMETRY_COUNTS"
"DSHOT_TELEMETRY_COUNTS",
"RPM_LIMITER"
};

View file

@ -107,6 +107,7 @@ typedef enum {
DEBUG_GPS_DOP,
DEBUG_FAILSAFE,
DEBUG_DSHOT_TELEMETRY_COUNTS,
DEBUG_RPM_LIMITER,
DEBUG_COUNT
} debugType_e;

View file

@ -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 "UDL-1.0.0-locked"
#define FC_VERSION_STRING STR(FC_VERSION_MAJOR) "." STR(FC_VERSION_MINOR) "." STR(FC_VERSION_PATCH_LEVEL)
extern const char* const targetName;

View file

@ -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,

View file

@ -853,7 +853,8 @@ const clivalue_t valueTable[] = {
{ PARAM_NAME_MOTOR_PWM_PROTOCOL, VAR_UINT8 | MASTER_VALUE | MODE_LOOKUP, .config.lookup = { TABLE_MOTOR_PWM_PROTOCOL }, PG_MOTOR_CONFIG, offsetof(motorConfig_t, dev.motorPwmProtocol) },
{ PARAM_NAME_MOTOR_PWM_RATE, VAR_UINT16 | MASTER_VALUE, .config.minmaxUnsigned = { 200, 32000 }, PG_MOTOR_CONFIG, offsetof(motorConfig_t, dev.motorPwmRate) },
{ "motor_pwm_inversion", VAR_UINT8 | MASTER_VALUE | MODE_LOOKUP, .config.lookup = { TABLE_OFF_ON }, PG_MOTOR_CONFIG, offsetof(motorConfig_t, dev.motorPwmInversion) },
{ PARAM_NAME_MOTOR_POLES, VAR_UINT8 | MASTER_VALUE, .config.minmaxUnsigned = { 4, UINT8_MAX }, PG_MOTOR_CONFIG, offsetof(motorConfig_t, motorPoleCount) },
//street league modification
{ PARAM_NAME_MOTOR_POLES, VAR_UINT8 | MASTER_VALUE, .config.minmaxUnsigned = { 12, 12 }, PG_MOTOR_CONFIG, offsetof(motorConfig_t, motorPoleCount) },
{ "motor_output_reordering", VAR_UINT8 | MASTER_VALUE | MODE_ARRAY, .config.array.length = MAX_SUPPORTED_MOTORS, PG_MOTOR_CONFIG, offsetof(motorConfig_t, dev.motorOutputReordering)},
// PG_THROTTLE_CORRECTION_CONFIG
@ -931,6 +932,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 = { 17, 17 }, PG_MIXER_CONFIG, offsetof(mixerConfig_t, govenor_i) },
{ "rpm_limiter_d", VAR_UINT16 | MASTER_VALUE, .config.minmaxUnsigned = { 3, 3 }, PG_MIXER_CONFIG, offsetof(mixerConfig_t, govenor_d) },
{ "rpm_limiter_rpm_limit", VAR_UINT16 | MASTER_VALUE, .config.minmaxUnsigned = { 305, 305 }, PG_MIXER_CONFIG, offsetof(mixerConfig_t, govenor_rpm_limit) },
{ "rpm_limiter_afterburner", VAR_UINT16 | MASTER_VALUE, .config.minmaxUnsigned = { 45, 45 }, 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 = { 1, 1 }, 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 = { 3000, 3000 }, 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, 50000 }, 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 +1746,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

View file

@ -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);

View file

@ -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);

View file

@ -78,7 +78,9 @@ typedef enum {
BOXSTICKCOMMANDDISABLE,
BOXBEEPERMUTE,
BOXREADY,
BOXBOOST,
CHECKBOX_ITEM_COUNT
} boxId_e;
typedef enum {

View file

@ -20,9 +20,7 @@
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#include <math.h>
#include <float.h>
#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
}
@ -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 +

View file

@ -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);

View file

@ -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 = 17.0f,
.govenor_d = 3.0f,
.govenor_rpm_linearization = true,
.govenor_idle_rpm = 60,
.govenor_acceleration_limit = 60,
.govenor_deceleration_limit = 60,
.govenor_k_factor = 3000,
.govenor_rpm_limit = 305.0f,
.govenor_rpm_afterburner = 45,
.govenor_rpm_afterburner_duration = 5,
.govenor_rpm_afterburner_reset = false,
.govenor_rpm_afterburner_hold_to_use = false,
.govenor_rpm_afterburner_tank_count = 1,
.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 = 12;
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;
@ -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;
}
}

View file

@ -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;

View file

@ -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);
}

View file

@ -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;

View file

@ -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;
}
}

View file

@ -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