Demo: LVAR write access for any aircraft control

Hi all,

After following the SDK discussions for a time now, I like to contribute a small demo code that might help others to also understand how to directly access LVARS from outside the sim. This allows for instance to set the Airbus Nav mode (LS, LOC, NAV, ARC, MAP), or the range of the MFD. The method that I use for this, is to build a small standalone WASM (WebAssembly) that handles any EventID that is send through a SimConnect interface.

The code that allows the above mentioned LVAR binding is elaborated below. Perhaps this is trivial to some, but to me it really helps to continue binding of hardware rotaries to the FlyByWire A320 that I was not aware of how to access before (through SimConnect at least).

I don’t maintain a GitHub, so the raw code is provided below. If it is helpful, at a later stage (when I included all LVARS) I may upload the WASM directly here. Till then, when compiling this in Visual Studio, I found myself manually adding the following two VC++ include directories:

  • C:\MSFS SDK\WASM\wasi-sysroot\include
  • C:\Program Files (x86)\Windows Kits\10\Include

[1] Required header files.

#include <MSFS/MSFS_WindowsTypes.h>
#include <MSFS/MSFS.h>
#include <MSFS/Legacy/gauges.h>
#include <SimConnect.h>

[2] Custom event handler that interacts with the LVARs.

// Define the SimConnect custom eventHandler.
static void FSAPI EventHandler(ID32 event, UINT32 evdata, PVOID userdata) {
	// Any event number from 0x11000 through 0x1FFFF is available
    // but might collide with other mods.
	switch (event) {
	case 0x11000: {
		// This sets the MFD NAV Mode.
		ID idA320 = check_named_variable("A320_Neo_MFD_NAV_MODE_1");
		// @TODO: range validation.
		// evdata options:
		// 0: LS
		// 1: LOC
		// 2: NAV
		// 3: ARC
		// 4: MAP
		set_named_variable_value(idA320, evdata);
		break;
	}
	case 0x11001: {
		// This sets the MFD NAV Range
		ID idA320 = check_named_variable("A320_Neo_MFD_Range_1");
		// @TODO: range validation.
		// evdata options:
		// 0: 10 nm
		// 1: 20 nm
		// 2: 40 nm
		// 3: 80 nm
		// 4: 160 nm
		// 5: 320 nm
		set_named_variable_value(idA320, evdata);
		break;
	}
	}
}

[3] Register the event handler function.

// This is called when the WASM is loaded into the system.
extern "C" MSFS_CALLBACK void module_init(void) {
	register_key_event_handler((GAUGE_KEY_EVENT_HANDLER)EventHandler, NULL);
}

[4] For completeness, also unregister when done.

extern "C" MSFS_CALLBACK void module_deinit(void) {
	unregister_key_event_handler((GAUGE_KEY_EVENT_HANDLER)EventHandler, NULL);
}

After compiling the WASM, it can be placed into a separate folder inside the community folder, for instance I choose

  • Community/lvar-access/module/WASM_module3.wasm

You must (at least it seems) also add in the Community/lvar-access/ folder also the following:

  • manifest.json
  • layout.json

The content that I put in there is based on the (awesome!) MobiFlight G1000/3000 Event ID module.

manifest.json:

{
  "dependencies": [],
  "content_type": "MISC",
  "title": "WASM Event Module",
  "manufacturer": "",
  "creator": "Your name",
  "package_version": "0.1.0",
  "minimum_game_version": "1.11.6",
  "release_notes": {
    "neutral": {
      "LastUpdate": "",
      "OlderHistory": ""
    }
  }
}

layout.json

{
  "content": [
    {
      "path": "modules/WASM_Module3.wasm",
      "size": 11158,
      "date": 132517602483582047
    }
  ]
}

Note: “size” is the size of the WASM file in bytes. I have no clue about the “date” format, but it doesn’t seem to botter that much.

Then, in any other SimConnect code, call for instance:

hr = SimConnect_MapClientEventToSimEvent(hSimConnect, EVENT_MFD_MODE_1, "#0x11000");
// ...
DOUBLE evdata;
// Set to evdata the required value 0 through 4. 
SimConnect_TransmitClientEvent(hSimConnect, objectID, EVENT_MFD_MODE_1, evdata, SIMCONNECT_GROUP_PRIORITY_HIGHEST, SIMCONNECT_EVENT_FLAG_GROUPID_IS_PRIORITY);

Hope this helps some people, and I am curious to learn more about how others tackle this issue.

Kind regards,
Maurice

14 Likes

Thanks for this! Looks interesting - I’m going to look into this when I get time.
Do you know if there is also a way from within the WASM module to list the available lvars, and if they are read-only or also updatable?

John

Hi John,

Thanks for your interest in this topic. I am not sure if I understand your question. Currently, I obtain the list of LVARs that are available by loading a flight in the simulator, and (in developer mode) I select Windows > Model behaviors. In the window that opens, I select the tab “LocalVariables” and try to interpret their functionality from the name. You can check your guess, by clicking the button in the cockpit, and observe a change in the indicated value in the window that just opened. Also, FlyByWire has a list in their repository at GitHub (in the root/docs folder).

Other then that, I found that within the case structure that handles the custom SimConnect EvenIDs (see my opening post), you can also use that to emulate MCDU (flight computer) key presses. Simply:

case 0x11002: {
		// Press MCDU button DIR
		FLOAT64* a;
		SINT32* b;
		PCSTRINGZ* c;
		execute_calculator_code("(>H:A320_Neo_CDU_1_BTN_DIR)", a, b, c);
		break;
	} 

This emulates a key press of the CDU DIR button. Ideally, variables a, b, and c should just be NULL, but for some reason that did not compile because it doesn’t match the function prototype. Nonetheless, the above implementation seems to work just fine. I imagine it can be compressed in terms of coding, by using the evdata variable to encode the button (when calling this routine from your SimConnect application).

That’s it for now, I’ll update the topic when I have a more complete WASM written up.

Regards,
Maurice

2 Likes

@VFRMau, thank you for taking the time to share your findings. This is a big help, and am looking forward to following the progress.

Thanks, that’s good to hear! I continued working on the WASM today and expect to release an early version perhaps tomorrow or in the course of the weekend. That version will have at least all LVAR/Button access that allow you to interact with the MCDU and the EFIS (Nav mode, range, display items as CSTR). Hopefully that can be of some help to others as well. Let me know if there are specific buttons you already need access to now!

Regards,
Maurice

Hi Maurice-

using nullptr instead of NULL compiles for me and avoids having to declare a, b and c

execute_calculator_code("(>H:AS430_MSG_Push)", nullptr, nullptr, nullptr);

Using the null pointer also compiles nicely, thanks for that @The727Pilot . I’ve now uploaded the full code that handles MCDU and EFIS panel interactions onto a GitHub page accessable here:

msfs-wasm-lvar-access/lvar-access.cpp at main · markrielaart/msfs-wasm-lvar-access · GitHub.

For instance, to set the EFIS range button, trigger event ID “#0x11001” and provide input argument 0x0Z03, with Z = 0, 1, … 4 (corresponding to range selection 10, 20, …, 320 nm). In general, the argument to set EFIS buttons is 0x0abc, with a the passed argument (typically 0 for off, 1 for on, or 0 through integer number for dials with multiple set values), and ‘bc’ the index of the LVAR in the LVAR_EFIS_PANEL array.

Similarly, to emulate an MCDU button press, trigger event ID “#0x11000” and as value pass the index number (starting at 0 for Button 0, etc) for the button you want to press.

A compiled WASM is also availble at the GitHub page as 'Initial release (v0.1). Let me know if you need more help, and I am curious to learn if this also works for instance with FSUIPC and other services as I don’t use that at the moment.

Maurice

For anyone using the Working Title CJ4 mod, WT was kind enough to provide the current set of Lvars and Hevents for their mod (subject to change and updates):

Good job ! Do you know how to interface with mobilight? Offset or the new MSFS EVENTS ?

Unfortunately I am not using MobiFlight or similar software, as I interface directly with MSFS through C++ scripts that invoke SimConnect calls/dispatches. If your 3rd party software allows for FSUIPC offsets, you might want to try offset 0x11000 and 0x11001 as mentioned in the source code. Or better, if given the option to provide an Event ID or event name, provide “#0x11000” or “#0x11001”. As value, provide for instance to event 0x11001 (EFIS panel controls) the following:

0x0003: MFD left, Range 10 nm
0x0103: MFD left, Range 20 nm
0x0203: MFD left, Range 40 nm
...
0x0503: MFD left, Range 320 nm

But perhaps someone else who is experienced in using MobiFlight, FSUIPC or other software packages can provide you with a better hint.

Maurice

Hi, Thanks for sharing this code. This code could have been much useful if all LVAR’s for all airplanes were available. I tried to check LVARs for the B747, but most of the LVARs were for the A320. It is a total mess, not your code, but the way MS-ASOBO have implemented this.

Hi @Voss1917 , you’re absolutely right that the LVARs I provide up till now are related to the A320 EFIS/MCDU controls due to personal preference at this time. I’ll take a look at the 747 and see if I can similarly add the EFIS controls of that plane to the WASM if you’re looking for that?

1 Like

Great work. Tried MCDU part and it works fine here using plain simconnect. Only thing is the ‘dot’ MCDU button def seems to be missing?

Hi Maurice,

Ok, thanks for the info. I was thinking in terms of FSUIPC. I need to be able to show what lvars are available for a particular aircraft, and also get values back from the WASM module to FSUIPC.
Currently you can integrate the MobiFlight WASM module to use their events via FSUIPC, but I need to add lvar support (read, write, list).
I will investigate the use of the simConnect Client Data area for the data transfer, but would need to be able to get a list of available lvars and access their values from within the WASM module, hence the question.
Anyway thanks for your work on this. I’ll take a look at your github repo and see if I can use this in FSUIPC. I’ve a few things to finish first, but hopefully I can start on this in a day or two.
And sorry for the late response - didn’t get a notification on this for some reason.

John

Thanks, yes I am very interested in the B747 LVars. How and where did you find the
“H:A320_Neo_CDU_1_BTN_0)”,
“(>H:A320_Neo_CDU_1_BTN_1)”,
“(>H:A320_Neo_CDU_1_BTN_2)”,
LVars?

And where did the event number from 0x11000 through 0x1FFFF came from?

Thanks for all your inputs!

To find the >H: functions, in the Developer mode, select Windows > Model Behaviors. Then make sure to select the airplane interior file, for instance A320_NEO_INTERIOR.XML or 747-8I_INTERIOR.XML. After that, try figuring out the child elements that may be related to the component of your interest. For instance, for the B747 MCDU buttons, you need to look here:

  • Root compenents: PEDESTAL_FWD
  • Childs.0: CDUS
  • Childs.1: CDU_1
  • Childs.2: BTN_CDU1_INIT
    This allows you to find for instance (>H:B747_8_FMC_1_BTN_INIT).

Later I’ll take a look at adding more LVARs and H/K buttons. It must be noted that LVAR updates do not always propagate through the simulator, so whenever possible it seems preferable to send calculator_codes rather than directly writing to LVARS.

Keep you posted!
Maurice

And where did the event number from 0x11000 through 0x1FFFF came from?

I forgot to reply on this, it is in th gauge.h file (in your MSFS SDK, for me <SDK root>/WASM/include/MSFS/Legacy/gauges.h, at line 3191, and 3192:

#define THIRD_PARTY_EVENT_ID_MIN 0x00011000
#define THIRD_PARTY_EVENT_ID_MAX 0x0001FFFF

Regards,

1 Like

Thanks again for your info.

Any idea how to get the local variables state, e.g. mcp annunciators from a wasm?

A good wasm should have the function of both raising events and reading variables.

And also, what is the difference between events starting with H:, A:, K:, L:.?

Is there a definition somewhere?

Great that this is of help to you, for sure you can also retrieve LVAR values, using get_named_variable(), similar to how it is set in the code I provided. Make sure to check the methods available in the Gauges API (it’s both included in the MSFS SK docs, and online from P3D: Gauges API. The alphabet functions deal with different types of variables, some info is available here (Variables Overview). It seems like MSFS has introduced some new Variable types, but I have perhaps missed the documentation on that so far?

Variables and events are very different things:

Variables

L: are the usual LVar from FSX. The “L” naming might be misleading, since it sounds a bit like “Local” vars, when in fact they are really global user variables.

A: are “Airplane” variables, however they work from other Simobjects like Ground Vehicles too, but not all kind of objects have the same variables available, the User airplane has all of them, AI airplanes have less, Ground Vehicles even less and SimpleObject have less than Ground vehicles. They are the “simulation” variables which you usually read only, because they are handled by the simulation so, even if some of them can be written, you’ll likely “fight” against the simulation which will write to them too. You usually change their values indirectly: by sending an EVENT which result in the simulation changing its state.

H: are new “Html” variables used by the new Html5/Javascript gauge system

O: variables are local variables that store some states of simobjects with an XML behavior

Events

K: are not variables, they are Events ( K as in “keyboard”, even if they might not be assigned to the keyboard at all ), so you SEND them to the sim to perform an action, which usually results in a related A: variable to change. So, for example, if you send the K: PARKING BRAKE event, it will affect the PARKING BRAKE POSITION A: variable.

Right now, you can access A: variables with Simconnect, even from an external .EXE written in any language that supports it ( usually C# or C++ ), but to access L: variables you must use a WASM module or gauge that will call the Panels interface which maps 1:1 the old Gauges SDK.

H: and O: variables are usually accessed from Html/JS code, there’s no documented way to access them with an API.

You can send Events both from Simconnect or from the Panels interface in WASM modules/gauges.

10 Likes