Running Native .NET Core Apps on Raspberry Pi (ARM)

Introduction

As this is my first post on this blog, I will be writing a bit about a tutorial I posted on Hackster.io some time ago. The focus will be on my overall experience with instructions on how to run .NET Core Apps on ARM platforms, in this case Raspberry Pi 3.


About the tutorial

Microsoft has announced that it’s .NET Core is on its way to the Raspberry Pi 3, and an official .NET 2.0 Core, adapted to run on ARM devices, is coming later this year.

Recently .NET Core became available for ARM32 platforms. As it currently stands it’s not an official release, the build of .NET Core offered on Github will work with both Ubuntu 16.04, Ubuntu 14.04 and Windows 10 IoT Core with no official support.

To demonstrate this we’ll be using Leibniz formula for π to calculate the π.

Instructions

The first thing that we want is to install .NET Core on our main machine. Install .NET Core 2.0 SDK depending on your OS. If you want to make your own app (instead of using the sample app from this tutorial), make sure that you have Visual Studio installed.

Once you have installed .NET Core 2.0 SDK (either by extracting binaries or using the installer) on your PC go to your terminal/commandline, create a folder named picalc and go into it.

mkdir picalc
cd picalc

Now we need to create a new app, so to create a template run the following:

dotnet new console -n picalc
cd picalc
Creating a new .NET Core App
Creating a new .NET Core App using the Windows Command-line

 

Since we want out program to compile for ARM32, we need to edit our picalc.csproj so add the following line bellow :

<RuntimeIdentifiers>win8-arm;ubuntu.14.04-arm;ubuntu.16.04-arm</RuntimeIdentifiers>

The final file should look like this (Note: The Runtime Framework Version will vary, so it may not be the same):

<Project Sdk="Microsoft.NET.Sdk"> 
 <PropertyGroup> 
   <OutputType>Exe</OutputType> 
   <TargetFramework>netcoreapp2.0</TargetFramework> 
   <RuntimeFrameworkVersion>2.0.0-beta-001737-00</RuntimeFrameworkVersion> 
   <RuntimeIdentifiers>win8-arm;ubuntu.14.04-arm;ubuntu.16.04-arm</RuntimeIdentifiers>
 </PropertyGroup> 
</Project> 

A quick explanation:

  • win8-arm – Adds compilation support for Windows 10 IoT Core.
  • ubuntu.14.04-arm – Adds compilation support for Ubuntu 14.04.
  • ubuntu.16.04-arm – Adds compilation support for Ubuntu 16.04.

 

Now come the coding part, edit Program.cs the content of it as follows:

using System; using System; 
using System.Threading; 
namespace picalc 
{ 
   class Program 
   { 
       static string[] text = { 
@"      ___                       ___           ___           ___       ___     ", 
@"     /\  \          ___        /\  \         /\  \         /\__\     /\  \    ", 
@"    /::\  \        /\  \      /::\  \       /::\  \       /:/  /    /::\  \   ", 
@"   /:/\:\  \       \:\  \    /:/\:\  \     /:/\:\  \     /:/  /    /:/\:\  \  ", 
@"  /::\~\:\  \      /::\__\  /:/  \:\  \   /::\~\:\  \   /:/  /    /:/  \:\  \ ", 
@" /:/\:\ \:\__\  __/:/\/__/ /:/__/ \:\__\ /:/\:\ \:\__\ /:/__/    /:/__/ \:\__\", 
@" \/__\:\/:/  / /\/:/  /    \:\  \  \/__/ \/__\:\/:/  / \:\  \    \:\  \  \/__/", 
@"      \::/  /  \::/__/      \:\  \            \::/  /   \:\  \    \:\  \      ", 
@"       \/__/    \:\__\       \:\  \           /:/  /     \:\  \    \:\  \     ", 
@"                 \/__/        \:\__\         /:/  /       \:\__\    \:\__\    ", 
@"                               \/__/         \/__/         \/__/     \/__/    " }; 
       static double Pi = 0; 
       static double n = 1; 
       static long i = 0L; 
       static void Main(string[] args) 
       { 
           while (i < long.MaxValue) 
           { 
               foreach (string s in text) 
               { 
                   Console.WriteLine(s); 
               } 
               Console.WriteLine(); 
               Console.WriteLine(); 
               Console.WriteLine(); 
               Pi += (4.0 / n); 
               n += 2.0; 
               Pi -= (4.0 / n); 
               n += 2.0; 
               Console.WriteLine("Generated value of Pi: " + Pi); 
               i++; 
               Thread.Sleep(50); 
               Console.Clear(); 
           } 
       } 
   } 
}

Of course you can always write your own code, this was a sample code I made for calculating Pi on Pi day. If you are to write your code, I suggest writing and testing it with C# Console Apps in Visual Studio. After you are done writing it replace the contents of Program.cs.

Program.cs content
Program.cs content

 

Now that we have our code in place, before restoring all dependencies, make sure there is a nuget.config file next to your other files and it’s content is:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <packageSources>
    <add key="dotnet-core" value="https://dotnet.myget.org/F/dotnet-core/api/v3/index.json" />
  </packageSources>
</configuration>

After we have checked the nuget configuration, it’s time to restore all dependencies, so run the following:

dotnet restore

Finally we can compile our app into an executable. To do that run:

dotnet publish -r <runtime identifier>

is the platform you want to compile for, in this case either win8-arm, ubuntu.14.04-arm or ubuntu.16.04-arm. For example:

dotnet publish -r win8-arm

To compile your app for Windows 10 IoT Core, and:

dotnet publish -r ubuntu.16.04-arm

To compile your app for Ubuntu 16.04.

 

Now since we have compiled our program it’s time to transfer it to our Raspberry Pi. Under:

./bin/Debug/netcoreapp2.0/<runtime identifier>/publish

you will see the whole self contained app that you need to copy to your Raspberry Pi. I recommend using SFTP for transferring files if you are using Ubuntu on your Raspberry Pi. Again, is the platform you published/compiled for.

Compiled Executable for Ubuntu 16.04
Compiled Executable for Ubuntu 16.04

 

The final step is to setup your Raspberry Pi, install the prerequisites and run your program.

For Linux (Ubuntu):

  • Install Ubuntu 14.04 or 16.04 on your Raspberry Pi.
  • Install the prerequisite packages for .NET Core:
    sudo apt-get install libunwind8 libunwind8-dev gettext libicu-dev liblttng-ust-dev libcurl4-openssl-dev libssl-dev uuid-dev unzip
  • Copy your app to the Raspberry Pi, go into the directory and to manage permissions run:
    chmod u+x <ExecutableName>

    where is the name of the executable. In this case:

    chmod u+x picalc

    and finally execute your app by running:

    ./picalc

For Windows 10 IoT Core:

  • Install Windows 10 IoT Core on your Raspberry Pi.
  • Copy your app (.dll file) to your Raspberry Pi.
  • Download the CLI from Github Daily BuildsWindows (arm32).
  • Copy the contents of the zip to System32 folder on your Raspberry Pi over FTP.
  • Go to device portal, navigate to your .dll file and run:
    dotnet picalc.dll

Now you’ll see your Raspberry Pi calculating Pi!

Raspberry Pi calculating Pi
Raspberry Pi calculating Pi

Troubleshooting

Error while restoring packages

If you are having issues during the dotnet restore step, make sure to add the following line in the NuGet configuration file ( C:\Users\yourusername\AppData\Roaming\NuGet\NuGet.Config):

https://dotnet.myget.org/F/dotnet-core/api/v3/index.json

The Conclusion

As of me writing this post .NET Core ARM support is still experimental and available only on Github daily builds. Official support for ARM platforms should come sometime in 2017 (hopefully). My personal opinion is that this is a great move by Microsoft to shift C# apps on Linux based systems to use official .NET Core instead of Mono. Some people may ask

Why should you run .NET Core apps on Windows 10 IoT Core when you have Windows UWP apps?

and I personally think this is just a method of writing one code and compiling to multiple platforms. All in all, I think this is a great step forwards finally allowing developers to write full C# apps for Raspberry Pi and other ARM based SBCs.

Leave a Reply

Your email address will not be published. Required fields are marked *