ASP.NET Core 3.0 on Raspberry Pi: Controlling LED
Learn more about controlling LED using ASP.NET Core 3.0 and a Raspberry Pi.
Join the DZone community and get the full member experience.
Join For FreeAfter getting .NET Core SDK and ASP.NET Core 3.0 to work on my Raspberry Pi and Windows 10 IoT Core, I wanted to see if I could communicate with some electronics right from my web application. It is possible; here is how to do it.
The sample solution with code shown and discussed here is available on my GitHub repository gpeipman/AspNetCore3LedOnOff. It has a little bit more code, and LED can be controlled from the web application home page.
Getting Started With LED Blinking
To communicate with GPIO, we need .NET Core IoT libraries. It’s in alpha, so it’s experimental, yet it works well, at least for me.
I added my Raspberry Pi disk as a network drive and then opened my web application from Visual Studio and added NuGet reference to System.Device.Gpio package. Don’t forget to check Include prereleases checkbox as this package has no stable version yet.
Next, I took and LED-blink example from .NET Core IoT libraries sample repository and made sure that sample works on my board. Here’s how I wired things together.
All the blinking work in my sample project was done in the Main() method of Program.cs file.
public static void Main(string[] args)
{
var pin = 17;
var lightTimeInMilliseconds = 1000;
var dimTimeInMilliseconds = 200;
using (GpioController controller = new GpioController())
{
controller.OpenPin(pin, PinMode.Output);
Console.WriteLine($"GPIO pin enabled for use: {pin}");
Console.CancelKeyPress += (object sender, ConsoleCancelEventArgs eventArgs) =>
{
controller.Dispose();
};
while (true)
{
Console.WriteLine($"Light for {lightTimeInMilliseconds}ms");
controller.Write(pin, PinValue.High);
Thread.Sleep(lightTimeInMilliseconds);
Console.WriteLine($"Dim for {dimTimeInMilliseconds}ms");
controller.Write(pin, PinValue.Low);
Thread.Sleep(dimTimeInMilliseconds);
}
}
}
Quick hint. I added content of
Main()
method above toMain()
method of my web application and commented out calls to theCreateHostBuilder()
method. It doesn’t matter if it’s web application or not – it’s still .NET Core application.
If everything works and led starts blinking, then we are ready for moving to ASP.NET Core 3.0.
Moving to ASP.NET Core
As I like to go with small steps when playing with new stuff, I defined two new controller actions – LedOn()
and LedOff()
. Sometimes, I like to be a noobie like my students, and these were my controller actions at first run.
public IActionResult LedOn()
{
using (GpioController controller = new GpioController())
{
controller.OpenPin(Pin, PinMode.Output);
controller.Write(Pin, PinValue.High);
Thread.Sleep(2000);
}
return Content("Led on");
}
public IActionResult LedOff()
{
using (GpioController controller = new GpioController())
{
controller.OpenPin(Pin, PinMode.Output);
controller.Write(Pin, PinValue.Low);
}
return Content("Led off");
}
Guess what? It doesn’t work. Okay, it works because there are no errors but the LED does nothing. The reason is simple – controller actions dispose the GPIO controller class instance and turns off all pins automatically.
We need an instance of GPIO controller and we cannot dispose it in controller actions. To make controller actions work, I added these two lines to the Program class.
public const int LedPin = 17;
public static GpioController Controller = new GpioController();
We now have a static instance of the GPIO controller and constant for pin where LED is waiting.
When the application starts, we need to open the LED pin and make sure that LED is turned off. When the application closes, we have to dispose of the GPIO controller. This is my Configure()
method of the Startup()
class after these changes.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IHostApplicationLifetime applicationLifetime)
{
Program.Controller.OpenPin(Program.LedPin, PinMode.Output);
Program.Controller.Write(Program.LedPin, PinValue.Low);
applicationLifetime.ApplicationStopped.Register(() =>
{
Program.Controller.ClosePin(Program.LedPin);
Program.Controller.Dispose();
});
// Default code follows
}
The controller actions that use the static instance of GPIO controller can be found here.
public IActionResult LedOn()
{
Program.Controller.Write(Program.LedPin, PinValue.High);
return Content("Led on");
}
public IActionResult LedOff()
{
Program.Controller.Write(Program.LedPin, PinValue.Low);
return Content("Led off");
}
Those who want to see if the web application can turn LED on and off can use these URLs:
- On – /Home/LedOn
- Off – /Home/LedOff
Now it works but it’s not ASP.NET Core-ish enough.
Introducing LedClient Class
We all probably know how much bad mess and code smell there will be if raw static instances are used from other layers of the application. Not to mention all kind of internal details that are exposed to other parts of the application.
As we can now turn LED on and off from controller actions, it’s time to make our code clean and follow best practices of our industry.
- Static instance — we don’t have to use static instances from classes on .NET Core as we have dependency injection. We can register some types as static and then inject these to controllers. By the way, .NET Core dependency injection also supports disposing.
- GPIO logic is visible — we can take MVC controller actions as client code that consumes GPIO controllers in a specific way. Currently, MVC knows dirty secrets about the GPIO controller world and it means that hardware and web layers are bound together too tightly. It’s like screaming for trouble.
I know it’s more code but I want the GPIO and MVC worlds to have as little contact as possible. Those who write the web side of the application should use some client class without messing directly with the GPIO controller and pins.
So, I wrote the LedClient
class that wraps around the GPIO-controller-related code.
public class LedClient : IDisposable
{
private const int LedPin = 17;
private GpioController _controller = new GpioController();
private bool disposedValue = false;
public LedClient()
{
_controller.OpenPin(LedPin, PinMode.Output);
_controller.Write(LedPin, PinValue.Low);
}
public void LedOn()
{
_controller.Write(LedPin, PinValue.High);
}
public void LedOff()
{
_controller.Write(LedPin, PinValue.Low);
}
protected virtual void Dispose(bool disposing)
{
if (!disposedValue)
{
if (disposing)
{
_controller.Dispose();
}
disposedValue = true;
}
}
public void Dispose()
{
Dispose(true);
}
}
When we use something through .NET Core dependency injection, we need to register it. Here’s the ConfigureServices()
method of my Startup class.
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews();
services.AddRazorPages();
services.AddSingleton<LedClient>();
}
Now we can use constructor injection with HomeController to inject instance of LedClient. Here’s my HomeController with LedClient class.
public class HomeController : Controller
{
private readonly LedClient _ledClient;
public HomeController(LedClient ledClient)
{
_ledClient = ledClient;
}
public IActionResult Index()
{
return View();
}
public IActionResult LedOn()
{
_ledClient.LedOn();
return Content("Led on");
}
public IActionResult LedOff()
{
_ledClient.LedOff();
return Content("Led off");
}
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
public IActionResult Error()
{
return View(new ErrorViewModel
{
RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier
});
}
}
This is it. We have now ASP.NET Core 3.0 web application that runs on Raspberry Pi and is able to communicate with LED using GPIO.
Supporting Multiple Users
Our LedClient
class is okay for one-man scenarios until this one man is not too fast pressing F5 in the browser window where some of the controller actions are open that controls LED. LedClient
is also not safe for multi-user scenarios as users may try to turn LED on or off at the same time.
We have to guarantee that different requests cannot access LED at the same time. The easiest way is to use a lock statement to keep requests on the row when they are about to change the LED state.
public class LedClient : IDisposable
{
private const int LedPin = 17;
private GpioController _controller = new GpioController();
private bool disposedValue = false;
private object _locker = new object();
public LedClient()
{
_controller.OpenPin(LedPin, PinMode.Output);
_controller.Write(LedPin, PinValue.Low);
}
public void LedOn()
{
lock (_locker)
{
_controller.Write(LedPin, PinValue.High);
}
}
public void LedOff()
{
lock (_locker)
{
_controller.Write(LedPin, PinValue.Low);
}
}
protected virtual void Dispose(bool disposing)
{
if (!disposedValue)
{
if (disposing)
{
_controller.Dispose();
}
disposedValue = true;
}
}
public void Dispose()
{
Dispose(true);
}
}
Not sure how perfect this solution is but it works and parallel requests cannot write to pin at the same time anymore.
Wrapping Up
Running ASP.NET Core 3.0 on Raspberry Pi and Windows 10 IoT is a supported scenario, and using the pre-release version of .NET Core IoT Library also enables us to write web applications that communicate with GPIO connected devices. For web applications, we have to consider multi-user scenarios and make sure that parallel requests can’t use hardware at the same time. Here, we went with simple locking, but actual locking strategy depends on devices and use cases that the application provides.
Published at DZone with permission of Gunnar Peipman, DZone MVB. See the original article here.
Opinions expressed by DZone contributors are their own.
Comments