田牌魔术 | .NET Core 3.0 + Azure 远程点亮树莓派上的一盏灯

2019-10-10 11:12:18 浏览数 (1)

导语

3年前,我写过一篇《Windows 10 IoT Core Azure 远程控制LED》,实现了《生活大爆炸》中的注孤生实验,让信号从家里出发,绕地球转一圈,经过微软美国数据中心,返回家里点亮树莓派上连接的一个 LED 灯泡。然而3年后的现在,Windows 10 IoT Core 以及UWP 已经冰冰凉透心凉,甚至微软至今也没有支持树莓派4的 Windows 版本。我只能苟且偷生,委曲求全,开荤 Linux,使用 .NET Core 重现了这个实验。微软和社区对于 .NET Core IoT 非常积极,提供了比 UWP 好用不少的 IoT 基础库,让我这个项目迁移非常方便。

.NET Core IoT 全家桶:https://github.com/dotnet/iot

在开始之前,如果你还没有在树莓派上配置.NET Core环境,可以参考我之前写的:

在树莓派4上安装 .NET Core 3.0 运行时及 SDK

“自启动”树莓派上的 .NET Core 3.0 环境

基本原理

我们要从自己电脑上发送信号到 Azure IoT Hub,树莓派上的.NET Core程序会监听消息,并控制LED开关。

创建 Azure IoT Hub

请根据微软文档创建 IoT Hub。最后记下你的连接字符串,看起来就像这样:

HostName=<Your Hub Name>.azure-devices.net;SharedAccessKeyName=iothubowner;SharedAccessKey=<Your Key>

微软文档:https://docs.microsoft.com/en-us/azure/iot-hub/quickstart-send-telemetry-dotnet?WT.mc_id=AZ-MVP-5002809

下载并安装 Azure Device Explorer (https://aka.ms/aziotdevexp)

把连接字符串粘贴到 Configuration / Connection Information里,点击 Update 按钮。

切换到 Management 选项卡,点击 Create ,输入你的设备名称,勾选 Auto Generate Keys

树莓派物理连接

将一个LED连接到树莓派:

长脚连接到 GPIO 17

短脚连接到接地(GROUND)

附:树莓派4 GPIO 全家桶图(来自网络)

复制粘贴

创建一个 .NET Core 3.0 控制台程序。添加 Microsoft.Azure.Devices.Client 以及 System.Device.Gpio NuGet 包。前者用于和Azure IoT Hub通讯,后者用于控制 GPIO 来开关LED灯泡。

<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>

<OutputType>Exe</OutputType>

<TargetFramework>netcoreapp3.0</TargetFramework>

</PropertyGroup>

<ItemGroup>

<PackageReference Include="Microsoft.Azure.Devices.Client" Version="1.21.1" />

<PackageReference Include="System.Device.Gpio" Version="1.0.0" />

</ItemGroup>

</Project>

Program.cs 最终代码如下。记得把里面的字符串常量换成你自己的 Azure IoT Hub 信息。

using System;

using System.Device.Gpio;

using System.Text;

using System.Threading;

using System.Threading.Tasks;

using Microsoft.Azure.Devices.Client;

using Microsoft.Azure.Devices.Client.Exceptions;

namespace AzureLightControl

{

class Program

{

private const string IotHubUri = "<Your Hub Name>.azure-devices.net";

private const string DeviceKey = "<Your Key>";

private const string DeviceId = "<Your Device ID>";

private const int Pin = 17;

private static CancellationToken _ct;

static async Task Main(string[] args)

{

Console.WriteLine("------------------------------");

Console.WriteLine(" Azure IoT Hub Light Control");

Console.WriteLine("------------------------------");

var cts = new CancellationTokenSource();

_ct = cts.Token;

Console.CancelKeyPress = (sender, eventArgs) =>

{

Console.WriteLine($"{DateTime.Now} > Cancelling...");

cts.Cancel();

eventArgs.Cancel = true;

};

try

{

var t = Task.Run(Run, cts.Token);

await t;

}

catch (IotHubCommunicationException)

{

Console.WriteLine($"{DateTime.Now} > Operation has been canceled.");

}

catch (OperationCanceledException)

{

Console.WriteLine($"{DateTime.Now} > Operation has been canceled.");

}

finally

{

cts.Dispose();

}

Console.ReadKey();

}

private static async Task Run()

{

using var deviceClient = DeviceClient.Create(IotHubUri, new DeviceAuthenticationWithRegistrySymmetricKey(DeviceId, DeviceKey));

using var controller = new GpioController();

controller.OpenPin(Pin, PinMode.Output);

Console.WriteLine($"{DateTime.Now} > Connected to the best cloud on the planet.");

Console.WriteLine($"Azure IoT Hub: {IotHubUri}");

Console.WriteLine($"Device ID: {DeviceId}");

Console.WriteLine($"{DateTime.Now} > GPIO pin enabled for use: {Pin}");

while (!_ct.IsCancellationRequested)

{

Console.WriteLine($"{DateTime.Now} > Waiting new message from Azure...");

var receivedMessage = await deviceClient.ReceiveAsync(_ct);

if (receivedMessage == null) continue;

var msg = Encoding.ASCII.GetString(receivedMessage.GetBytes());

Console.WriteLine($"{DateTime.Now} > Received message: {msg}");

switch (msg)

{

case "on":

Console.WriteLine($"{DateTime.Now} > Turn on the light.");

controller.Write(Pin, PinValue.High);

break;

case "off":

Console.WriteLine($"{DateTime.Now} > Turn off the light.");

controller.Write(Pin, PinValue.Low);

break;

default:

Console.WriteLine($"Unknown command: {msg}");

break;

}

await deviceClient.CompleteAsync(receivedMessage, _ct);

}

}

}

}

这里面,deviceClient.ReceiveAsync() 负责监听 Azure IoT Hub 有没有发来新消息,并处理收到的消息,完事后调用 CompleteAsync() 告诉 Azure 这个消息已经处理好了,这样的话设备再次连接到 Azure 就不会重复处理这条消息。

处理消息十分直接,读取消息内容为字符串,如果写着"on",就向GPIO 17输出高电位,即点亮灯泡。如果是"off",就输出低电位关闭灯泡。

能跑就行

将源代码或者发布后的dll全家桶复制到树莓派。然后在树莓派上用 .NET CLI 启动程序。在 PC 上,通过 Device Explorer 向设备发送 on 或 off 消息。

现在你学会了通过互联网控制家里灯泡开关的魔术:

0 人点赞