转载

Windows 10 IoT 开发:Azure远程控制开关灯泡

看过美剧《生活大爆炸》的朋友可能记得,里面有一个场景就是那群物理学家搞了个互联网控制的灯泡。从家里电脑上按下开关,信号绕地球一大群回来,点亮家里的台灯。虽然很屌丝,但是今天我成功在树莓派3上用Windows 10 IoT Core和Microsoft Azure实现了这个实验。

首先,Azure的参考文献有两篇, 强烈建议大家先阅读 ,本文不会再重复这两篇文章里的基础步骤:

Get started with Azure IoT Hub for .NET

https://azure.microsoft.com/en-us/documentation/articles/iot-hub-csharp-csharp-c2d/

一、创建Azure IoT Hub和注册设备

创建Iot Hub的步骤参考 Get started with Azure IoT Hub for .NET 里的" Create an IoT Hub "章节,步骤完全一致。

注册设备我用了一个简单的方法,不用去做"Create a device identity"里的步骤。微软官方有个开源工具 https://github.com/Azure/azure-iot-sdks/tree/master/tools/DeviceExplorer

下载之后,到主界面填写连接字符串,然后点击 Update 按钮。

Windows 10 IoT 开发:Azure远程控制开关灯泡

连接字符串在Azure Portal里有可以拿到。参考文章里也有说明。主要用到的就是 HostnameSharedAccessKeyNameSharedAccessKey

格式如下:

HostName=你的HUB名称.azure-devices.net;SharedAccessKeyName=iothubowner;SharedAccessKey=你的KEY

Windows 10 IoT 开发:Azure远程控制开关灯泡

然后在Management标签下面点击Create,填写你的设备名称,勾选"Auto Generate Keys"注册你的树莓派。

Windows 10 IoT 开发:Azure远程控制开关灯泡

注册成功以后你应该能看到这个设备的信息被加入表格了,然后就可以进行下一步了。

二、树莓派物理连接

需要材料:LED灯泡一只、杜邦线2根

LED 树莓派
长脚 DC 3.3v
短脚 GPIO 04

注意,如果你用的LED额定电压不是3.3V的,请加上对应的电阻,以防烧掉。

连接完成看上去就像这样:

Windows 10 IoT 开发:Azure远程控制开关灯泡

三、爆代码

我们一共需要2个工程,第一个是树莓派上用的,这个是做接收端的,用来接受Azure发来的信息并控制LED的开关。另一个是电脑上用的,做“遥控器”,向Azure发送控制信息。

Windows 10 IoT 开发:Azure远程控制开关灯泡

最后工程的结构应该长的是这样的:

Windows 10 IoT 开发:Azure远程控制开关灯泡

树莓派端工程:

用VS2015创建一个UWP工程,比如AzureRemoteLight,然后加入"Windows IoT Extensions for the UWP"的引用。

Windows 10 IoT 开发:Azure远程控制开关灯泡

还需要添加的NuGet引用是:

"Microsoft.Azure.Devices.Client": "1.0.5"

同时建议大家把依赖项Newtonsoft.Json更新到最新版,目前是

"Newtonsoft.Json": "8.0.3"

我自己的这个工程还引用了MvvmLight和我自己的Edi.UWP.Helpers,当然这些不是必须的,只是用来装逼的。完整的project.json:

{   "dependencies": {     "Edi.UWP.Helpers": "1.0.11",     "Microsoft.Azure.Devices.Client": "1.0.5",     "Microsoft.NETCore.UniversalWindowsPlatform": "5.0.0",     "MvvmLight": "5.2.0",     "Newtonsoft.Json": "8.0.3"   },   "frameworks": {     "uap10.0": {}   },   "runtimes": {     "win10-arm": {},     "win10-arm-aot": {},     "win10-x86": {},     "win10-x86-aot": {},     "win10-x64": {},     "win10-x64-aot": {}   } }

画界面:

其实对于单纯完成开灯关灯这个功能来说,界面不是必要的。当然,有界面的话,逼格更高一点。我画了一个这样的界面,看起来很牛逼:

Windows 10 IoT 开发:Azure远程控制开关灯泡

主要就是2个地方:Azure IoT Hub Connection显示是否能成功连接到Azure(稍后会发一个message到azure更新这个label的状态)

CloudToDeviceLog显示从Azure接受到的控制信息。

完整XAML代码:

<Page     x:Class="AzureRemoteLight.MainPage"     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"     xmlns:local="using:AzureRemoteLight"     xmlns:d="http://schemas.microsoft.com/expression/blend/2008"     xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"     DataContext="{Binding Source={StaticResource Locator}, Path=Main}"     mc:Ignorable="d">      <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" Padding="12">         <Grid.RowDefinitions>             <RowDefinition Height="Auto" />             <RowDefinition Height="*" />         </Grid.RowDefinitions>                  <Image Source="Assets/cloud-hero.png" Grid.Row="0" Height="80" HorizontalAlignment="Right" VerticalAlignment="Top" />                  <StackPanel Grid.Row="0">             <TextBlock Text="Windows 10 IoT + Microsoft Azure" Style="{StaticResource SubtitleTextBlockStyle}" />             <TextBlock Text="Remote Light Control" Style="{StaticResource SubheaderTextBlockStyle}" />         </StackPanel>          <Grid Grid.Row="1" Margin="0,20,0,0">             <Grid.RowDefinitions>                 <RowDefinition Height="Auto" />                 <RowDefinition Height="*" />             </Grid.RowDefinitions>                          <Image Grid.Row="0" Grid.RowSpan="2" Source="Assets/Windows_Insiders_Flag.png" HorizontalAlignment="Right" Height="300" VerticalAlignment="Bottom" />                          <StackPanel Orientation="Horizontal" Grid.Row="0">                 <TextBlock Text="Azure IoT Hub Connection: " />                 <TextBlock Text="{Binding IsAzureConnected}" />             </StackPanel>                          <Border Grid.Row="1" BorderBrush="#CCC" BorderThickness="1" Margin="0,10,0,0" Padding="10">                 <TextBlock TextWrapping="Wrap" Text="{Binding CloudToDeviceLog}" Foreground="#CCC" FontFamily="Consolas" />             </Border>         </Grid>     </Grid> </Page>

ViewModel代码

首先,我们需要定义控制LED开关的两个对象,我的个人习惯是定义为属性,然后在执行的时候实例化。

#region GPIO Settings  public GpioController GpioController { get; }  public GpioPin LedPin { get; }  #endregion

然后还有连接Azure IoT Hub的属性

#region Azure IoT Hub Settings  public DeviceClient DeviceClient { get; }  public string IotHubUri { get; } = "你的HUB名称.azure-devices.net";  public string DeviceKey { get; } = "设备对应的KEY";  public string DeviceId => "你的设备名称";  #endregion

这些属性值可以到Device Explorer里的Management标签下面去拿。

最后还有负责界面显示的两个属性

#region Display Fields  private bool _isAzureConnected; private string _cloudToDeviceLog;  public bool IsAzureConnected {     get { return _isAzureConnected; }     set { _isAzureConnected = value; RaisePropertyChanged(); } }  public string CloudToDeviceLog {     get { return _cloudToDeviceLog; }     set { _cloudToDeviceLog = value; RaisePropertyChanged(); } }  #endregion

然后在构造函数里初始化DeviceClient和GPIO端口

public MainViewModel() {     DeviceClient = DeviceClient.Create(IotHubUri, new DeviceAuthenticationWithRegistrySymmetricKey(DeviceId, DeviceKey));      GpioController = GpioController.GetDefault();     if (null != GpioController)     {         LedPin = GpioController.OpenPin(4);         LedPin.SetDriveMode(GpioPinDriveMode.Output);     } }

还要写一个方法,向Azure发送一条信息,试试看连接是不是成功

public async Task SendDeviceToCloudMessagesAsync() {     try     {         var telemetryDataPoint = new         {             deviceId = DeviceId,             message = "Hello"         };         var messageString = JsonConvert.SerializeObject(telemetryDataPoint);         var message = new Message(Encoding.ASCII.GetBytes(messageString));          await DeviceClient.SendEventAsync(message);         Debug.WriteLine("{0} > Sending message: {1}", DateTime.Now, messageString);          IsAzureConnected = true;     }     catch (Exception ex)     {         Debug.WriteLine(ex.Message);     } }

别忘了在MainPage.xaml.cs里调用一下:

public sealed partial class MainPage : Page {     private MainViewModel _vm;      public MainPage()     {         this.InitializeComponent();          _vm = this.DataContext as MainViewModel;          Loaded += async (sender, args) =>         {             // send device connected message             await _vm.SendDeviceToCloudMessagesAsync();         };     }

运行

首先要保证树莓派的系统时间是正确的,这步是必须的,不然SAS Token是要过期的。如果时间不对,重启几次设备应该就能同步正确。w32tm /resync /force这条明令是逗你们玩的,千万别相信它有用。

在运行之前,打开Device Explorer的Data标签,然后点击Monitor,以接受树莓派发送到Azure的信息。

然后用ARM, Remote Machine的配置到树莓派上去部署和执行,成功的话,Device Explorer里会接受到这样的信息:

Windows 10 IoT 开发:Azure远程控制开关灯泡

然后我们就能继续爆代码了。

在ViewModel里再加个方法,用来接收Azure发过来的信息,然后根据信息内容向LED输出高低电平实现控制灯泡开关:

public async Task ReceiveCloudToDeviceMessageAsync() {     CloudToDeviceLog = "Receiving events...";     Debug.WriteLine("/nReceiving cloud to device messages from service");      while (true)     {         Message receivedMessage = await DeviceClient.ReceiveAsync();         if (receivedMessage == null) continue;          var msg = Encoding.ASCII.GetString(receivedMessage.GetBytes());         CloudToDeviceLog += "/nReceived message: " + msg;          if (msg == "on")         {             LedPin.Write(GpioPinValue.Low);         }          if (msg == "off")         {             LedPin.Write(GpioPinValue.High);         }          await DeviceClient.CompleteAsync(receivedMessage);     } }

消息用的是string类型,如果内容是"on"就开灯,如果是"off"就关灯。

当然,也要在MainPage.xaml.cs里再调用一下这个方法

public sealed partial class MainPage : Page {     private MainViewModel _vm;      public MainPage()     {         this.InitializeComponent();          _vm = this.DataContext as MainViewModel;          Loaded += async (sender, args) =>         {             // send device connected message             await _vm.SendDeviceToCloudMessagesAsync();              // receive remote light control events             await _vm.ReceiveCloudToDeviceMessageAsync();         };     } }

现在再执行,就应该能在树莓派上看到这样的画面:

Windows 10 IoT 开发:Azure远程控制开关灯泡

至此,树莓派端工作就完成了。

控制端工程

创建一个WPF工程,比如LightController,不能是UWP(马上解释)。然后添加Microsoft.Azure.Devices的NuGet包。这个包不支持UWP,所以建不出UWP的控制端,Stupid!

然后在MainWindow里画两个按钮,分别用来发送开灯的消息和关灯的消息:

<Window x:Class="LightController.MainWindow"         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"         xmlns:d="http://schemas.microsoft.com/expression/blend/2008"         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"         xmlns:local="clr-namespace:LightController"         mc:Ignorable="d"         Title="MainWindow" Height="350" Width="525">     <Grid>         <StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">             <Button x:Name="BtnTurnOn" Content="Turn Light On" HorizontalAlignment="Center" Click="BtnTurnOn_OnClick" />             <Button x:Name="BtnTurnOff" Content="Turn Light Off" Margin="0,10,0,0" Click="BtnTurnOff_OnClick" />         </StackPanel>     </Grid> </Window>

后台代码:

public partial class MainWindow : Window {     static ServiceClient serviceClient;     static string connectionString = "你的IOT HUB连接字符串";      public MainWindow()     {         InitializeComponent();          serviceClient = ServiceClient.CreateFromConnectionString(connectionString);     }      private async Task TurnLight(bool isOn)     {         await SendCloudToDeviceMessageAsync(isOn);     }      private static async Task SendCloudToDeviceMessageAsync(bool isOn)     {         var commandMessage = new Message(Encoding.ASCII.GetBytes(isOn ? "on" : "off"));         await serviceClient.SendAsync("你的设备名称", commandMessage);     }      private async void BtnTurnOn_OnClick(object sender, RoutedEventArgs e)     {         await TurnLight(true);     }      private async void BtnTurnOff_OnClick(object sender, RoutedEventArgs e)     {         await TurnLight(false);     } }

这里发送的消息也是string,和树莓派端的匹配,on表示开灯,off表示关灯。

IOT HUB的连接字符串就是Device Explorer里的Configruation标签里用的连接字符串,它们是一模一样的!

四、运行

因为有控制端和接收端,所以我们要同时启动2个工程。在VS里,对solution名称点右键,选择属性,然后这样搞:

Windows 10 IoT 开发:Azure远程控制开关灯泡

启动后,在电脑上点击WPF控制端里的两个按钮就能控制LED开关了,并且在树莓派的屏幕上也能显示接受的控制信息:

Windows 10 IoT 开发:Azure远程控制开关灯泡

最后,完整代码在这里:

https://github.com/EdiWang/Windows-IoT-AzureRemoteLight

原文  http://edi.wang/post/2016/4/10/windows-10-iot-azure-remote-light
正文到此结束
Loading...