2018 年 12 月

第 33 卷,第 12 期

物联网 - 使用 Azure IoT Central 加速 IoT 开发

作者 Dawid Borycki

IoT 开发人员面临的挑战有很多。幸运的是,Microsoft IoT 技术能帮助你排除万难。使用 Windows 10 IoT Core,可以开发适用于智能设备的通用 Windows 平台 (UWP) 应用 (bit.ly/2yJf6RJ)。使用 Azure 机器学习工作室,可以图表方式创建机器学习 (ML) 算法 (bit.ly/2yF2yes)。此外,若要创建使用多个 Azure IoT 服务或自主开发 ASP.NET MVC 应用(.NET Framework 或 .NET Core)的 Web 应用,有多种方法可供选择。

虽然这些技术提供了开发自定义 IoT 解决方案的综合方法,但结合使用它们可能会很难,尤其是在以往没有云或 Web 编程经验时。为了解决此问题,Microsoft 创建了 Azure IoT 套件(请参阅我之前的文章 (bit.ly/2yFaIU6)),后来又将它重命名为 Azure IoT 解决方案加速器 (bit.ly/2pYaraX)。解决方案加速器提供了大量定位为解决典型 IoT 问题的预配置应用,包括仪表板、ML 模型、流数据逻辑和关联一切的编程组件。不过,解决方案加速器仍然非常复杂。因此,为了进一步简化 IoT 开发,Microsoft 引入了 Azure IoT Central,这是一款基于云的托管服务,可用于快速创建 IoT 后端。这是新式门户,其中包含用于遥测和数据处理等的仪表板和基础服务。可使用此后端连接、监视和管理 IoT 设备。

在本文中,我将介绍如何使用 Azure IoT Central 创建图 1 中所示的解决方案。此解决方案使用自定义 IoT Central 应用,以描述遥测数据、设备位置和设置以及两个关键绩效指标。操作员可使用此仪表板直观呈现遥测,并通过设置远程控制设备。遥测数据是从(右上角显示的).NET Core 控制台应用流式传输。可以从 bit.ly/2D34XnV 获取此应用的完整源代码。

我将创建的 IoT 解决方案
图1:我将创建的 IoT 解决方案

Azure IoT 服务和解决方案

在介绍我是如何创建 IoT 解决方案之前,我将简要回顾一下其他使用 Azure IoT 开发 IoT 应用的可行方法。第一种方法是,可以通过实例化和手动配置专用 Azure IoT 服务,创建完全自定义解决方案,如图 2 所示。在这种情况下,通常是从 IoT 中心入手,它可用作云网关,用于实现双向通信和设备管理(例如,设备注册)。然后,可使用 Azure 流分析,预处理或转换通过 IoT 中心从远程设备传输的数据。此服务可用于筛选掉非遥测数据,或平均分配遥测数据的短部分,以减少急剧度量波动。然后,可以将预处理过的数据发送到 Power BI 仪表板进行直观呈现、暂留在专用云存储中,或传输到事件中心以进行更复杂的分析。事件中心可以将预处理过的数据发送到 ML 模型,以检测异常或预测受监视进程的趋势。不过,此类方法可能非常耗时,主要是因为没有预先配置的 Web 门户,进而无法为用户和操作员提供便捷界面。

典型 IoT 解决方案:矩形表示服务,箭头表示解决方案组件之间的数据流
图 2:典型 IoT 解决方案:矩形表示服务,箭头表示解决方案组件之间的数据流

第二种方法是,可以从 IoT 解决方案加速器入手,这些是能够根据需求调整的模板。IoT 解决方案加速器依据的 Azure IoT 服务与完全自定义解决方案相同。不过,加速器随附预配置服务,以及展示如何利用这些服务的示例代码。在这种情况下,仍要执行一些操作,但可以从可调整的模板入手。

第三种方法是,可使用 Azure IoT Central 快速创建后端,其中有完全准备就绪的 IoT 应用。这种方法可最大限度地减少工作量和必须掌握的技能。无需掌握任何云知识,即可创建功能齐全且可缩放的新式 IoT 解决方案。所有内容都是自动为你创建,所依据的 Azure IoT 服务与完全自定义解决方案或使用解决方案加速器时相同。因此,可以集中精力开发自己的内容,而无需兼顾 IoT 解决方案的其他部分。接下来,我将使用 Azure IoT Central 快速创建云终结点。

创建 IoT Central 应用

为了创建 IoT Central 应用,我使用了 IoT Central 门户 (apps.azureiotcentral.com)。此门户在我使用自己的 Microsoft 帐户登录后显示应用管理器。此管理器会显示所有应用,尽管最初当然不会有任何应用。因此,我单击了“新建应用”按钮,同时打开 IoT 应用创建器,这样就可以选择是使用 7 天免费试用版,还是使用付费订阅 (bit.ly/2QLvk4t),并提供应用模板、应用名称和 URL。我选择了“免费计划”和“自定义应用”模板,然后将应用名称设置为“MSDN IoT Central 应用”,这样做自动创建了以下 URL:msdn-iot-central-app.azureiotcentral.com。最后,我单击了“创建”按钮,几秒钟后应用就准备就绪了。

IoT Central 应用包含以下两个重要元素:实际视图和导航(左侧栏)。可使用侧栏切换以下各种视图:“主页”、“Device Explorer”、“设备集”、“分析”、“作业”、“应用生成器”和“管理”。在本文中,我将主要使用“Device Explorer”和“应用生成器”视图。

新创建的应用显示默认“主页”窗体。可以自定义此视图,具体方法是单击蓝色“编辑”按钮来激活编辑视图。在编辑视图中,可以添加多个组件,如链接、标签、图像、设备设置和地图等。不过,在创建仪表板前,必须有遥测数据。遥测数据最初是由模拟设备生成。若要添加此类设备,可以单击默认“主页”上的“创建设备模板”面板。最终可能会更想要使用“应用生成器”,在其中可以选择“自定义设备”选项。设备模板定义了设备,包括遥测数据、设置、事件和远程命令 (bit.ly/2CjsoYH)。

设备模板

无论如何选择创建设备模板,请先指定模板名称(我命名的是“MSDN-DeviceTemplate”),再单击“创建”按钮以预配设备。图 3 中的视图随即显示。使用此视图配置设备的各个方面:

  • 度量:可指定设备将向云提供的数据种类。还可以定义设备状态和事件。
  • 设置:可创建可用于参数化每个设备的设备专用设置。
  • 属性:可配置设备属性(例如,物理位置)。
  • 命令:可定义能够从云发送到设备以更新设备状态的命令。
  • 规则:可定义设备规则。这些规则可以监视数据,并触发相应操作。
  • 仪表板:可创建设备的仪表板。此类仪表板可用于创建设备摘要,其中可包括遥测图、图像、关键绩效指标、指明设备位置的地图、状态和事件历史记录。

设备模板
图 3:设备模板

遥测、设置和属性

接下来,我定义了设备模板。我先创建了温度和湿度这两个度量,再定义了 IsTelemetryActive 设置,此设置便于操作员远程启用或禁用遥测(图 1 中的“空闲”状态)。如果 IsTelemetryActive 设置为 false,远程设备不会流式传输任何数据。最后,设备有一个属性“Location”,其中包含设备的地理空间地址。

若要创建遥测度量,请先单击“编辑模板”按钮。如图 4**** 所示,“新建度量”按钮显示在“遥测”正上方。单击此按钮后,可以在“遥测”、“状态”和“事件”选项之间进行选择。我将单击“遥测”,这会激活另一个视图,可用于按以下方式配置温度度量(见图 4 的中间栏):

  • 显示名称:温度
  • 字段名:温度
  • 单位:摄氏度
  • 最小值:-20
  • 最大位数:2
  • 颜色:根据需求选择任意颜色

配置遥测
图 4:配置遥测

虽然大多数遥测设置都非常直观明了,但请注意,“字段名”是在设备和云之间传输数据时所使用的名称。所以使用“字段名”正确序列化和反序列化表示遥测数据的对象。

定义湿度度量值与定义温度大致相同,区别在于使用以下配置:

  • 显示名称:湿度
  • 字段名:湿度
  • 单位:%
  • 最小值:0
  • 最大值:100
  • 小数位数:2

定义度量后,便可以添加设备设置。为此,请单击“设置”选项卡,并确保当前处于编辑模式。然后,右侧便会显示可用的设置类型。选择“切换类型”,并对它进行以下配置:

  • 显示名称:遥测是否已启用
  • 字段名:IsTelemetryActive
  • 启用显示文本:True
  • 禁用显示文本:错

最后,添加设备属性:Location。为此,请打开“属性”选项卡。此时,左侧列出了可用属性类型。此列表称为“库”。单击“Location”对象。然后,对此属性进行以下配置:

  • 显示名称: 设备位置
  • 字段名:位置
  • 初始值:Microsoft, 1 Microsoft Way, Redmond, WA 98052

请注意,“初始值”字段中包含在你开始键入时就填充的自动建议列表。

配置遥测、设置和属性后,我现在可以准备设备仪表板了,它将所有信息都合并到新式图形界面中。

创建仪表板

创建仪表板与创建设置和属性类似,都是先启用模板编辑模式,这会打开包含可用 UI 组件列表的库。这些组件位于 Azure IoT Central 仪表板的左侧。在你选择某项后,它就会显示在仪表板中。然后,模板编辑器的左侧显示 UI 组件的可配置设置,如图 5 所示。继续操作并单击“折线图”。接下来,将“标题”设置为“遥测”,启用所有三个切换开关,将“时间范围”设置为“过去 30 分钟”,再单击“湿度”和“温度”旁边最右侧的图标,以便绘制这两个度量值。

配置图表
图 5:配置图表

然后,我向仪表板添加了地图,以显示设备位置。若要创建此类地图,请从库中选择“地图”UI 组件,再配置它的属性。将“标题”设置为“位置”,并从“Location 属性”列表中选择“设备位置”。

接下来,我创建了两个显示关键绩效指标 (KPI) 的磁贴。这些 KPI 是根据在过去 30 分钟内获取的传感器读数计算出的平均温度和最大湿度(见图 1 的右侧部分)。若要创建此类磁贴,请使用库中的 KPI 组件,同时对它们进行以下配置:

“平均温度”KPI:

  • 标题:平均温度
  • 时间范围:过去 30 分钟
  • 度量类型:遥测
  • 度量值:温度

“最大湿度”KPI:

  • 标题:最大湿度
  • 时间范围:过去 30 分钟
  • 度量类型:遥测
  • 度量值:湿度

最后,我创建了显示 IsTelemetryActive 设置实际值的磁贴。为此,我使用了“设置和属性”UI 组件,其中有以下两个可配置选项:“标题”(文本框)和“设置和属性”(显示可用列和选定列的双面板控件)。先使用文本框将标题设置为“设备位置”,再使用第二个控件将“设备位置”从可用列拖到选定列。在所有 UI 组件就位后,我就可以将它们放置到仪表板中,如前面的图 1 所示。

预配实际设备

前面的论述证实,借助 IoT Central,可以为 IoT 解决方案快速创建采用新式外观的仪表板。不过,到目前为止,我们仅依赖模拟设备。接下来将探讨如何将实际设备连接到 IoT Central 应用。为此,可使用 Device Explorer,按照 IoT Central 文档 (bit.ly/2Ch4gWA) 中的说明继续操作。简而言之,先单击(顶部面板中的)“新建”按钮,从下拉列表中选择“实际设备”,再提供设备的名称和唯一标识符。随后,我将这两个选项分别设置为“MSDN-DeviceTemplate – msdn-device-id1”和“msdn-device-id-1”(见图 6****)。

显示模拟设备和实际设备的 Device Explorer
图 6:显示模拟设备和实际设备的 Device Explorer

如果现在单击 Explorer 中的实际设备,将会看到它的遥测、设置、属性和仪表板与模拟设备完全相同。不过,由于设备未连接,尚无任何度量。此外,实际设备模板的右上角还显示其他超链接:“阻止”和“连接”。使用“阻止”,可以阻止设备,这样 IoT Central 应用就不会接受来自远程设备的任何请求。“连接”显示将设备连接到云所必需的范围 ID 和凭据,如图 7 所示。

设备连接
图 7:设备连接

IoT Central 支持使用两种方法来授权设备。可以使用共享访问签名 (SAS),也可以使用 X.509 证书。bit.ly/2ClDv3z 上详细介绍了这两种方法。在我要开发的客户端应用中,我将使用 SAS 方法。因此,为了能够继续进一步操作,我将需要记下图 7 中“设备连接”屏幕上显示的范围 ID、设备 ID 以及主密钥或辅助密钥。我将使用 dps_cstr 命令行工具生成连接字符串。(可以从 bit.ly/2Cj3Ejv 下载此工具的 Windows 版本。) 然后,若要获取实际连接字符串,请打开命令行,并键入以下命令:

dps_cstr <scope_id> <device_id> <SAS Key>

对于图 7**** 中显示的参数,dps_cstr 工具生成了以下连接字符串:

HostName=saas-iothub-28681fd2-94c7-4938-bf7e-7ae3e94a407c.azure-devices.net;DeviceId=msdn-device-id1;SharedAccessKey=nQqFzf6TvnQA+zFI4MVaSSBeZgsYSY0P7KXrl6z6oDE=

实现客户端应用

为了创建客户端应用,我开发了基于控制台应用模板的 C# .NET Core 应用。无需在 UWP Windows 10 IoT Core 应用中进行任何更改,即可使用大多数代码。不过,为了最大限度地减少实现 IoT Central 客户端应用所需的工作负载,我决定使用 .NET Core。

我使用了 Visual Studio 2017 Community Edition(从“新建项目”对话框入手)。我选择了“控制台应用”项目模板 (.NET Core 2.1),将应用名称设置为“IoTCentralClient”,并安装了 Microsoft.Azure.Devices.Client NuGet 包以便快速连接到 IoT 中心。

然后,我继续执行实际实现(从图 8 中显示的 DeviceClientHelper 类入手)。

图 8:DeviceClientHelper 类

public static class DeviceClientHelper
{
  private static readonly string connectionString
    = "<your_connection_string>";
  private static DeviceClient deviceClient;
  public static DeviceClient Init()
  {
    if (deviceClient == null)
    {
      deviceClient = DeviceClient.
        CreateFromConnectionString(connectionString);
    }
    return deviceClient;
  }
}

DeviceClientHelper 类使用 Microsoft.Azure.Devices.Client.DeviceClient 将连接与 IoT 中心相关联。为此,DeviceClient 使用 dps_cstr 工具生成的连接字符串。连接字符串作为参数传递给 DeviceClient 类的 CreateFromConnectionString 静态方法。

遥测

在准备连接后,我创建了 Data 类(将发送到云的遥测的抽象表示形式),如图 9**** 所示。

图 9:Data 类

public class Data
{
  public double Temperature { get; set; }
  public double Humidity { get; set; }
  public Message ToMessage()
  {
    var dataJson = JsonConvert.SerializeObject(this);
    return new Message(Encoding.ASCII.GetBytes(dataJson));
  }
  public override string ToString()
  {
    return $"Temperature: {Temperature,6:F2}, Humidity: {Humidity,6:F2}";
  }
}

请注意,Data 类的公共属性是用于云终结点,以正确反序列化从设备发送的对象。因此,属性名必须与设备模板中使用的字段名相对应。否则,云将无法正确分析遥测数据。

接下来,为了反序列化遥测对象,我编写了 ToMessage 方法,如图 9 所示。在第一步中,ToMessage 获取表示 Data 对象的 JSON 格式字符串。这是通过 JsonConvert.SerializeObject 完成的。在第二步中,JSON 字符串通过 Encoding.ASCII.GetBytes 转换为字节数组。此操作的结果用于实例化 Microsoft.Azure.Devices.Client.Message 类。然后,可使用 DeviceClient 类的 SendEventAsync 方法,将此类的实例发送到 IoT 中心。

接下来,我编写了用于生成和发送遥测数据的 Generator 类。此类通过伪随机生成温度值和湿度值,模拟实际传感器读数(请参阅随附源代码中的 Data/Generator.cs)。为此,Generator 使用 System.Random 类,合成给定范围内的传感器读数:

private Random randomNumberGenerator = new Random();
private double GetRandomValue(MeasurementRange measurementRange)
{
  var randomValueRescaled = randomNumberGenerator.NextDouble()
    * measurementRange.ValueRange();
  return measurementRange.Min + randomValueRescaled;
}

度量范围是由 MeasurementRange 类的实例表示:

public class MeasurementRange
{
  public double Min { get; set; }
  public double Max { get; set; }
  public double ValueRange()
  {
    return Max - Min;
  }
}

Generator 类有上述类型 (MeasurementRange) 的两个字段。它们分别对应于温度和湿度度量值:

private readonly MeasurementRange temperatureRange
  = new MeasurementRange() { Min = -20, Max = 60 };
private readonly MeasurementRange humidityRange
  = new MeasurementRange() { Min = 0, Max = 100 };

请注意,这些范围与之前在设备模板中指定的范围相同。

Generator 类使用 DeviceClient 和 CancellationToken 的实例。第一个用于发送遥测,而第二个则会中断无限遥测循环。DeviceClient 和 CancellationToken 的实际实例是通过 Generator 类构造函数进行传递:

private DeviceClient deviceClient;
private CancellationToken cancellationToken;
public Generator(DeviceClient deviceClient,
  CancellationToken cancellationToken)
{
  Check.IsNull(deviceClient);
  Check.IsNull(cancellationToken);
  this.deviceClient = deviceClient;
  this.cancellationToken = cancellationToken;
}

Check 类是静态帮助程序类,用于验证参数是否为 null(请参阅随附代码 Helpers/Check.cs)。

遥测数据是在 Generator 类的实例方法 TelemetryAction 中实现的 while 循环内生成并发送到云,如图 10**** 所示。

图 10:TelemetryAction 方法

public bool IsTelemetryActive { get; set; } = true;
private void TelemetryAction()
{
  while (!cancellationToken.IsCancellationRequested)
  {
    var telemetryData = new Data()
    {
      Temperature = GetRandomValue(temperatureRange),
      Humidity = GetRandomValue(humidityRange)
    };
    if (IsTelemetryActive)
    {
      deviceClient.SendEventAsync(telemetryData.ToMessage());
      Console.WriteLine($"Sending telemetry: {telemetryData}");
    }
    else
    {
      Console.WriteLine("Idle");
    }
      Task.Delay(delayTime).Wait();
  }
}

请注意,仅当 IsTelemetryActive 属性为 true 时,遥测才会发送到云。可使用 IoT Central 应用中的“设置”选项卡(图 4),对云终结点更改此属性。

TelemetryAction 是通过基于任务的异步模式在后台执行:

public Task Start()
{
  telemetryTask = new Task(TelemetryAction);
  telemetryTask.Start();
  return telemetryTask;
}

合并组合

在准备好 DeviceClientHelper 和 Generator 类后,我在 Program 类中合并使用了它们(请参阅随附代码中的 Program.cs)。我从实现静态 Program.Main 方法入手,如图 11**** 所示。

图 11:Program.Main 方法

private static CancellationTokenSource cancellationTokenSource
   = new CancellationTokenSource();
static void Main(string[] args)
{
  // Configure cancel key press handler (to stop the app)
  Console.CancelKeyPress += new ConsoleCancelEventHandler(
    CancelKeyPressHandler);
  // Connect to the cloud
  var deviceClient = DeviceClientHelper.Init();
  // Telemetry generator produces random temperature
  // and humidity, and then sends them both to the cloud
  var telemetryGenerator = new Generator(
    deviceClient, cancellationTokenSource.Token);
  // Associate handler to update device properties according to cloud requests
  deviceClient.SetDesiredPropertyUpdateCallbackAsync(
    PropertyUpdateCallback, telemetryGenerator).Wait();
  // Start telemetry
  telemetryGenerator.Start().Wait();
}

Program.Main 先将事件处理程序连接到 Console.CancelKeyPress 事件,以便(使用取消令牌)停止遥测循环并关闭应用:

private static void CancelKeyPressHandler(object sender,
  ConsoleCancelEventArgs e)
{
  if (e.SpecialKey == ConsoleSpecialKey.ControlC)
  {
    cancellationTokenSource.Cancel();
    Environment.Exit(0);
  }
}

接下来,Main 方法使用 DeviceClientHelper 的静态 Init 方法连接到 IoT 中心。Init 返回之后传递给 Generator 类构造函数的 DeviceClient 类实例。

DeviceClient 类实例的 SetDesiredPropertyUpdateCallbackAsync 方法现在用于设置,在操作员更改云终结点处设备设置时调用的回调。向此回调 PropertyUpdateCallback(见图 12)提供 Microsoft.Azure.Devices.Shared.TwinCollection 类实例。此对象表示设备设置集合。尤其是,类索引器可用于读取选定设置的值。可使用在云终结点处配置的字段名来标识特定设置。在图 12**** 中,我展示了如何读取 IsTelemetryActive 设置值,并用它来更新 Generator 类实例的相应属性。

图 12:PropertyUpdateCallback

private static readonly string telemetryActivePropertyName =
  "IsTelemetryActive";
private static readonly string propertyValue = "value";
private static Task PropertyUpdateCallback(
  TwinCollection desiredProperties, object userContext)
{
  if (desiredProperties.Contains(telemetryActivePropertyName))
  {
    var telemetryGenerator = userContext as Generator;
    telemetryGenerator.IsTelemetryActive =
      desiredProperties[telemetryActivePropertyName][propertyValue];
  }
  return Task.CompletedTask;
}

最后,Main 方法通过调用 Generator 类实例的 Start 方法来启动遥测。

必须执行客户端应用,才能测试它。如果连接字符串有效,应用便会连接到 IoT 中心,并开始流式传输遥测数据。合成的每个度量都会在控制台中打印输出,可以在云仪表板中看到这些度量值(回头参阅图 1)。还可以远程更改 IsTelemetryActive,以暂时禁用遥测。为此,请打开 Azure IoT Central 应用的“设置”选项卡并切换开关,再单击“更新”按钮。在这种情况下,客户端应用会打印输出“空闲”字符串,而不是实际遥测数据。

总结

在本文中,我介绍了如何使用 Azure IoT Central 快速创建 IoT 解决方案的功能齐全且采用新式外观的自定义 Web 应用。我还展示了如何创建设备模板,并用它来呈现遥测数据、设备位置和 KPI。然后,我开发了 C# 客户端应用,并连接它以将遥测数据流式传输到云中。最后,我介绍了如何响应通过 IoT Central 应用请求执行的设备设置更改。无需之前掌握任何云或 Web 编程知识,即可根据本文中的所有信息,快速开发 IoT 解决方案的新式 Web 仪表板。


Dawid Borycki**** 是软件工程师、生物医学研究员、作家和会议演讲者。他喜欢学习有关软件试验和原型设计的新技术。Borycki 是 Microsoft Press 出版的以下两本书籍的作者:《混合现实编程》(2018 年)和《物联网编程》(2017 年)。**

衷心感谢以下 Microsoft 技术专家对本文的审阅:Bruno Sonnino
Bruno Sonnino 是一名 Windows 开发 Microsoft MVP。他从事开发人员、顾问、作家和培训师工作已有 20 多年,并热衷于软件开发。


在 MSDN 杂志论坛讨论这篇文章