Developing own WASM module + SimConnect client - all kinds of questions

Thanks to a document I found on this forum here written by @geoffda, I was able to build my first kind of “Hello world” WASM module. It simply initializes the WASM module, and writes some lines on the Console output of FS2020.

I used Visual Studio 2019 and created a new project of type “MSFS WASM Module”, which automatically creates the necessary files (see here if you want to know how to do this).

Based on the document above, I slightly changed the contents of the cpp-file (the standard cpp file in VS2019 looks slightly different than the one Geoff shows in his document), and added a few fprintf lines that I expect to appear in the FS2020 Console.

#include <stdio.h>
#include <MSFS/MSFS.h>
#include "WASM_Module1.h"

extern "C" MSFS_CALLBACK void module_init(void)
{
	fprintf(stderr, "HABI: stderr Line 1\n");
	fprintf(stdout, "HABI: stdout Line 1\n");
	fprintf(stderr, "HABI: stderr Line 2\n");
	fprintf(stdout, "HABI: stdout Line 2\n");
	fprintf(stdout, "HABI: stdout Line 3\n");
	fprintf(stderr, "HABI: stderr Line 3\n");
	fprintf(stdout, "HABI: stdout Line 4\n");
	fprintf(stdout, "HABI: stdout Line 5\n");
	fprintf(stderr, "HABI: stderr Line 4\n");
	fprintf(stderr, "HABI: stderr Line 5\n");
}

After building this code, the target file “WASM_Module1.wasm” is created. I loaded this WASM file in the community folder and created the 2 required json files according the following structure:

Community
|-- WASM_Module1 (folder)
|---- modules (folder)
|------ WASM_Module1.wasm (file)
|---- layout.json (file)
|---- manifest.json (file)

The content of layout.json and manifest.json are like this:

layout.json (the size of my WASM file is 28318 bytes):

{
  "content": [
    {
      "path": "modules/WASM_Module1.wasm",
      "size": 28318,
      "date": 132836382172023550
    }
  ]
}

manifest.json

{
  "dependencies": [],
  "content_type": "MISC",
  "title": "WASM_Module1",
  "manufacturer": "",
  "creator": "Hans Billiet",
  "package_version": "0.1.0",
  "minimum_game_version": "12.13.0",
  "release_notes": {
    "neutral": {
      "LastUpdate": "",
      "OlderHistory": ""
    }
  }
}

Then I started FS2020, used the “Windows/Console” and filtered on the word “HABI”. This shows the below:

image

Based on this output, my assumption is that the “stderr” is logging in the Errors (red cross), and stdout is logging in the Messages (blue i).

First question: Why do I only see one line for my Messages? If you look at my code above, you should see the same lines for Messages as for Errors. But it seems that the system stops printing in the Messages after the first newline (“\n”) is sent. Is this a known issue? Maybe this is the reason why the source code of @MobiFlight (see here) also only uses stderr?

Second question: Is there a way to restart the WASM module while FS2020 is running. That would avoid restarting FS2020 every time you have a code change (very long process :frowning: ).

I like to make drawings. Is the below drawing the correct understanding of how FS2020, WASM and SimConnect work together?

image

WASM is used for Lvars and Hvars, which are not available directly through SimConnect. SimConnect is dealing directly with the other vars such as A, K, O, etc…, right?

I use the following script (.bat file) to force a reload of the FSUIPC WASM module:

@echo off
copy /b "D:\MSFS2020\Community\fsuipc-lvar-module\layout.json"+,,"D:\MSFS2020\Community\fsuipc-lvar-module\layout.json"
copy /b "D:\MSFS2020\Community\fsuipc-lvar-module\modules\FSUIPC7_WASM.wasm"+,,"D:\MSFS2020\Community\fsuipc-lvar-module\modules\FSUIPC7_WASM.wasm"

I haven’t used this for a while (since around SU4) so cannot guarantee this still works, but worth a try.
Adapt too your own WASM.

I think the size also needs to have changed in your layout.json - see
Restarting StandaloneModule.wasm - #4 by VFRMau

John

I have acquired some basic knowledge on SimConnect with the help of a post of @dragonlaird that you can find here. I seem now to be able to read and control SimVars using SimConnect.

Next step is reading and controlling LVars, which requires the use of a WASM module. With my first babysteps, I accomplished to read and write the FWB A32NX variable “A32NX_EFIS_L_OPTION” and show some results in the Console window (no communication with the outside world still).

In my WASM module, I subscribed to the EVENT_FRAME. In the DispatchProc I then read and write to the LVar “A32NX_EFIS_L_OPTION” every 60 ticks.

case SIMCONNECT_RECV_ID_EVENT_FRAME:
{
	if (counter == 0)
	{
		counter = 60;

		ID varID = check_named_variable("A32NX_EFIS_L_OPTION");

		if (varID == -1)
			fprintf(stderr, "WASM_HABI: A32NX_EFIS_L_OPTION does not exist");
		else
		{
			value = get_named_variable_value(varID);
			fprintf(stderr, "WASM_HABI: get_named_variable_value(A32NX_EFIS_L_OPTION) = %u", value);

			value = (value + 1) % 6;

			set_named_variable_value(varID, value);
			fprintf(stderr, "WASM_HABI: set_named_variable_value(A32NX_EFIS_L_OPTION) = %u", value);
		}
	}
	else
		counter--;

The result is a nice “lightshow” on the left EFIS panel :slight_smile: and the below result in the Console window:

image

Next step will be to build some interface using the SimConnect DataArea to allow me to set and get LVars from the outside world.

Some questions:

  1. I asked this already in other posts, and nobody could give a working solution, but if there are still people out there that know how to “restart” the WASM module while FS2020 is running, please let me know how. That would make developing a WASM module a lot less of a nightmare.
  2. Is it correct that these “gauge”-functions only work with double? What if I have to deal with strings? Is “execute_calculator_code” then the only option?
  3. Is there a way to “register” for LVars so that I’m only triggered when they change? Something like the flag SIMCONNECT_DATA_REQUEST_FLAG_CHANGED when used with SimConnect_RequestDataOnSimObject?

Heya. I’ve been doing lots of similar investigations over the last 6 months for my ‘Better Bravo Lights’ project which replaces the standard Aerosoft Bravo Throttle lights tool with something much more capable. I ended up creating my own LVar-reading module. All the source is here:

To answer two of your latest questions:

  1. The only way I’ve found to ‘restart’ the WASM module is to use the MSFS SDK to load and re-load the WASM module. That way you genuinely can recompile and redeploy new versions of the WASM module without having to restart MSFS.
  2. get_named_variable_xxx only works with double as far as I can make out.
  3. No, there’s no way to register for changes. My LVar module scans the registered LVars a few times a second and sends messages to my client when they change. I’ve seen other LVar modules do the same sorts of things so it’s a real shame there isn’t yet an industry standard LVar module (or a first-class Microsoft-supported API) that everybody could use.

Hello @RoystonSidhe,

In the meantime, I’ve acquired a lot of expertise by making my own tools. I have posted a tutorial here.

Can you give a bit more explanation here? What exactly do you mean with using the MSFS SDK? I’m currently working on my WASM module, and restarting the sim each time is really time consuming!

Thanks

Ah, you have been busy! That would certainly have been a helpful tutorial as I was getting going with this stuff!

I’ll put together some instructions for reloading the WASM without restarting the sim. It’s been a few months since I’ve done it so I’ll need to refresh my own memory. But it’s definitely doable, and I’ve definitely been doing it whilst developing my WASM. Edit the C++, build, reload in MSFS, repeat. No sim restarts.

Back shortly…

Ah, here we go. I’d already documented it, in fact, in my project’s build instructions:

Specifically:

So, the trick is not to do any hand-assembling of the module, creating layout.json files, or putting anything in the Community folder. You need an actual MSFS SDK project (which points at the .vcxproj file). Once you’ve got the project file you can build your .DLL in VS, and you turn it into an internally-deployed WASM directly inside MSFS and its SDK using the ‘Build All’ button.

My project file is here: https://github.com/RoystonS/BetterBravoLights/tree/main/MSFSWASMProject

(I’m also not hand-assembling the final module: I’m just using the official ‘fspackagetool.exe’ from the MSFS SDK to create the final deployment. Details in my build script here: https://github.com/RoystonS/BetterBravoLights/blob/main/build-and-publish.ps1#L27)

I’ve just uninstalled VS2019 and upgraded VS2022 and so my build has completely broken so I can’t reproduce it right now. I’ll get that tidied up and double-check what’s going on.

Thanks a lot @RoystonSidhe,

I will have a look at it later, but looks quite promising. I’m first trying to solve a nasty issue in which I use a structure between WASM and SimConnect Client, but there seems to be something wrong with the alignment of the members when I’m using a float (tried with float, double, FLOAT64, …). It currently makes me crazy… probably need a few more hours to dig that out.

And yes, I’m very busy for the moment. You can expect another kind of tutorial from me in the coming weeks. I have expanded my earlier tutorial with a kind of USB server that allows connecting/disconnecting Arduino’s (or other types, as long as they use the serial over USB protocol). So stay tuned!

Hello @RoystonSidhe,

I’m still missing something. How do you create this xml file? From where does “MSFSWASMProject” come from? In my solution (includes a C# form project and the C++ WASM project), I don’t find such folder nor xml file.

Is this something that gets created from the MSFS Dev mode? I have no knowledge on this at all! So if you say “yes”, then please can you guide me to some documentation?

Thanks!

Yep, you have to create two separate things. There’s the WASM module C++ project (which you can create using the C++ MSFS template that the MSFS SDK installs into VS2019) and a separate MSFS project that you create using MSFS itself.

I’m just battling with this stuff myself. I hadn’t realised that the MSFS plugins only existed for VS2019 and not for VS2022, so I’m downgrading my project to VS2019.

Try creating a new project using [DevMode]->New Project. You’ll end up with a project structure that you can compare to mine (https://github.com/RoystonS/BetterBravoLights/tree/main/MSFSWASMProject). You can then tweak the configuration by hand and (somehow - still working on that - will update you when I figure it out again) point it at your .cxproj file, allowing you to rebuild your WASM and reload it inside MSFS.

My setup in the above GitHub repo looks slightly broken - I seem to have an old copy of a C++ project inside my module definition from when I was playing around with it originally. I’m attempting to repair and tidy it up now.

Hello @RoystonSidhe,

I’m getting crazy! Trying to include a FLOAT64 in a structure that I exchange between my WASM Module (C++) and my SimConnect Client (C#), and there is something wrong. It works with UInt16, but not with the FLOAT64. Maybe you have an idea?

This works perfect:

WASM-module:

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
struct LVar {
	UINT16 lvID;
	UINT16 DefineID;
	UINT16 Offset;
	UINT16 Value; // < It works with UINT16
	char Name[MESSAGE_SIZE];
	char Event[EVENT_NAME_SIZE];
};

SimConnect Client:

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
public struct LVarAck
{
    public UInt16 lvID;
    public UInt16 DefineID;
    public UInt16 Offset;
    public UInt16 Value;  // < It works with UInt16
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
    public String str;
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
    public String sEvent;
};

This doesn’t work:

WASM-module:

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
struct LVar {
	UINT16 lvID;
	UINT16 DefineID;
	UINT16 Offset;
	FLOAT64 Value; // < It DOES NOT work with FLOAT64
	char Name[MESSAGE_SIZE];
	char Event[EVENT_NAME_SIZE];
};

SimConnect Client:

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
public struct LVarAck
{
    public UInt16 lvID;
    public UInt16 DefineID;
    public UInt16 Offset;
    public double Value;  // < It DOES NOT work with double
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
    public String str;
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
    public String sEvent;
};

In the WASM Module, a FLOAT64 is in fact a double, as you can see in below code that resides in gauge.h:

// Floating-point types
typedef double  FLOAT64, *PFLOAT64, **PPFLOAT64;

But when I receive the structure in my SimConnect Client via Client Data, I clearly see that there is something not aligned. As you can see in the below screenshot, the structure str which should be right behind the double (although, strange that the order of the variables seems changed?), I see clearly that some extra data is written where the string str should start. It’s like the FLOAT64 in the WASM module consumes more space and is occupying the first characters of my string str.

image

The weird thing is that I have another structure that works perfectly with FLOAT64/double:

WASM-Module:

struct Result {
	FLOAT64 exeF;
	SINT32 exeI;
	char exeS[256];
};

SimConnect Client:

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
public struct Result
{
    public double exeF;
    public Int32 exeI;
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
    public String exeS;
};

Help!

Interesting. I do have an array of FLOAT64s in my WASM comms structure here:

Here’s how I unmarshal them in the C#:

That’s for an array, but it’s probably worth trying the explicit ‘R8’ unmanaged type declaration.

As you can see from my C# structure, I had similar alignment issues and ended up taking complete control of every field, not trusting what the underlying marshaling defaults were doing.

(In other news, I’ve just managed to get my LVar module loading into MSFS dynamically again. A lot of stuff has changed in the last few months which broke my previous mechanism. I’m just getting some repro steps together for you…)

Ok, so here’s how to create a proper MSFS project that’ll get your WASM file reloading without restarting the sim, and so you can use the real MSFS tools to create your final package (rather than the MSFSLayoutGenerator tool)…

Inside MSFS, click [DevMode]->New project
image

You should see a ‘Create new project’ dialog:

image

When you eventually click ‘Create new project’ on this new dialog, MSFS is going to create a folder - named after the ‘Project Name’ field, in the ‘Projects Folder’ location. The Project Name is just a build-time thing and isn’t visible to end users. In the screenshot I show the settings I’ve used here for my tool.

After clicking ‘Create new project’ you may be prompted with a dialog asking for creator and company name. Or you may not.

Then you’ll see a ‘Wizard selection’ dialog. You’re not creating an Airport or a Visual effect, so select ‘Custom’:
image

The UI will expand to allow you to type in a Display title and a Package name.
image
The Display title is the user-visible main name for your package (it’ll become the “title” in your manifest.json file).
The Package name is going to be the on-disk actual package directory name: all lower-case-with-dashes. (Note that it’ll silently add any “Default company name” setting as a prefix.)
Leave the Content-Type as ‘MISC’.
Click ‘Next’

Eventually you’re going to have two “asset groups” in your MSFS project. One is created automatically, called ‘ContentInfo’, which provides some basic package details such as a thumbnail. We’ll now add another asset group which actually deploys the WASM file.

image

Name your custom asset group ‘Module’ (or anything you like, but ‘Module’ is a reasonable name for the group deploying your .wasm file), and set the Asset type to ‘Copy’. (It’s literally going to copy your compiled WASM file to the output, and not do any processing on it.)
Click ‘Create’.

In the Project Editor, click View->Inspector and you’ll see all your Project details:

Since about November 2021 you now HAVE to put a thumbnail into your package
otherwise it won’t build. You need to create a JPEG file (not PNG) that’s 412x170.
Click on the ‘Content Manager Thumbnail’ block and give it the JPEG file. It’ll copy
that file into PackageDefinitions\package-name\ContentInfo\thumbnail.jpg.

That’s your ContentInfo asset group finished.

Down in the ‘Asset Groups’ section you’ll find the other “Module (Copy)” asset group that we created.
image
Click ‘Edit’. You’ll be taken to a dialog that allows you to control where you’re copying files from
(the AssetDir) and where you’re copying them to (the OutputDir).
By default the values are pretty nasty:

Change the OutputDir value to modules\ as that’s the directory you want your .wasm
file to end up in.
Set the AssetDir to wherever you build your .wasm file to. I keep things simple and arrange for my .wasm file to end up in PackageSources\modules\:

In Project Editor, save your Project.

Copy your .wasm file to your AssetDir (for me, PackageSources\modules).

Ensure you have the MSFS console open (Windows->Console) and click ‘Build All’ in Project Editor. MSFS will freeze up for around 15 seconds, you should see a whole load of entries in the console and it should load your WASM file and start emitting any debug output to the MSFS console.

To reload your .wasm file, rebuild it, copy it to the AssetDir, hit ‘Build All’ and you’re done. It’ll unload the old copy and load the new.

I’ve automated that copying by adding a custom ‘Post Build Event’ to my C++ module compilation settings:

So, the round trip for me is:

  1. Do a build in VS
  2. Click ‘Build All’ in MSFS

Armed with this MSFS project you can also build the actual final output package using the fspackagetool.exe from the MSFS SDK Tools folder. It takes a little while (I think it’s basically booting up MSFS in a command-line tool!) but it produces a perfect output package as defined by MSFS.

Hope that all helps? It’s been a useful reminder for me.

(In my case it was all broken because I’d hand-edited my main project .xml file to change the output folder from ‘Packages’ to ‘’. MSFS has been changed so it now wipes the output folder during a build… destroying all my input files.)

3 Likes

Hello @RoystonSidhe,

I found it. It has to do with packing. You can read some explanation here. I got it working and all makes sense now.

In my SimConnect Client I used:

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]

My C++ code seems to ignore this Pack = 1 directive, and is using a default packing, which is pack = 8. This means that the structure in my WASM Module (C++) is mapped as below:

Address 0: UINT16 lvID
Address 2: UINT16 DefineID
Address 4: UINT16 Offset
Address 6: padding             < this padding is automatically added to align at 8 bytes
Address 8: FLOAT64 Value
Address 16: ...

But in my SimConnect Client, I used “Pack = 1”. I don’t know why, but I must have copied this from some other source without realizing what it does. This instructs the compiler to align the elements on 1 byte if possible. This means that the padding is not added, hence the misalignment.

Address 0: UInt16 lvID
Address 2: UInt16 DefineID
Address 4: UInt16 Offset
Address 6: double Value < HERE THE MISALINGMENT STARTS
Address 14: ...

That’s the reason why the structure in my WASM Module occupied 304 bytes (default packing of 8), and in my SimConnect Client it only occupied 302 bytes (pack = 1).

I first compensated this by adding a ■■■■■ UInt16 after the Offset field, and this worked as a charm. After that, I simply removed the “pack = 1” directive, which resulted in the same packing as for the WASM Module. Although, I’m not so sure if the default packing is always 8, and feel more comfortable by forcing the packing, so I finally found the way to control the packing in C++ using the “#pragma” directive.

#pragma pack(push, 1) // packing is now 1
struct LVar {
	UINT16 lvID;
	UINT16 DefineID;
	UINT16 Offset;
	FLOAT64 Value;
	char Name[MESSAGE_SIZE];
	char Event[EVENT_NAME_SIZE];
};
#pragma pack(pop) // packing is 8 again

It looks like we learned something new today :nerd_face:

I try to follow all the steps you explained to create a MSFS project. I’m doing this almost “blind”, because I have no clue what I’m doing :rofl:

I’m stuck at this step. I seem not to be able to type anything in this field. If I’m using the “…”, and select the folder where my wasm module is built (because that is the source, right?), then it also doesn’t do anything.

image

But apart from that, IT WORKED!!! I created a new WASM Module and just added the word “TEST” in my initialization line, and after about 10 seconds, I see it appearing in the Console Window.

Man, you really saved me a lot of time!!! I will reference this part of the post in my tutorial, because a lot of people will like this! THANKS A MILLION!!!

Yep, the AssetDir field isn’t directly editable (which is really annoying for a field that’ll produce a relative path). You have to use the file browser to select the path, which at that point in the process probably doesn’t exist!

Very glad it worked. Now that you’ve got a project you can tweak the generated .xml files rather more easily than the MSFS editor dialogues allow. Yup, it definitely saves a LOT of time!

Now I just need to figure out how to display something on the screen in MSFS without resorting to an InGamePanel. I just want to get a brief message on the screen and it seems almost impossible!

Ah, good stuff. So it wasn’t the size or marshalling of your FLOAT. It was the layout due to the things that came before. Nasty!

Sorry if it isn’t the right place to ask, but I wasn’t able to find something more fitting… As you guys seem to have some experience with Simconnect and the FBW A32NX I was hoping you might be able to point me in the right direction (or perhaps you’d be interested), for the following request:

I’m working on a debriefing training tool that’s used in aviation and we’re exhibiting at an expo in about a month’s time (company: VRef Solutions).

To showcase our product we’re using a MSFS VR setup and we’re planning on using the FBW A32NX to let visitors take it for a spin. Ideally we want to integrate our app with MSFS and detect certain events (takeoff, altitude info, landing, malfunctions, etc). Also it’d be great to be able to restart a session for the next visitor from our custom app preloaded with the FMC setup etc. I believe most of this should be possible through the Simconnect API, however I’m no C++ or C# developer. So we’re looking for someone to build a relatively simple API to connect an iPad app to MSFS.

Is there anyone here that would be able to build a relatively simple middleware tool to feed the events through to our app and vice versa?

Of course we’re willing to pay!

Or if anyone knows where to find a dev that would be willing to do this on short notice, that’d be much appreciated!

Thanks,
Fabian

Hi,

I have for some time looked for a solution on how to load my WASM without having to reload MSFS after every WASM modification. Then I found this topic and tried it out.

My WASM ends up the Copys\company\modules. Should it not ends up in the Community folder?

Where does it get mounted?

I get this in the Console window after hitting Build All and can’t rely verify that I succeded to load and start the WASM.
The final message from PackageBuilder is: Finished: 2 skipped, 0 done and 0 failed took 0s050ms

IIs this correct? How can I verify that the WASM loaded correctly?