Previously I’ve written about creating a .NET Core 2 Web API and hosting it on a Raspberry Pi 3, and this time I’ll expand on this work to interact with GPIO pin logic levels.

This is the latest in a series of posts helping developers write .NET Core 2 code to interact with IoT hardware, in a way which is agnostic towards the device operating system. I’ve written a few bits and pieces about how to change GPIO pin status with a console application previously – but with a WebAPI, we can now control GPIO status with HTTP Post requests. So with this capability, you can imagine how we could control a physical device from something like a browser application, or even a HoloLens or Xbox app.

TL:DR – as usual, the source code is up on GitHub here.

Create the Web API project for the Raspberry Pi

This bit is easy – once you have .NET Core 2 on your machine, just install the template from Nuget using the command below:

dotnet new -i RaspberryPi.WebApi::*

And then pick a folder on your development environment to create a new project called GpioSwitcherWebApio with the command:

dotnet new piwebapi -n GpioSwitcherWebApi

At this point you’ll have all the code you need to run a .NET Core 2 Web API project on your Raspberry Pi.

If you want to read more about this, check out a longer post here.

Create a controller to change pin status

Let’s open our project, and add a dependency on the Bifröst project – this helps us in a couple of ways. We can write the same code to target both Ubuntu and Windows 10 IoT Core devices, and there’s also a Bifröst UWP app that helps us access GPIO hardware on Windows 10 IoT devices. Open up a Package Manager prompt in Visual Studio 2017 and enter:

Install-Package Bifrost.Devices.Gpio.Core -Version 0.0.1
Install-Package Bifrost.Devices.Gpio.Abstractions -Version 0.0.1
Install-Package Bifrost.Devices.Gpio -Version 0.0.2

If you’re not using Visual Studio 2017, you can just modify the GpioSwitcherWebApi.csproj file and add the package references shown below:

    <PackageReference Include="Bifrost.Devices.Gpio" Version="0.0.2" />
    <PackageReference Include="Bifrost.Devices.Gpio.Abstractions" Version="0.0.1" />
    <PackageReference Include="Bifrost.Devices.Gpio.Core" Version="0.0.1" />
    <PackageReference Include="Microsoft.AspNetCore" Version="2.0.0-preview1-*" />
    <PackageReference Include="Microsoft.AspNetCore.Mvc" Version="2.0.0-preview1-*" />
    <PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="2.0.0-preview1-*" />

Next, we can edit the default ValuesController which comes with the project – I’ve renamed mine to be PinsController.cs, which is a title better suited to the action we’re going to carry out.

I want my controller to have three actions for Ubuntu or Windows IoT devices:

  • Get() – an HttpGet action which returns a list of Gpio pins which are presently exported, and their current status (high/low).
  • Get(int pinId) – an HttpGet action which returns the status of the Gpio pin with the number pinId.
  • SwitchPin(int pinId, int status) – an HttpPost action which allows me to select a GpioPin with number pinId, and set it to a value of status (which is either 1 or 0, corresponding to high or low).

The Bifröst libraries make setting up our controller to modify GPIO pin statuses very easy.

The code below is just an example – obviously in a properly structured application, modifying GPIO pin levels would be managed through an interface to a service layer.

First, we need to instantiate a static instance of the GpioContoller object – so we can add a private member variable and a class constructor, as shown below.

private IGpioController gpioController;
public PinsController()
    Console.WriteLine("In controller - instantiating GpioController instance");
    gpioController = GpioController.Instance;

Next, we need to write the HttpGet action that returns a list of Gpio pins which are presently exported, and their current status. The code below shows the controller action which achieves this, and returns a 200 OK Http code.

public IActionResult Get()
    Console.WriteLine("About to list pin statuses.");
    return Ok(gpioController.Pins);

We also want to be able to find the present status of a Gpio pin by passing the pin number to the HttpGet method, and we can do this with the code below.

public IActionResult Get(int pinId)
    GpioPinValue pinStatus;
    Console.WriteLine("About to get pin status.");
    var pin = gpioController.OpenPin(pinId);
    pinStatus = pin.Read();
    Console.WriteLine("Returning pin status.");
    return Ok(pinStatus.ToString());

Finally, the interesting bit – rather than just reading pin logic levels, I’d like to be able to modify them – I think the most logical Http verb to use here is the HttpPost verb, so I can post values for the pin number I want to change, and the level I want to change it to, using the code below:

public void SwitchPin(int pinId, int status)
    Console.WriteLine("About to change pin status.");
    var pin = gpioController.OpenPin(pinId);
    if (status == 1)
        Console.WriteLine("Going on");
        Console.WriteLine("Going off");

To see the complete controller file already coded, check it out here.

If you’ve followed the steps above correctly, you should be able to build the WebAPI application in your normal way (e.g. in Visual Studio 2017 use Ctrl+Shift+B, or from a terminal in VSCode, execute the dotnet build command.

If you’ve pulled the project from GitHub here, there’s a file named build.ps1 which you can run from a PowerShell prompt to do the build for you. There are other more useful ones generated for publishing and deploying to Ubuntu 16.04 or Windows 10 IoT Core.

Deploying to your Raspberry Pi device

I’ve previously written step by step instructions on how to deploy code to a Raspberry Pi 3 running Ubuntu 16.04 or Windows 10 IoT Core, so I won’t repeat all of that here – the easiest way to do this is just to run the into PowerShell scripts I’ve uploaded to Github, and I briefly cover these below along with the parameters these scripts need to run.

To run these scripts successfully, make sure you’ve switched on your Raspberry Pi 3, logged into the device, and have connected it to your network.

Deploying to Ubuntu 16.04

  • Make sure you’ve got PuTTY installed on your development machine.
  • Get the IP address of your Raspberry Pi 3 (mine is
  • Get the username you logged in with (default is ubuntu).
  • Get the path you want to deploy your WebAPI to (mine is /home/ubuntu/GpioWebAPI)

Using the script hosted here, run the command in PowerShell:

.\deploy-ubuntu.ps1 -ip -username ubuntu -destination /home/ubuntu/GpioWebAPI

The WebAPI binaries will be build and published for an Ubuntu OS, and then copied to your Raspberry Pi.

Deploying to Windows 10 IoT Core

This is a little bit more complex – you have to deploy your WebAPI, and also deploy the Bifröst UWP app (you need the Bifröst UWP app on Windows to allow your .NET Core 2 app to read and change logic levels of your GPIO pins).

First, deploy the Web API application

  • Get the IP address of your Raspberry Pi 3 (mine is
  • The name of the Web API application, which for me is GpioSwitcherWebApi.

Using the script below (you can get a copy from here), run the command to create the destination directory and add a firewall rule:

.\setup-windows.ps1 -ip -applicationName GpioSwitcherWebApi

Now run the script below (you can get a copy from here), which copies the binaries to your Raspberry Pi 3.

.\deploy-windows.ps1 -ip -applicationName GpioSwitcherWebApi

Thanks to wind-rider for their pull request with excellent powershell scripts.

Next, deploy the Bifröst Windows Device Bridge

How to deploy this UWP app is described in detail here, but it’s just a standard UWP app deployment. You can download the code from here, load it into Visual Studio Community 2017, and deploy it to your Raspberry Pi 3 hosting Windows 10 IoT Core.


Start the Kestrel web server to start the Web API

This is straightforward – for a Raspberry Pi hosting Ubuntu 16.04, I ssh in using PuTTY and run:

sudo /home/ubuntu/GpioWebApi/GpioSwitcherWebApi

And for a Raspberry Pi hosting Windows 10 IoT Core, I ssh in using PowerShell, navigate to where deployed the app, and run:


The web server will start up after a few seconds and it’s ready to test.

Testing our Web API by changing GPIO pin logic levels

We can test this really easily by issuing HttpGet or HttpPost requests to our webserver. Let’s test this for Pin 26 on our Raspberry Pi – I’ve connected an LED between Pin 26 and ground.

For my Windows 10 Raspberry Pi, I can just browse to the address:

This will return a JSON list of pins and logic levels (it’s probably an empty list if you’ve not run this before).

To switch a pin on, let’s use a Firefox plug in like HttpRequester.

  • For the URL, enter the URL above (
  • Select the “Parameters” tab (as shown below) and add name-value pairs of:
    • pinId = 26
    • status = 1
  • Now click on the “POST” button

The site responds with a HTTP status of 200 OK, and the logic level of GPIO pin 26.


The logic level is 1, which means the white LED attached to pin 26 will turn on.

Windows 10 Web API - LED On

If we want to find the status of Pin 26 now, we can read it with an HTTP get request of:

As shown below, there’s a GET request which returns a status of 200 OK and a text value of High – which is what we expect as we’ve just turned the pin on.


Finally let’s just issue an HTTP Get request with no pin Id specified to get all statuses – again we receive a 200 OK code and a JSON object listing the open GPIO pin and its status.


For testing Ubuntu the process is identical except for I had to substitute the IP address of my Ubuntu Raspberry Pi (which is Repeating the process above turns on the orange LED attached to the Ubuntu Raspberry Pi (see below)

Raspberry Pi running Ubuntu LED On

Wrapping up

That’s all for this time – we’ve seen how to access GPIO pins from a .NET Core 2 Web API, and deploy that application to a Raspberry Pi 3 running either Windows 10 IoT Core or Ubuntu 16.04. This technique allows us to use the Raspberry Pi’s capabilities from a wider variety of interfaces than just a console – so we could use a browser, or even a HoloLens or Xbox app.