I’ve previously written about how to access the underlying Raspberry Pi 3 hardware with .NET Core on Ubuntu – but what about accessing GPIO on Windows 10 IoT Core with .NET Core? Presently the .NET Standard doesn’t have any API surface which would allow my .NET Core 2 application to switch GPIO pins on or off.

It’s easy to access pins if I write a UWP app, because the library to access GPIO is in the Windows.Devices.Gpio namespace. But this UWP app can’t be deployed to Ubuntu.

If I only wanted to run my code on Ubuntu (and didn’t care about running my code on Windows 10 IoT Core) then I could use libraries like PiSharp, or Raspberry# IO, or RaspberryGPIOManager. And whereas I can run this code on my Pi 3 which has Windows 10, it won’t actually do anything because it’s only the filesystem (not actually affecting GPIO statuses).

So we’ve two solutions for two different platforms, which isn’t really in keeping with the idea of writing cross-platform code for .NET Core. I don’t really want to have to write a .NET Framework app for my code to run on Windows 10 IoT Core, and then a separate .NET Core app for my code to run on Ubuntu 16.04. I want to write code once and be able to run it on different operating systems.

Can I write a single .NET Core application which can access hardware and run on both Ubuntu and Windows 10?

Project Bifröst

Project Bifröst allows me to write .NET Core code that accesses hardware and runs on both Ubuntu 16.04 and Windows 10 IoT Core. It is a Windows UWP application which runs on my Raspberry Pi 3 under Windows 10 IoT Core, and acts as a bridge between my .NET Core application and the Windows.Devices libraries which make hardware available to UWP applications.

The project is in a very early phase right now – it just switches GPIO pins on and off. I’ve a product roadmap planned – but more on that in a later post.


Basically it allows us to write code in .NET Core and access pins through an interface in both Ubuntu 16.04 and Windows 10 IoT Core devices.


In Ubuntu, we can control the status of GPIO pins by modifying the file system, e.g. If we want to switch GPIO 26 to a status of on:

  • We choose the pin to open by writing the number “26” to the file “/sys/class/gpio/export“,
  • We choose the pin’s drive mode by writing the text “out” to the file “/sys/class/gpio/gpio26/direction“, and
  • We switch on the pin by writing the number “1” to the file “/sys/class/gpio/gpio26/value“.

In Windows 10 IoT Core, we can control the status of GPIO pins using the Windows.Devices.Gpio API.

  • We instantiate a GpioController object,
  • We choose the pin to open by instantiating a GpioPin object,
  • We choose the pin’s drive mode using the SetDriveMode method on the GpioPin object, and
  • We switch on the pin by using the Write method on the GpioPin object.

Bifröst uses the UWP file system notifications feature to monitor for changes to the file system in Windows, and then translates those modifications into Windows.Device API calls.

How to download, build, deploy and use Bifröst

Ubuntu 16.04

After you’ve installed Ubuntu 16.04, you don’t need to install Bifröst – it’s a UWP app and only needed for Windows 10 IoT Core.

Windows 10 Iot Core

There’s no such thing as a Windows App Store for the Raspberry Pi, so the easiest way is to download the source, build and deploy. I’ve provided some pretty comprehensive instructions below.

I code using Visual Studio 2017 (which has a free community edition available here). I also have .NET Core 2 (presently in preview, available here) installed on my machine.

  • Get Bifröst. The code for Bifröst is available here (you can clone the whole code base, including libraries needed to write Bifröst applications, from here).
  • Connect a Raspberry Pi 3 with Windows 10 IoT Core installed. I’ve blogged previously about how to set this up, but the simplest way is to use the “Set up a new device” option from the Windows IoT Dashboard download, and install a Windows 10 IoT Core build to an approved SD card. Then insert this card into your Raspberry Pi 3 and power it up. It’s best to have an ethernet connection (so you can deploy apps more quickly than over wireless) and a monitor connected (so if something.goes wrong you’ll at least be able to visually debug it).


  • Open the Bifröst solution file in Visual Studio 2017.


  • You might need to set the Bifrost solution to be default – right click in the solution explorer and select “Set as startup project…”.


  • Change the project configuration’s Solution Platform to ARM.


  • Change the project configuration’s destination device to “Remote Machine”.


  • Usually your Raspberry Pi 3 will be automatically detected (as shown below).


  • But if it isn’t enter in the IP address of your Pi 3 into the textbox as shown below, and click on “Select”.


Now you can build the application (the keyboard shortcut is Ctrl+Shift+B) and deploy it to run (shortcut is just F5) on your Raspberry Pi 3. The splash screen with a bridge icon is shown below.


I prefer to have Bifröst up and running when I start my Pi – so I’ve modified the settings in the App Manager page of the Device Portal (at, though you will almost certainly need to use a different IP address for your Pi 3), and set my Bifröst app to run at start up.


How to access GPIO pins with platform independent .NET Core 2

Let’s use a simple example of turning a GPIO pin on and off.

I’ve written three libraries targetting .NET Standard 1.6 – available here – which I include in my Raspberry Pi 3 projects. These contain the necessary libraries, interfaces and enumerations to switch GPIO pins on and off.

In the solution, I’ve also included a sample library – named GpioSwitcher – which works on both Ubuntu 16.04 and Windows 10 IoT Core. The code below shows part of GpioSwitcher’s main method in the Program.cs file –  it opens a pin, sets the drive mode, and writes the pin value as high or low. This code works on Raspberry Pi 3 devices running either Ubuntu 16.04 or Windows 10 IoT Core.

// create gpio controller
Debug.WriteLine("About to instantiate the switch controller");
var controller = GpioController.Instance;
// open pin
Debug.WriteLine("Opening pin " + pinNumber);
var pin = controller.OpenPin(pinNumber);
// set direction
Debug.WriteLine("Setting the direction to out");
// set value
if (logicLevel == 1)
    Debug.WriteLine("Setting the value to high");
    Debug.WriteLine("Setting the value to low");

Deploying your application

UPDATE: I’ve written a post here describing a better and simpler way to deploy this application to Windows or Ubuntu using a PowerShell script.

I go into a lot of detail in the steps below – some of these steps will be really obvious to anyone who has deployed an application to a Raspberry Pi before, or has used an SSH client to remotely access a Raspberry Pi.

Deploying to Ubuntu

You’ll need to be able to ssh into your Raspberry Pi 3 running Ubuntu 16.04 – I’ve written a long post on how to set up Ubuntu 16.04 and install PuTTY and pscp here, which are useful tools for deploying applications to remote machines.

SSH into your Raspberry Pi and create a directory where you can drop the the GpioSwitcher application binaries.

mkdir /home/ubuntu/GpioSwitcher

Back on your dev machine, build the source code (I use Visual Studio 2017), and publish it by running the command below in a command prompt from the directory containing the project’s csproj file.

dotnet publish -r ubuntu.16.04-arm

Then browse to publication directory (which will be all the way in \GpioSwitch\bin\Debug\netcoreapp2.0\ubuntu.16.04-arm\publish), and open a command prompt from this directory. Run the command below:

pscp -r * ubuntu@

Once the project files are transferred to my Raspberry Pi 3, I change the permissions of the files in this directory to allow them to be executed as root using the command:

sudo chmod u+x,o+x *


Now you can alter the status of the GPIO pin using the command below (which switches the status of pin 26 to high (i.e. logic 1).

sudo -E /home/ubuntu/GpioSwitch/GpioSwitcher 26 1

Deploying to Windows 1o IoT Core

Open an explorer window to your Windows 10 Raspberry Pi 3 device (you can do this by right clicking on your device in the Windows IoT Dashboard and selecting “Open network share”.


Once you’ve opened the explorer window you can create a directory to hold the Windows application (I created a directory called “CoreTest”) as shown below.


Build the source code using Visual Studio 2017, and publish it by running the command below in a command prompt from the directory containing the project’s csproj file.

dotnet publish -r win8-arm

Then browse to publication directory (which will be all the way in \GpioSwitch\bin\Debug\netcoreapp2.0\win8-arm\publish), and copy all the files from here into the directory you just created on the Raspberry Pi 3. You can drag and drop, or you can use xcopy like the example command below (obviously you’ll have to change the source and destination directories to match your environment).

xcopy /y 
 "C:\Users\Jeremy\Documents\Visual Studio 2017\Projects\Magellanic.Hardware\GpioSwitcher\bin\Debug\netcoreapp2.0\win8-arm\publish" 

Now you can ssh into your Raspberry Pi 3 and run this command – I find the easiest way is to open a PowerShell prompt from the Windows IoT Dashboard by right clicking on the device and select “Launch PowerShell” (as shown in the image below).


This will open a PowerShell prompt and a dialog challenging you for your administrative password.


It takes a few seconds to connect (maybe even up to 30 seconds) – until then it just shows an empty blue screen – but eventually the prompt will return, as shown below.


At this point, I can navigate to the directory where I copied my console application (C:\CoreTest\) and then I can run the executable with the command below (which switches the status of pin 26 to high (i.e. logic 1).:

./GpioSwitcher 26 1

I’ve tested this out on two Raspberry Pi 3 devices running side by side – I designed and printed out a simple jig to hold the two devices and two mounted LEDs which are connected to Pin 26 on each of the devices so I can deploy and test each operating system side by side (you obviously don’t need two devices, I just find it more convenient than swapping out the SD card when I want to switch from Ubuntu to Windows).

Printing Pi Holder


Summing up

I’ve written some code that allows .NET Core applications to interact with GPIO pins. This code acts as a bridge between the application and the Windows.Device.Gpio API surface. This is early days for project Bifröst – it doesn’t do much right now except allow you to turn GPIO pins on and off, and the deployment process is a step-by-step manual process. But I only have to write my hardware access code once, and I can run it on different operating systems – I’ll work to expand the features with the aim of making it more functional.