MSFS2020 RPN Tips and Tricks - MobiFlight/MobiFlight-Connector GitHub Wiki
RPN TIPS AND TRICKS
This page is work in progress, intended to contain an unsorted list of snippets of RPN code to solve different little problems. The techniques used here can most likely be used in other instances to solve other situations. For these case examples, the code presented should be put into the Preset Code box of the Input or Output Config Wizard, as the case may be.
Please be aware that Mobiflight itself does not execute any RPN code nor knows any of the syntax. This is all sent "as is" to MSFS2020 where the Gauge Calculator function evaluates the code on the simulator side.
1. Changing the units on a variable to suit the K: Transponder Set event.
The K event for setting the Xpndr or Squawk code is (>K:XPNDR_SET) and it expects to receive this code in units "BCO16". How do you convert a four digit decimal code to BCO16? Easy. Have MSFS do the conversion for you by using an Lvar as intermediate storage,
Say you want to set the code to 7000,
7000 (>L:My Xpndr Code, number) (L:My Xpndr Code, BCO16) (>K:XPNDR_SET)
Here, we just store the human readable decimal value in a user defined Lvar and immediately recall its value with a different unit (BCO16) and feed this converted value to the K: event.
The same code can be used to very easily clear the transponder code:
0 (>L:My Xpndr Code, number) (L:My Xpndr Code, BCO16) (>K:XPNDR_SET)
2. Automate Spoiler deployment.
In case your favorite airplane does not have automatic spoiler deployment when landing, here is how to do it with RPN. This trick involves using an Output config to run the automatic deployment code. The K: event for spoiler deployment is (>K:SPOILERS_ON). There is a boolean A: variable that tells us when the airplane is on the ground, which we can use to trigger the event: (A:SIM ON GROUND)
First, set the spoilers to "Armed" mode by setting a user L: variable tied to a button in your panel. This code will toggle this Lvar, so you can switch the "Armed" mode ON and OFF, in case you change your mind in mid flight, and the code also checks that the airplane is not on the ground, so you cannot arm the spoilers if the plane is on the ground. This same L: variable can be used in another Output config to turn on a Led indicator for your panel.
Input config for the button (Custom Input code box):
(A:SIM ON GROUND) ! if{ (L:My_Spoilers_Armed, bool) ! (>L:My_Spoilers_Armed, bool) }
To automate the deployment, we use an Output config entering this code in the "Variable field" of the config wizard:
Output config code:
(A:SIM ON GROUND) (L:My_Spoilers_Armed) and if{ (>K:SPOILERS_ON) 0 (>L:My_Spoilers_Armed) }
This code will check if the airplane touches the ground and the spoilers are armed, if true, then the spoilers are immediately deployed. Once the spoilers are deployed, the code also turns off the armed mode automatically, so you cannot redeploy the spoilers.
WARNING: the use of the Output config Variable field to run RPN scripts is not the designed purpose of this feature and it is limited to a maximum of 1024 characters. Exceeding this maximum length will most likely cause malfunctions in Mobiflight. If for any reason you need to have code that exceeds 1024 characters, you should break the code into multiple output configs.
3. Potentiometer simple calibration.
Mobiflight supports analog input (potentiometers) since 2022. However, there is currently no direct function in the MF Connector or in the firmware to calibrate the range given by a particular potentiometer. The Arduino mega can read values for an analog potentiometer in the range 0-1023, based on the voltage detected in the assigned pin. Each potentiometer can be slightly different and return different values in this range or you may be using only a part of the potentiometer range of rotation, but still want this limited range to operate the sim function in its full range.
The Hubhop database website provides a tool designed to automate the potentiometer parameters and generate pseudo RPN code that can be copied to Mobiflight input configs. The following explanation is for the math involved in calibrating the potentiometer range and translate that into the sim expected input range.
You can find the Hubhop potentiometer tool at https://hubhop.mobiflight.com/tools/
In this example let's assume your pot reads a minimum value of 18 and a maximum value of 1020. This code will map these readings into a range of 0 to 1023. You enter this in your analog input config wizard, Custom Input box.
@ 18 - 1023 1020 18 - / * 0 max 1023 min [here goes the rest of the code]
The code presented above only remaps the 18 to 1020 minimum and maximum range of values from the example potentiometer to the standard 0 to 1023 of an Arduino analog input. The formula in more readable notation is:
(potval - minval) * 1023 / (maxval - minval)
Of course, you will need to determine and use the appropriate values for your potentiometer, which can be quite different. For example, if you only use part of the potentiometer movement in your panel, your readings could be limited say from 203 to 850.
4. Adjusting the Potentiometer Readings to a Different Range.
Many events in MSFS require an input range different from what a potentiometer normally reads in arduino. For example flaps spoilers require an input in the range of 0 to 16383. Since the normal range of readings in a potentiometer is 0 to 1023, we need to adjust these readings before sending them to the appropriate simulator event. The simplest way is direct multiplication or division by an appropriate factor.
The Hubhop database website provides a tool designed to automate the potentiometer parameters and generate pseudo RPN code that can be copied to Mobiflight input configs. The following explanation is for the math involved in calibrating the potentiometer range and translate that into the sim expected input range.
You can find the Hubhop potentiometer tool at https://hubhop.mobiflight.com/tools/
In the spoilers example I mentioned, the appropriate factor would be the maximum value of the input range divided by the maximum value of the potentiometer reading, i.e. 16383/1023 = 16.0147. Accordingly, the simplest code to do this, assuming no additional range corrections are needed is as follows:
@ 16.0147 * 0 max 16383 min (>K:SPOILERS_SET)
In case your potentiometer requires additional range adjustment to achieve the standard range, as explained above, the value may require further processing to actually get to the expected range for the input event in MSFS2020. For example, the set throttle K: event (>K:THROTTLE_SET) requires input in the range 0 to 16383. For this second case, the easiest would be to substitute the 1023 by 16383. Please note we continue using as example the same potentiometer used in the previous example with a minimum reading of 18 and a maximum of 1020.
@ 18 - 16383 1020 18 - / * 0 max 16383 min (>K:THROTTLE_SET)
Other airplanes in MSFS2020, like the FBW A320, require a throttle value in the range -16383 to +16383, depending how the throttle calibration is done, where negative values are for reverse thrust, so the code will need to be adjusted
@ 18 - 32767 1020 18 - / * 16383 - -16383 max 16383 min (>K:THROTTLE_SET)
5. Running An Event Sequence with Pauses
Another variation on the use of the SIMULATION TIME variable is running a sequence of events with a pause or delay. As example, turning on two fuel pumps with 5 seconds delay each.
Start by configuring a button in your panel to toggle a user Lvar:
(L:MyPumpsOn) ! (>L:MyPumpsOn)
You will need to create an output config and put this code in the Variable field:
(L:MyPumpsOn) if{ (L:MyStartTime) 0 == if{ (E:SIMULATION TIME, second)
(>L:MyStartTime) (>K:FUEL_PUMP1) }
els{ (E:SIMULATION TIME, second) (L:MyStartTime) - 5 > if{ (>K:FUEL_PUMP2)
0 (>L:MyPumpsOn) 0 (>L:MyStartTime) } } }
The code checks to see if MyPumpsOn is 1 (button was pressed), if false, nothing is done. If true, it initializes the five second timer MyStartTime and turns on the first pump with the K:FUEL_PUMP1 event. The simulation continues to run normally, while the code checks and waits for five seconds (the els clause). Once the five seconds expire, the if clause inside the els clause is true, the code turns on the second pump with K:FUEL_PUMP2 and resets back to 0 both Lvars used and is ready for the next button press.
This technique can be used to introduce a delay in any procedure you wish to execute in the simulator, without stopping the simulator's normal execution. Just as another example, turning a light on 3 seconds after a switch is pressed.
6. Passing a Parameter Down Without Having to Recall a Variable
In this tip, we can make more efficient code by not having to recall the same variable more than once. There are two ways to do this.
Method A - Using s0 and l0 RPN instructions. These are the store in register and load from register instructions. The number 0 after the instruction refers to the register number to be used. There are ten registers numbered 0 to 9. This example code below if for the Runway Lights of the FBW A320 turn on event.
1 s0 (>L:LIGHTING_TAXI_2) 2 l0 (>K:2:TAXI_LIGHTS_SET) 3 l0 (>K:2:TAXI_LIGHTS_SET)
At the beginning of the code, 1, the ON value, is pushed in the stack but instead of using it, s0 is executed to store the value without popping it out into register 0. The same value is popped and used to set the L: variable and then the two K: events using the command l0 (load stack from register 0).
Please note the same code can now be used to turn off the Runway lights by just substituting the 1 at the beginning with 0. This tip is very useful when the same value needs to be used multiple times.
0 s0 (>L:LIGHTING_TAXI_2) 2 l0 (>K:2:TAXI_LIGHTS_SET) 3 l0 (>K:2:TAXI_LIGHTS_SET)
The value to be stored and loaded can be a constant as in these examples or from another A: or L: variable.
Method B - Using the "d" RPN instruction. This tip can be handy when you need to pass the value once down the code. The d instruction is for duplicate the top value in the stack. A stack of 1 0 3 after d would result in 1 0 3 3. So we can use this duplicate to sequentially feed the same value to two code actions.
(L:my PAUSE_ON,bool) ! d (>L:my PAUSE_ON,bool) if{ (>K:PAUSE_ON) } els{ (>K:PAUSE_OFF) }
This code is used on a button to toggle the sim pause function. The user defined boolean Lvar is reversed with the ! operator and duplicated in the stack. The first copy is used to store the reversed value back in the same variable (toggle action). The second copy is passed down and used in evaluating the if statement. If true, set the pause ON. If false, set the pause OFF.
7. Using analog input to map arbitrary discrete values to a K: event
In this tip we configure a potentiometer (analog input) to send arbitrary discrete values to the simulator, depending on the reading from the potentiometer. I assume you already know how to connect a potentiometer to the arduino and how to create an analog input configuration in the MFConnector app. If not, check this wiki tutorial:
https://github.com/MobiFlight/MobiFlight-Connector/wiki/Example-Analog-Input-Potentiometer
For example, we can set the Flaps in an airplane that uses five positions, which are not necessarily spaced linearly, so that proportionally changing the potentiometer scale does not work as intended. The code presented is intended to be put into Custom Input of the analog input configuration wizard in Mobiflight.
For comparison purposes, the "normal" way to do the pot value linear conversion would be something like the following code:
@ 1023 / 16383 * (>K:FLAPS_SET)
The pot reading "@", which can have a value from 0 to 1023, is divided by 1023 and multiplied by 16383 to return a value from 0 to 16383. This is perfectly reasonable and works if the expected values are linear.
For arbitrary or non-linear values the following code is one possible solution. We will use the RPN case statement to accomplish this task. Let's assume that we want to send the following table of arbitrary values to the K: event.
pot K:event
0 0
1 8000
2 10000
3 14000
4 16383
Note that the values for the K: event are arbitrary and non-linear. The following code will send the selected values to the K:FLAPS_SET event:
16383 14000 10000 8000 0 5 @ 1023 / 5 * case (>K:FLAPS_SET)
The first five values are the arbitrary values to be sent in reverse order, followed by the number of elements (5 in this case), followed by an expression that evaluates to a range of 0 to 5 ( @ 1023 / 5 *
), followed by the "case" keyword and lastly, the setting for the K: event. In the case
statement the value of the expression determines which arbitrary value from the list is selected to be returned. If the value of the expression is greater than 0 but less than 1, the first value is selected, if greater than 1 but less than 2, the second value is selected and so forth.
Please note that the values that are selected can be any value and in any order, not necessarily in order of magnitude. This allows for much flexibility in deciding what is returned to the simulator event.