デバイスをリモート監視構成済みソリューションに接続する (Linux)

シナリオの概要

このシナリオでは、次のテレメトリをリモート監視構成済みソリューションに送信するデバイスを作成します。

  • 外部温度
  • 内部温度
  • 湿度

わかりやすくするために、デバイス上のコードではサンプル値を生成しますが、デバイスに実際のセンサーを接続し、実際のテレメトリを送信して、サンプルを拡張することをお勧めします。

このデバイスでは、ソリューション ダッシュボードから呼び出されたメソッドと、ソリューション ダッシュボードで設定されている必要なプロパティ値に応答することもできます。

このチュートリアルを完了するには、アクティブな Azure アカウントが必要になります。 アカウントがない場合は、無料試用アカウントを数分で作成することができます。 詳細については、「Azure の無料試用版サイト」を参照してください。

開始する前に

デバイス用のコードを作成する前に、リモート監視構成済みソリューションをプロビジョニングし、そのソリューションに新しいカスタム デバイスをプロビジョニングする必要があります。

リモート監視構成済みソリューションをプロビジョニングする

このチュートリアルで作成するデバイスは、リモート監視構成済みソリューションのインスタンスにデータを送信します。 リモート監視構成済みソリューションを Azure アカウントにまだプロビジョニングしていない場合は、次の手順を使用します。

  1. https://www.azureiotsolutions.com/ ページで、 +をクリックしてソリューションを作成します。
  2. [リモート監視] パネルで [選択] をクリックして、ソリューションを作成します。
  3. [Create Remote monitoring solution (リモート監視ソリューションの作成)] ページで任意のソリューション名を入力し、デプロイ先のリージョンを選択したら、使用する Azure サブスクリプションを選択します。 その後、 [ソリューションの作成]をクリックします。
  4. プロビジョニング プロセスが完了するまで待機します。

警告

構成済みソリューションでは、課金対象の Azure サービスを使用します。 不必要な課金を避けるために、使用が済んだら、必ずサブスクリプションから構成済みソリューションを削除してください。 https://www.azureiotsolutions.com/ ページで、構成済みのソリューションをサブスクリプションから完全に削除できます。

リモート監視ソリューションのプロビジョニング プロセスが完了したら、 [起動] をクリックしてブラウザーでソリューション ダッシュボードを開きます。

ソリューションのダッシュボード

リモート監視ソリューションでデバイスをプロビジョニングする

Note

ソリューションにデバイスを既にプロビジョニングしている場合は、この手順を省略して構いません。 クライアント アプリケーションを作成するときに、デバイスの資格情報が必要です。

デバイスが構成済みソリューションに接続するには、有効な資格情報を使用して IoT Hub に対してデバイス自身の ID を証明する必要があります。 デバイスの資格情報は、ソリューション ダッシュボードから取得できます。 このチュートリアルの後半で、クライアント アプリケーションにデバイスの資格情報を含めます。

デバイスをリモート監視ソリューションに追加するには、ソリューション ダッシュボードで次の手順を実行します。

  1. ダッシュボードの左下隅にある [デバイスの追加]をクリックします。

    デバイスを追加する

  2. [カスタム デバイス] パネルで、[新規追加] をクリックします。

    カスタム デバイスを追加する

  3. [デバイス ID を自分で定義する] を選択します。 mydevice などのデバイス ID を入力します。[ID の確認] をクリックして、その名前がまだ使用されていないことを確認し、[作成] をクリックしてデバイスをプロビジョニングします。

    デバイス ID を追加する

  4. デバイスの資格情報 (デバイス ID、IoT Hub ホスト名、デバイス キー) を書き留めておきます。 クライアント アプリケーションがリモート監視ソリューションに接続する際に、この値が必要になります。 次に、 [Done] をクリックします。

    デバイスの資格情報を表示する

  5. ソリューション ダッシュボードのデバイスの一覧でデバイスを選択します。 次に、[デバイスの詳細] パネルで、[デバイスの有効化] をクリックします。 現在、デバイスの状態は [実行中] です。 リモート監視ソリューションはデバイスからテレメトリを受信し、デバイス上でメソッドを呼び出すことができます。

Linux で C のサンプル クライアントをビルドして実行する

次の手順では、リモート監視が事前構成されたソリューションと通信するクライアント アプリケーションを作成する方法を示します。 このアプリケーションは C で記述し、Ubuntu Linux 上でビルドおよび実行します。

次の手順を完了するには、Ubuntu バージョン 15.04 または 15.10 が実行されているデバイスが必要です。 次に進む前に、次のコマンドを使用して、前提条件となるパッケージを Ubuntu デバイスにインストールします。

sudo apt-get install cmake gcc g++

デバイスにクライアント ライブラリをインストールする

Azure の IoT Hub クライアント ライブラリは、 apt-get コマンドを使用してパッケージとして Ubuntu デバイスにインストールし、使用できます。 IoT Hub クライアント ライブラリとヘッダー ファイルが含まれるパッケージを Ubuntu コンピューターにインストールするには、次の手順を実行します。

  1. シェルで、コンピューターに Azure IoT リポジトリを追加します。

    sudo add-apt-repository ppa:aziotsdklinux/ppa-azureiot
    sudo apt-get update
    
  2. azure-iot-sdk-c-dev パッケージをインストールします

    sudo apt-get install -y azure-iot-sdk-c-dev
    

Parson JSON パーサーをインストールする

IoT Hub クライアント ライブラリでは、メッセージ ペイロードの解析に Parson JSON のパーサーが使用されます。 次のコマンドを使用して、コンピューター上の適切なフォルダーに Parson GitHub リポジトリを複製します。

git clone https://github.com/kgabis/parson.git

プロジェクトを準備する

Ubuntu マシンで、 remote_monitoringという名前のフォルダーを作成します。 remote_monitoring フォルダー内:

  • main.c、remote_monitoring.cremote_monitoring.hおよびCMakeLists.txt の 4 つのファイルを作成します。
  • parson という名前のフォルダーを作成します。

Parson リポジトリのローカル コピーから remote_monitoring/parson フォルダーに parson.c ファイルと parson.h ファイルをコピーします。

テキスト エディターで、 remote_monitoring.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"

IoT デバイスの動作を指定する

IoT Hub シリアライザー クライアント ライブラリでは、モデルを使用して、デバイスが IoT Hub とやり取りするメッセージの形式を指定します。

  1. #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]";
    
  2. 次のコードを追加して、デバイスと 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);
    

デバイスの動作を実装する

ここで、モデルに定義された動作を実装するコードを追加します。

  1. 次の関数を追加します。この関数は、ソリューション ダッシュボードで設定された必要なプロパティを処理します。 この必要なプロパティはモデルで定義されています。

    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);
    }
    
  2. 次の関数を追加します。この関数は、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;
    }
    
  3. 次の関数を追加します。この関数は、構成済みソリューションにメッセージを送信します。

    /* 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);
    }
    
  4. 次のコールバック ハンドラーを追加します。このハンドラーは、新しく報告されたプロパティ値を、デバイスが構成済みソリューションに送信するときに実行されます。

    /* 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);
    }
    
  5. 次の関数を追加して、クラウドで構成済みソリューションにデバイスを接続し、データを交換します。 この関数では、次の手順を実行します。

    • プラットフォームを初期化する。
    • シリアル化ライブラリに 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関数を呼び出す

テキスト エディターで、remote_monitoring.h ファイルを開きます。 次のコードを追加します。

void remote_monitoring_run(void);

テキスト エディターで、 main.c ファイルを開きます。 次のコードを追加します。

#include "remote_monitoring.h"

int main(void)
{
    remote_monitoring_run();

    return 0;
}

アプリケーションの構築と実行

次の手順では、 CMake を使用してクライアント アプリケーションをビルドする方法について説明します。

  1. テキスト エディターで、remote_monitoring フォルダーの CMakeLists.txt ファイルを開きます。

  2. 次の手順を追加して、クライアント アプリケーションをビルドする方法を定義します。

    macro(compileAsC99)
      if (CMAKE_VERSION VERSION_LESS "3.1")
        if (CMAKE_C_COMPILER_ID STREQUAL "GNU")
          set (CMAKE_C_FLAGS "--std=c99 ${CMAKE_C_FLAGS}")
          set (CMAKE_CXX_FLAGS "--std=c++11 ${CMAKE_CXX_FLAGS}")
        endif()
      else()
        set (CMAKE_C_STANDARD 99)
        set (CMAKE_CXX_STANDARD 11)
      endif()
    endmacro(compileAsC99)
    
    cmake_minimum_required(VERSION 2.8.11)
    compileAsC99()
    
    set(AZUREIOT_INC_FOLDER "${CMAKE_SOURCE_DIR}" "${CMAKE_SOURCE_DIR}/parson" "/usr/include/azureiot" "/usr/include/azureiot/inc")
    
    include_directories(${AZUREIOT_INC_FOLDER})
    
    set(sample_application_c_files
        ./parson/parson.c
        ./remote_monitoring.c
        ./main.c
    )
    
    set(sample_application_h_files
        ./parson/parson.h
        ./remote_monitoring.h
    )
    
    add_executable(sample_app ${sample_application_c_files} ${sample_application_h_files})
    
    target_link_libraries(sample_app
        serializer
        iothub_client
        iothub_client_mqtt_transport
        aziotsharedutil
        umqtt
        pthread
        curl
        ssl
        crypto
        m
    )
    
  3. remote_monitoring フォルダーに、CMake によって生成される make ファイルを格納するフォルダーを作成し、次に示すように cmake コマンドと make コマンドを実行します。

    mkdir cmake
    cd cmake
    cmake ../
    make
    
  4. クライアント アプリケーションを実行し、テレメトリを IoT Hub に送信します。

    ./sample_app
    

デバイスのテレメトリをダッシュボードに表示する

リモート監視ソリューションのダッシュボードでは、デバイスが IoT Hub に送信するテレメトリを表示できます。

  1. ブラウザーでリモート監視ソリューションのダッシュボードに戻り、左側のパネルの [デバイス] をクリックし、[デバイスの一覧] に移動します。

  2. [デバイスの一覧] で、デバイスの状態が [実行中] であることを確認してください。 実行中でない場合は、[デバイスの詳細] パネルで [デバイスの有効化] をクリックします。

    デバイスの状態を表示する

  3. [ダッシュボード] をクリックしてダッシュボードに戻り、[表示するデバイス] ボックスの一覧からテレメトリを表示するデバイスを選択します。 サンプル アプリケーションから送信されるテレメトリは、内部温度では 50 単位、外部温度では 55 単位、湿度では 50 単位です。

    デバイス テレメトリの表示

デバイスでメソッドを呼び出す

リモート監視ソリューションのダッシュボードでは、IoT Hub を介してデバイスでメソッドを呼び出すことができます。 たとえば、リモート監視ソリューションで、デバイスの再起動をシミュレートするメソッドを呼び出すことができます。

  1. リモート監視ソリューションのダッシュボードで、左側のパネルの [デバイス] をクリックし、[デバイスの一覧] に移動します。

  2. [デバイスの一覧] で、デバイスの [デバイス ID] をクリックします。

  3. [デバイスの詳細] パネルで、[メソッド] をクリックします。

    デバイス メソッド

  4. [メソッド] ドロップダウンで [InitiateFirmwareUpdate] を選択し、[FWPACKAGEURI] にダミーの URL を入力します。 [メソッドの呼び出し] をクリックして、デバイスでメソッドを呼び出します。

    デバイス メソッドを呼び出す

  5. デバイスがメソッドを処理しているとき、デバイス コードを実行しているコンソールにメッセージが表示されます。 メソッドの結果は、ソリューション ポータルの履歴に追加されます。

    メソッドの履歴を表示する

次のステップ

構成済みソリューションのカスタマイズ」では、このサンプルを拡張する方法をいくつか確認できます。 可能な拡張には、実際のセンサーの使用やその他のコマンドの実装などがあります。

azureiotsuite.com サイトでのアクセス許可の詳細について確認できます。