Category Archives: nanoframework

Installing nanoFramework on the ATOM Lite ESP32 (M5Stack)

I have a wonderful little M5Stack ATOM Lite, ESP32 based dev kit to play with and as I’d had such success with the M5Core2 and nanoFramework, I thought I’d try the framework on the ATOM lite.

You, can check the device and the “Firmware Target” for the device from Recommended devices to start with .NET nanoFramework. So, for this device the target is ESP32_PICO.

If we connect your device to your computer’s USB port (hopefully the device will be recognised, if not see my previous post on setting up the M5Core2) execute the following command from the CLI, we’ll flash the device with the nanoFramework

nanoff --target ESP32_PICO --update --serialport COM10

Change the COM port to whatever your device is on. Also I’m again assuming you’ve installed nanoff, if not try running the following from the CLI “dotnet tool install -g nanoff”.

The ATOM lite comes with WiFi, bluetooth a NeoPixel RGB LED, button and even infrared.

Once you’ve installed nanoFramework, create a new nanoFramework project in Visual Studio 2022 (seem my previous posts on setting this up if you’ve not already got everything setup).

Let’s start with the LED, we’ll simply change the colour of the LED. First, you’ll need to add the package nanoFramework.AtomLite via NuGet. Next copy and paste this code into the Program.cs

while (true)
{
    AtomLite.NeoPixel.Image.SetPixel(0, 0, Color.Gray);
    AtomLite.NeoPixel.Update();

    Thread.Sleep(5000);

    AtomLite.NeoPixel.Image.SetPixel(0, 0, Color.Green);
    AtomLite.NeoPixel.Update();

    Thread.Sleep(5000);

    AtomLite.NeoPixel.Image.SetPixel(0, 0, Color.Red);
    AtomLite.NeoPixel.Update();
}

Turning your M5Core2 into a nanoFramework based web server

Like most of my posts regarding nanoFramework and the M5Core2, I owe a debt to those who created this stuff and I’m really just going through some of the samples etc. Trying them out and documenting my findings. This post is no different, it’s based on the Welcome to the .NET nanoFramework WebServer repository

Add the NuGet package nanoFramework.WebServer to your nanoFramework project.

You’ll need to also include code to connect to your WiFi, so checkout my post on that subject – Wifi using nanoFramework on the M5Core2.

Assuming you’ve connected to your WiFi, we can set up a WebServer like this

using var server = new WebServer(80, HttpProtocol.Http, new[] { typeof(PowerController) });
server.Start();

Thread.Sleep(Timeout.Infinite);

The first line supplies an array of controllers, so you can have multiple controllers for your different endpoints. In this case we’ve just got the single controller PowerController. This is a simple class that includes RouteAtrribute and MethodAttribute adorned methods to acts as routes/endpoints.

Let’s look at the PowerController, which just returns some M5Core2.Power values when accessed via http://m5core2_ip_address/power.

public class PowerController
{
   [Route("power")]
   [Method("GET")]
   public void PowerRoute(WebServerEventArgs e)
   {
      var power = M5Core2.Power;
           
      var sb = new StringBuilder();
      sb.AppendLine("Power:");
      sb.AppendLine($"  Adc Frequency: {power.AdcFrequency}");
      sb.AppendLine($"  Adc Pin Current: {power.AdcPinCurrent}");
      sb.AppendLine($"  Adc Pin Current Setting: {power.AdcPinCurrentSetting}");
      sb.AppendLine($"  Adc Pin Enabled: {power.AdcPinEnabled}");
      sb.AppendLine($"  Batt. Temp. Monitor: {power.BatteryTemperatureMonitoring}");
      sb.AppendLine($"  Charging Current: {power.ChargingCurrent}");
      sb.AppendLine($"  Charging Stop Threshold: {power.ChargingStopThreshold}");
      sb.AppendLine($"  Charging Voltage: {power.ChargingVoltage}");
      sb.AppendLine($"  Dc Dc1 Voltage: {power.DcDc1Voltage.Millivolts} mV");
      sb.AppendLine($"  Dc Dc2 Voltage: {power.DcDc2Voltage.Millivolts} mV");
      sb.AppendLine($"  Dc Dc3 Voltage: {power.DcDc3Voltage.Millivolts} mV");
      sb.AppendLine($"  EXTEN Enable: {power.EXTENEnable}");
      sb.AppendLine($"  VOff Voltage: {power.VoffVoltage}");
      sb.AppendLine($"  Gpio0 Behavior: {power.Gpio0Behavior}");
      sb.AppendLine($"  Gpio0 Value: {power.Gpio0Value}");

      e.Context.Response.ContentType = "text/plain";
      WebServer.OutPutStream(e.Context.Response, sb.ToString());
}

As you can see from the last line of code, we send the response back with our payload, the string of power information.

We can also return HTTP codes using

WebServer.OutputHttpCode(e.Context.Response, HttpStatusCode.OK);

This is great, but what’s the IP address of our IoT device, so I can access the web server?

Well, ofcourse you could check your router or DHCP server, but better still, let’s output the IP address to the M5Core2 screen using

Console.WriteLine(IPGlobalProperties.GetIPAddress().ToString());

We can support multiple routes per method, such as

[ublic class PowerController
{
[Route("power")]
[Route("iotpower")]
[Method("GET")]
public void PowerRoute(WebServerEventArgs e)
{
// code removed
}

Note: Routes are usually case insensitive, unless you add the CaseSensitiveAttribute to your method.

Interacting with the M5Core2 Accelerometer and Gryoscope using nanoFramework

The M5Core includes an accelerometer which allows us to measure the rate of acceleration, as well as a gyroscope to sense angular movement.

We initialize the combined AccelerometerGyroscope and calibrate it by using the following code. The number, 100 in this case, is the number of iterations to calibrate the AccelerometerGyroscope

M5Core2.AccelerometerGyroscope.Calibrate(100);

Let’s look at the code to read the accelerometer and gyroscope (we’ll also read the internal temperature of the AccelerometerGyroscope)

Console.Clear();

M5Core2.AccelerometerGyroscope.Calibrate(100);

while (true)
{
   var accelerometer = M5Core2.AccelerometerGyroscope.GetAccelerometer();
   var gyroscope = M5Core2.AccelerometerGyroscope.GetGyroscope();
   var temperature = M5Core2.AccelerometerGyroscope.GetInternalTemperature();

   Console.CursorLeft = 0;
   Console.CursorTop = 1;

   Console.WriteLine("Accelerator:");
   Console.WriteLine($"  x={accelerometer.X}");
   Console.WriteLine($"  y={accelerometer.Y}");
   Console.WriteLine($"  z={accelerometer.Z}");
   Console.WriteLine("Gyroscope:");
   Console.WriteLine($"  x={gyroscope.X}");
   Console.WriteLine($"  y={gyroscope.Y}");
   Console.WriteLine($"  z={gyroscope.Z}");
   Console.WriteLine("Internal Temp:");
   Console.WriteLine($"  Celsius={temperature.DegreesCelsius}");

   Thread.Sleep(20);
}

nanoFramework Console (using the M5Core2)

The nanoFramework comes with a Console class, for the M5Stack this is in the namespace nanoFramework.M5Stack.Console

Before we uses the Console we need to initialize the screen, this essentially creates the screen buffer and assigns a font from the application’s resource. As I’m testing this stuff on the M5Core2, the code looks like this.

M5Core2.InitializeScreen();

Now we can simply use the Console like we would for a Console application on Windows.

// clear the console
Console.Clear();

// output some test
Console.WriteLine("Some Text");

// change the foreground colour
Console.ForegroundColor = Color.Red;
Console.WriteLine("Some Red Text");

// change foreground and background colours
Console.BackgroundColor = Color.Yellow;
Console.ForegroundColor = Color.White;
Console.WriteLine("Some Green Text on Yellow Background");

We can also change the font by supplying a font resource, the default included is consolas_regular_16.tinyfnt. We would add the font as a resource and create the font like this

Console.Font = Resource.GetFont(Resource.FontResources.consolas_regular_16);

We can move the cursor around using

Console.CursorLeft = 3;
Console.CursorTop = 5;

We can also get the height and width of our window via the Console using

Console.WriteLine($"Height: {Console.WindowHeight}, Width: {Console.WindowWidth}");

nanoFramework accessing a webservice using the M5Core2

In the previous post Wifi using nanoFramework on the M5Core2 we looked at connecting our M5Core2 using it’s wireless network capability, to our WiFi network. Next, let’s look at how we access a website, for example I’ll try to access the Cheer Lights API.

We’ll need to add the NuGet package nanoFramework.System.Net.Http.Client which exposes the HttpClient functionality for the nanoFramework.

Just like the full .NET framework/and core. We should create an HttpClient for the lifetime of the application. So we’d have something like this

public static class CheerLights
{
   private static readonly HttpClient HttpClient = new HttpClient();
}

The Cheer Lights API is a simple call to https://api.thingspeak.com/channels/1417/field/2/last.txt which will return a #hex colour, for example #008000. So. we might write some code, such as this in a funtion within the CheerLights class

var requestUri = "https://api.thingspeak.com/channels/1417/field/2/last.txt";

var response = HttpClient.Get(requestUri);
response.EnsureSuccessStatusCode();
var responseBody = response.Content.ReadAsString();

This would work for a non-HTTPS site, but for HTTPS requires that we get the CA certificate for the site we want to interact with (I haven’t yet found a way to use HTTPS without this).

To get the certificate, navigate to the page using a browser (I’m using Microsoft EDGE). Click on the padlock, click on connection is secure, then click on the show certificate button. Select Details then in the Certificate Hierarchy select the root CA certificate and export this (renaming as a txt file). This will be what we used for the HttpsAuthentCert (as we’ll see in a moment).

Now we want to include the certificate which we can do as a resource or just embedding the text into the code. So now we’d have something like this

try
{
   HttpClient.HttpsAuthentCert = new X509Certificate(
                        @"-----BEGIN CERTIFICATE-----
MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBh
MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD
QTAeFw0wNjExMTAwMDAwMDBaFw0zMTExMTAwMDAwMDBaMGExCzAJBgNVBAYTAlVT
MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j
b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IENBMIIBIjANBgkqhkiG
9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4jvhEXLeqKTTo1eqUKKPC3eQyaKl7hLOllsB
CSDMAZOnTjC3U/dDxGkAV53ijSLdhwZAAIEJzs4bg7/fzTtxRuLWZscFs3YnFo97
nh6Vfe63SKMI2tavegw5BmV/Sl0fvBf4q77uKNd0f3p4mVmFaG5cIzJLv07A6Fpt
43C/dxC//AH2hdmoRBBYMql1GNXRor5H4idq9Joz+EkIYIvUX7Q6hL+hqkpMfT7P
T19sdl6gSzeRntwi5m3OFBqOasv+zbMUZBfHWymeMr/y7vrTC0LUq7dBMtoM1O/4
gdW7jVg/tRvoSSiicNoxBN33shbyTApOB6jtSj1etX+jkMOvJwIDAQABo2MwYTAO
BgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA95QNVbR
TLtm8KPiGxvDl7I90VUwHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUw
DQYJKoZIhvcNAQEFBQADggEBAMucN6pIExIK+t1EnE9SsPTfrgT1eXkIoyQY/Esr
hMAtudXH/vTBH1jLuG2cenTnmCmrEbXjcKChzUyImZOMkXDiqw8cvpOp/2PV5Adg
06O/nVsJ8dWO41P0jmP6P6fbtGbfYmbW0W5BjfIttep3Sp+dWOIrWcBAI+0tKIJF
PnlUkiaY4IBIqDfv8NZ5YBberOgOzW6sRBc4L0na4UU+Krk2U886UAb3LujEV0ls
YSEY1QSteDwsOoBrp+uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj4dzp30d8tbQk
CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4=
-----END CERTIFICATE-----");

   var requestUri = "https://api.thingspeak.com/channels/1417/field/2/last.txt";
   var response = HttpClient.Get(requestUri);
   response.EnsureSuccessStatusCode();
   var responseBody = response.Content.ReadAsString();

Wifi using nanoFramework on the M5Core2

When we’re creating an IoT device, the likelihood is we’ll want to connect to Wifi and the internet (well it’s sort of in the IoT name isn’t it).

Please note, this post is heavily based upon the nanoFramework samples, so do go and view them for a whole host of excellent sample code.

The nanoFramework comes with a NuGet package named nanoFramework.System.Device.Wifi which you’ll need to add to your project.

Now we can use the WifiNetworkHelper class to connect to your DHCP server (your router generally) using the ConnectDhcp method. Now this will also save the configuration of the network ssid and password.

const string ssid = "YOUR_SSID";
const string password = "YOUR_WIFI_PASSWORD";

var cs = new CancellationTokenSource(60000);
var success = WifiNetworkHelper.ConnectDhcp(ssid, password, requiresDateTime: true, token: cs.Token);
if (!success)
{
    Debug.WriteLine($"Cannot connect to the WiFi, error: {WifiNetworkHelper.Status}");
    if (WifiNetworkHelper.HelperException != null)
    {
        Debug.WriteLine($"ex: {WifiNetworkHelper.HelperException}");
    }
}
else
{
   Debug.WriteLine("Connected successfully");
}

As mentioned, the ConnectDhcp method saves our configuration, so once that’s happened, we can switch to using the Reconnect method instead, i.e.

var cs = new CancellationTokenSource(60000);
var success = WifiNetworkHelper.Reconnect(requiresDateTime: true, token: cs.Token);
if (!success)
{
    Debug.WriteLine($"Cannot connect to the WiFi, error: {WifiNetworkHelper.Status}");
    if (WifiNetworkHelper.HelperException != null)
    {
        Debug.WriteLine($"ex: {WifiNetworkHelper.HelperException}");
    }
}
else
{
   Debug.WriteLine("Connected successfully");
}

The ssid and password are stored using the Wireless80211Configuration on the device. So we can check if our configuration has been stored – this ofcourse also means we can get access to the configuration – beware if security is an issue that these are then available as plain text

var configuration = Wireless80211Configuration.GetAllWireless80211Configurations();
foreach (var config in configuration)
{
   Debug.WriteLine($"SSID: {config.Ssid}, Password: {config.Password}");
}

.NET nanoframework on an M5Core2

Every now and then I get the desire to do some electronics – I’m not very good at it, so it often ends up with me hitting a wall where I need to learn more electronics. Anyway, the devices available now are truly amazing. Before I used Raspberry Pi’s which are amazing, but now I’m using the Raspberry Pi Pico W and just got an even more (although ofcourse more expensive) amazing piece of kit in the M5Core2.

This thing has a touch screen, USB-C, the obligatory LED, vibration, microphone, speaker and more. But what’s really interesting is that it’s supported by .NET nanoframework.

Note: The pico also has a port for using with nanoframework, but for now I’m leaving that set-up with MicroPython.

Getting Started

Let’s start with the hardware

  • Plug your device into a USB port on your computer
  • If the device is not detected, got to Driver Installation and select the latest driver for your OS
  • If all went well it will appear on a COM port. In my case I’m on Windows I checked whether the COM PORT was added to your device manager, mine’s on COM9

The power button is on the left-hand side of the M5Core2 at the top, press this once and you should see a wonderful UI full of bits and bobs which detects sound and movement, sadly we’re going to get rid of that when we install nanoframework – so have a play now whilst you can.

Note: To turn the M5Core2 off, hold the power button for 6 seconds.

Getting nanoframework up and running

Follow the steps on the nanoframework Getting Started Guild – Managed (C#).

You’ll want to install the nano firmware flasher/nanoff using

dotnet tool install -g nanoff

You’re going to want to flash the M5Core2 using nanoff like this (remember this will clear the M5Core2 so if you want to play with the installed app first do so now – as you can tell I wish I’d played with the the default app a little more)

nanoff --target M5Core2 --update --serialport COM9

Replace the COM9 port name with whatever port your device is on.

Writing the LED Blink App.

I’m going be using Visual Studio 2022 to write my code. So, go to the extensions menu item and install the nanoframework extension. This will install project templates and other goodies.

Note: I found this didn’t work great first time, so had to try to remove and do again, but got there in the end, so persevere if it fails first time.

Although the M5Core2 has a screen, the ubiquitous first application for a microcontroller is the LED blinking application. Now the M5Core2 does have an LED but it’s the power LED, but that’s not an issue nanoframework has our back.

Once the extension is installed and working, creating a new application using the Blank Application (.NET nanoframework) template, you’ll see some code which looks like any other C# project. Actually, nanoframework it supports top level statements, so we’ll look at changing this code in a moment.

First off, we need to install this NuGet package

nanoframework.M5Core2

It will install quite a few extra packages.

Replace everything with Program.cs with the following

using nanoFramework.M5Stack;
using Console = nanoFramework.M5Stack.Console;

M5Core2.InitializeScreen();

Console.WriteLine("Led ON");
M5Core2.PowerLed = false;

In the above code I’m showing how we can write the M5Core2 LCD screen using Console (we need to initialize the screen to begin with) and I’m setting the PowerLed to false (umm yes, false seems to be ON, not sure if this is a bug or if this is some electronics things).

Before we can run anything, locate the Device Explorer view. In Visual Studio either type this into the Search (Ctrl+Q) textbox or go to View | Other Windows | Device Explorer. Ensure your hardware is plugged into a USB port and powered up and if need be, refresh the list in the Device Explorer until you see something like M5Core2 @ COM9. Feel free to ping the device using the ping button to check it’s running nanoCLR (this will be displayed in the Output Window of Visual Studio, .NET nanoFramework extension option).

Now press the run button (assuming .NET nanoFramework Device is selected). In the Output Window, Debug view, you’ll see the compiler run then the deployment process takes place, or in my case, it doesn’t work straight away. If yours works, jump ahead and ignore the bit on fixing the error I had.

If you get Error: a3000000, I found this was version issue, noticing that it also says Link failure: some assembly references cannot be resolved!!. If you check the Error List Window you may see it mentions a version issue, and suggests using AutoGenerateBindingRedirects. It appears nanoFramework does not support assembly binding, see this link.

So we need to check the Output Debug window, I found that after the Error: a3000000 I had this

Assembly: Iot.Device.Axp192 (1.2.0.0) needs assembly 'UnitsNet.Power' (4.145.0.0)

But before he Error: a3000000 I also have all these

Assembly: Blinky (1.0.0.0) needs assembly 'nanoFramework.M5Core2' (1.1.82.38397)
Assembly: nanoFramework.M5Core2 (1.1.82.38397) needs assembly 'Iot.Device.Mpu6886' (1.2.0.0)
Assembly: nanoFramework.M5Core2 (1.1.82.38397) needs assembly 'Iot.Device.Rtc' (1.2.0.0)
Assembly: nanoFramework.M5Core2 (1.1.82.38397) needs assembly 'Iot.Device.Axp192' (1.2.0.0)
Assembly: Iot.Device.Mpu6886 (1.2.0.0) needs assembly 'UnitsNet.Temperature' (4.145.0.0)
Assembly: Iot.Device.Rtc (1.2.0.0) needs assembly 'UnitsNet.Temperature' (4.145.0.0)
Assembly: Iot.Device.Axp192 (1.2.0.0) needs assembly 'UnitsNet.ElectricPotential' (4.145.0.0)
Assembly: Iot.Device.Axp192 (1.2.0.0) needs assembly 'UnitsNet.ElectricCurrent' (4.145.0.0)
Assembly: Iot.Device.Axp192 (1.2.0.0) needs assembly 'UnitsNet.Temperature' (4.145.0.0)

Obviously, there’s a versioning problem so we need to go into the NuGet Package manager and go through each of the items listed and change to the expected versions, either rolling back or forward out packages. I basically updated everything on the left hand side to of the list (above) to the latest versions and all, eventually started to work.

Once everything builds and deploys via the run button your M5Core2 should show the text “Led OFF” and the Power Led is off. Now switch the code to the following

Console.WriteLine("Led ON");
M5Core2.PowerLed = false;

and run again.

Once deployed and started on the device, the Power Led should be on and the text on the screen will say “Led ON”.

We’ve now got the bare bones of a .NET nanoFramework application running on the ESP32 M5Core2.

References

Nanoframework M5Stack