I presented in the previous post my adventures with porting .NET nanoFramework to Linux and NuttX. In this article I will document how you can run .NET nanoFramework applications using this port on Raspberry Pi Zero.

.NET nanoFramework Solution (Linux Mono)

You can use any solution created by the .NET nanoFramework extension for Visual Studio. Unfortunately there is not yet a template for the dotnet CLI (to be independent of VS 2019 for example). For this case I created an example that you can clone the repository.

⚠️ For the following steps we will use commands that are part of Mono. Make sure you have the latest mono-complete package installed on your Linux distro.

Follow the steps with a terminal opened:

1- Clone the repository of examples:

git clone https://github.com/dotnuttx/nanoFrameworkPOSIX-samples.git

2- Go to the Blink folder:

cd nanoFrameworkPOSIX-samples/Blink

For this example we will compile the following Program.cs:

using System;
using System.Diagnostics;
using System.Threading;
using System.Device.Gpio;
using nanoFramework.Runtime.Native;

Debug.WriteLine($"Running nanoFramework on {SystemInfo.OEMString}");

try
{
    GpioController gpioController = new GpioController();
    int ledPinNumber;
    PinValue ledValue = PinValue.Low;
    GpioPin led;

    switch (SystemInfo.TargetName) {
        case "pi-zero":
            // pin 16 in the header is the gpio23
            ledPinNumber = 23;
        break;
        case "pi-pico":
            // onboard LED
            ledPinNumber = 25;
        break;
        default:
            throw new Exception($"Your target [{SystemInfo.TargetName}] does not support GPIOs");
    }

    // initialize pin
    led = gpioController.OpenPin(ledPinNumber, PinMode.Output);

    // blink forever
    while (true)
    {
        Debug.WriteLine($"Blinking {ledValue}");

        ledValue = !(bool)ledValue;
        led.Write(ledValue);

        Thread.Sleep(500);
    }
}
catch (Exception ex)
{
    Debug.WriteLine(ex.Message);
    Debug.WriteLine(ex.StackTrace);
}

3- Download the NuGet packages listed in packages.config, run:

nuget restore

⚠️ The v2.6.4.6 version of the interpreter for Linux needs exactly the 1.10.5-preview.18 version of the nanoFramework.CoreLibrary package

4- Compile the solution, run:

msbuild

After the build you should have the following files with the .pe extension in the project's bin/Debug/ folder:

ls -l bin/Debug/*.pe

-rw-r--r-- 1 castello castello   960 Jun 23 00:34 bin/Debug/Blink.pe
-rwxr--r-- 1 castello castello  5684 Jun 19 08:09 bin/Debug/System.Device.Gpio.pe
-rwxr--r-- 1 castello castello 31668 Jun 19 07:25 bin/Debug/mscorlib.pe
-rwxr--r-- 1 castello castello  3412 Jun 19 07:44 bin/Debug/nanoFramework.Runtime.Events.pe
-rwxr--r-- 1 castello castello  1496 Jun 19 07:46 bin/Debug/nanoFramework.Runtime.Native.peRuntime.Native.pe

These are the Portable executables/assemblies that the .NET nanoFramework interpreter knows how to read and execute. If you have them listed in the bin/Debug/ folder, congratulations 🎉 you have successfully compiled the application.

Connecting LED to Raspberry Pi Zero

As we are going to blink we need an LED connected to the GPIO of our Raspberry Pi Zero. For this example I'm using GPIO23 pin 16 in the default Raspberry Pi B header:

⚠️ You can use any other GPIO available in your Pi Zero header. Remember to change the ledPinNumber reference in Program.cs

nanoFramework Runtime (Raspberry Pi OS arm32v6)

You can download pre-compiled Raspberry Pi Zero interpreter binaries here: https://github.com/dotnuttx/nf-Community-Targets/releases

⚠️ The following steps must be ran in a Raspberry Pi Zero terminal session. Connect via serial or SSH.

To install the runtime, download the binary, add execute permissions and move to /usr/bin/:

wget https://github.com/dotnuttx/nf-Community-Targets/releases/download/v2.6.4.6/dotnet-nf.armel-Linux.2646
chmod +x dotnet-nf.armel-Linux.2646
sudo mv dotnet-nf.armel-Linux.2646 /usr/bin/dotnet-nf

Now we can finally use the runtime, to run our compiled application in the first steps. The binary takes as argument the path of a folder where it will load all listed .pe files. On your development computer go to the Blink/bin/Debug/ folder of the example repository and copy all the .pe files for the Raspberry Pi Zero board. If you have configured SSH on your Raspberry Pi Zero you can run:

scp -r bin/Debug/*.pe pi@192.168.1.98:/home/pi

⚠️ Remembering that 192.168.1.98 is just an example of usage, change it to the local ip address of your Raspberry Pi Zero

⚠️ If you haven't set up SSH or don't have a Wi-Fi connection available, or even if you have the non-Wi-Fi supported version of Pi Zero, one option is to copy the .pe files to the boot partition from Raspberry Pi OS SDCard.

With the .pe files on the board you can now run your application:

⚠️ If you have copied the files via SSH to /home/pi

dotnet-nf /home/pi/

⚠️ If you have copied the files to the boot partition of the SDCard. Here I created a new nf folder, so the command was:

dotnet-nf /boot/nf/

If you have something like the following output and your LED is blinking:

Congratulations 🎉! You ran your first blink using .NET nanoFramework on a Raspberry Pi Zero.

Conclusion

Having nanoFramework running on Linux arm32v6 is an interesting option. For example, "full" dotnet only supports arm32v7 and arm64v8. Another option might be Mono, which also has releases for arm32v6. The mono-runtime package on the Raspberry Pi OS installs ~20.7MB

⚠️ Remembering that this is something I am working on weekends and during my free time. It's 'EXTREMELY EXPERIMENTAL' and I'm not being funded by any group or institution for do it.

Did you like the possibility? If this is in any way helpful, or makes sense to you, let me know. Send me a hello on Twitter @math_castello or Linkedin 👍