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