共用方式為


教學課程:從後端服務設定裝置

在裝置生命週期中,您可能需要從後端服務設定 IoT 裝置。 當您將所需的組態傳送至裝置時,您也想要從這些裝置接收狀態和合規性更新。 例如,您可以設定裝置的目標工作溫度範圍,或從裝置收集韌體版本資訊。

若要在裝置與 IoT 中樞之間同步狀態資訊,您可以使用 裝置雙胞胎裝置對應項是 JSON 檔,與特定裝置相關聯,並由 IoT 中樞儲存在雲端中,您可以在其中查詢它們。 裝置對應項包含所需屬性報告屬性標記

  • 所需的屬性由後端應用程式設定,並由裝置讀取。
  • 報告的 屬性 是由裝置設定,並由後端應用程式讀取。
  • 標籤是由後端應用程式設定,絕不會傳送至裝置。 您可以使用標籤來組織您的裝置。

本教學課程說明如何使用所需和報告的內容來同步處理狀態資訊。

裝置上和雲端中的裝置對應項圖表。

在本教學課程中,您會執行下列工作:

  • 建立 IoT 中樞,並將測試裝置新增至身分識別登錄。
  • 使用所需的屬性將狀態資訊傳送至模擬裝置。
  • 使用報告屬性從您的模擬裝置接收狀態資訊。

如尚未擁有 Azure 訂用帳戶,請在開始之前先建立免費帳戶

先決條件

  • 本教學課程會使用 Azure CLI 來建立雲端資源。 如果您已經有 IoT 中樞並已註冊裝置,您可以跳過這些步驟。 有兩種方法可以執行CLI命令:

  • 您在本教學課程中執行的兩個範例應用程式是使用 Node.js撰寫。 您需要在開發電腦上 Node.js v10.x.x 或更新版本。

    • 您可以從 nodejs.org 下載適用於多個平台的 Node.js。

    • 您可以使用下列命令,在開發電腦上驗證目前版本的 Node.js:

      node --version
      
  • 複製或下載 Azure IoT Node.js 範例 中的範例 Node.js 專案。

  • 請確定您的防火牆已開啟連接埠 8883。 本教學課程中的裝置範例使用 MQTT 通訊協定,透過連接埠 8883 進行通訊。 此連接埠在某些企業和教育網路環境中可能會被封鎖。 如需解決此問題的詳細資訊和方法,請參閱連線到 IoT 中樞 (MQTT)。

設定 Azure 資源

若要完成本教學課程,您的 Azure 訂用帳戶必須包含 IoT 中樞,並將裝置新增至裝置身分識別登錄。 裝置身分識別登錄中的條目可以讓您在本指南中運行的模擬裝置連接到您的中樞。

如果您尚未在訂用帳戶中設定 IoT 中樞,您可以使用下列 CLI 腳本來設定。 此腳本會使用名稱 tutorial-iot-hub ,並附加 IoT 中樞名稱的隨機數。 您可以在執行時,將此名稱替換為您自己的全域唯一名稱。 腳本會在美國 中部 區域中建立資源群組和中樞,您可以將其變更為離您較近的區域。 腳本會擷取您的 IoT 中樞服務連接字串,您可以在後端範例中使用該字串來連線到 IoT 中樞:

let "randomIdentifier=$RANDOM*$RANDOM"  
hubname="tutorial-iot-hub-$randomIdentifier"
location=centralus

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

# 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.
# Change the sku to S1 to create a standard-tier hub if necessary.
az iot hub create --name $hubname --location $location --resource-group tutorial-iot-hub-rg --partition-count 2 --sku F1

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

本教學課程使用名為 MyTwinDevice 的模擬裝置。 下列指令碼會將此裝置新增至您的身分登錄,並擷取其連接字串:

# 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 connection-string show --device-id MyTwinDevice --hub-name $hubname --resource-group tutorial-iot-hub-rg -o table

將狀態資訊傳送至裝置

您可以使用所需的屬性,將狀態資訊從後端應用程式傳送至裝置。 在本節中,您將瞭解如何:

  • 設定裝置以接收和處理所需的屬性。
  • 將所需的屬性從後端應用程式傳送至裝置。

所需屬性範例

您可以以任何方便應用程式的方式建構所需的屬性。 此範例使用一個稱為 fanOn 的最上層屬性,並將其餘屬性分組到單獨 的元件中。 下列 JSON 程式碼片段顯示本教學課程所使用所需屬性的結構。 JSON 位於 desired.json 檔案中。

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

在裝置應用程式中接收所需的屬性

若要檢視接收所需屬性的模擬裝置範例程式碼,請流覽至您下載的範例 Node.js 專案中的 iot-hub/Tutorials/DeviceTwins 資料夾。 然後在文本編輯器中打開 SimulatedDevice.js 文件。

下列各節說明在模擬裝置上執行的程式碼,以回應從後端應用程式傳送的所需屬性變更。

擷取裝置對應項物件

當您向 IoT 中樞註冊裝置時,您會取得裝置連接字串作為輸出。 裝置會使用裝置連接字串來驗證其在雲端中註冊的身分識別。 下列程式碼會使用裝置連接字串連線到 IoT 中樞:

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

下列程式碼會從用戶端物件取得一個雙胞體:

// 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'));

建立處理常式

您可以為所需的屬性更新建立處理常式,以回應 JSON 階層中不同層級的更新。 例如,此處理常式會看到從後端應用程式傳送至裝置的所有所需屬性變更。 delta 變數包含從解決方案後端傳送的所需屬性:

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

下列處理常式只會回應對 fanOn 所需屬性所做的變更:

// 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}';
});

多個屬性的處理常式

在本教學課程的範例所需屬性 JSON 中,元件下的氣候節點包含兩個屬性 minTemperaturemaxTemperature

裝置的本機對應項物件會儲存一組完整的所需屬性和報告屬性。 從後端傳送的差異可能僅更新了所需屬性的子集。 在下列程式碼片段中,如果模擬裝置接收到僅對 minTemperaturemaxTemperature 其中之一所做的更新,它將會以本機對應項中的值作為另一個值,來設定裝置:

// 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();
    }
});

處理插入、更新和刪除作業

後端傳送的所需屬性無法表明正在對某個特定的所需屬性執行何種作業。 您的程式碼必須從儲存在本機的目前所需屬性集以及從中樞傳送的變更中推斷作業。

下列程式碼片段顯示模擬裝置如何處理所需屬性中 元件 清單的插入、更新和刪除作業。 您可以瞭解如何使用 Null 值來指出應該刪除元件:

// 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];
        }
      }
    });
  }
});

從後端應用程式傳送所需的屬性

您已瞭解裝置如何實作處理常式來接收所需的屬性更新。 本節說明如何從後端應用程式將所需的屬性變更傳送至裝置。

若要檢視接收所需屬性的模擬裝置範例程式碼,請流覽至您下載的範例 Node.js 專案中的 iot-hub/Tutorials/DeviceTwins 資料夾。 然後在文本編輯器中打開 ServiceClient.js 文件。

下列程式碼片段說明如何連線至裝置身分識別登錄,並存取特定裝置的對應項:

// 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');

下列程式碼片段顯示後端應用程式傳送至裝置的不同所需屬性 修補程式

// 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
      }
    }
  }
};

下列程式碼片段顯示後端應用程式如何將所需的屬性更新傳送至裝置:

// 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));
    }
  });
}

從裝置接收狀態資訊

您的後端應用程式會從裝置接收狀態資訊,作為已報告的屬性。 裝置會設定回報的屬性,並將其傳送至您的集線器。 後端應用程式可從儲存於中樞的裝置對應項讀取報告屬性的目前值。

將已報告的屬性從裝置發送

您可以用修補程式的形式傳送報告屬性值的更新。 下列程式碼片段顯示模擬裝置傳送的修補程式範本。 模擬裝置會在將修補程式傳送至中樞之前更新修補程式中的欄位:

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

模擬裝置會使用下列函式,將包含報告屬性的修補程式傳送至中樞:

// 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));
  });
}

處理報告屬性

後端應用程式會透過裝置雙胞存取裝置當前報告的屬性值。 下列程式碼片段顯示後端應用程式如何讀取模擬裝置的報告屬性值:

// 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);
}

執行應用程式

在本節中,您會執行兩個範例應用程式,以觀察後端應用程式將所需的屬性更新傳送至模擬裝置應用程式。

若要執行模擬裝置和後端應用程式,您需要裝置和服務連接字串。 您在這個教學課程一開始建立資源時,已經記下了連接字串。

若要執行模擬裝置應用程式,請開啟 Shell 或命令提示字元視窗,然後流覽至您下載的 Node.js 專案中的 iot-hub/Tutorials/DeviceTwins 資料夾。 然後執行下列命令:

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

若要執行後端應用程式,請開啟另一個 shell 或命令提示字元視窗。 然後流覽至您下載的 Node.js 專案中的 iot-hub/Tutorials/DeviceTwins 資料夾。 然後執行下列命令:

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

觀察所需屬性更新

下列螢幕快照顯示模擬裝置應用程式的輸出,並醒目提示它如何處理 maxTemperature 所需屬性的更新。 您可以查看頂層處理程式和氣候元件處理程式的執行方式:

螢幕擷取畫面顯示最上層處理常式和氣候元件處理常式的執行方式。

下列螢幕擷取畫面顯示後端應用程式的輸出,並醒目提示它如何將更新傳送至 maxTemperature 所需的屬性:

螢幕擷取畫面,顯示後端應用程式的輸出,並醒目提示其如何傳送更新。

觀察已報告的屬性更新

下列螢幕擷取畫面顯示模擬裝置應用程式的輸出,並醒目提示它如何將報告的屬性更新傳送至您的中樞:

顯示模擬裝置更新其對應項狀態的螢幕擷取畫面。

下列螢幕快照顯示後端應用程式的輸出,並醒目提示它如何從裝置接收和處理報告的屬性更新:

螢幕擷取畫面,顯示後端應用程式接收裝置報告屬性的屬性。

清理資源

如果您打算完成下一個教學課程,請保留資源群組和 IoT 中樞,以便稍後重複使用它們。

如果您不再需要 IoT 中樞,請刪除它和入口網站中的資源群組。 若要這樣做,請選取包含 IoT 中樞的 tutorial-iot-hub-rg 資源群組,然後選取 [刪除]。

或者,使用 CLI:

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

後續步驟

在本教學課程中,您已瞭解如何在裝置與 IoT 中樞之間同步處理狀態資訊。 接下來的教學課程將介紹如何使用裝置孿生來實作裝置更新流程。