.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 core, arduino, IOT

Using .NET Core 2 to read serial data from an Arduino UNO over USB

If you’ve worked with an Arduino and used a Windows development machine, you’ll probably have used the Arduino IDE to deploy code, and used the IDE’s built in serial monitor to read messages back from the Arduino device. And if you want to use these messages in a .NET application, there’s already good support in the .NET Framework – but what about .NET Core?

In this post, I’ll look at how to use VSCode to create an Arduino simple project which writes to a serial port, and deploy this to an Arduino Uno using VSCode. Then I’ll write about how to use the preview NuGet package to read this data using .NET Core. I’ll finish up with some issues I’ve observed.

Note – this article about .NET Core targets Windows 10 32-bit/64-bit editions – this won’t work if you’re targeting ARM devices with Windows 10 IoT Core (i.e. win10-arm). I’ll cover targeting the ARM platform in a later post.

Also, you’ll obviously need an Arduino for this – I’ve found an Arduino UNO is ideal.

This post has two main sections – setting up a example project on an Arduino using VSCode to write data to a serial port, and then how to access this data using a .NET Core application.

Writing data to a serial port with an Arduino

Setting up VSCode for Arduino development

First of all, you’ll need VSCode on your development machine – you can download it from here. I’ll not describe this in detail – it’s pretty much a point and click installer.

You’ll also need the Arduino IDE installed on your machine so VSCode can access the necessary libraries – you can download it from here. Again this is a very straightforward installation.

Next, install the Arduino extension for VSCode. There are great instructions here – but in summary, from VSCode:

  • Hit ‘Ctrl + P’
  • Type:
ext install vscode-arduino

At this point, you’ll have the Arduino extension for VSCode, presently at version 0.2.4.

Create an Arduino project in VSCode which writes to the serial port

From VSCode, you can create a new Arduino project with the following steps:

  • Create a folder on your development machine to hold the Arduino project – I’ve called mine ArduinoSerialExample
  • Open this folder in VSCode.
  • In VSCode, hit ‘Ctrl + Shift + P’ to open the VSCode Command Palette.
  • Type: Arduino: Initialize – VSCode will offer to create a file with extension app.ino.

screenshot.1501514685

  • Rename this to ArduinoSerialExample.ino. It’s important that this file (also known as an Arduino sketch) has the same name as the parent directory.
  • At this point, VSCode will ask what Arduino device is being used – I’m using an Arduino UNO, so I selected this from the list.

screenshot.1501514840

Your VSCode workspace is now initialised for Arduino development.

  • Update the code in the ArduinoSerialExample.ino file to have the contents shown below.
int i = 0;
 
void setup(){
  Serial.begin(9600);
}
 
void loop(){
  Serial.print("Hello Pi ");
  Serial.println(i);
  delay(1000);
  i++;
}

Important tip – if you copy and paste into VSCode, make sure you don’t accidentally copy unexpected unicode characters, as these will cause compiler errors.

  • Now hit ‘Ctrl + Shift + R’ to compile (also known as Verify) the script – if everything works, you should see an output similar to the text below.
[Starting] Verify sketch - ArduinoSerialExample.ino
Loading configuration...
Initialising packages...
Preparing boards...
Verifying...

Sketch uses 1,868 bytes (5%) of program storage space. Maximum is 32,256 bytes.
Global variables use 198 bytes (9%) of dynamic memory, leaving 1,850 bytes for local variables. Maximum is 2,048 bytes.
[Done] Finished verify sketch - ArduinoSerialExample.ino

Test this project works with a real Arduino

There’s a couple of last steps – connecting the physical Arduino to your development machine, and choosing the serial port.

If you look at the bottom right corner of VSCode, you should see that there’s still a prompt to select the serial port (as shown below).

screenshot.1501515586

Once I plugged my Arduino UNO into a USB port on my machine, I was able to click on theprompt (highlighted in a red box in the image above), and VSCode prompts me to select a serial port, as shown below.

screenshot.1501518211

I selected COM4, and this updates VSCode to show the serial port in the bottom right corner of the screen, as shown below.

screenshot.1501278934

I’m now ready to rest the Arduino is writing to the serial port.

I can upload the sketch to the Arduino by hitting ‘Ctrl + Shift + U’ – this will re-compile the sketch and upload it to the Arduino.

Next, hit open the command palette again (by hitting ‘Ctrl + Shift + P’, and type ‘Arduino Open Serial Monitor’, and select the option to open the Serial Monitor from the dropdown list.

screenshot.1501519010

The serial monitor opens, and I’m able to see output being logged to the console from the Arduino through the serial port COM4, as shown below.

screenshot.1501518933

Accessing the serial port data on a PC using .NET Core

TL:DR; I’ve uploaded the project to GitHub here.

First set up the .NET Core 2 solution – a console project and a .NET Standard 2.0 class library

Create a new project to hold your .NET solution. I like to manage solutions using the command line – I create a solution using the command:

dotnet new sln -n ReadSerialDataFromArduino

Inside this solution folder, create a new .NET Core console project – I do this using the command:

dotnet new console -n ReadFromArduino

Also create a new .NET Standard 2.0 library project inside the solution folder – again, I do this using the command:

dotnet new classlib -n ReadSerialInputFromUSB

Now we can add these two projects to the solution using the commands below

dotnet sln add .\ReadFromArduino\ReadFromArduino.csproj
dotnet sln add .\ReadSerialInputFromUSB\ReadSerialInputFromUSB.csproj

And we can see the projects in the solution using the command below:

dotnet sln list

And this command presents the expected output of:

Project reference(s)
--------------------
ReadFromArduino\ReadFromArduino.csproj
ReadSerialInputFromUSB\ReadSerialInputFromUSB.csproj

Finally for this section, I want to add the library as a reference to my console application with the command:

dotnet add .\ReadFromArduino\ReadFromArduino.csproj reference .\ReadSerialInputFromUSB\ReadSerialInputFromUSB.csproj

Add the .NET Core System.IO.Ports preview package

The System.IO.Ports package (available here on nuget.org) allows access to the serial port through a .NET Core application. I can add this to my .NET Standard 2.0 class library by navigating into the ReadSerialInputFromUSB directory, and run the command below:

dotnet add package System.IO.Ports --version 4.4.0-preview2-25405-01

So now the project structure is is place – we can add the bits of code that actually do things.

Let’s use C# to list what serial ports are available to us. I’ve created a class in the ReadSerialInputFromUSB project named SerialInformation, and added a static method called GetPorts().

using System;
using System.IO.Ports;
 
namespace ReadSerialInputFromUSB
{
    public class SerialInformation
    {
        public static void GetPorts()
        {
            Console.WriteLine("Serial ports available:");
            Console.WriteLine("-----------------------");
            foreach(var portName in SerialPort.GetPortNames())
            {
                Console.WriteLine(portName);
            }
        }
    }
}

And we can access this through the main method in the ReadFromArduino project:

using ReadSerialInputFromUSB;
 
namespace ReadFromArduino
{
    class Program
    {
        static void Main(string[] args)
        {
            SerialInformation.GetPorts();
        }
    }
}

If we build this and run the project (using dotnet build and dotnet run) the output is:

Serial ports available:
-----------------------
COM4

This is exactly what we’d expect from earlier, where VSCode identified COM4 as the port being used by the Arduino.

And if we can get the data from the Arduino into a variable and write to the console, we can do that by using the DataReceived event and using the ReadExisting() method on the serial port object, as shown below:

public void ReadFromPort()
{
    // Initialise the serial port on COM4.
    // obviously we would normally parameterise this, but
    // this is for demonstration purposes only.
    this.SerialPort = new SerialPort("COM4")
    {
        BaudRate = 9600,
        Parity = Parity.None,
        StopBits = StopBits.One,
        DataBits = 8,
        Handshake = Handshake.None
    };
 
    // Subscribe to the DataReceived event.
    this.SerialPort.DataReceived += SerialPortDataReceived;
 
    // Now open the port.
    this.SerialPort.Open();
}
 
private void SerialPortDataReceived(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.
    Debug.Write(serialdata);
}

I can call this in my console project’s main method using the code below:

static void Main(string[] args)
{
    SerialInformation.GetPorts();
 
    var serialInformation = new SerialInformation();
 
    serialInformation.ReadFromPort();
 
    Console.ReadKey();
 
    serialInformation.SerialPort.Close();
}

So when I run this console application, the COM4 serial port is opened, and writes whatever it receives to the debug output.

You can see the source code for the Serial.IO.Ports library on GitHub in the CoreFX library, and there’s access to the nightly builds on myget.org.

This library is great for connecting to (and reading from) serial ports using a .NET Core application running on a Windows x32/x64 machine. However, one issue is this library doesn’t work with ARM – either for Windows 10 IoT Core or for Linux.

Wrapping up

Using the Serial.IO.Ports preview library available on NuGet, it’s possible to read from serial ports using a .NET Core 2 application on a Windows 32-bit/64-bit machine, and I’ve a very simple example of how to do this available on GitHub here. So far there’s not an implementation in the Serial.IO.Ports library which works for ARM architectures, but I’ll look at options for closing this gap in future posts.


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

Contributing to the .NET Core SDK source code for the first time, and how OSS helped me

The .NET Core source code has been open sourced on GitHub for a while now, and the community is free to raise issues and submit pull requests – though I’d not really expected that I’d ever actually need to. That’s mainly because I always expect that thousands of other talented developers will have tested the code paths I’m working with and found (and solved) those issues before me.

But shortly after I installed .NET Core 2.0.0 Preview 1, I found that all my .NET Core projects that I had written for Windows 10 IoT Core suddenly stopped working – and the reason was that the executable file wasn’t being generated any more after I published the project.

I tested the hell out of this – I originally suspected that I had done something wrong or different, and I really didn’t want to report an issue and then find I was the one who had actually made a mistake. But I eventually concluded that something had changed in the code, so I raised a bug under the title “Publishing to win10-arm or win8-arm doesn’t generate an exe file for a Console application“, and this ultimately led to me committing some test code to the .NET Core codebase.

So the fact that .NET Core is completely open source and receiving community contributions suddenly became extremely relevant to me – previously I’d have just had to suffer the problem.

None of this stuff I write about below is a particularly big deal – just a part of software development – but dipping my toe into the waters of a massively public open source project was, well, a bit nerve wracking.

In some ways I felt like when I start a new job, where I’ve joined a team that has patterns and practices that I’m not entirely familiar with – I’m always worried I’ll do something that makes things harder for other developers, invokes justified wrath… and reminds me that it’s only Imposter Syndrome if I’m not actually stupid.

None of the stuff I was worried about happened – and was it was never going to happen. The .NET development team were super helpful, open, friendly, and encouraged me right from the start – and there were safety nets all along the way to stop anything bad happening. They even suggested a workaround to solve my problem on the same day I raised the issue, which massively helped me before the resolution was merged in.

I’ve written about my experiences below – things I got right, and things I got wrong – hopefully this will be useful to other developers thinking about putting their toe in the same waters.

Tips for a good issue report

The first part of this was writing up the issue – I think that there are essentially three parts to a good issue report:

  • Steps to recreate the issue
  • Actual behaviour
  • Expected behaviour – don’t forget to say why you think this is the expected behaviour.

What sort of things do I need to do when submitting a pull request to the .NET Core repositories?

I wasn’t the developer who actually solved the issue – the .NET team get the credit for that – but I did see an opportunity to write a test to make sure the issue didn’t reoccur, and I submitted a PR for that code change.

First, fork the .NET Core SDK repository

This bit’s really easy – just click on the “Fork” button in the top right corner of the GitHub repository. This’ll create a fork of the original Microsoft source code in your own GitHub profile.

Clone the repo locally, and make sure you choose the correct branch to code against

I used TortoiseGit to clone the repository to my local development machine, and just started coding – and that turned out to be a bit too quick on the draw. I don’t think this is written down anywhere, but I should have targeted the release/2.0.0 branch.

How do I choose the right branch? I think the best way is to look at some recently closed pull requests, and see where the other developers are pushing their code.

With TortoiseGit, it’s easy to switch branches.

  • Right click on the root of the repo you’ve cloned, select “TortoiseGit > Switch/Checkout”.

screenshot.1497111687

  • A window will appear, where you can select the branch you want from a dropdown list. In the image below, you can see I’ve selected the release/2.0.0 branch. Click OK to switch your local repo to the code in this branch.

screenshot.1497111727

I initially (but wrongly) wrote my code against the default branch – in some repositories that’s possibly ok, but at the time of writing, the best branch to target in the .NET SDK repo is release/2.0.0. By the time I realised I should have targeted the release/2.0.0 branch and tried to switch to it, GitHub invited me to resolve lots of conflicts in files I hadn’t touched. Rather than trying to rebase and introducing lots of risk, I just closed the original pull request, selected the correct branch, and opened a new pull request which included my code change. Don’t make the same mistake I did!

Test that you can build the branch before making any changes

Once your locally cloned repository targets the correct branch, you should try building the code before making any changes. If it doesn’t build at this point or tests fail, then at least you know the problem isn’t caused by something you did.

In the root folder of the source for .NET Core’s SDK, there are three files which can be used to build the code:

  • build.cmd
  • build.ps1
  • build.sh

Open a command prompt, and run whichever one of the three options that is your favourite.

If you find that the code doesn’t build or the tests don’t pass, check the build status on the repo’s home page.

Make your changes, commit them, and push the changes to the right branch in your remote fork on GitHub

Don’t forget your unit tests, make sure everything builds, and comment your changes appropriately.

Now create a pull request

From your forked repository, hit the “New Pull Request” button. Here are a few things that I think are useful to think about:

  • You’ll need to enter a comment – make sure it’s a useful one.
  • Describe why your change is valuable – does it fix an issue? Is it a unit test, related to another pull request?
  • If you can, link to an issue or pull request in the comment to give the reviewers some context.
  • I try not to submit a pull request which changes many files – lots of changes make it difficult to review. If you have to change lots of files, try to explain why it wasn’t possible to separate this out into smaller chunks.
  • And remember to open the pull request against the correct branch!

screenshot.1497100934

What happens when I submit the pull request?

Once you submit your first pull request, it’ll immediately be assigned a label “cla-required” by the dnfclas bot.

screenshot.1496089436

cla is short for “contribution licence agreement“.

dnfclas means “dot net foundation contribution licence agreement” and is the Pull Request Bot.

To proceed beyond this point, you need to click on the link to https://cla2.dotnetfoundation.org to sign a Contribution Licence Agreement. When you click on that link, you’ll be redirected to a page like this.

screenshot.1496089699

Sign in using your GitHub credentials, and you’ll be invited to enter some details and sign the agreement. If you sign it, you’ll eventually be shown a page like the one below.

screenshot.1496089798

At this point, the dnfclas bot automatically recognises that you’ve signed the agreement (you don’t need to tell it), and it updates the label in the pull request from “cla-required” to “cla-signed”. You’ll see this on your pull request as an update, similar to the one below.

screenshot.1496089456

As you might expect, there’s a series of integration environments where your pull request will be tested. For the .NET Core SDK continuous integration process, there are presently 10 environments where code is automatically tested:

  • OSX10.12 Debug
  • OSX10.12 Release
  • Ubuntu14.04 Debug
  • Ubuntu14.04 Release
  • Ubuntu16.04 Debug
  • Ubuntu16.04 Release
  • Windows_NT Debug
  • Windows_NT Release
  • Windows_NT_FullFramework Debug
  • Windows_NT_FullFramework Release

There are lots of dotnet repositories, and an issue which manifests itself in one repo might have the root cause in another one – and this was the case for me. The issue that I observed in the SDK actually started in the .NET CoreFx repository.

It takes a while for fixes in one repo to flow across to the other, so if you submit a unit test to one repo for a fix that lives somewhere else, the test might fail for a while – and that’ll stop it being merged in immediately.

So if you’re only submitting tests, expect that all the checks will fail until the code you’re covering with your unit test flows across to the .NET Core SDK continuous integration environment.

screenshot.1496092980

Once the fixed code has flowed through, you’ll see this (assuming your code works…):

screenshot.1496355381

The .NET Team will choose a reviewer for you – you don’t need to choose anyone

Finally – and probably most importantly – someone from the .NET Core SDK team will review your code. I think it’s mandatory (as well as courteous) to address any comments from your reviewer – these are helpful pointers from a team of super smart people who care about good code.

Other gotchas

One thing that caught me out was that GitHub marked some of the review comments as “outdated” (as shown below). I should have clicked on these – if I had, I would have seen a few comments that I hadn’t addressed.

screenshot.1496092853

Another thing was I wish I had a copy of Resharper on my development machine – one of the review comments was that I had left an unused variable in my code. Resharper would have caught this error for me.

Wrapping up

So, much to my surprise, I’ve contributed to the .NET Core codebase – albeit in a very small way!

screenshot.1497101854

In summary, I was a bit nervous about submitting my first pull request to the .NET Core SDK repository – but I decided to create a simple test which covered a bug fix from the .NET team. Apart from signing a contribution licence agreement, this was a pretty standard process of submitting a pull request for review and automated testing. One really nice thing is that changes are tested not only against Windows, but also different versions of Ubuntu and OSX.  Also, if you’re about to submit your own pull request to a .NET Core repo, I’d recommend checking out other pull requests first as a guideline – and don’t forget to look at what branch the developers are merging to.

Hopefully this description of my experiences will help other developers thinking of contributing feel a bit more confident. I’d recommend to anyone thinking of making their first contribution, choose something small – it’ll help you get familiar with the 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, .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.