.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, .net core, Bifröst, Powershell, Raspberry Pi 3

Project Bifröst – improving deployment of Raspberry Pi apps written in .NET Core 2

Bifröst is a project for developers who want to write .NET Core 2 applications that use IoT devices, and want to target Ubuntu and Windows with the same code.

Last time I wrote an introductory post explaining how this UWP application works and why it’s useful if you want to access GPIO pins. I wrote quite a lot about how to deploy a sample .NET Core application (called GpioSwitcher) – and it occurred to me while writing the post that the deployment process for Raspberry Pi apps from Windows currently sucks. So I’ve written some PowerShell scripts to neatly manage application deployment, which are in the project’s GpioSwitcher directory.

The deployment scripts mentioned in this post aren’t part of the Bifröst UWP application – rather they’re part of a broader project effort to improve the quality of life for .NET developers working in the IoT space.

Improving deployment for the GpioSwitcher application

If you want to just build the application, open PowerShell at the GpioSwitcher project root and run:

.\build.ps1

Deploy to a running Raspberry Pi 3 with Windows 10

This script has two parameters:

  • ip: the ip address of the Raspberry Pi 3 (mine is 192.168.1.125)
  • destination: the network path to where you want the application to be copied (I created a folder named CoreTest in the root, so the network path is “C$\CoreTest”)
.\deploy-windows.ps1 -ip 192.168.1.125 -destination "c$\CoreTest"

So to now run the application and to set pin 26 to logic high: ssh into your Raspberry Pi 3 (either using PuTTY or Powershell), navigate to the directory that you copied the application to, and run:

./GpioSwitcher 26 1

Deploy to a running Raspberry Pi 3 with Ubuntu 16.04

This script has three parameters:

  • ip: the ip address of the Raspberry Pi 3 (mine is 192.168.1.110)
  • destination: the path to where you want the application to be copied (I created a folder named GpioSwitch in my user directory, so the path is “/home/ubuntu/GpioSwitch”)
  • username: the user id that you’ve logged in with (my userid is “ubuntu”)
.\deploy-ubuntu.ps1 -ip 192.168.1.110 -destination "/home/ubuntu/GpioSwitcher" -username ubuntu

(This process will prompt you to enter your Raspberry Pi password.)

So to now run the application and to set pin 26 to logic high: ssh into your Raspberry Pi 3 (either using PuTTY or Powershell), navigate to the directory that you copied the application to, and run:

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

This is a much better way to deploy applications to a Raspberry Pi hosting Windows or Ubuntu.

.net, .net core, Bifröst, Raspberry Pi 3

Write .NET Core 2 once, run anywhere: hardware access on Raspberry Pi 3 with Ubuntu and Windows 10 IoT Core

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

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

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

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

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

Project Bifröst

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

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

rasppi1

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

Recap

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

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

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

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

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

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

Ubuntu 16.04

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

Windows 10 Iot Core

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

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

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

screenshot.1492453516

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

screenshot.1492449445

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

screenshot.1492449598.png

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

screenshot.1492449624.png

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

screenshot.1492449715

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

screenshot.1492454159

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

screenshot.1492449771.png

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

screenshot.1492454372

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

screenshot.1492454574

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

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

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

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

// create gpio controller
Debug.WriteLine("About to instantiate the switch controller");
var controller = GpioController.Instance;
 
// open pin
Debug.WriteLine("Opening pin " + pinNumber);
var pin = controller.OpenPin(pinNumber);
 
// set direction
Debug.WriteLine("Setting the direction to out");
pin.SetDriveMode(GpioPinDriveMode.Output);
 
// set value
if (logicLevel == 1)
{
    Debug.WriteLine("Setting the value to high");
    pin.Write(GpioPinValue.High);
}
else
{
    Debug.WriteLine("Setting the value to low");
    pin.Write(GpioPinValue.Low);
}

Deploying your application

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

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

Deploying to Ubuntu

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

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

mkdir /home/ubuntu/GpioSwitcher

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

dotnet publish -r ubuntu.16.04-arm

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

pscp -r * ubuntu@192.168.1.111:/home/ubuntu/GpioSwitcher

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

sudo chmod u+x,o+x *

 

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

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

Deploying to Windows 10 IoT Core

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

screenshot.1492461963

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

screenshot.1492462069

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

dotnet publish -r win8-arm

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

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

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

screenshot.1489958874

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

screenshot.1489959150

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

screenshot.1489959232

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

./GpioSwitcher 26 1

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

Printing Pi Holder

WP_20170417_17_23_20_Pro

Summing up

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