Uncovering input events using the MSFS2020 model behavior dialog

One of the most common questions asked in the MobiFlight Discord is how to control something in a plane. With the incredible MSFS2020 SimConnect event support in MobiFlight pretty much anything is possible… if you can figure out what event to use.

HubHop is a great resource and should be everyone’s first stop when researching events. Chances are someone else has already done the digging and figured out what works.

But what do you do if the thing you want isn’t in HubHop? That’s where the model behaviour dialog comes in. Let’s walk through how to use it to research the event to use for setting parking brake on a TBM930.

Step 1: Enable developer mode in Flight Simulator

This step is easy 😀 Run Flight Simulator then go to Options > General Options > Developers and toggle Developer Mode to On. Restart flight simulator (restarting is important otherwise the later steps won’t show anything useful).

Step 2: Spawn the plane somewhere

This is also easy 😀 Use the World map to spawn the plane you want to dig into somewhere in the world. For this example I spawned the TBM930 at KPAE.

Step 3: Bring up the model behavior dialog and disable minimizing data on load

Once you’ve spawned at the airport open the model behavior dialog from the developer menu across the top of the screen by going to Tools > Behaviors. Then do the following:

  1. In the dropdown at the top center of the dialog select the XML file that contains the events you’re interested in. In most cases the file is named after the plane and has _interior.xml added to the end. For the TBM930 the filename is TBM930_INTERIOR.XML.
  2. Click the InputEvents tab
  3. Uncheck the Minimize data on Load option
  4. Click Reload user container
  5. Wait for the plane to reload then select the XML file from step 1 in the dropdown at the top center of the dialog again.

Steps 3 and 4 ensure that the code behind the different events will be visible for us to inspect.

When you’re done the dialog should look like this:

The model behavior dialog with the InputEvents tab ready for event inspection.

Not seeing any events listed? Check and make sure that you’ve selected the correct XML file from the dropdown. If it doesn’t say INTERIOR in the name you probably have the wrong one selected.

Still don’t see any events listed? Sometimes it happens. Try restarting the sim and going through steps 1-5 again.

Step 4: Find the relevant event

This is where things get a bit tricky since there’s no search feature on the InputEvents tab. It helps to have some idea of what system the input you want belongs to. In the case of the parking brake I guessed that since it relates to wheels it would be in the LANDING_GEAR section so I expanded that:

Model behavior dialog with the LANDING_GEAR events expanded.

Sometimes it’s nice to be lucky 😀 LANDING_GEAR_ParkingBrake looks promising. To find out what makes that specific event work:

  1. Expand the event by clicking the triangle next to LANDING_GEAR_ParkingBrake
  2. Click the Add to control panel button
  3. Expand the entry added to the right side of the dialog by clicking the triangle next to LANDING_GEAR_ParkingBrake
  4. Click the Set tab

Those steps result in a dialog like this:

Model behavior dialog with the LANDING_GEAR_ParkingBrake set code exposed.

At this point we know exactly how the sim manages the parking brake:

p0 0 max 1 min (>K:PARKING_BRAKE_SET) 
(A:BRAKE PARKING POSITION, bool) 100 * (>L:ParkingBrake_Position)

But what does it mean? How do we use it? This is where it takes a bit of practice and understanding RPN. Generally speaking you are trying to identify which Kvar is getting used. In this case it looks like (K:PARKING_BRAKE_SET) is the magic, and it is either 0 or 1. Chances are 0 means off and 1 means on.

Step 5: Putting the event to use

At this point you can jump over to MobiFlight and try the event out. Create a new input config with an action type of Microsoft Flight Simulator 2020 and check the Show Preset Code box to get a text box for the event code. For this example, in On Press, I would try this:

1 (>K:PARKING_BRAKE_SET)

And for On Release I would try this:

0 (>K:PARKING_BRAKE_SET)

It should look something like this:

Step 6: Document it for others

Once you’ve confirmed the event works as expected head on over to HubHop and add entries so everyone else can benefit going forward!

Step 7: Upvote the feature request so we don’t have to do this anymore

Realistically this is all a big pain and shouldn’t be this hard. Upvote the feature request to let Asobo know you’d like a proper event system where it isn’t necessary to dig through RPN code to make the plane work with external controls!


Adding workarounds to MSFS2020 for MobiFlight

Update 2024-02-10: If you are trying to access the inaccessible switches on the TBM930 there’s an add-on available that exposes them as Lvars. You can use that add-on to implement the below workaround without having to edit the file yourself.

With the release of Sim Update 5, Asobo and Microsoft dramatically changed how sim events work to manipulate airplane controls. Unfortunately this broke the experience for many switches when trying to set them via MobiFlight.

Wish you didn’t have to apply this workaround? Me too. Help change this by upvoting the feature request for better access!

What happens without a workaround is you can control the switches via MobiFlight but the in-sim switch doesn’t update to reflect the current state. For example you can turn the inertial separator of the TBM930 on with MobiFlight and the MFD will show the “Inert sep” warning correctly but the switch on the in-sim de-ice panel will still be in the off position.

Luckily there’s a workaround. In this post I’ll walk through the steps to apply the workaround for two switches in the TBM 930: propeller de-ice and inertial separator.

Step 1: Download and install the TBM930 improvement mod

Technically you can make this change to the sim files directly but since the TBM930 improvement mod is a must-have if you fly the TBM930 we’ll be applying the workaround to its files instead.

Download and install the mod.

Step 2: Open the tbm930_interior.xml file in a text editor

The file to edit is located in the aircraft-tbm930-improvement\SimObjects\Airplanes\Asobo_TBM930\model folder, inside the MSFS2020 Community folder. Open the tbm930_interior.xml in the text editor of your choice. I prefer Visual Studio Code but it can be any text editor like Notepad, Notepad++, etc.

Step 3: Add the workaround to the file

Locate the following section near the top of the tbm930_interior.xml file:

    <!-- INSTRUMENT ################################# -->
    <Component ID="INSTRUMENT">

Above that section paste the workaround in:

    <!-- ############################################### -->
    <Component ID="MobiFlight_External_Control">
      <UseTemplate Name="ASOBO_GT_Update">
        <UPDATE_CODE>
            (L:MF_TBM930_DEICE_Engine_1_Set, Number) s0 0 &gt; if{ l0 1 - (&gt;B:DEICE_Engine_1_Set) 0 (&gt;L:MF_TBM930_DEICE_Engine_1_Set) }
        </UPDATE_CODE>
        <FREQUENCY>1</FREQUENCY>
      </UseTemplate>
    </Component>

The resulting file should look like this:

Screenshot of the workaround applied to the TBM930_interior.xml file.

Save the file, close it, and restart Microsoft Flight Simulator.

Step 4: Update your MobiFlight switch configurations

For the On Press and On Release actions of each switch you’ll need to update the configuration to use the MSFS2020 – Custom Input option and add the correct custom event. They are:

SwitchEventCustom input
Prop de-iceOn Press2 (>L:MF_TBM930_DEICE_Propeller_1_Set)
Prop de-iceOn Release1 (>L:MF_TBM930_DEICE_Propeller_1_Set)
Inertial separatorOn Press2 (L:>MF_TBM930_DEICE_Engine_1_Set)
Inertial separatorOn Release1 (L:>MF_TBM930_DEICE_Engine_1_Set)
Custom inputs to use in MobiFlight with the workaround.

For example the On Press event for the prop de-ice switch should look like this:

Screenshot of a properly configured prop de-ice switch On Press event in MobiFlight using the workaround.

Step 5: Fly!

That’s all there is to it! Now you’re ready to fly and enjoy the physical switches working as they should with the sim.

Using this technique on other planes

While this post focused on modifying the TBM930 the same technique works on other planes as well. For other planes you’ll edit the *_interior.xml file for that plane, likely in the Asobo official file, and insert a similar block of XML that defines new events to wrap the Bvars in the sim.

When creating your custom event it’s important to offset the value by one from 0. This ensures the event only takes effect if the value is explicitly set by MobiFlight. For example if the sim expects a value of 0 to turn something off and 1 to turn something on the custom event should instead take a value of 1 for off, 2 for on, and then subtract 1 from the provided value before passing it to the sim.

You can see this in action with any of the above custom events, for example:

(L:MF_TBM930_FUEL_Selection_Set, Number) s0 0 &gt; if{ l0 1 - (&gt;B:FUEL_Selection_set) 0 (&gt;L:MF_TBM930_FUEL_Selection_Set) }

The custom event checks to see if the value is greater than 0 before doing anything. If it’s 0 nothing happens and the sim will function as if the workaround isn’t applied at all. If it’s greater than 0 then 1 is subtracted from the specified value (to turn it into what the sim expects), then it is passed to the sim.

A few closing notes

The workaround will get overwritten every time a new version of the TBM930 improvement mod is released. You’ll need to go back to step 3 and apply the change after every improvement mod update.

There are other switches in the TBM930 that need these changes, such as the fuel selector switch. You can find them documented on HubHop, the unofficial repository of all MSFS2020 events. When adding additional workarounds you only need to insert the new Lvar from hubhop into the existing workaround block applied in step 3. For example to apply the workaround for the fuel selector switch the workaround block winds up looking like this:

Screenshot showing multiple workarounds applied to the TBM930_interior.xml file.

The MobiFlight wiki is a wealth of knowledge for how to do things like this. Check out the article on how to use custom input page or how to use the developer tools to uncover events.

2021-10-14: Updated RPN to use registers instead of duplicating on the stack. Corrected a > to &gt;.


Component selection and schematic for a custom ATmega32u4 PCB

Feb 26, 2023: While this information is still accurate I would never, ever, do an ATmega32u4 PCB anymore. Instead I use an RP2040 chip. So much easier to find, cheaper, and easier to deal with.

I built a custom PCB that replicates the TBM930 de-ice panel for use with Microsoft Flight Simulator 2020 and Mobiflight. This is part 1 of a series where I walk through how I managed to do it with an ATmega32u4 directly on the PCB instead of relying on an external Arduino board.

Why an ATmega32u4 (the chip used on the SparkFun Pro Micro) instead of the ATmega2560 (the chip used on the Arduino Mega 2560)? Easy: onboard USB. The 2560 doesn’t have onboard USB and if you look at the schematic for the Mega 2560 you’ll see it also has… a 32u4 to handle the USB communication.

The chip itself has, as you’d expect, a full datasheet. But if you’re like me you just want to know what decoupling capacitors and whatnot are required and how to lay them out on a PCB. Sadly that isn’t really explained in the datasheet so I wound up doing a lot of reading in random forums of people trying to do similar custom boards and looking at the schematics for the Pro Micro and Arduino Leonardo. Distilling all that down got me this schematic:

Schematic illustrating an ATmega32u4’s basic connections.

Important things to note:

In addition the incoming USB port has components to ensure proper power and USB operation. I wanted to include ESD protection but JLCPB didn’t have any available as basic parts so I left it out.

Necessary components for the USB input to an ATmega32u4.

Of note are the 22Ω resistors on the D+ and D- lines.

The Arduino bootloader expects the 32u4 to have an external oscillator providing the clock so that also needs to be included in the schematic:

Necessary components for an external crystal oscillator with an ATmega32u4.

The oscillator is 16MHz and per the datasheet I added 20pF capacitors. 22pF capacitors should work too but 20pF is what I had on hand so that’s what I used.

Finally there has to be a way to get the bootloader onto the chip. I did that by exposing an AVR-ISP header and reset switch on the board. The reset switch really wasn’t necessary but it was easy to include so I added it. The reset circuit design is pulled directly from the chip’s application notes (see figure 2-1).

AVR-ISP and reset circuit for an ATmega32u4.

That’s all it takes! When I first looked at the schematics for the Sparkfun Pro Micro I found it pretty intimidating (and frankly quite messy) but once I walked through it the actual required components aren’t that bad.

The complete schematic is available in GitHub. To see how this got laid out on the PCB check out part 2 of the series.


Creating custom Microsoft Flight Simulator panels using an ATmega32u4

Mobiflight is an amazing piece of software that makes it possible to easily make physical controls for planes in flight simulators with an off-the-shelf Arduino, some buttons, and some LEDs. I’ve had a ton of fun with it and even designed a custom PCB for a general purpose radio stack. The radio stack is cool but it still relines on an Arduino being stuck to the back.

But did you know it’s possible to make a custom PCB with an ATmega32u4 right on the board and have it work with Mobiflight, no external Arduino required?

TBM930 de-ice panel PCB
TBM930 de-ice panel custom PCB for use with Microsoft Flight Simulator 2020 and Mobiflight.

In this series of blog posts I’ll walk through how I designed the TBM930 de-ice panel as a custom PCB with an onboard ATmega32u4 that works with Mobiflight.

While this PCB is intended for use with Mobiflight the steps involved apply to any PCB you might want to make that uses the 32u4.

Questions about any of the above after reading through it? Come ask in the MobiFlight Discord!


Planes I’ve flown on

This post will keep the running list and count. Last updated February 8, 2025.

Horizon flights include:

Missing from the list is AS2285 from FCA to SEA on 2014-02-16. For some reason Horizon flights don’t seem to be listed in the on-time performance database.


Seattle Storm 2015 Season Statistics

It’s that time of year! Season’s over so time to roll out the Seattle Storm 2015 photography stats. Here we go!

Overall Statistics

Total kept photos: 5,122
Web gallery candidates: 659 (13%)

I typically keep about 10% of the total shots taken at any given game so I fired the shutter somewhere north of 50,000 times this season.

Game with the most kept photos: Lynx v Storm, September 11, 2015 (268)
Game with the fewest kept photos: Mercury v Storm, May 26 2015 (132)

The Lynx game isn’t a surprise since I spent a good part of that game capturing photos of all the LED displays in KeyArena for sponsorship season-recap packets. It was also the game where Legacy season ticket holders had their photo taken with Ramu Tokashiki.

Fewest photos isn’t a surprise either, since that was the preseason game 🙂

Non-game event with the most kept photos: Media Day, May 23, 2015. (184)
Non-game event with the fewest kept photos: WNBA Fit open practice, July 14, 2015. (57)

Players & People

Most photographed player: Sue Bird (385)
Least photographed player: Monica Wright (40)

Technical Details

Shots by Camera

Canon EOS 5D Mk III: 1562
Canon EOS 1Dx: 3560

The 1Dx is my primary camera, it takes the most photos. The 5D is usually for downcourt action with the 300mm lens, and most of those don’t work out.

Shots by Lenses

Canon 17-40 f/4.0L: 101
Canon 24-70 f/2.8L: 3217
Canon 70-200 f/2.8L IS II: 1502
Canon 300mm f/2.8L IS II: 302

The 24-70 at the top of the list is no real surprise as it is my workhorse lens for all non-game activities. Every sponsor event, fan shot, etc. gets taken with it. The 17-40 count is low as I rarely use the lens for game activities. Typically it’s only for the occasional VIP event or post remote photo (most of which never turn out anyway).


Categories: Uncategorized

My Favourite Renee Montgomery Photos

The Storm announced a trade today, sending Renee Montgomery to the Minnesota Lynx. Renee joined the Storm this season and immediately became one of my favourite players to photograph. Here’s a few photos of her from this season that make me smile.

#WhatsWithAllTheBabies #DontTouchMyCameraMmmmKay #FannyPacksAreCoolAgainIPromise #IGotInstagramBecauseOfYou #IStillDontKnowHowYouLevitatedLikeThat #IveNeverBeenToCheesecakeFactory #SeriouslyWhatsWithAllTheBabies #GoodLuckInMinny

20150523_WebGallery_01

20150523_WebGallery_02

Renee Montgomery flies through the air as she attempts to save a ball headed out-of-bounds. (Neil Enns/Storm Photos)

Renee Montgomery levitates through the Mercury defense. (Neil Enns/Storm Photos)

20150616_Sun_v_Storm_0043

20150715_WebGallery_07

20150530_WebGallery_04

20150617_WebGallery_05

20150715_WebGallery_08


Categories: Uncategorized

Trip Report: SEA-MKE Inaugural Alaska Airlines Flight

I took the SEA-MKE inaugural this morning, my first ever inaugural Alaska flight. I expected some festivities and Alaska didn’t disappoint. Here’s some pics and blahblahblah about the flight and the new plane.

We departed from C9. Seems like everyone who was someone from Alaska and Horizon was there (everyone doing ramp service on the plane was wearing Horizon polos). They had a band playing and cheese curds were served, along with a cheese-shaped cake and cupcakes:



The flight was, of course, on the brand new “Boeing” E175:



The gate crew dressed for the occasion (as did the ground and flight crews):



The ground crew brought out a classic for the fun as well, and there was a LOT of ground crew hanging around for photos with the plane:



Before boarding there was a ribbon cutting ceremony, cut by an Alaska MVPG 75K member:


Everyone who boarded got a little certificate to commemorate the event. If you had a paper boarding card they stamped it, otherwise (like us) you could have them stamp the certificate:


Onboard it was definitely a brand new plane. Everything in great shape and still smelling fresh. We were in row 6, which felt like it had more leg room than business class on most domestic flights!




Service was pretty good, although it’s just a two person cabin crew which means service takes a loooong time in economy. For the first service pass it was just the back FA doing all the work, and there was no recognition of elite status that I could see (although I did not get charged for my drink). Sadly the Alaska Beyond in-flight services weren’t available yet, which meant no wifi. According to the FA this will be up and running on July 13th.

Meal options included the fruit and cheese plate, or a hot Cuban sandwich. I opted to try the Cuban sandwich. For passengers in first class they had the same Cuban sandwich and a side salad. I can’t say I’d recommend the sandwich to anyone. Get the fruit and cheese plate instead.

We arrived in MKE 40 minutes early and they had some trouble getting the jetbridge working. After about 5 mins they figured it out and we were in the terminal, greeted by a ton of local media and random official looking people. They gave us all local coffee (Colectivo Blue Heeler, pre-ground) and “leather” luggage tags embossed with the Alaska and MKE airport logos.


They had a fancy cake for the MKE-SEA departure, as well as lots of signage throughout the airport announcing the new flight. There was a band pre-security as well. They didn’t seem to be playing grunge though. Weird.



Anyway, it was fun being on an inaugural, and I’m looking forward to doing it again in September to Nashville!


Categories: Uncategorized

Decoding the I-405 test toll signs

My morning commute takes me down I-405 in Seattle from the SR522 interchange to the SR520 interchange. I have a lot of time to stare at the new I-405 HOT lane signs. After seeing a wide range of test toll codes I’m confident in the following mapping:

Symbol Value
s $
c .
h 0
n 1
d 2
r 3
l 4
t 5
g 7

Here’s all the toll values you’ll see from $0.75 (minimum toll) up to $2.75:

Code Cost
shcgt $0.75
sdchh $2.00
sdcdt $2.25
sdcth $2.50
sdcgt $2.75
slchh $4.00
slcdt $4.25
slcth $4.50
slcgt $4.75

Edit: changed “l” from 1 to 3 based on a tip from @WSDOTGoodToGo. Now I need to figure out what letter is a 1.

Edit: @WSDOTGoodToGo confirmed “l” is a 4.

Edit: After dealing with construction traffic on a Saturday night I’m fairly confident n < r < l. Since L is 4 and the only missing numbers were 1 and 3, that makes n == 1 and r == 3.


My Favourite Camille and Tanisha Photos

Earlier this year the Storm announced that Camille Little was traded to the Connecticut Sun. Today came the news that Tanisha signed as a free agent with the New York Liberty.

I’ve had the privilege of photographing the two of them for the last four years, and some of my best Storm photos of all time involve either one, or more often both, of them. Here’s a sampling of a few of my favourites.

20110527_MediaDay_0740
Tanisha takes a break from studying her lines during Media Day on May 27th, 2011. This was the first Storm event I ever covered and I still remember how happy I was finding this photo at the end of a long day of shooting. I love how it gives a peek into some of the players’ obligations off the court.

20120601_Storm_v_Shock_0228
Every player has a pre-game routine, and every game for four years I’ve watched Tanisha come out and give an explosive high 5 to Doppler. This is just one example from a game against the Tulsa Shock on June 1st, 2012.

20120823_Storm_v_Fever_0704
Camille isn’t shy about throwing her body around to get a foul call, but as I saw during a game against the Indiana Fever on August 23rd, 2012, sometimes even the opposing players can be surprised by her drives.

20140619_Storm_v_Stars_1022
Remember how I said Camille isn’t afraid to throw her body around? At least once a season this is the view I got through the viewfinder before Camille came crashing down near (or sometimes on!) me. Thankfully neither of us ever got hurt, and the cameras always escaped unscathed!

20130607_Storm_v_Shock_0285
The Storm under head coach Brian Agler were known for their strong defensive skills. During a game against the Tulsa Shock on June 7th, 2013, Tanisha and Camille teamed up to show how it’s done.

20120930_Storm_v_Lynx_1317
Sometimes the battles are at the offensive end of the court. Here Camille manages to pull in the offensive rebound while Tanisha holds back pressure during a game against the Minnesota Lynx on September 30th, 2012.

20130602_Storm_v_Mercury_0276
It was always a thrill to see Tanisha drive to the basket against a star player and somehow make the layup. During a game against the Phoenix Mercury on June 2nd, 2013, Tanisha did just that: taking it right to Britney Griner for two points.

20140627_Storm_v_Lynx_1906
On June 27th, 2014, Camille scored a career-high 31 points during an 81-71 win against the Minnesota Lynx. After the game she realized what had happened and smiled in amazement during a post-game interview with Voice of the Storm Dick Fain.

20130514_Media_Day_0014A
On May 14th, 2013, Tanisha and Camille pose for a selfie. Media Day is the best.

20130514_Media_Day_0204A

Thanks for all the great memories guys, and best of luck on your new teams!