教程:从后端服务配置设备Tutorial: Configure your devices from a back-end service

在从设备接收遥测数据的同时,你可能需要从后端服务配置设备。As well as receiving telemetry from your devices, you may need to configure your devices from your back-end service. 将所需的配置发送到设备时,还可能需要从这些设备接收状态与符合性更新。When you send a desired configuration to your devices, you may also want to receive status and compliance updates from those devices. 例如,可以设置设备的目标工作温度范围,或者从设备收集固件版本信息。For example, you might set a target operational temperature range for a device or collect firmware version information from your devices.

若要在设备与 IoT 中心之间同步状态信息,请使用设备孪生。 To synchronize state information between a device and an IoT hub, you use device twins. 设备孪生是与特定设备关联的 JSON 文档,由云中的 IoT 中心存储,可在 IoT 中心对其进行查询A device twin is a JSON document, associated with a specific device, and stored by IoT Hub in the cloud where you can query them. 设备孪生包含所需属性、报告属性和标记。 A device twin contains desired properties, reported properties, and tags. 所需属性由后端应用程序设置,由设备读取。A desired property is set by a back-end application and read by a device. 报告属性由设备设置,由后端应用程序读取。A reported property is set by a device and read by a back-end application. 标记由后端应用程序设置,永远不会发送到设备。A tag is set by a back-end application and is never sent to a device. 使用标记可以组织设备。You use tags to organize your devices. 本教程介绍如何使用所需属性和报告属性来同步状态信息:This tutorial shows you how to use desired and reported properties to synchronize state information:

孪生摘要

将在本教程中执行以下任务:In this tutorial, you perform the following tasks:

  • 创建 IoT 中心并将测试设备添加到标识注册表。Create an IoT hub and add a test device to the identity registry.
  • 使用所需属性将状态信息发送到模拟设备。Use desired properties to send state information to your simulated device.
  • 使用报告属性从模拟设备接收状态信息。Use reported properties to receive state information from your simulated device.

如果没有 Azure 订阅,请在开始前创建一个试用帐户If you don’t have an Azure subscription, create a trial account before you begin.

先决条件Prerequisites

本快速入门中运行的两个示例应用程序是使用 Node.js 编写的。The two sample applications you run in this quickstart are written using Node.js. 开发计算机上需要有 Node.js v10.x.x 或更高版本。You need Node.js v10.x.x or later on your development machine.

可从 nodejs.org 为下载适用于多个平台的 Node.js。You can download Node.js for multiple platforms from nodejs.org.

可以使用以下命令验证开发计算机上 Node.js 当前的版本:You can verify the current version of Node.js on your development machine using the following command:

node --version

https://github.com/Azure-Samples/azure-iot-samples-node/archive/master.zip 下载示例 Node.js 项目并提取 ZIP 存档。Download the sample Node.js project from https://github.com/Azure-Samples/azure-iot-samples-node/archive/master.zip and extract the ZIP archive.

设置 Azure 资源Set up Azure resources

若要完成本教程,你的 Azure 订阅必须包含一个 IoT 中心,其中的某个设备已添加到设备标识注册表。To complete this tutorial, your Azure subscription must contain an IoT hub with a device added to the device identity registry. 在本教程中运行的模拟设备可以通过设备标识注册表中的条目连接到中心。The entry in the device identity registry enables the simulated device you run in this tutorial to connect to your hub.

如果尚未在订阅中设置 IoT 中心,可使用以下 CLI 脚本设置一个。If you don't already have an IoT hub set up in your subscription, you can set one up with the following CLI script. 此脚本使用 tutorial-iot-hub 作为 IoT 中心的名称。运行此脚本时,应将此名称替换为自己的唯一名称。This script uses the name tutorial-iot-hub for the IoT hub, you should replace this name with your own unique name when you run it. 此脚本在“美国中部”区域创建资源组和中心。可以更改为更靠近自己的区域。 The script creates the resource group and hub in the Central US region, which you can change to a region closer to you. 此脚本将检索 IoT 中心服务连接字符串,在后端示例中,我们将使用该连接字符串连接到 IoT 中心:The script retrieves your IoT hub service connection string, which you use in the back-end sample to connect to your IoT hub:

hubname=tutorial-iot-hub
location=chinaeast

# Install the IoT extension if it's not already installed:
az extension add --name azure-cli-iot-ext

# Create a resource group:
az group create --name tutorial-iot-hub-rg --location $location

# Create your free-tier IoT Hub. You can only have one free IoT Hub per subscription:
az iot hub create --name $hubname --location $location --resource-group tutorial-iot-hub-rg --sku F1

# Make a note of the service connection string, you need it later:
az iot hub show-connection-string --name $hubname -o table

本教程使用名为 MyTwinDevice 的模拟设备。This tutorial uses a simulated device called MyTwinDevice. 以下脚本将此设备添加到标识注册表,并检索其连接字符串:The following script adds this device to your identity registry and retrieves its connection string:

# Set the name of your IoT hub:
hubname=tutorial-iot-hub

# Create the device in the identity registry:
az iot hub device-identity create --device-id MyTwinDevice --hub-name $hubname --resource-group tutorial-iot-hub-rg

# Retrieve the device connection string, you need this later:
az iot hub device-identity show-connection-string --device-id MyTwinDevice --hub-name $hubname --resource-group tutorial-iot-hub-rg -o table

发送状态信息Send state information

使用所需属性将状态信息从后端应用程序发送到设备。You use desired properties to send state information from a back-end application to a device. 本部分介绍以下操作:In this section, you see how to:

  • 在设备上接收并处理所需属性。Receive and process desired properties on a device.
  • 从后端应用程序发送所需属性。Send desired properties from a back-end application.

若要查看接收所需属性的模拟设备示例代码,请导航到下载的示例 Node.js 项目中的 iot-hub/Tutorials/DeviceTwins 文件夹。To view the simulated device sample code that receives desired properties, navigate to the iot-hub/Tutorials/DeviceTwins folder in the sample Node.js project you downloaded. 然后在文本编辑器中打开 SimulatedDevice.js 文件。Then open the SimulatedDevice.js file in a text editor.

以下部分描述了在模拟设备上运行的、对发送自后端应用程序的所需属性更改做出响应的代码:The following sections describe the code that runs on the simulated device that responds to desired property changes sent from the back end application:

检索设备孪生对象Retrieve the device twin object

以下代码使用设备连接字符串连接到 IoT 中心:The following code connects to your IoT hub using a device connection string:

// Get the device connection string from a command line argument
var connectionString = process.argv[2];

以下代码从客户端对象中获取孪生:The following code gets a twin from the client object:

// Get the device twin
client.getTwin(function(err, twin) {
  if (err) {
    console.error(chalk.red('Could not get device twin'));
  } else {
    console.log(chalk.green('Device twin created'));

所需属性示例Sample desired properties

可以使用对应用程序有利的任何方式来构建所需属性。You can structure your desired properties in any way that's convenient to your application. 本示例使用一个名为 fanOn 的顶级属性,并将剩余的属性分组到单独的组件中。This example uses one top-level property called fanOn and groups the remaining properties into separate components. 以下 JSON 片段显示了本教程所用的所需属性的结构:The following JSON snippet shows the structure of the desired properties this tutorial uses:

{
  "fanOn": "true",
  "components": {
    "system": {
      "id": "17",
      "units": "farenheit",
      "firmwareVersion": "9.75"
    },
    "wifi" : { 
      "channel" : "6",
      "ssid": "my_network"
    },
    "climate" : {
      "minTemperature": "68",
      "maxTemperature": "76"
    }
  }
}

创建处理程序Create handlers

可针对所需属性更新创建处理程序,用于响应 JSON 层次结构中的不同级别发生的更新。You can create handlers for desired property updates that respond to updates at different levels in the JSON hierarchy. 例如,此处理程序可以监视从后端应用程序发送到设备的所有所需属性更改。For example, this handler sees all desired property changes sent to the device from a back-end application. delta 变量包含从解决方案后端发送的所需属性:The delta variable contains the desired properties sent from the solution back end:

// Handle all desired property updates
twin.on('properties.desired', function(delta) {
    console.log(chalk.yellow('\nNew desired properties received in patch:'));

以下处理程序仅响应对 fanOn 所需属性所做的更改:The following handler only reacts to changes made to the fanOn desired property:

// Handle changes to the fanOn desired property
twin.on('properties.desired.fanOn', function(fanOn) {
    console.log(chalk.green('\nSetting fan state to ' + fanOn));

    // Update the reported property after processing the desired property
    reportedPropertiesPatch.fanOn = fanOn ? fanOn : '{unknown}';
});

多个属性的处理程序Handlers for multiple properties

在前面显示的所需属性 JSON 示例中,components 下的 climate 节点包含两个属性:minTemperaturemaxTemperatureIn the example desired properties JSON shown previously, the climate node under components contains two properties, minTemperature and maxTemperature.

设备的本地 twin 对象存储一组完整的所需属性和报告属性。A device's local twin object stores a complete set of desired and reported properties. 从后端发送的 delta 可能只会更新所需属性的某个子集。The delta sent from the back end might update just a subset of desired properties. 在以下代码片段中,如果模拟设备只是收到了对某个 minTemperaturemaxTemperature 的更新,则它会使用另一个值的本地孪生中的值来配置设备:In the following code snippet, if the simulated device receives an update to just one of minTemperature and maxTemperature, it uses the value in the local twin for the other value to configure the device:

// Handle desired properties updates to the climate component
twin.on('properties.desired.components.climate', function(delta) {
    if (delta.minTemperature || delta.maxTemperature) {
      console.log(chalk.green('\nUpdating desired tempertures in climate component:'));
      console.log('Configuring minimum temperature: ' + twin.properties.desired.components.climate.minTemperature);
      console.log('Configuring maximum temperture: ' + twin.properties.desired.components.climate.maxTemperature);

      // Update the reported properties and send them to the hub
      reportedPropertiesPatch.minTemperature = twin.properties.desired.components.climate.minTemperature;
      reportedPropertiesPatch.maxTemperature = twin.properties.desired.components.climate.maxTemperature;
      sendReportedProperties();
    }
});

本地 twin 对象存储一组完整的所需属性和报告属性。The local twin object stores a complete set of desired and reported properties. 从后端发送的 delta 可能只会更新所需属性的某个子集。The delta sent from the back end might update just a subset of desired properties.

处理插入、更新和删除操作Handle insert, update, and delete operations

从后端发送的所需属性并不会指示当前正在对特定的所需属性执行哪个操作。The desired properties sent from the back end don't indicate what operation is being performed on a particular desired property. 代码需要根据当前存储在本地的所需属性集,以及中心发送的更改来推断操作。Your code needs to infer the operation from the current set of desired properties stored locally and the changes sent from the hub.

以下片段演示模拟设备如何处理针对所需属性中的组件列表执行的插入、更新和删除操作。The following snippet shows how the simulated device handles insert, update, and delete operations on the list of components in the desired properties. 可以查看如何使用 null 值来指示应删除某个组件:You can see how to use null values to indicate that a component should be deleted:

// Keep track of all the components the device knows about
var componentList = {};

// Use this componentList list and compare it to the delta to infer
// if anything was added, deleted, or updated.
twin.on('properties.desired.components', function(delta) {
  if (delta === null) {
    componentList = {};
  }
  else {
    Object.keys(delta).forEach(function(key) {

      if (delta[key] === null && componentList[key]) {
        // The delta contains a null value, and the
        // device has a record of this component.
        // Must be a delete operation.
        console.log(chalk.green('\nDeleting component ' + key));
        delete componentList[key];

      } else if (delta[key]) {
        if (componentList[key]) {
          // The delta contains a component, and the
          // device has a record of it.
          // Must be an update operation.
          console.log(chalk.green('\nUpdating component ' + key + ':'));
          console.log(JSON.stringify(delta[key]));
          // Store the complete object instead of just the delta
          componentList[key] = twin.properties.desired.components[key];

        } else {
          // The delta contains a component, and the
          // device has no record of it.
          // Must be an add operation.
          console.log(chalk.green('\nAdding component ' + key + ':'));
          console.log(JSON.stringify(delta[key]));
          // Store the complete object instead of just the delta
          componentList[key] = twin.properties.desired.components[key];
        }
      }
    });
  }
});

从后端向设备发送所需属性Send desired properties to a device from the back end

前面已介绍设备如何实现处理程序来接收所需属性的更新。You've seen how a device implements handlers for receiving desired property updates. 本部分介绍如何将所需属性更改从后端应用程序发送到设备。This section shows you how to send desired property changes to a device from a back-end application.

若要查看接收所需属性的模拟设备示例代码,请导航到下载的示例 Node.js 项目中的 iot-hub/Tutorials/DeviceTwins 文件夹。To view the simulated device sample code that receives desired properties, navigate to the iot-hub/Tutorials/DeviceTwins folder in the sample Node.js project you downloaded. 然后在文本编辑器中打开 ServiceClient.js 文件。Then open the ServiceClient.js file in a text editor.

以下代码片段演示如何连接到设备标识注册表并访问特定设备的孪生:The following code snippet shows how to connect to the device identity registry and access the twin for a specific device:

// Create a device identity registry object
var registry = Registry.fromConnectionString(connectionString);

// Get the device twin and send desired property update patches at intervals.
// Print the reported properties after some of the desired property updates.
registry.getTwin(deviceId, async (err, twin) => {
  if (err) {
    console.error(err.message);
  } else {
    console.log('Got device twin');

以下片段演示后端应用程序发送到设备的不同所需属性补丁: The following snippet shows different desired property patches the back end application sends to the device:

// Turn the fan on
var twinPatchFanOn = {
  properties: {
    desired: {
      patchId: "Switch fan on",
      fanOn: "false",
    }
  }
};

// Set the maximum temperature for the climate component
var twinPatchSetMaxTemperature = {
  properties: {
    desired: {
      patchId: "Set maximum temperature",
      components: {
        climate: {
          maxTemperature: "92"
        }
      }
    }
  }
};

// Add a new component
var twinPatchAddWifiComponent = {
  properties: {
    desired: {
      patchId: "Add WiFi component",
      components: {
        wifi: { 
          channel: "6",
          ssid: "my_network"
        }
      }
    }
  }
};

// Update the WiFi component
var twinPatchUpdateWifiComponent = {
  properties: {
    desired: {
      patchId: "Update WiFi component",
      components: {
        wifi: { 
          channel: "13",
          ssid: "my_other_network"
        }
      }
    }
  }
};

// Delete the WiFi component
var twinPatchDeleteWifiComponent = {
  properties: {
    desired: {
      patchId: "Delete WiFi component",
      components: {
        wifi: null
      }
    }
  }
};

以下片段演示后端应用程序如何将所需属性更新发送到设备:The following snippet shows how the back-end application sends a desired property update to a device:

// Send a desired property update patch
async function sendDesiredProperties(twin, patch) {
  twin.update(patch, (err, twin) => {
    if (err) {
      console.error(err.message);
    } else {
      console.log(chalk.green(`\nSent ${twin.properties.desired.patchId} patch:`));
      console.log(JSON.stringify(patch, null, 2));
    }
  });
}

运行应用程序Run the applications

在本部分,我们将运行两个示例应用程序,以观察后端应用程序如何将所需属性更新发送到模拟设备应用程序。In this section, you run two sample applications to observe as a back-end application sends desired property updates to a simulated device application.

若要运行模拟设备和后端应用程序,需要使用设备和服务连接字符串。To run the simulated device and back-end applications, you need the device and service connection strings. 在本教程的开头部分创建资源时,我们已记下这些连接字符串。You made a note of the connection strings when you created the resources at the start of this tutorial.

若要运行模拟设备应用程序,请打开 shell 或命令提示符窗口,并导航到下载的 Node.js 项目中的 iot-hub/Tutorials/DeviceTwins 文件夹。To run the simulated device application, open a shell or command prompt window and navigate to the iot-hub/Tutorials/DeviceTwins folder in the Node.js project you downloaded. 然后运行以下命令:Then run the following commands:

npm install
node SimulatedDevice.js "{your device connection string}"

若要运行后端应用程序,请打开另一个 shell 或命令提示符窗口。To run the back-end application, open another shell or command prompt window. 导航到下载的 Node.js 项目中的 iot-hub/Tutorials/DeviceTwins 文件夹。Then navigate to the iot-hub/Tutorials/DeviceTwins folder in the Node.js project you downloaded. 然后运行以下命令:Then run the following commands:

npm install
node ServiceClient.js "{your service connection string}"

以下屏幕截图显示模拟设备应用程序的输出,并突出显示它如何处理对 maxTemperature 所需属性做出的更新。The following screenshot shows the output from the simulated device application and highlights how it handles an update to the maxTemperature desired property. 可以看到顶级处理程序和气候组件处理程序的运行方式:You can see how both the top-level handler and the climate component handlers run:

模拟设备

以下屏幕截图显示后端应用程序的输出,并突出显示它如何发送对 maxTemperature 所需属性做出的更新:The following screenshot shows the output from the back-end application and highlights how it sends an update to the maxTemperature desired property:

后端应用程序

接收状态信息Receive state information

后端应用程序从设备接收报告属性形式的状态信息。Your back-end application receives state information from a device as reported properties. 设备会设置报告属性,并将其发送到中心。A device sets the reported properties, and sends them to your hub. 后端应用程序可以从中心内存储的设备孪生读取报告属性的当前值。A back-end application can read the current values of the reported properties from the device twin stored in your hub.

从设备发送报告属性Send reported properties from a device

可以补丁的形式发送对报告属性值所做的更新。You can send updates to reported property values as a patch. 以下片段演示了模拟设备发送的补丁的模板。The following snippet shows a template for the patch the simulated device sends. 模拟设备先更新补丁中的字段,然后将补丁发送到中心:The simulated device updates the fields in the patch before sending it to the hub:

// Create a patch to send to the hub
var reportedPropertiesPatch = {
  firmwareVersion:'1.2.1',
  lastPatchReceivedId: '',
  fanOn:'',
  minTemperature:'',
  maxTemperature:''
};

模拟设备使用以下函数将包含报告属性的补丁发送到中心:The simulated device uses the following function to send the patch that contains the reported properties to the hub:

// Send the reported properties patch to the hub
function sendReportedProperties() {
  twin.properties.reported.update(reportedPropertiesPatch, function(err) {
    if (err) throw err;
    console.log(chalk.blue('\nTwin state reported'));
    console.log(JSON.stringify(reportedPropertiesPatch, null, 2));
  });
}

处理报告属性Process reported properties

后端应用程序通过设备孪生访问设备的当前报告属性值。A back-end application accesses the current reported property values for a device through the device twin. 以下片段演示后端应用程序如何读取模拟设备的报告属性值:The following snippet shows you how the back-end application reads the reported property values for the simulated device:

// Display the reported properties from the device
function printReportedProperties(twin) {
  console.log("Last received patch: " + twin.properties.reported.lastPatchReceivedId);
  console.log("Firmware version: " + twin.properties.reported.firmwareVersion);
  console.log("Fan status: " + twin.properties.reported.fanOn);
  console.log("Min temperature set: " + twin.properties.reported.minTemperature);
  console.log("Max temperature set: " + twin.properties.reported.maxTemperature);
}

运行应用程序Run the applications

在本部分,我们将运行两个示例应用程序,以观察模拟设备应用程序如何将报告属性更新发送到后端应用程序。In this section, you run two sample applications to observe as a simulated device application sends reported property updates to a back-end application.

运行的两个示例应用程序,与前面在查看如何将所需属性发送到设备时所运行的应用程序相同。You run the same two sample applications that you ran to see how desired properties are sent to a device.

若要运行模拟设备和后端应用程序,需要使用设备和服务连接字符串。To run the simulated device and back-end applications, you need the device and service connection strings. 在本教程的开头部分创建资源时,我们已记下这些连接字符串。You made a note of the connection strings when you created the resources at the start of this tutorial.

若要运行模拟设备应用程序,请打开 shell 或命令提示符窗口,并导航到下载的 Node.js 项目中的 iot-hub/Tutorials/DeviceTwins 文件夹。To run the simulated device application, open a shell or command prompt window and navigate to the iot-hub/Tutorials/DeviceTwins folder in the Node.js project you downloaded. 然后运行以下命令:Then run the following commands:

npm install
node SimulatedDevice.js "{your device connection string}"

若要运行后端应用程序,请打开另一个 shell 或命令提示符窗口。To run the back-end application, open another shell or command prompt window. 导航到下载的 Node.js 项目中的 iot-hub/Tutorials/DeviceTwins 文件夹。Then navigate to the iot-hub/Tutorials/DeviceTwins folder in the Node.js project you downloaded. 然后运行以下命令:Then run the following commands:

npm install
node ServiceClient.js "{your service connection string}"

以下屏幕截图显示模拟设备应用程序的输出,并突出显示它如何将报告属性更新发送到中心:The following screenshot shows the output from the simulated device application and highlights how it sends a reported property update to your hub:

模拟设备

以下屏幕截图显示后端应用程序的输出,并突出显示它如何从设备接收和处理报告属性更新:The following screenshot shows the output from the back-end application and highlights how it receives and processes a reported property update from a device:

后端应用程序

清理资源Clean up resources

如果你打算完成下一篇教程,请保留资源组和 IoT 中心,以便到时重复使用。If you plan to complete the next tutorial, leave the resource group and IoT hub and reuse them later.

如果不再需要 IoT 中心,请在门户中删除该中心与资源组。If you don't need the IoT hub any longer, delete it and the resource group in the portal. 为此,请选择包含 IoT 中心的 tutorial-iot-hub-rg 资源组,然后单击“删除” 。To do so, select the tutorial-iot-hub-rg resource group that contains your IoT hub and click Delete.

或者使用 CLI:Alternatively, use the CLI:

# Delete your resource group and its contents
az group delete --name tutorial-iot-hub-rg

后续步骤Next steps

在本教程中,你已学习了如何在设备与 IoT 中心之间同步状态信息。In this tutorial, you learned how to synchronize state information between your devices and your IoT hub. 请继续学习下一篇教程,了解如何使用设备孪生实现固件更新过程。Advance to the next tutorial to learn how to use device twins to implement a firmware update process.