获得.NET 核心 SDK 和 ASP.NET Core 3.0 以处理我的树莓派和 Windows 10 物联网核心后,我想看看是否可以直接从我的 Web 应用程序与一些电子产品进行通信。这是可能的;这里是如何做到这一点。
此处显示并讨论的代码示例解决方案可在 GitHub 存储库gpeipman/AspNetCore3LedOnOff上找到。它有更多的代码,LED可以从Web应用程序主页控制。
LED 闪烁入门
我开始与相同的默认Web应用程序,我在我的博客文章ASP.NET核心3.0在树莓皮和Windows 10物联网核心。为了与GPIO通信,我们需要.NET核心物联网库。它在阿尔法,所以它是实验性的,但它工作得很好,至少对我来说是这样。
我添加了我的树莓派磁盘作为一个网络驱动器,然后打开我的网络应用程序从可视化工作室,并添加了NuGet引用系统.Device.Gpio包。不要忘记选中”包括预发布”复选框,因为此程序包尚未具有稳定版本。
接下来,我从.NET Core IoT 库示例存储库中取走了和 LED 闪烁示例,并确保示例在我的板上工作。以下是我如何将事物连接在一起。
我的示例项目中的所有闪烁工作都Program.cs文件的 Main() 方法中完成。
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);
}
}
}
快速提示。我向
Main()
Web 应用程序的方法添加了上述方法的内容,Main()
并注释了对CreateHostBuilder()
该方法的调用。不管它是不是 Web 应用程序 ,它仍然是 .NET 核心应用程序。
如果一切工作,并引导开始闪烁,那么我们准备移动到ASP.NET核心3.0。
迁移到 ASP
有时候,我喜欢像我的学生一样做一个无主,这些是我的控制器操作在第一次运行。
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");
}
猜猜怎么着?它不工作。好吧,它的工作原理,因为没有错误,但LED不执行任何操作。原因很简单 – 控制器操作可释放 GPIO 控制器类实例,并自动关闭所有引脚。
我们需要一个GPIO控制器的实例,我们不能在控制器操作中释放它。为了使控制器操作正常工作,我将这两行添加到程序类。
public const int LedPin = 17;
public static GpioController Controller = new GpioController();
我们现在有一个 GPIO 控制器的静态实例,以及 LED 正在等待的引脚的常量。
当应用程序启动时,我们需要打开 LED 引脚并确保 LED 已关闭。当应用程序关闭时,我们必须释放 GPIO 控制器。这是我 Configure()
Startup()
这些更改后的类方法。
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
}
可在此处找到使用 GPIO 控制器静态实例的控制器操作。
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");
}
想要查看 Web 应用程序是否可以打开和关闭 LED 的用户可以使用以下 URL:
- 上/ 首页/Ledon
- 关闭= /首页/LedOff
现在它的工作原理,但它不够ASP.NET核心。
介绍 LedClient 类
我们可能都知道,如果从应用程序的其他层使用原始静态实例,将会有多大的糟糕混乱和代码气味。更不用说公开给应用程序其他部分的所有内部详细信息了。
由于我们现在可以打开和关闭控制器操作的 LED,因此是时候清理我们的代码并遵循我们行业的最佳实践了。
- 静态实例— 我们不必使用 .NET Core 上类的静态实例,因为我们具有依赖项注入。我们可以将某些类型注册为静态,然后将这些类型注入控制器。顺便说一下,.NET 核心依赖项注入也支持处置。
- GPIO 逻辑是可见的– 我们可以以特定方式将 MVC 控制器操作作为客户端代码使用 GPIO 控制器。目前,MVC 知道有关 GPIO 控制器世界的肮脏机密,这意味着硬件和 Web 层紧密地绑定在一起。这就像为麻烦而尖叫。
我知道这是更多的代码,但我希望GPIO和MVC世界有尽可能少的接触
因此,我编写 LedClient
了环绕 GPIO 控制器相关代码的类。
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);
}
}
当我们通过 .NET Core 依赖项注入使用某些内容时,我们需要注册它。下面是 ConfigureServices()
我的启动类的方法。
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews();
services.AddRazorPages();
services.AddSingleton<LedClient>();
}
现在,我们可以使用构造函数注入与 HomeController 来注入 LedClient 的实例。这里是我的家庭控制器与LedClient类。
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
});
}
}
就是这样。我们现在ASP.NET核心3.0网络应用程序,运行在树莓派,并能够与LED使用GPIO通信。
支持多个用户
我们的 LedClient
类适合一人方案,直到这个人在浏览器窗口中按 F5 的速度不是太快,其中一些控制器操作处于打开状态,控制 LED。 LedClient
ame时间。
我们必须保证不同的请求不能同时访问 LED。最简单的方法是在请求即将更改 LED 状态时使用 lock 语句将请求保留在行上。
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);
}
}
不确定此解决方案有多完美,但它工作正常,并行请求不能再同时写入引脚树莓派和 Windows 10 IoT 上的 NET Core 3.0 是受支持的方案,使用 .NET Core IoT 库的预发布版本还使我们能够编写与 GPIO 连接的设备通信的 Web 应用程序。对于 Web 应用程序,我们必须考虑多用户方案,并确保并行请求不能同时使用硬件。在这里,我们进行了简单的锁定,但实际的锁定策略取决于应用程序提供的设备和用例。