デバイスのライフサイクルの一環として、バックエンド サービスから IoT デバイスを構成することが必要になる場合があります。 必要な構成をデバイスに送信する場合は、それらのデバイスから状態とコンプライアンスの更新プログラムも受信する必要があります。 たとえば、デバイスの目標動作温度範囲を設定したり、デバイスからファームウェアのバージョン情報を収集したりできます。
デバイスと IoT ハブの間で状態情報を同期するには、 デバイス ツインを使用します。 デバイス ツインは、特定のデバイスに関連付けられた JSON ドキュメントであり、IoT Hub によってクラウドに格納され、そこでクエリを実行できます。 デバイス ツインには、 必要なプロパティ、 報告されるプロパティ、タグが含 まれています。
- 必要なプロパティは、バックエンド アプリケーションによって設定され、デバイスによって読み取られます。
- 報告されるプロパティはデバイスによって設定され、バックエンド アプリケーションによって読み取られます。
- タグはバックエンド アプリケーションによって設定され、デバイスに送信されることはありません。 タグを使用してデバイスを整理します。
このチュートリアルでは、必要なプロパティと報告されたプロパティを使用して状態情報を同期する方法について説明します。
このチュートリアルでは、次のタスクを実行します。
- IoT ハブを作成し、ID レジストリにテスト デバイスを追加します。
- 必要なプロパティを使用して、シミュレートされたデバイスに状態情報を送信します。
- 報告されたプロパティを使用して、シミュレートされたデバイスから状態情報を受信します。
Azure サブスクリプションをお持ちでない場合は、開始する前に 無料アカウント を作成してください。
[前提条件]
このチュートリアルでは、Azure CLI を使用してクラウド リソースを作成します。 デバイスが登録されている IoT ハブが既にある場合は、これらの手順をスキップできます。 CLI コマンドを実行するには、次の 2 つの方法があります。
Azure Cloud Shell で Bash 環境を使用します。 詳細については、Azure Cloud Shell の Bash のクイックスタートに関するページを参照してください。
CLI 参照コマンドをローカルで実行する場合は、Azure CLI を インストール します。 Windows または macOS で実行している場合は、Docker コンテナーで Azure CLI を実行することを検討してください。 詳細については、「Docker コンテナーで Azure CLI を実行する方法」を参照してください。
- az login コマンドを使用して、Azure CLI にサインインします。
- 初回使用時にインストールを求められたら、Azure CLI 拡張機能をインストールします。 拡張機能の詳細については、Azure CLI で拡張機能を使用する方法に関するページを参照してください。
- az version を実行し、インストールされているバージョンおよび依存ライブラリを検索します。 最新バージョンにアップグレードするには、az upgrade を実行します。
このチュートリアルで実行する 2 つのサンプル アプリケーションは、Node.jsを使用して記述されています。 開発用コンピューター Node.js v10.x.x 以降が必要です。
nodejs.org から複数のプラットフォームの Node.js をダウンロードできます。
次のコマンドを使用して、開発用コンピューター上の Node.js の現在のバージョンを確認できます。
node --version
Node.js用の Azure IoT サンプルからサンプル Node.js プロジェクトを複製またはダウンロードします。
ポート 8883 がファイアウォールで開放されていることを確認してください。 このチュートリアルのデバイス サンプルでは、ポート 8883 経由で通信する MQTT プロトコルを使用します。 このポートは、一部の企業および教育ネットワーク環境でブロックされる可能性があります。 この問題を回避する方法の詳細については、「 IoT Hub への接続 (MQTT)」を参照してください。
Azure リソースを設定する
このチュートリアルを完了するには、デバイス ID レジストリにデバイスが追加された IoT ハブが Azure サブスクリプションに含まれている必要があります。 デバイス ID レジストリのエントリを使用すると、このチュートリアルで実行するシミュレートされたデバイスをハブに接続できます。
サブスクリプションに IoT ハブをまだ設定していない場合は、次の CLI スクリプトを使用して設定できます。 このスクリプトでは、名前 tutorial-iot-hub を使用し、IoT ハブ名に乱数を付加します。 実行時に、この名前をあなた自身のグローバルに一意な名前に置き換えることができます。 このスクリプトは 、米国中部 リージョンにリソース グループとハブを作成します。このリソース グループとハブは、近くのリージョンに変更できます。 このスクリプトでは、IoT Hub サービスの接続文字列を取得します。この接続文字列は、バックエンド サンプルで 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 というシミュレートされたデバイスを使用します。 次のスクリプトは、このデバイスを ID レジストリに追加し、その接続文字列を取得します。
# 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 と呼ばれる最上位のプロパティを 1 つ使用し、残りのプロパティを個別のコンポーネントにグループ化 します。 次の 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 ハブにデバイスを登録すると、デバイス接続文字列が出力として取得されます。 デバイス接続文字列は、クラウドで登録されている ID で認証するためにデバイスによって使用されます。 次のコードは、デバイス接続文字列を使用して 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 階層内のさまざまなレベルの更新に応答する、必要なプロパティ更新のハンドラーを作成できます。 たとえば、このハンドラーは、バックエンド アプリケーションからデバイスに送信されたすべての必要なプロパティ変更を確認します。 デルタ変数には、ソリューション バックエンドから送信される必要なプロパティが含まれています。
// Handle all desired property updates
twin.on('properties.desired', function(delta) {
console.log(chalk.yellow('\nNew desired properties received in patch:'));
次のハンドラーは、 fanOn desired プロパティに加えられた変更にのみ対応します。
// 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 では、コンポーネントの下の気候ノードに minTemperature と maxTemperature の 2 つのプロパティが含まれています。
デバイスのローカル ツイン オブジェクトには、必要なプロパティと報告されるプロパティの完全なセットが格納されます。 バックエンドから送信された delta で、必要なプロパティのサブセットのみが更新される可能性があります。 次のコード スニペットでは、シミュレートされたデバイスが minTemperature と maxTemperature のいずれか 1 つに対する更新を受け取った場合、ローカル ツインの値を使用してもう一方の値を使用してデバイスを構成します。
// 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 ファイルを開きます。
次のコード スニペットは、デバイス ID レジストリに接続し、特定のデバイスのツインにアクセスする方法を示しています。
// 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);
}
アプリケーションの実行
このセクションでは、2 つのサンプル アプリケーションを実行して、バックエンド アプリケーションが必要なプロパティの更新をシミュレートされたデバイス アプリケーションに送信することを確認します。
シミュレートされたデバイスとバックエンド アプリケーションを実行するには、デバイスとサービスの接続文字列が必要です。 このチュートリアルの開始時にリソースを作成したときに、接続文字列を書き留めておきます。
シミュレートされたデバイス アプリケーションを実行するには、シェルまたはコマンド プロンプト ウィンドウを開き、ダウンロードした Node.js プロジェクトの iot-hub/Tutorials/DeviceTwins フォルダーに移動します。 次に、次のコマンドを実行します。
npm install
node SimulatedDevice.js "{your device connection string}"
バックエンド アプリケーションを実行するには、別のシェルまたはコマンド プロンプト ウィンドウを開きます。 次に、ダウンロードした Node.js プロジェクトの iot-hub/Tutorials/DeviceTwins フォルダーに移動します。 次に、次のコマンドを実行します。
npm install
node ServiceClient.js "{your service connection string}"
望まれたプロパティの更新を観察する
次のスクリーンショットは、シミュレートされたデバイス アプリケーションからの出力を示し、 maxTemperature desired プロパティへの更新を処理する方法を示しています。 最上位のハンドラーと気候コンポーネント ハンドラーの両方がどのように実行されるかを確認できます。
次のスクリーンショットは、バックエンド アプリケーションからの出力を示し、 maxTemperature desired プロパティに更新を送信する方法を示しています。
報告されたプロパティの更新を観察する
次のスクリーンショットは、シミュレートされたデバイス アプリケーションからの出力を示し、報告されたプロパティの更新をハブに送信する方法を示しています。
次のスクリーンショットは、バックエンド アプリケーションからの出力を示し、デバイスから報告されたプロパティの更新を受信して処理する方法を示しています。
リソースをクリーンアップする
次のチュートリアルを完了する予定の場合は、リソース グループと IoT ハブを残して後で再利用します。
IoT ハブが不要になった場合は、ポータルでそのハブとリソース グループを削除します。 これを行うには、IoT ハブを含む tutorial-iot-hub-rg リソース グループを選択し、[削除] を選択 します。
または、CLI を使用します。
# Delete your resource group and its contents
az group delete --name tutorial-iot-hub-rg
次のステップ
このチュートリアルでは、デバイスと IoT ハブの間で状態情報を同期する方法について説明しました。 デバイス ツインを使用してデバイス更新プロセスを実装する方法については、次のチュートリアルに進んでください。