デバイスをリモート監視構成済みソリューションに接続する (Windows)
シナリオの概要
このシナリオでは、次のテレメトリをリモート監視構成済みソリューションに送信するデバイスを作成します。
- 外部温度
- 内部温度
- 湿度
わかりやすくするために、デバイス上のコードではサンプル値を生成しますが、デバイスに実際のセンサーを接続し、実際のテレメトリを送信して、サンプルを拡張することをお勧めします。
このデバイスでは、ソリューション ダッシュボードから呼び出されたメソッドと、ソリューション ダッシュボードで設定されている必要なプロパティ値に応答することもできます。
このチュートリアルを完了するには、アクティブな Azure アカウントが必要になります。 アカウントがない場合は、無料試用アカウントを数分で作成することができます。 詳細については、「Azure の無料試用版サイト」を参照してください。
開始する前に
デバイス用のコードを作成する前に、リモート監視構成済みソリューションをプロビジョニングし、そのソリューションに新しいカスタム デバイスをプロビジョニングする必要があります。
リモート監視構成済みソリューションをプロビジョニングする
このチュートリアルで作成するデバイスは、リモート監視構成済みソリューションのインスタンスにデータを送信します。 リモート監視構成済みソリューションを Azure アカウントにまだプロビジョニングしていない場合は、次の手順を使用します。
- https://www.azureiotsolutions.com/ ページで、 +をクリックしてソリューションを作成します。
- [リモート監視] パネルで [選択] をクリックして、ソリューションを作成します。
- [Create Remote monitoring solution (リモート監視ソリューションの作成)] ページで任意のソリューション名を入力し、デプロイ先のリージョンを選択したら、使用する Azure サブスクリプションを選択します。 その後、 [ソリューションの作成]をクリックします。
- プロビジョニング プロセスが完了するまで待機します。
警告
構成済みソリューションでは、課金対象の Azure サービスを使用します。 不必要な課金を避けるために、使用が済んだら、必ずサブスクリプションから構成済みソリューションを削除してください。 https://www.azureiotsolutions.com/ ページで、構成済みのソリューションをサブスクリプションから完全に削除できます。
リモート監視ソリューションのプロビジョニング プロセスが完了したら、 [起動] をクリックしてブラウザーでソリューション ダッシュボードを開きます。
リモート監視ソリューションでデバイスをプロビジョニングする
Note
ソリューションにデバイスを既にプロビジョニングしている場合は、この手順を省略して構いません。 クライアント アプリケーションを作成するときに、デバイスの資格情報が必要です。
デバイスが構成済みソリューションに接続するには、有効な資格情報を使用して IoT Hub に対してデバイス自身の ID を証明する必要があります。 デバイスの資格情報は、ソリューション ダッシュボードから取得できます。 このチュートリアルの後半で、クライアント アプリケーションにデバイスの資格情報を含めます。
デバイスをリモート監視ソリューションに追加するには、ソリューション ダッシュボードで次の手順を実行します。
ダッシュボードの左下隅にある [デバイスの追加]をクリックします。
[カスタム デバイス] パネルで、[新規追加] をクリックします。
[デバイス ID を自分で定義する] を選択します。 mydevice などのデバイス ID を入力します。[ID の確認] をクリックして、その名前がまだ使用されていないことを確認し、[作成] をクリックしてデバイスをプロビジョニングします。
デバイスの資格情報 (デバイス ID、IoT Hub ホスト名、デバイス キー) を書き留めておきます。 クライアント アプリケーションがリモート監視ソリューションに接続する際に、この値が必要になります。 次に、 [Done] をクリックします。
ソリューション ダッシュボードのデバイスの一覧でデバイスを選択します。 次に、[デバイスの詳細] パネルで、[デバイスの有効化] をクリックします。 現在、デバイスの状態は [実行中] です。 リモート監視ソリューションはデバイスからテレメトリを受信し、デバイス上でメソッドを呼び出すことができます。
Windows で C のサンプル ソリューションを作成する
次の手順では、リモート監視が事前構成されたソリューションと通信するクライアント アプリケーションを作成する方法を示します。 このアプリケーションは C で記述し、Windows 上でビルドおよび実行します。
Visual Studio 2015 または Visual Studio 2017 にスタート プロジェクトを作成し、IoT Hub デバイス クライアントの NuGet パッケージを追加します。
Visual Studio で、Visual C++ Win32 コンソール アプリケーション テンプレートを使用して、C コンソール アプリケーションを作成します。 プロジェクトの名前として「 RMDevice」と入力します。
Win32 アプリケーション ウィザードの [アプリケーション設定] ページで、[コンソール アプリケーション] が選択されていることを確認し、[プリコンパイル済みヘッダー] と [Security Development Lifecycle (SDL) checks (Security Development Lifecycle (SDL) チェック)] をオフにします。
ソリューション エクスプローラーで、stdafx.h、targetver.h、stdafx.cpp の各ファイルを削除します。
ソリューション エクスプローラーで、RMDevice.cpp ファイルの名前を RMDevice.c に変更します。
ソリューション エクスプローラーで、RMDevice プロジェクトを右クリックし、[NuGet パッケージの管理] をクリックします。 [参照] をクリックし、次の NuGet パッケージを検索してインストールします。
- Microsoft.Azure.IoTHub.Serializer
- Microsoft.Azure.IoTHub.IoTHubClient
- Microsoft.Azure.IoTHub.MqttTransport
ソリューション エクスプローラーで、RMDevice プロジェクトを右クリックして [プロパティ] をクリックし、プロジェクトの [プロパティ ページ] ダイアログ ボックスを開きます。 詳細については、Visual C++ プロジェクトのプロパティの設定に関するページを参照してください。
[リンカー] フォルダーをクリックして、[入力] プロパティ ページをクリックします。
crypt32.lib を、[追加の依存ファイル] プロパティに追加します。 [OK] をクリックし、もう一度 [OK] をクリックして、プロジェクトのプロパティ値を保存します。
Parson JSON ライブラリを RMDevice プロジェクトに追加し、必要な #include
ステートメントを追加します。
次のコマンドを使用して、コンピューター上の適切なフォルダーに Parson GitHub リポジトリを複製します。
git clone https://github.com/kgabis/parson.git
Parson リポジトリのローカル コピーから parson.h と parson.c の各ファイルを RMDevice プロジェクト フォルダーにコピーします。
Visual Studio で RMDevice プロジェクトを右クリックして [追加] をクリックし、[既存の項目] をクリックします。
[既存項目の追加] ダイアログで、RMDevice プロジェクト フォルダー内にある parson.h と parson.c の各ファイルを選択します。 [追加] をクリックして、この 2 ファイルをプロジェクトに追加します。
Visual Studio で RMDevice.c ファイルを開きます。 既存の
#include
ステートメントを次のコードで置き換えます。#include "iothubtransportmqtt.h" #include "schemalib.h" #include "iothub_client.h" #include "serializer_devicetwin.h" #include "schemaserializer.h" #include "azure_c_shared_utility/threadapi.h" #include "azure_c_shared_utility/platform.h" #include "parson.h"
Note
ビルドすることで、プロジェクトに正しい依存関係が設定されたことを確認できます。
IoT デバイスの動作を指定する
IoT Hub シリアライザー クライアント ライブラリでは、モデルを使用して、デバイスが IoT Hub とやり取りするメッセージの形式を指定します。
#include
ステートメントの後に次の変数宣言を追加します。 リモート監視ソリューション ダッシュボードで、プレースホルダー [Device Id] と [Device Key] の値を、書き留めておいたデバイス用の値に置き換えます。 ソリューション ダッシュボードの IoT Hub ホスト名を使用して、[IoTHub Name] を置き換えます。 たとえば、IoT Hub ホスト名が contoso.azure-devices.net である場合は、[IoTHub Name] を contoso に置き換えます。static const char* deviceId = "[Device Id]"; static const char* connectionString = "HostName=[IoTHub Name].azure-devices.net;DeviceId=[Device Id];SharedAccessKey=[Device Key]";
次のコードを追加して、デバイスと IoT Hub との通信を可能にするモデルを定義します。 このモデルでは、デバイスで次の操作が可能であることを指定します。
- テレメトリとして温度、外部温度、湿度、およびデバイス ID を送信する。
- デバイスに関するメタデータを IoT Hub に送信する。 デバイスは、起動時に DeviceInfo オブジェクトで基本的なメタデータを送信します。
- 報告されたプロパティを IoT Hub でデバイス ツインに送信する。 報告されたプロパティは、構成、デバイス、およびシステムのプロパティにグループ化されます。
- IoT Hub のデバイス ツインで設定された必要なプロパティを受信し、そのプロパティを操作する。
- ソリューション ポータルで呼び出された Reboot および InitiateFirmwareUpdate ダイレクト メソッド に応答する。 デバイスは、報告されたプロパティを使用して、サポートするダイレクト メソッドに関する情報を送信します。
// Define the Model BEGIN_NAMESPACE(Contoso); /* Reported properties */ DECLARE_STRUCT(SystemProperties, ascii_char_ptr, Manufacturer, ascii_char_ptr, FirmwareVersion, ascii_char_ptr, InstalledRAM, ascii_char_ptr, ModelNumber, ascii_char_ptr, Platform, ascii_char_ptr, Processor, ascii_char_ptr, SerialNumber ); DECLARE_STRUCT(LocationProperties, double, Latitude, double, Longitude ); DECLARE_STRUCT(ReportedDeviceProperties, ascii_char_ptr, DeviceState, LocationProperties, Location ); DECLARE_MODEL(ConfigProperties, WITH_REPORTED_PROPERTY(double, TemperatureMeanValue), WITH_REPORTED_PROPERTY(uint8_t, TelemetryInterval) ); /* Part of DeviceInfo */ DECLARE_STRUCT(DeviceProperties, ascii_char_ptr, DeviceID, _Bool, HubEnabledState ); DECLARE_DEVICETWIN_MODEL(Thermostat, /* Telemetry (temperature, external temperature and humidity) */ WITH_DATA(double, Temperature), WITH_DATA(double, ExternalTemperature), WITH_DATA(double, Humidity), WITH_DATA(ascii_char_ptr, DeviceId), /* DeviceInfo */ WITH_DATA(ascii_char_ptr, ObjectType), WITH_DATA(_Bool, IsSimulatedDevice), WITH_DATA(ascii_char_ptr, Version), WITH_DATA(DeviceProperties, DeviceProperties), /* Device twin properties */ WITH_REPORTED_PROPERTY(ReportedDeviceProperties, Device), WITH_REPORTED_PROPERTY(ConfigProperties, Config), WITH_REPORTED_PROPERTY(SystemProperties, System), WITH_DESIRED_PROPERTY(double, TemperatureMeanValue, onDesiredTemperatureMeanValue), WITH_DESIRED_PROPERTY(uint8_t, TelemetryInterval, onDesiredTelemetryInterval), /* Direct methods implemented by the device */ WITH_METHOD(Reboot), WITH_METHOD(InitiateFirmwareUpdate, ascii_char_ptr, FwPackageURI), /* Register direct methods with solution portal */ WITH_REPORTED_PROPERTY(ascii_char_ptr_no_quotes, SupportedMethods) ); END_NAMESPACE(Contoso);
デバイスの動作を実装する
ここで、モデルに定義された動作を実装するコードを追加します。
次の関数を追加します。この関数は、ソリューション ダッシュボードで設定された必要なプロパティを処理します。 この必要なプロパティはモデルで定義されています。
void onDesiredTemperatureMeanValue(void* argument) { /* By convention 'argument' is of the type of the MODEL */ Thermostat* thermostat = argument; printf("Received a new desired_TemperatureMeanValue = %f\r\n", thermostat->TemperatureMeanValue); } void onDesiredTelemetryInterval(void* argument) { /* By convention 'argument' is of the type of the MODEL */ Thermostat* thermostat = argument; printf("Received a new desired_TelemetryInterval = %d\r\n", thermostat->TelemetryInterval); }
次の関数を追加します。この関数は、IoT Hub で呼び出されたダイレクト メソッドを処理します。 このダイレクト メソッドはモデルで定義されています。
/* Handlers for direct methods */ METHODRETURN_HANDLE Reboot(Thermostat* thermostat) { (void)(thermostat); METHODRETURN_HANDLE result = MethodReturn_Create(201, "\"Rebooting\""); printf("Received reboot request\r\n"); return result; } METHODRETURN_HANDLE InitiateFirmwareUpdate(Thermostat* thermostat, ascii_char_ptr FwPackageURI) { (void)(thermostat); METHODRETURN_HANDLE result = MethodReturn_Create(201, "\"Initiating Firmware Update\""); printf("Recieved firmware update request. Use package at: %s\r\n", FwPackageURI); return result; }
次の関数を追加します。この関数は、構成済みソリューションにメッセージを送信します。
/* Send data to IoT Hub */ static void sendMessage(IOTHUB_CLIENT_HANDLE iotHubClientHandle, const unsigned char* buffer, size_t size) { IOTHUB_MESSAGE_HANDLE messageHandle = IoTHubMessage_CreateFromByteArray(buffer, size); if (messageHandle == NULL) { printf("unable to create a new IoTHubMessage\r\n"); } else { if (IoTHubClient_SendEventAsync(iotHubClientHandle, messageHandle, NULL, NULL) != IOTHUB_CLIENT_OK) { printf("failed to hand over the message to IoTHubClient"); } else { printf("IoTHubClient accepted the message for delivery\r\n"); } IoTHubMessage_Destroy(messageHandle); } free((void*)buffer); }
次のコールバック ハンドラーを追加します。このハンドラーは、新しく報告されたプロパティ値を、デバイスが構成済みソリューションに送信するときに実行されます。
/* Callback after sending reported properties */ void deviceTwinCallback(int status_code, void* userContextCallback) { (void)(userContextCallback); printf("IoTHub: reported properties delivered with status_code = %u\n", status_code); }
次の関数を追加して、クラウドで構成済みソリューションにデバイスを接続し、データを交換します。 この関数では、次の手順を実行します。
- プラットフォームを初期化する。
- シリアル化ライブラリに Contoso の名前空間を登録する。
- デバイスの接続文字列でクライアントを初期化する。
- Thermostat モデルのインスタンスを作成する。
- 報告されたプロパティ値を作成して送信する。
- DeviceInfo オブジェクトを送信する。
- 1 秒ごとにテレメトリを送信するループを作成する。
- すべてのリソースの初期化を解除する。
void remote_monitoring_run(void) { if (platform_init() != 0) { printf("Failed to initialize the platform.\n"); } else { if (SERIALIZER_REGISTER_NAMESPACE(Contoso) == NULL) { printf("Unable to SERIALIZER_REGISTER_NAMESPACE\n"); } else { IOTHUB_CLIENT_HANDLE iotHubClientHandle = IoTHubClient_CreateFromConnectionString(connectionString, MQTT_Protocol); if (iotHubClientHandle == NULL) { printf("Failure in IoTHubClient_CreateFromConnectionString\n"); } else { #ifdef MBED_BUILD_TIMESTAMP // For mbed add the certificate information if (IoTHubClient_SetOption(iotHubClientHandle, "TrustedCerts", certificates) != IOTHUB_CLIENT_OK) { printf("Failed to set option \"TrustedCerts\"\n"); } #endif // MBED_BUILD_TIMESTAMP Thermostat* thermostat = IoTHubDeviceTwin_CreateThermostat(iotHubClientHandle); if (thermostat == NULL) { printf("Failure in IoTHubDeviceTwin_CreateThermostat\n"); } else { /* Set values for reported properties */ thermostat->Config.TemperatureMeanValue = 55.5; thermostat->Config.TelemetryInterval = 3; thermostat->Device.DeviceState = "normal"; thermostat->Device.Location.Latitude = 47.642877; thermostat->Device.Location.Longitude = -122.125497; thermostat->System.Manufacturer = "Contoso Inc."; thermostat->System.FirmwareVersion = "2.22"; thermostat->System.InstalledRAM = "8 MB"; thermostat->System.ModelNumber = "DB-14"; thermostat->System.Platform = "Plat 9.75"; thermostat->System.Processor = "i3-7"; thermostat->System.SerialNumber = "SER21"; /* Specify the signatures of the supported direct methods */ thermostat->SupportedMethods = "{\"Reboot\": \"Reboot the device\", \"InitiateFirmwareUpdate--FwPackageURI-string\": \"Updates device Firmware. Use parameter FwPackageURI to specify the URI of the firmware file\"}"; /* Send reported properties to IoT Hub */ if (IoTHubDeviceTwin_SendReportedStateThermostat(thermostat, deviceTwinCallback, NULL) != IOTHUB_CLIENT_OK) { printf("Failed sending serialized reported state\n"); } else { printf("Send DeviceInfo object to IoT Hub at startup\n"); thermostat->ObjectType = "DeviceInfo"; thermostat->IsSimulatedDevice = 0; thermostat->Version = "1.0"; thermostat->DeviceProperties.HubEnabledState = 1; thermostat->DeviceProperties.DeviceID = (char*)deviceId; unsigned char* buffer; size_t bufferSize; if (SERIALIZE(&buffer, &bufferSize, thermostat->ObjectType, thermostat->Version, thermostat->IsSimulatedDevice, thermostat->DeviceProperties) != CODEFIRST_OK) { (void)printf("Failed serializing DeviceInfo\n"); } else { sendMessage(iotHubClientHandle, buffer, bufferSize); } /* Send telemetry */ thermostat->Temperature = 50; thermostat->ExternalTemperature = 55; thermostat->Humidity = 50; thermostat->DeviceId = (char*)deviceId; while (1) { unsigned char*buffer; size_t bufferSize; (void)printf("Sending sensor value Temperature = %f, Humidity = %f\n", thermostat->Temperature, thermostat->Humidity); if (SERIALIZE(&buffer, &bufferSize, thermostat->DeviceId, thermostat->Temperature, thermostat->Humidity, thermostat->ExternalTemperature) != CODEFIRST_OK) { (void)printf("Failed sending sensor value\r\n"); } else { sendMessage(iotHubClientHandle, buffer, bufferSize); } ThreadAPI_Sleep(1000); } IoTHubDeviceTwin_DestroyThermostat(thermostat); } } IoTHubClient_Destroy(iotHubClientHandle); } serializer_deinit(); } } platform_deinit(); }
構成済みソリューションに送信されるテレメトリ メッセージの例を次に示します。
{"DeviceId":"mydevice01", "Temperature":50, "Humidity":50, "ExternalTemperature":55}
サンプルのビルドと実行
remote_monitoring_run関数を呼び出し、デバイス アプリケーションをビルドして実行するコードを追加します。
main 関数を、remote_monitoring_run 関数を呼び出す次のコードに置き換えます。
int main() { remote_monitoring_run(); return 0; }
[ビルド]、[ソリューションのビルド] の順にクリックして、デバイス アプリケーションをビルドします。
ソリューション エクスプローラーで、RMDevice プロジェクトを右クリックし、[デバッグ]、[新しいインスタンスを開始] の順にクリックして、サンプルを実行します。 アプリケーションが、事前構成済みのソリューションにサンプル テレメトリを送信し、ソリューションのダッシュボードで設定された目的のプロパティ値を受け取り、ソリューションのダッシュボードから呼び出されたメソッドに応答すると、コンソールにメッセージが表示されます。
デバイスのテレメトリをダッシュボードに表示する
リモート監視ソリューションのダッシュボードでは、デバイスが IoT Hub に送信するテレメトリを表示できます。
ブラウザーでリモート監視ソリューションのダッシュボードに戻り、左側のパネルの [デバイス] をクリックし、[デバイスの一覧] に移動します。
[デバイスの一覧] で、デバイスの状態が [実行中] であることを確認してください。 実行中でない場合は、[デバイスの詳細] パネルで [デバイスの有効化] をクリックします。
[ダッシュボード] をクリックしてダッシュボードに戻り、[表示するデバイス] ボックスの一覧からテレメトリを表示するデバイスを選択します。 サンプル アプリケーションから送信されるテレメトリは、内部温度では 50 単位、外部温度では 55 単位、湿度では 50 単位です。
デバイスでメソッドを呼び出す
リモート監視ソリューションのダッシュボードでは、IoT Hub を介してデバイスでメソッドを呼び出すことができます。 たとえば、リモート監視ソリューションで、デバイスの再起動をシミュレートするメソッドを呼び出すことができます。
リモート監視ソリューションのダッシュボードで、左側のパネルの [デバイス] をクリックし、[デバイスの一覧] に移動します。
[デバイスの一覧] で、デバイスの [デバイス ID] をクリックします。
[デバイスの詳細] パネルで、[メソッド] をクリックします。
[メソッド] ドロップダウンで [InitiateFirmwareUpdate] を選択し、[FWPACKAGEURI] にダミーの URL を入力します。 [メソッドの呼び出し] をクリックして、デバイスでメソッドを呼び出します。
デバイスがメソッドを処理しているとき、デバイス コードを実行しているコンソールにメッセージが表示されます。 メソッドの結果は、ソリューション ポータルの履歴に追加されます。
次のステップ
「構成済みソリューションのカスタマイズ」では、このサンプルを拡張する方法をいくつか確認できます。 可能な拡張には、実際のセンサーの使用やその他のコマンドの実装などがあります。
azureiotsuite.com サイトでのアクセス許可の詳細について確認できます。