Portable IoT: Deep dive into how Meadow runs on Windows, Linux and Microcontrollers.

In an earlier blog I talked about why Meadow apps on the F7 (i.e. our Feather and Core Compute based products) are always named “App.dll”, but if you’re running Meadow on Linux or Windows, that explanation makes things a bit muddy. In this deep dive, we’re going to walkthrough the source code to show how Meadow works under the hood to run seamlessly in any compute context, including our MCU boards, Linux installations like Raspberry Pi, or even in Desktop with full hardware simulation.

MeadowOS Class and App Startup

First, let’s talk about the MeadowOS class.

This class has the responsibility of building up the Meadow software stack. It creates the ILogger, IDevice and IApp instances, injects them into the Resolver, initializes the hardware implementation, and finally calls into Initialize and Run in your app.

On the F7 platforms, since we (Wilderness Labs) control the OS, we start up the runtime (Mono in that case) and Mono directly calls static void Main in Meadow.dll. Meadow.dll in turn creates and calls into your IApp implementation. This is an important distinction. The runtime does not load and run your application assembly, instead it loads Meadow.dll and that in turn loads your application assembly.

Since we control the code for the entire startup path, this is all hidden from you as a developer. We can, behind the scenes, do all of the startup and bootstrapping work necessary for your application to have all of the nice APIs to access I/O and all you have to do is implement Initialize() and Run().

App Startup on Desktop (Linux/Windows/Mac)

On Linux or Windows, however, we can’t follow this path. On those platforms, Meadow is just a library loaded by the public, Microsoft-supplied, runtime (e.g. .NET 7). We don’t control what gets loaded, and application developers are used to the runtime loading their app assembly directly.

However, we still need all of those critical underlying Meadow bits to get created an initialized. What we opted for is to require a single, early call that you make in your app’s Main to Meadow.OS.Start(). This “bootstrap” pattern is similar to what is used by other frameworks like UWP, Xamarin, MAUI and Avalonia.

Once your application calls MeadowOS.Start(), the same path and functionality in the Meadow stack gets created and initialized, just like it does on the F7. It creates the ILogger, the IDevice, your IApp and then it calls Initialize and Start. (If you’re using something like Meadow.Avalonia, that’s a name collision, so we had to alias to InitializeMeadow)

Physical pin definitions make different platforms inherently divergent, but by using this common code path for the Meadow.Core services and objects, we help keep your Meadow application code as portable as possible.

If you have any questions, feel free to ask the team over on our public Slack channel.