.net core, arduino, Bifröst, IOT, Making, Raspberry Pi 3

Using .NET Core 2 on Raspbian Jessie to read serial data from an Arduino

I’ve previously written about how to use the System.IO.Ports library in .NET Core 2 to read serial data from an Arduino using a Windows 10 PC – but this library doesn’t work for Linux machines. This time I’m going to look at how to read data from an Arduino over USB with .NET Core running on a Raspberry Pi 3 with Raspbian Jessie.

There’s a few steps to getting this running.

  • First you’ll need:
    • A development machine (I’m using Windows 10),
    • A Raspberry Pi 3,
    • An Arduino,
    • A USB cable to link your Arduino and Raspberry Pi.
  • Next I’ll write a simple sketch and deploy it to my Arduino.
  • I’ll connect the Arduino to my Raspberry Pi 3, and check that the Pi can see my Arduino and the serial input using minicom.
  • Finally I’ll write and deploy a .NET Core application written in C# to my Raspberry Pi, and I’ll show how this application can read serial input from the Arduino.

As usual, I’ve uploaded all my code to GitHub and you can see it here.

For a few of the steps in this guide, I’ll refer to other sources – there’s not a lot of value in me writing a step by step guide for things which are commonly understood.

For example, I have a fresh install of Raspbian Jessie on my Raspberry Pi 3, and then I set up SSH on the Pi. I also use Putty, PSCP, Plink and Cake.net to deploy from my Windows machine to the Raspberry Pi (I’ve blogged in detail about this here).

Writing a sketch that writes serial data from the Arduino

In a previous post, I use VSCode to deploy a simple Arduino application that writes serial data. I could have used the Arduino IDE just as easily – and it’s widely known how to verify and upload sketches to the Arduino, so I’m not going to go into this part in great detail.

The code for the Arduino sketch is below:

int i = 0;
 
void setup(){
  Serial.begin(9600);
}
 
void loop(){
  Serial.print("Hello Pi ");
  Serial.println(i);
  delay(1000);
  i++;
}

One thing worth noting is that the Baud rate is 9600 – we’ll use this information later.

I can test this is writing serial data by plugging the Arduino into my Windows 10 development machine, and using VSCode or the Arduino IDE. I’ve shown a screenshot below of the serial monitor in my Arduino IDE, which just prints the text “Hello Pi” followed by a number. This confirms that my Arduino is writing data to the serial port.

screenshot.1502137097

Let’s test the Raspberry Pi can receive the serial data

Before writing a C# application in .NET Core to read serial data on my Raspberry Pi, I wanted to test that my Pi can receive data at all (obviously after connecting my Arduino to my Raspberry Pi using a USB cable).

This isn’t mandatory for this guide – I just wanted to definitely know that the Pi and Arduino communication was working before starting to write my C# application.

First, I need to find the name of the serial port used by the Arduino. I can find the names of serial ports by using PuTTY to SSH into my Pi 3, and then running the command:

ls -l /dev | grep dialout

Before connecting my Arduino UNO to my Raspberry Pi, this reports back two serial ports – ttyAMA0 and ttyS0.

screenshot.1502138314

After connecting my Arduino to my Raspberry Pi, this now reports back three serial ports – ttyACM0, ttyAMA0, and ttyS0.

screenshot.1502138155

Therefore I know the port used by my Arduino over USB is /dev/ttyACM0.

As an aside – not all Arduino boards will use the port /dev/ttyACM0. For example, I repeated this with my Arduino Nano and Arduino Duemilanove, and found they both use /dev/ttyUSB0, as shown below:

screenshot.1502211857

But for my Arduino Yun and my Arduino Primo, the port is /dev/ttyACM0.

screenshot.1502212282

So the point here is that you need to check what your port name is when you connect it to a Linux machine, like your Pi – the port name can be different, depending on what kind of hardware you connect.

Finally, if you’re interested in why “tty” is used in the Linux world for ports, check out this post.

Tools to read serial data

Minicom is a serial communication program which will confirm my Pi is receiving serial data from the Arduino. It’s very easy to install – I just used PuTTY to SSH into my Pi 3, and ran the command:

sudo apt-get install minicom

Then I was able to run the command below, using the port name (/dev/ttyACM0) and the Baud rate (9600).

minicom -b 9600 -o -D /dev/ttyACM0

If the Raspberry Pi is receiving serial data from the Arduino, it’ll be written to the SSH terminal.

Some posts I’ve read say it’s necessary to disable serial port logins to allow the Arduino to send messages to the Raspberry Pi, and modify files in the “/boot” directory – but on Jessie, I didn’t actually find this to be necessary – it all worked out of the box with my fresh install of Raspbian Jessie. YMMV.

Another alternative to prove serial communication is working is to install the Arduino IDE onto the Raspberry Pi, and just open the serial monitor on device. Again, installing the IDE on your Pi is very easy – just run the command below at a terminal:

sudo apt-get install arduino

This will even install an Arduino shortcut into the main Raspbian menu.

screenshot.1502139590.png

Once you’ve started the IDE and connected the Arduino to a USB port, select the serial port /dev/ttyACM0 (shown available on the Tools menu in the screenshot below):

screenshot.1502139935

Then open the serial monitor to check that the “Hello Pi” messages are coming through correctly (as shown below):

screenshot.1502140145

Writing the C# application

Now that I’m sure that the physical connection between the Arduino and Pi works, I can start writing the C# application.

TL:DR; I’ve uploaded my working code to GitHub here.

When writing my code, I wanted to stay close to the existing API provided by Microsoft in their library System.IO.Ports, which allows Windows machines to read from the serial port (I’ve blogged about this here). I was able to look at their source code on GitHub, and from this I designed the serial port interface below:

using Bifrost.IO.Ports.Core;
using System;
 
namespace Bifrost.IO.Ports.Abstractions
{
    public interface ISerialPort : IDisposable
    {
        int BaudRate { getset; }
 
        string PortName { getset; }
 
        bool IsOpen { getset; }
 
        string ReadExisting();
 
        void Open();
        
        event SerialDataReceivedEventHandler DataReceived;
 
        void Close();
    }
}

I like interfaces because consumers can use this interface, and don’t care if change my implementation behind the interface. It also makes my libraries more testable with mocking libraries. You can see this interface on GitHub here.

The next task was to design a .NET Core implementation for this interface which would work for Linux. I’ve previously done something similar to this for I2C communication, using P/Invoke calls (I’ve written about this here). After reading the documentation and finding some inspiration from another open source sample here, I knew I needed the following six P/Invoke calls:

[DllImport("libc", EntryPoint = "open")]
public static extern int Open(string portName, int mode);
 
[DllImport("libc", EntryPoint = "close")]
public static extern int Close(int handle);
 
[DllImport("libc", EntryPoint = "read")]
public static extern int Read(int handle, byte[] data, int length);
 
[DllImport("libc", EntryPoint = "tcgetattr")]
public static extern int GetAttribute(int handle, [Outbyte[] attributes);
 
[DllImport("libc", EntryPoint = "tcsetattr")]
public static extern int SetAttribute(int handle, int optionalActions, byte[] attributes);
 
[DllImport("libc", EntryPoint = "cfsetspeed")]
public static extern int SetSpeed(byte[] attributes, int baudrate);

These calls allow me to:

  • Open a port in read/write mode and get an integer handle to this port;
  • I can also get a list of attributes, specify the baudrate attribute, and then set these attributes.
  • Given the handle to the port, I can read from the port into an array of bytes.
  • Finally, I can also close the connection.

I’ll look at the most important elements below.

Opening the serial port

If we have instantiated a port with a name (/dev/ttyACM0) and a Baud rate (9600), we can use these P/Invoke calls in C# to open the port.

public void Open()
{
    int handle = Open(this.PortName, OPEN_READ_WRITE);
 
    if (handle == -1)
    {
        throw new Exception($"Could not open port ({this.PortName})");
    }
 
    SetBaudRate(handle);
 
    Task.Delay(2000);
 
    Task.Run(() => StartReading(handle));
}

You’ll notice that if the request to open the port is successful, it’ll return a non-negative integer, which will be the handle to the port that we’ll use throughout the rest of the class.

Setting the Baud rate is straightforward – we get the array of port attributes using the port’s handle, specify the Baud rate, and then send this array of attributes back to the device.

private void SetBaudRate(int handle)
{
    byte[] terminalData = new byte[256];
 
    GetAttribute(handle, terminalData);
    SetSpeed(terminalData, this.BaudRate);
    SetAttribute(handle, 0, terminalData);
}

I give the port a couple of seconds to settle down – I often find that the first few messages come through out of order, or with missing bytes – and then run the “StartReading” method in a separate thread using Task.Run.

Reading from the serial port

Reading from the port is quite straightforward too – given the handle, we just use the P/Invoke call “Read” to copy the serial data into a byte array which is stored as a member variable. Before invoking an event corresponding to a successful read, I check that there actually is valid data returned (i.e. the return value is non-negative), and that any data returned isn’t just a single newline character. If it passes this test, I pass control to the event handler for the DataReceived event.

private void StartReading(int handle)
{
    while (true)
    {
        Array.Clear(serialDataBuffer, 0, serialDataBuffer.Length);
 
        int lengthOfDataInBuffer = Read(handle, serialDataBuffer, SERIAL_BUFFER_SIZE);
 
        // make sure there is data in the buffer, and check if it's just one character that it's not just a newline character
        if (lengthOfDataInBuffer != -1 && !(lengthOfDataInBuffer == 1 && serialDataBuffer[0== ASCII_NEWLINE_CODE))
        {
            DataReceived.Invoke(thisnew SerialDataReceivedEventArgs());
        }
    }
}

Putting it all together

I’ve put my interfaces and implementations into separate .NET Standard libraries so that I can re-use them in my other .NET Core applications. And when I write a sample program for my Raspberry Pi to read from my Arduino, the implementation is very similar to the implementation that works for Windows x86/x64 devices reading from an Arduino (covered in this post).

using Bifrost.IO.Ports;
using Bifrost.IO.Ports.Core;
using System;
 
namespace SerialSample
{
    class Program
    {
        static void Main(string[] args)
        {
            var serialPort = new SerialPort()
            {
                PortName = "/dev/ttyACM0",
                BaudRate = 9600
            };
 
            // Subscribe to the DataReceived event.
            serialPort.DataReceived += SerialPort_DataReceived;
 
            // Now open the port.
            serialPort.Open();
 
            Console.ReadKey();
 
            serialPort.Close();
        }
 
        private static void SerialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            var serialPort = (SerialPort)sender;
 
            // Read the data that's in the serial buffer.
            var serialdata = serialPort.ReadExisting();
 
            // Write to debug output.
            Console.Write(serialdata);
        }
    }
}

Once I’ve compiled my project, and deployed it to my Raspberry Pi (using my build.cake script), I can SSH into my Pi and run the .NET Core application – and this displays the “Hello Pi” serial output being sent by the Arduino, as expected.

screenshot.1502145572

Wrapping up

It’s possible to read serial data from an Arduino connected by USB to a Raspberry Pi 3 using C#. Obviously the code here is just a proof of concept – it doesn’t use handshaking, parity bits or stop bits, and it only reads from the Arduino, but writing back to the serial port could be achieved using the P/Invoke call to the “write” function. Hopefully this post is useful to anyone trying to use serial communications between a Raspberry Pi 3 and an Arduino.


About me: I regularly post about .NET – if you’re interested, please follow me on Twitter, or have a look at my previous posts here. Thanks!

 

.net, Cake, Continuous Integration, Raspberry Pi 3, UWP

Deploy a UWP application to a Windows 10 device from the command line with Cake

I’ve wanted to improve my continuous integration process for building, testing and deploying UWP applications for a while. For these UWP apps, I’ve been tied to using VS2017 for build and deploy operations – and VS2017 is great, but I’ve felt restricted by the ‘point and click’ nature of these operations in VS2017.

Running automated tests for any .NET project is well documented, but until relatively recently I’ve not had a really good way to use a command line to:

  • build my UWP project and solution,
  • run tests for the solution,
  • build an .appxbundle file if the tests pass, and
  • and deploy the appxbundle to my Windows 10 device.

Trying to find out what’s going on under the hood is the kind of challenge that’s catnip for me, and this is my chance to share what I’ve learned with the community.

This is part of a series of posts about building and deploying different types of C# applications to different operating systems.

If you follow this blog, you’ll probably know I normally deploy apps to my Raspberry Pi 3, but the principles in here could be applied to other Windows 10 devices, such as an Xbox or Windows Phone.

Step 1 – Create the demo UWP and test projects.

I’ll keep the description of this bit quick – I’ll just use the UWP template in Visual Studio 2017 – it’s only a blank white screen but that’s ok for this demonstration.

screenshot.1500076564

I’ve also created an empty unit test project – again the function isn’t important for this demonstration, we just need a project with a runnable unit test.

screenshot.1500076646

I’ve written a simple dummy ‘test’, shown below – this is just created for the purposes of demonstrating how Cake can run a Unit Test project written using MSTest:

using Microsoft.VisualStudio.TestTools.UnitTesting;
 
namespace UnitTestProject2
{
    [TestClass]
    public class UnitTest1
    {
        [TestMethod]
        public void TestMethod1()
        {
            Assert.IsTrue(true);
        }
    }
}

Step 2: Let’s build our project and run the tests using Cake

Open a powershell prompt (I use the package manager console in VS2017) and navigate to the UWP project folder. Now get the Cake bootstrapper script and example Cake build file using the commands below:

Invoke-WebRequest http://cakebuild.net/download/bootstrapper/windows -OutFile build.ps1

Invoke-WebRequest https://raw.githubusercontent.com/cake-build/example/master/build.cake -OutFile build.cake

I edited the build.cake file to have the text below – this script cleans the binaries, restores the NuGet packages for the projects, builds them and runs the MSTests we created.

#tool nuget:?package=NUnit.ConsoleRunner&version=3.4.0
//////////////////////////////////////////////////////////////////////
// ARGUMENTS
//////////////////////////////////////////////////////////////////////

var target = Argument("target", "Default");
var configuration = Argument("configuration", "Release");

//////////////////////////////////////////////////////////////////////
// PREPARATION
//////////////////////////////////////////////////////////////////////

// Define directories.
var buildDir = Directory("./App3/bin") + Directory(configuration);

//////////////////////////////////////////////////////////////////////
// TASKS
//////////////////////////////////////////////////////////////////////

Task("Clean")
    .Does(() =>
{
    CleanDirectory(buildDir);
});

Task("Restore-NuGet-Packages")
    .IsDependentOn("Clean")
    .Does(() =>
{
    NuGetRestore("../App3.sln");
});

Task("Build")
    .IsDependentOn("Restore-NuGet-Packages")
    .Does(() =>
{
    if(IsRunningOnWindows())
    {
      // Use MSBuild
      MSBuild("../App3.sln", settings =>
        settings.SetConfiguration(configuration));
    }
    else
    {
      // Use XBuild
      XBuild("../App3.sln", settings =>
        settings.SetConfiguration(configuration));
    }
});

Task("Run-Unit-Tests")
    .IsDependentOn("Build")
    .Does(() =>
{
    MSTest("../**/bin/" + configuration + "/UnitTestProject2.dll");
});

//////////////////////////////////////////////////////////////////////
// TASK TARGETS
//////////////////////////////////////////////////////////////////////

Task("Default")
    .IsDependentOn("Run-Unit-Tests");

//////////////////////////////////////////////////////////////////////
// EXECUTION
//////////////////////////////////////////////////////////////////////

RunTarget(target);

Cake’s built in benchmarking shows the order in which the tasks are executed

Task Duration 
--------------------------------------------------
Clean                  00:00:00.0124995 
Restore-NuGet-Packages 00:00:03.5300892 
Build                  00:00:00.8472346 
Run-Unit-Tests         00:00:01.4200992 
Default                00:00:00.0016743 
--------------------------------------------------
Total:                 00:00:05.8115968

And obviously if any of these steps had failed (for example if a test failed), execution would stop at this point.

Step 3: Building an AppxBundle in Cake

If I want to build an appxbundle for a UWP project from the command line, I’d run the code below:

MSBuild ..\App3\App3.csproj /p:AppxBundle=Always /p:AppxBundlePlatforms="x86|arm" /Verbosity:minimal

There’s four arguments have told MSBuild about:

  • The location of the csproj file that I want to target
  • I want to build the AppxBundle
  • I want to target x86 and ARM platforms (ARM doesn’t work on its own)
  • And that I want to minimise the verbosity of the output logs.

I could use StartProcess to get Cake to run MSBuild in a task, but Cake already has methods for MSBuild (and many of its parameters) baked in. For those parameters which Cake doesn’t know about, it’s very easy to use the WithProperty fluent method to add the argument’s parameter and value. The code below shows how I can implement the command to build the AppxBundle in Cake’s C# syntax.

var applicationProjectFile = @"../App3/App3.csproj";
 
// ...

MSBuild(applicationProjectFile, new MSBuildSettings
    {
        Verbosity = Verbosity.Minimal
    }
    .WithProperty("AppxBundle", "Always")
    .WithProperty("AppxBundlePlatforms", "x86|arm")
);

After this code runs in a task, an AppxBundle is generated in a folder in the project with the path:

AppPackages\App3_1.0.0.0_Debug_Test\App3_1.0.0.0_x86_arm_Debug.appxbundle

The path and file name isn’t massively readable, and is also likely to change, so I wrote a short method to search the project directories and return the path of the first AppxBundle found.

private string FindFirstAppxBundlePath()
{
    var files = System.IO.Directory.GetFiles(@"..\", @"*.appxbundle", SearchOption.AllDirectories);
    
    if (files.Count() > 0)
    {
        return files[0];
    }
    else
    {
        throw new System.Exception("No appxbundle found");
    }
}

Now that I have the path to the AppxBundle, I’m ready to deploy it to my Windows device.

Step 4: Deploying the AppxBundle

Microsoft have provided a command line tool in the Windows 10 SDK for deploying AppxBundles – this tool is called WinAppDeployCmd. The syntax used to deploy an AppxBundle is:

WinAppDeployCmd install -file "\MyApp.appxbundle" -ip 192.168.0.1

It’s very straightforward to use a command line tool with Cake – I’ve blogged about this before and how to use StartProcess to call an executable which Cake’s context is aware about.

But what about command line tools which Cake doesn’t know about? It turns out that it’s easy to register tools within Cake’s context – you just need to know the path to the tool, and the code below shows how to add the UWP app deployment tool to the context:

Setup(context => {
    context.Tools.RegisterFile(@"C:\Program Files (x86)\Windows Kits\10\bin\x86\WinAppDeployCmd.exe");
});

If you don’t have this tool on your development machine or CI box, it might be because you don’t have the Windows 10 SDK installed.

So with this tool in Cake’s context, it’s very simple to create a dedicated task and pull the details of this tool out of context for use with StartProcess, as shown below.

Task("Deploy-Appxbundle")
	.IsDependentOn("Build-Appxbundle")
	.Does(() =>
{
    FilePath deployTool = Context.Tools.Resolve("WinAppDeployCmd.exe");
 
    Information(appxBundlePath);
 
    var processSuccessCode = StartProcess(deployTool, new ProcessSettings {
        Arguments = new ProcessArgumentBuilder()
            .Append(@"install")
            .Append(@"-file")
            .Append(appxBundlePath)
            .Append(@"-ip")
            .Append(raspberryPiIpAddress)
        });
 
    if (processSuccessCode != 0)
    {
        throw new Exception("Deploy-Appxbundle: UWP application was not successfully deployed");
    }
});

And now we can run our Cake script to automatically build and deploy the UWP application – I’ve pasted the benchmarking statistics from Cake below.

Task                     Duration
--------------------------------------------------
Clean                    00:00:00.0821960
Restore-NuGet-Packages   00:00:09.7173174
Build                    00:00:01.5771689
Run-Unit-Tests           00:00:03.2204312
Build-Appxbundle         00:01:09.6506712
Deploy-Appxbundle        00:02:13.8439852
--------------------------------------------------
Total:                   00:03:38.0917699

And to prove it was actually deployed, here’s a screenshot of the list of apps on my Raspberry Pi (from the device portal) before running the script…

screenshot.1500907026

…and here’s one from after – you can see the UWP app was successfully deployed.

screenshot.1500907690

I’ve uploaded my project’s build.cake file into a public gist – you can copy this and chan-ge it to suit your particular project (I haven’t uploaded a full UWP project because sometimes people have issues with the *.pfx file).

Wrapping up

I’ve found it’s possible to build and deploy a UWP app using the command line, and beyond that it’s possible to integrate the build and deployment process into a Cake script. So even though I still create my application in VS2017 – and I’ll probably keep using VS2017 – it means that I have a much more structured and automated integration process.


About me: I regularly post about .NET – if you’re interested, please follow me on Twitter, or have a look at my previous posts here. Thanks!

.net core, Cake, Raspberry Pi 3

Running a .NET Core 2 app on Raspbian Jessie, and deploying to the Pi with Cake

I’ve managed to get .NET Core apps running on Windows 10 IoT Core, and on Ubuntu 16.04 (and also Ubuntu MATE), but until recently I’d never tried with Raspbian. I’ve read a few posts from people saying that they couldn’t get it to work, and a couple of nights ago I decided to bite the bullet and give it a go.

There’s a good post here on how to get the .NET Core runtime on a Raspberry Pi running Raspbian, and as always, there are a few tricks to getting things running from scratch. To augment this, I’ve created a “Hello World” application template for .NET Core Raspberry Pi on NuGet which I think will make things easier for the community.

At a very high level, the steps to getting a .NET Core 2 app on Raspbian are:

  • Install Raspbian onto an SD card and insert into your Raspberry Pi 3.
  • Set up SSH and test that you can log into your Raspberry Pi from your development machine.
  • Install .NET Core 2 onto the Raspberry Pi.
  • On your development machine, install the Raspberry Pi C# template for dotnet core.
  • Create a new console application using this template.
  • Deploy this application to your Pi running Raspbian using Cake.

I’ll run through each of these steps below.

Install Raspbian onto an SD card and insert into your Raspberry Pi 3

There are already great explanations of how to install the Raspbian OS onto a Raspberry Pi 3 – many people who have a Pi know how to do this already and I don’t really want to just repeat a well understood process here – so I’ve just put some useful links below:

Set up SSH and test that you can log into your Raspberry Pi from your development machine

Once you’ve set up Raspbian and booted your Pi to the desktop, you’ll need to allow SSH connections. These aren’t enabled by default but it’s very easy to configure this.

First open the main Raspberry Pi menu on your desktop as shown in the image below, and open the Preferences sub-menu to reveal the “Raspberry Pi Configuration” option.

2017-07-21-102129_1824x984_scrot

Open the “Raspberry Pi Configuration” option screen, and click on the “Interfaces” tab. There’s lots of useful settings here, but the one we want to enable is SSH – click on the “Enabled” radio button as shown below, and then click on OK. SSH is now enabled on your Pi.

2017-07-21-102157_1824x984_scrot

You’ll need to find the IP address of your Raspberry Pi – I think the easiest way is to open a terminal on your Pi and type:

hostname -I

This tells me that my Pi has the IP address 192.168.1.111.

Now we need to check you can log in from your development machine. I personally find it’s easiest to use PuTTY to do this. I’ve blogged about installing PuTTY before so I won’t repeat it all here – but a few tips are:

This bit is really important: When you install PuTTY using the installer, it ships with a couple of other programs – pscp.exe and plink.exe, which live in the same directory as putty.exe. You’ll need both of these to deploy the code to the Pi. Pscp.exe allows you to copy files from Windows to Linux, and plink allows you to remotely change permissions on the files you deploy.

It also makes life easier to add the putty installation directory to your machine’s path. If Cake doesn’t know where pscp and plink are on your machine, then you’ll probably see and error about unknown executables during deployment.

So open PuTTY, enter the Pi’s IP address and select the “Connection Type” to be SSH, as shown below:

screenshot.1500803365

When you click Open, a command prompt should open where you can type the username and password for the Pi 3.

screenshot.1500805617

The default username and password combo for Raspbian is “pi” and “raspberry“, and you should change the default password as soon as possible.

Install .NET Core 2 onto the Raspberry Pi

There’s a straightforward set of commands that you can run through PuTTY to install .NET Core 2 onto your Pi running Raspbian – I’ve written them below:

# Update the Raspbian Jessie install
sudo apt-get -y update

# Install the packages necessary for .NET Core
sudo apt-get -y install libunwind8 gettext

# Download the nightly binaries for .NET Core 2
wget https://dotnetcli.blob.core.windows.net/dotnet/Runtime/release/2.0.0/dotnet-runtime-latest-linux-arm.tar.gz

# Create a folder to hold the .NET Core 2 installation
sudo mkdir /opt/dotnet

# Unzip the dotnet zip into the dotnet installation folder
sudo tar -xvf dotnet-runtime-latest-linux-arm.tar.gz -C /opt/dotnet

# set up a symbolic link to a directory on the path so we can call dotnet
sudo ln -s /opt/dotnet/dotnet /usr/local/bin

Now you can test this install by running the dotnet –info command to see the version installed on Raspbian.

screenshot.1500810837

On your development machine, install the Raspberry Pi C# template

Now that we have .NET Core 2 installed on our Raspbian, we can go back to our development machine to create an application to run on the Pi.

First, install the template for creating Raspberry Pi applications

 dotnet new -i RaspberryPi.Template::*

This will create a new template available to dotnet core – you can list them all with the command:

dotnet new --list

And in the screenshot below, you can see there is now a new template called “Empty .NET Core IoT Project”, highlighted in red below.

screenshot.1500806357

Create a new console application using this template

It’s really easy to create a new console application now – just run the command below (obviously my application is called “HelloRaspbian”, but yours could be something different):

dotnet new coreiot -n HelloRaspbian

When you browse to this new application folder using your preferred development tool (mine is VSCode), you’ll see some files – we need to make a couple of changes.

First, run the command below to pull down the latest Cake build PowerShell file:

Invoke-WebRequest http://cakebuild.net/download/bootstrapper/windows -OutFile build.ps1

This command is also in the README.txt file which comes packaged with the application.

Now, open the build.cake file and you’ll see some defaults at the top of the file:

///////////////////////////////////////////////////////////////////////
// ARGUMENTS (WITH DEFAULT PARAMETERS FOR LINUX (Ubuntu 16.04, Raspbian Jessie, etc)
///////////////////////////////////////////////////////////////////////
var runtime = Argument("runtime", "linux-arm");
var destinationIp = Argument("destinationPi", "<>");
var destinationDirectory = Argument("destinationDirectory", @"<>");
var username = Argument("username", "<>");
var executableName = Argument("executableName", "HelloRaspbian");

Replaced those placeholders with the correct environment variables – I’ve shown my own settings below:

///////////////////////////////////////////////////////////////////////
// ARGUMENTS (WITH DEFAULT PARAMETERS FOR LINUX (Ubuntu 16.04, Raspbian Jessie, etc)
///////////////////////////////////////////////////////////////////////
var runtime = Argument("runtime", "linux-arm");
var destinationIp = Argument("destinationPi", "192.168.1.111");
var destinationDirectory = Argument("destinationDirectory", @"/home/pi/DotNetConsoleApps/RaspbianTest");
var username = Argument("username", "pi");
var executableName = Argument("executableName", "HelloRaspbian");

I’ve created a folder on the Pi to deploy my application to, using the command below at the PuTTY SSH prompt at my home directory (/home/pi/).

mkdir -p DotNetConsoleApps/RaspbianTest

Deploy this application to your Pi running Raspbian using Cake

Once I’ve replaced the placeholders in my Cake file, the only thing left to do is run the build.ps1 file from a PowerShell prompt.

screenshot.1500811107

To test this, go back to the PuTTY SSH prompt and navigate to your home directory and run:

./DotNetConsoleApps/RaspbianTest/HelloRaspbian

And you’ll get a text output saying “Hello Internet of Things!”

screenshot.1500810451

Wrapping up

I hope this post is useful to anyone trying to get a C# console application running on Raspbian. I think Raspbian is the default OS for Raspberry Pi users, so this should open up many development opportunities. My Raspberry Pi template makes creating the default console application easier, and Cake is a brilliant way to orchestrate the deployment process (rather than dragging and dropping files using tools like WinSCP, and having to change file permission manually). I’ll be blogging more on the future on deploying IoT applications to this platform.

I’ve written a few posts now about how to deploy C# Raspberry Pi applications to Windows 10 IoT Core, Ubuntu, and Raspbian (all using Cake as the orchestration tool) – next time I’ll write about how to use Cake to automatically build a UWP AppxBundle and deploy that AppxBundle to Windows 10 IoT Core.


About me: I regularly post about .NET – if you’re interested, please follow me on Twitter, or have a look at my previous posts here. Thanks!

.net, .net core, Raspberry Pi 3

Automating .NET Core deployments to different platforms with Cake

Recently I’ve been using Cake to automate code deployments to Windows and Ubuntu devices.

Since .NET Core 2 became available, I’ve been able to write C# applications to work with devices which can host different operating systems – specifically the Raspberry Pi, where I’ve been targeting both Windows 10 IoT Core and Ubuntu 16.04 ARM.

I can deploy my code to hardware and test it there because I own a couple of Pi devices – each running one of the operating systems mentioned above. Each operating system requires code to be deployed in different ways:

  • The Windows 10 IoT Core device just appears on my home workgroup as another network location, and it’s easy to deploy the applications by copying files from my Windows development machine to a network share location.
  • For Ubuntu 16.04 ARM, it’s a bit more complicated – I need to use the PuTTY secure client program (PSCP) to get files from my Windows development machine to the device running Ubuntu, and then use Plink to make application files executable.

But I’ve not really been happy with how I’ve automated code deployment to these devices. I’ve used Powershell scripts to manage the deployment – the scripts work fine, but I find there’s a bit of friction when jumping from programming C# to Powershell, and some of dependencies between scripts are not really intuitive.

It’s also a bit difficult to explain to people how to use the scripts, and that’s always a sure sign of a code smell.

Recently I’ve found a better way to manage my build and deployment tasks. At my local .NET user group, we had a demonstration of Cake which is a tool that allows me to orchestrate my build and deployment process in C#. It looked like it could help remove some of my deployment issues – and I’ve written about my experiences with it below.

Getting started

There’s lots more detail on how to get started in the CakeBuild.net website here, but I’ll run through the process that I followed.

Create a project

I’ve previously created a simple project template for a Raspberry Pi which is in a custom nuget package (I’ve written more about that here). You can install the template from nuget by running the command below.

dotnet new -i RaspberryPi.Template::*

This creates a simple hello world application targeting the .NET Core 2 framework.

dotnet new coreiot -n SamplePi

You don’t have to create a project for a Raspberry Pi to follow along with this post – you could just use the regular dotnet command for a new console project:

dotnet new console -n HelloWorld

Create the bootstrapper script

After the project was created, I opened the project folder in VSCode, opened the Powershell terminal, and ran the code below.

Invoke-WebRequest http://cakebuild.net/download/bootstrapper/windows -OutFile build.ps1

This creates a new file in the root of my project called “build.ps1“. It won’t do anything useful yet until we’ve defined our build and deployment process (which we’ll do in the next few sections) – but this bootstrapping script takes care of lots of clever things for us later. It verifies our build script compiles, and it’ll automatically pull down any library and plugin dependencies we need.

Create a Cake build script

The build script – called build.cake – will contain all of the logic and steps needed to build and deploy my code. There’s already an example repository on GitHub which has a few common tasks already. Let’s use the script in that sample repository as our starting point and download it to our project using the PowerShell script below.

Invoke-WebRequest https://raw.githubusercontent.com/cake-build/example/master/build.cake -OutFile build.cake

At this point, if you’re using VSCode, your project should look something like the image below.

screenshot.1499111294

Once you’ve loaded the example project’s build script – you can see it here – there’s a few things worth noting:

  • The build script uses C# (or more specifically, a C# domain specific language). This means there’s less friction between creating functional code in C# and orchestrating a build and deployment process in Cake. If you can write code in C#, you’ve got all the skills necessary to build and deploy your code using Cake.
  • Each section of the build and deployment process is nicely separated into logical modules, and it’s really clear for each step what tasks need to complete before that step can start. And because the code written in a fluent style, this means that clarity is already baked into the code.
  • The script shows how we can process arguments passed to the build script:
var target = Argument("target""Default");

The Argument method defines a couple of things:

  1. “target” is the name of the parameter passed to the build.ps1 script
  2. “Default” is the value assigned to the C# target variable if nothing is specified.

So we could get our build script to use something different to the default value using:

.\build.ps1 -target Clean // this would run the "Clean" task in our script, and all the tasks it depends on.

Customizing the build.cake script

Of course this build.cake file is just a sample to help me get started – I need to make a few changes for my own .NET Core 2 projects.

The build and deployment steps that I need to follow are listed below:

  • Clean the existing binary, object and publish directories
  • Restore missing nuget packages
  • Build the .NET Core code
  • Publish the application (targeting Windows or Ubuntu operating systems)
  • Deploy the application (targeting Windows or Ubuntu operating systems)

I’m going to write a different Cake task for each of the steps above.

Modify the build.cake script to clean the build directories

This is a very simple change to the existing build.cake file – I can specify the binary, object and publish directories using C# syntax, and then make a minor change to the task called “Clean” (which already exists in the build.cake file we created earlier).

var binaryDir = Directory("./bin");
var objectDir = Directory("./obj");
var publishDir = Directory("./publish");

// ...
Task("Clean")
    .Does(() =>
    {
        CleanDirectory(binaryDir);
        CleanDirectory(objectDir);
        CleanDirectory(publishDir);
    });

Modify the build.cake script to restore missing nuget packages

Again, there’s a task already in the build.cake file which could do this job for us called “Restore-nuget-packages”. This would work, but I’d like to clearly signal in code that I’m using the normal commands for .NET Core projects – “dotnet restore”.

DotNetCoreRestore is a method built into Cake (see the source code here).

I created the C# variable to hold the name of my project (csproj) file, and can call the task shown below.

var projectFile = "./SamplePi.csproj";

// ...
Task("Restore")
    .IsDependentOn("Clean")
    .Does(() =>
    {
        DotNetCoreRestore(projectFile);
    });

Notice how I’ve specified a dependency in the code, which requires that the “Clean” task runs before the “Restore” task can start.

Modify the build.cake script to build the project

The methods that Cake uses to restore and build projects are quite similar – I need to specify C# variables for the project file, and this time also what version of the .NET Core framework that I want to target. Of course this task depends on the “Restore” task we just created – but notice that we don’t need to specify the dependency on “Clean”, because that’s automatically inferred from the “Restore” dependency.

We also need to specify the framework version and build configuration – I’ve specified them as parameters with defaults of “.netcoreapp2.0” and “Release” respectively.

var configuration = Argument("configuration""Release");
var framework = Argument("framework""netcoreapp2.0");

// ...
Task("Build")
    .IsDependentOn("Restore")
    .Does(() =>
    {
        var settings = new DotNetCoreBuildSettings
        {
            Framework = framework,
            Configuration = configuration,
            OutputDirectory = "./bin/"
        };
 
        DotNetCoreBuild(projectFile, settings);
    });

Modify the build.cake script to publish the project

This is a little bit more complex because there are different outputs depending on whether we want to target Windows (the win10-arm runtime) or Ubuntu (the ubuntu.16.04-arm runtime). But it’s still easy enough to do this – we just create an argument to allow the user to pass their desired runtime to the build script. I’ve decided to make the win10-arm runtime the default.

var runtime = Argument("runtime""win10-arm");

// ...
Task("Publish")
    .IsDependentOn("Build")
    .Does(() =>
    {
        var settings = new DotNetCorePublishSettings
        {
            Framework = framework,
            Configuration = configuration,
            OutputDirectory = "./publish/",
            Runtime = runtime
        };
 
        DotNetCorePublish(projectFile, settings);
    });

Modify the build.cake script to deploy the project to Windows

I need to deploy to Windows and Ubuntu – I’ll consider these separately, looking at the easier one first.

As I mentioned earlier, it’s easy for me to deploy the published application to a device running Windows – since the device is on my network and I know the IP address, I can just specify the IP address of the device, and the directory that I want to deploy to. These can both be parameters that I pass to the build script, or set as defaults in the build.cake file.

var destinationIp = Argument("destinationPi""192.168.1.125");
var destinationDirectory = Argument("destinationDirectory"@"c$\ConsoleApps\Test");
 
// ...
 
Task("Deploy")
    .IsDependentOn("Publish")
    .Does(() =>
    {
        var files = GetFiles("./publish/*");
 
        var destination = @"\\" + destinationIp + @"\" + destinationDirectory;
        CopyFiles(files, destination, true);
 
    });

Modify the build.cake script to deploy the project to Ubuntu

This is a bit more complex – remember that deploying from a Windows machine to an Ubuntu machine needs some kind of secure copy program. We also need to be able to modify the properties of some files on the remote device to make them executable. Fortunately a Cake add-in already exists which helps with both of these operations!

There are hundreds of add-ins for Cake – you can find more of them here and search the API here.

First, let’s structure the code to differentiate between deployment to a Windows device and deployment to an Ubuntu device. It’s easy enough to work out if we’re targeting the Windows or Ubuntu runtimes by looking at the start of the runtime passed as a parameter. I’ve written the skeleton of this task below.

Task("Deploy")
    .IsDependentOn("Publish")
    .Does(() =>
    {
        var files = GetFiles("./publish/*");
 
        if (runtime.StartsWith("win"))
        {
            var destination = @"\\" + destinationIp + @"\" + destinationDirectory;
            CopyFiles(files, destination, true);
        }
        else
        {
            // TODO: logic to deploy to Ubuntu goes here
        }
    });

I found an add-in for securely copying files called Cake.Putty – you can read more about the Cake.Putty library on Github here.

All we need to do to get Cake to pull the necessary libraries and tools is add one line to our build.cake script:

#addin "Cake.Putty"

That’s it – we don’t need to explicitly start any other downloads, or move files around – it’s very similar to how we’d include a “using” statement at the top of a C# class to make another library available in the scope of that class.

So next we want to understand how to use this add-in – I’ve found there’s good documentation on how to use the methods available in the plugin’s GitHub repository here.

From the documentation on how to use the PSCP command in the add-in, I need to pass two parameters:

  • a string array of file paths as the first parameter, and
  • the remote destination folder as the second parameter.

The second parameter is easy, but the first one is a bit tricky – there’s a function built into Cake called GetFiles(string path) but this returns an IEnumerable collection, which obviously is different to a string array – so I can’t use that.

But this is a great example of an area where I’m really able to take advantage of being able to write C# in the build script. I can easily convert the IEnumerable collection to a string array using LINQ, and pass this as the correctly typed parameter.

var destination = destinationIp + ":" + destinationDirectory;
var fileArray = files.Select(m => m.ToString()).ToArray();
Pscp(fileArray, destination, new PscpSettings
    {
        SshVersion = SshVersion.V2,
        User = username
    });

So now the deployment code has a very clear intent and easily readable to a C# developer – a great advantage of using Cake.

Finally, I can use Plink (also available in the Cake.Putty add-in) to make the application executable on the remote machine – again we need to specify the file to make executable, and the location of this file, which is straightforward.

var plinkCommand = "chmod u+x,o+x " + destinationDirectory + "/SamplePi";
Plink(username + "@" + destination, plinkCommand);

So now our deployment task is written in C#, and can deploy to Windows or Ubuntu devices, as shown below.

Task("Deploy")
    .IsDependentOn("Publish")
    .Does(() =>
    {
        var files = GetFiles("./publish/*");
 
        if (runtime.StartsWith("win"))
        {
            var destination = @"\\" + destinationIp + @"\" + destinationDirectory;
            CopyFiles(files, destination, true);
        }
        else
        {
            var destination = destinationIp + ":" + destinationDirectory;
            var fileArray = files.Select(m => m.ToString()).ToArray();
            Pscp(fileArray, destination, new PscpSettings
                {
                    SshVersion = SshVersion.V2,
                    User = username
                }
            );
 
            var plinkCommand = "chmod u+x,o+x " + destinationDirectory + "/SamplePi";
            Plink(username + "@" + destination, plinkCommand);
        }
    });

I’ve noticed that this add-in doesn’t handle file paths which have spaces in them – but it works if the full file path has no spaces.

One last thing – I’ve included the parameters for a Windows deploy all the way through this post – however, if I wanted to change these, I could override the defaults by passing them to the ScriptArgs switch using a command like the one below:

.\build.ps1 
       -ScriptArgs '--runtime=ubuntu.16.04-arm', 
                   '--os=ubuntu', 
                   '--destinationPi=192.168.1.110', 
                   '--destinationDirectory=/home/ubuntu/ConsoleApps/Test', 
                   '--username=ubuntu', 
                   '--executableName=SamplePi' 
      -target Publish

I can pass values to the “-target” and “-configuration” parameters directly because they’re explicitly mentioned in the build.ps1 script – the rest have to be passed as a comma separated list of name-value pairs to the “-ScriptArgs” parameter. There’s a bit more on passing parameters to the build script on StackOverflow here.

I’ve pushed my new deployment scripts to GitHub here and the rest of this sample project to here.

Wrapping up

Cake allows me to write my build and deployment scripts in C# – this makes it much easier for developers who are familiar with C# to write automated deployment scripts. It also makes the dependencies between tasks really clear.

I’m much happier using this deployment mechanism rather than the one I had previously.  Cake especially helped me to deploy from a Windows development environment to a device running an Ubuntu operating system – and the principles I’ve learned and written about here don’t just apply to Raspberry Pi devices, I could use them if I wanted to develop a website in .NET Core on my Windows machine, and deploy to a web server running Linux.

Footnote: I’ve written about an improvement to the deployment process to Windows 10 IoT Core here – robocopy makes things much faster


About me: I regularly post about .NET – if you’re interested, please follow me on Twitter, or have a look at my previous posts here. Thanks!

.net, .net core, Raspberry Pi 3

Using .NET Core 2 to read from an I2C device connected to a Raspberry Pi 3 with Ubuntu 16.04

I’ve bought a lot of hardware devices – often I2C devices – to attach to my Raspberry Pi devices over the years – things like thermometers, gyroscopes, light intensity sensors and so on. And usually there’s a library supplied by the manufacturer of a device breakout board which shows me how to use the device in the .NET framework.

But what if there isn’t a library for my device? Or what if the library isn’t in .NET – or if it is in .NET, what if it isn’t compatible with .NET Core 2? I wanted to know if I could find a way to still read from my I2C devices while coding from scratch using .NET Core 2, and not depend on someone else writing the library for me.

PInvoke

I’ve written a couple of posts recently (one for Windows 10 IoT Core and one for Ubuntu 16.04) about how to create simple platform invocation service (also known as PInvoke) applications in .NET Core 2 – these posts describe calling native methods to capitalise some text, and deploy the application to a Raspberry Pi 3.

So since I found that it was so easy to use PInvoke with .NET Core and Ubuntu on the Raspberry Pi, I thought I’d try something more ambitious – accessing hardware device registers over an I2C bus using native libraries.

What is I2C?

I2C is a protocol often used to connect peripheral hardware devices (such as a thermometer) to a processor device such as a Raspberry Pi or an Arduino. Typically I find there are four wires needed to connect the Raspberry Pi to an I2C device – one for power (usually 3.3V or 5V), one for ground, one for a serial data line (sometimes labelled as SDA), and one for a serial clock line (sometimes labelled SCL).

As a software developer, I don’t need to worry too much about these wires – I just need to connect the correct 5V/3.3V and 0V wires, and connect the SDA wire to Pin 3 on my Raspberry Pi, and connect the SCL wire to Pin 5 on my Pi.

How can I set up my Ubuntu Raspberry Pi 3 to use I2C?

My Ubuntu installation on my Raspberry Pi 3 didn’t have I2C enabled out of the box – I needed to make a few simple changes.

  • I opened the file “/etc/modules” as sudo and added a couple of lines to the end:
i2c-dev
i2c-bcm2708
  • I opened the “/boot/config.txt” file as sudo and added a couple of lines to the end:
dtparam=i2c_arm=on
dtparam=i2c1=on
  • I then ran the command below:
sudo apt-get install -y i2c-tools

At this point I was able to run the command below:

i2cdetect -y 1

This command scans the I2C bus for attached devices. The “-y” switch means it doesn’t prompt me to type ‘Yes’ to confirm, and the “1” means I’m scanning the I2C-1 bus.

screenshot.1494270470

This showed me that my I2C bus is configured correctly, and highlighted that an external device is connected to my I2C-1 bus, and is accessable at address 0x48.

The device is actually a TMP102 temperature sensor – which I’ve written about before when I used a UWP application to read from this device.

How can I read from a device connected to my Raspberry Pi 3?

I happen to know for this device that the temperature is written into the first two bytes of the TMP102 device (from the datasheet), so I want my code to read these bytes.

Once I’ve connected my I2C device correctly to my Raspberry Pi 3, there are three steps to the code:

  • Open the I2C bus,
  • Specify the address of the device we want to control and read from, and
  • Read from the device.

Whereas this isn’t possible in standard .NET Core 2, there are three functions available in the GNU C library which will do this for us.

I’ve pasted the invocation signatures below to access these functions.

[DllImport("libc.so.6", EntryPoint = "open")]
public static extern int Open(string fileName, int mode);
 
[DllImport("libc.so.6", EntryPoint = "ioctl", SetLastError = true)]
private extern static int Ioctl(int fd, int request, int data);
 
[DllImport("libc.so.6", EntryPoint = "read", SetLastError = true)]
internal static extern int Read(int handle, byte[] data, int length);

So we can open the I2C-1 bus with the .NET code below:

int OPEN_READ_WRITE = 2; // constant, even for different devices
var i2cBushandle = Open("/dev/i2c-1", OPEN_READ_WRITE);

We can control the I2C slave device with address 0x48 on the I2C-1 device with the .NET code below:

int I2C_SLAVE = 0x0703; // constant, even for different devices
int registerAddress = 0x48; // different address for each I2C device
var deviceReturnCode = Ioctl(i2cBushandle, I2C_SLAVE, registerAddress);

And finally we can read two bytes into a byte array from the device with the code below:

var deviceDataInMemory = new byte[2];
Read(i2cBushandle, deviceDataInMemory, deviceDataInMemory.Length);

Putting it all together

First install .NET Core 2 using the executable from here, and then install the template for .NET Core 2 IOT projects using the command below:

dotnet new -i RaspberryPi.Template::*

Next create a project (for the TMP102 device) using the command

dotnet new coreiot -n Tmp102

Open the project, and replace the code in the Program.cs file with the code below:

using System;
using System.Runtime.InteropServices;
 
namespace RaspberryPiCore
{
    class Program
    {
        private static int OPEN_READ_WRITE = 2;
        private static int I2C_SLAVE = 0x0703;
 
        [DllImport("libc.so.6", EntryPoint = "open")]
        public static extern int Open(string fileName, int mode);
 
        [DllImport("libc.so.6", EntryPoint = "ioctl", SetLastError = true)]
        private extern static int Ioctl(int fd, int request, int data);
 
        [DllImport("libc.so.6", EntryPoint = "read", SetLastError = true)]
        internal static extern int Read(int handle, byte[] data, int length);
		
        static void Main(string[] args)
        {
            // read from I2C device bus 1
	    var i2cBushandle = Open("/dev/i2c-1", OPEN_READ_WRITE);
 
            // open the slave device at address 0x48 for communication
	    int registerAddress = 0x48;
	    var deviceReturnCode = Ioctl(i2cBushandle, I2C_SLAVE, registerAddress);
 
            // read the first two bytes from the device into an array
	    var deviceDataInMemory = new byte[2];
	    Read(i2cBushandle, deviceDataInMemory, deviceDataInMemory.Length);
 
            Console.WriteLine($"Most significant byte = {deviceDataInMemory[0]}");
            Console.WriteLine($"Least significant byte = {deviceDataInMemory[1]}");
        }
    }
}

Now build and publish using the commands below:

dotnet build
dotnet publish -r ubuntu.16.04-arm

And copy the published code (inside the “.\bin\Debug\netcoreapp2.0\ubuntu.16.04-arm\publish\” directory) to your Raspberry Pi 3 running Ubuntu.

Now if you run the Tmp102 executable (you might need to chmod it to have execute privileges), it’ll write the contents of the first two bytes to the console, which proves we’ve successfully connected to the device over the I2C bus and read from it.

screenshot.1494274133

Wrapping up

There’s obviously a lot more to I2C than this post, but it proves that we can use PInvoke and .NET Core 2 to read from devices using the I2C protocol. With this knowledge, I’m not dependent on hardware vendors supplying working .NET code for my I2C devices (although that obviously makes things easier!)


About me: I regularly post about .NET – if you’re interested, please follow me on Twitter, or have a look at my previous posts here. Thanks!

.net, .net core, Raspberry Pi 3

Using PInvoke with .NET Core 2 and Ubuntu 16.04 on the Raspberry Pi 3

I’ve written previously about how to use PInvoke with .NET Core 2 on a Raspberry Pi 3 running Windows 10 IoT Core – I tested it with a very simple example where I converted some text to upper case using the CharUpper method in the user32.dll library. I was able to invoke the CharUpper method using the code below:

[DllImport("user32.dll", CharSet = CharSet.Auto)]
static extern char CharUpper(char character);

You can see the full code at Github here.

I decided to see if I could repeat this simple example on Ubuntu using the built in libraries – and found that it is actually really easy to use PInvoke with .NET Core on Ubuntu also. I’ll run through the steps to repeat this on your own Raspberry Pi 3 running Ubuntu 16.04.

  • Install .NET Core 2 – you can get the installer from here.
  • Create a console app for the Raspberry Pi 3 – you can install a template using the code below:
dotnet new -i RaspberryPi.Template::*
  • And then you can create a new project using the command below:
dotnet new coreiot -n RaspberryPi_PInvoke
  • In the generated project, replace the code in Program.cs with the code below. I’ve highlighted the key part of the code in red – this uses the GNU C library, libc. I import the method “toupper”, but alias it as CharUpper which is the name of the function I used in the previous post.
using System;
using System.Runtime.InteropServices;
 
namespace RaspberryPi_PInvoke
{
    class Program
    {
        [DllImport("libc.so.6", EntryPoint = "toupper")]
        private static extern int CharUpper(int c);
 
        static void Main(string[] args)
        {
            var textToChange = "Hello Internet of Things!";
            var inputCharacterArray = textToChange.ToCharArray();
 
            // array of chars to hold the capitalised text
            var outputCharacterArray = new char[inputCharacterArray.Length];
 
            for(int i = 0; i < inputCharacterArray.Length; i++) 
            {
                var charToByte = (byte)inputCharacterArray[i];
                outputCharacterArray[i] = (char)CharUpper(charToByte);
            }
 
            Console.WriteLine($"Original text is {textToChange}");
            Console.WriteLine($"Changed text is {new string(outputCharacterArray)}");
        }
    }
}
  • Now build this using the command:
dotnet build
  • And publish for Ubuntu using the command:
dotnet publish -r ubuntu.16.04-arm
  • Finally, deploy this to your Raspberry Pi 3 running Ubuntu.

I use pscp to copy files from my Windows machine to the Pi 3 running Ubuntu, but you could also use rsync from Bash in Windows 10. Remember to make the file you need to run (RaspberryPi_PInvoke) executable on the Pi 3 using chmod.

When you run this application through a terminal, you’ll see that it converts the text “Hello Internet of Things!” to upper case.

screenshot.1494193840

Wrapping up

This post is very similar to a post I wrote previously about using PInvoke with Windows 10 IoT Core on the Raspberry Pi 3 – except this time, I use a function from the GNU C library, libc. This is an incredibly rich source of code, and I’ll write next time about how I can use this to access the I2C bus.

.net, .net core, Raspberry Pi 3

Controlling GPIO pins using a .NET Core 2 WebAPI on a Raspberry Pi, using Windows 10 or Ubuntu

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:

  <ItemGroup>
    <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-*" />
  ItemGroup>

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.

[HttpGet]
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.

[HttpGet("{pinId}")]
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:

[HttpPost]
public void SwitchPin(int pinId, int status)
{
    Console.WriteLine("About to change pin status.");
    var pin = gpioController.OpenPin(pinId);
 
    pin.SetDriveMode(GpioPinDriveMode.Output);
 
    if (status == 1)
    {
        Console.WriteLine("Going on");
        pin.Write(GpioPinValue.High);
    }
    else
    {
        Console.WriteLine("Going off");
        pin.Write(GpioPinValue.Low);
    }
}

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 192.168.1.110)
  • 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 192.168.1.110 -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 192.168.1.125)
  • 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 192.168.1.125 -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 192.168.1.125 -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.

screenshot.1493500867

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:

.\GpioSwitcherWebApi.exe

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:

http://192.168.1.125:5000/api/pins

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 (http://192.168.1.125:5000/api/pins).
  • 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.

screenshot.1493561495

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:

http://192.168.1.125:5000/api/pins/26

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.

screenshot.1493561789

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.

screenshot.1493562077

For testing Ubuntu the process is identical except for I had to substitute the IP address of my Ubuntu Raspberry Pi (which is 192.168.1.110). 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.


About me: I regularly post about .NET – if you’re interested, please follow me on Twitter, or have a look at my previous posts here. Thanks!