チュートリアル - MQTT を使用して、デバイス SDK を使用せずに IoT デバイス クライアントを開発する

可能であれば、いずれかの Azure IoT Device SDK を使用して、IoT デバイス クライアントを構築するべきです。 ただし、メモリに制約のあるデバイスの使用時などのシナリオでは、MQTT ライブラリを使用して IoT ハブと通信することが必要になる場合があります。

このチュートリアルのサンプルでは、Eclipse Mosquitto MQTT ライブラリを使用します。

このチュートリアルでは、以下の内容を学習します。

  • C 言語デバイス クライアント サンプル アプリケーションをビルドします。
  • MQTT ライブラリを使用してテレメトリを送信するサンプルを実行します。
  • MQTT ライブラリを使用して、IoT ハブから送信されたクラウドからデバイスへのメッセージを処理するサンプルを実行します。
  • MQTT ライブラリを使用してデバイス上のデバイス ツインを管理するサンプルを実行します。

Windows または Linux 開発マシンを使用して、このチュートリアルの手順を完了できます。

Azure サブスクリプションをお持ちでない場合は、開始する前に 無料アカウント を作成してください。

前提条件

Azure CLI の環境を準備する

  • 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 拡張機能をインストールします。 拡張機能の詳細については、Azure CLI で拡張機能を使用する方法に関するページを参照してください。

    • az version を実行し、インストールされているバージョンおよび依存ライブラリを検索します。 最新バージョンにアップグレードするには、az upgrade を実行します。

開発用コンピューターの前提条件

Windows を使用している場合:

  1. Visual Studio (Community、Professional、または Enterprise) をインストールする。 [C++ によるデスクトップ開発] ワークロードを必ず有効にする。

  2. CMake をインストールします。 [すべてのユーザーのシステム パスに CMake を追加する] オプションを有効にする。

  3. Mosquittox64 バージョン をインストールする。

Linux を使用している場合:

  1. 次のコマンドを実行して、ビルド ツールをインストールする。

    sudo apt install cmake g++
    
  2. 次のコマンドを実行して、Mosquitto クライアント ライブラリをインストールする。

    sudo apt install libmosquitto-dev
    

環境を設定する

IoT ハブがまだない場合は、次のコマンドを実行して、mqtt-sample-rg という名前のリソース グループ内に Free レベルの IoT ハブを作成します。 このコマンドでは、作成する IoT ハブの名前の例として、my-hub という名前を使用します。 ご利用の IoT ハブには、my-hub の代わりに使用する一意の名前を選んでください。

az group create --name mqtt-sample-rg --location eastus
az iot hub create --name my-hub --resource-group mqtt-sample-rg --sku F1 

IoT ハブの名前をメモしておきます。後で必要になります。

IoT ハブにデバイスを登録します。 次のコマンドは、mqtt-dev-01 という名前のデバイスを my-hub という名前の IoT ハブに登録します。 必ず IoT ハブの名前を使用してください。

az iot hub device-identity create --hub-name my-hub --device-id mqtt-dev-01

次のコマンドを使用して、デバイスに IoT ハブへのアクセスを許可する SAS トークンを作成します。 必ず IoT ハブの名前を使用してください。

az iot hub generate-sas-token --device-id mqtt-dev-01 --hub-name my-hub --du 7200

コマンドによって出力される SAS トークンをメモしておきます。後で必要になります。 SAS トークンはこのようになります。SharedAccessSignature sr=my-hub.azure-devices.net%2Fdevices%2Fmqtt-dev-01&sig=%2FnM...sNwtnnY%3D&se=1677855761

ヒント

既定では、SAS トークンは 60 分間有効です。 前のコマンドでは、--du 7200 オプションによって、トークンの有効期間が 2 時間に延長されます。 使用する準備が整う前に有効期間が切れた場合は、新しいトークンを生成します。 長い有効期間のトークンを作成することもできます。 詳細については、「az iot hub generate-sas-token」を参照してください。

サンプル リポジトリをクローンする

次のコマンドを使用して、サンプル リポジトリをローカル コンピューター上の適切な場所に複製します。

git clone https://github.com/Azure-Samples/IoTMQTTSample.git

リポジトリには次のものも含まれます。

  • paho-mqtt ライブラリを使用する Python のサンプル。
  • mosquitto_pub CLI を使用して IoT ハブを操作する手順。

C サンプルをビルドする

サンプルをビルドする前に、IoT ハブとデバイスの詳細を追加する必要があります。 複製された IoTMQTTSample リポジトリで、mosquitto/src/config.h ファイルを開きます。 次のように、IoT ハブ名、デバイス ID、SAS トークンを追加します。 必ず IoT ハブの名前を使用してください。

// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

#define IOTHUBNAME "my-hub"
#define DEVICEID   "mqtt-dev-01"
#define SAS_TOKEN  "SharedAccessSignature sr=my-hub.azure-devices.net%2Fdevices%2Fmqtt-dev-01&sig=%2FnM...sNwtnnY%3D&se=1677855761"

#define CERTIFICATEFILE CERT_PATH "IoTHubRootCA.crt.pem"

注意

IoTHubRootCA.crt.pem ファイルには、TLS 接続のための CA ルート証明書が含まれています。

mosquitto/src/config.h ファイルへの変更を保存します。

サンプルをビルドするには、シェルで次のコマンドを実行します。

cd mosquitto
cmake -Bbuild
cmake --build build

Linux では、バイナリは mosquitto フォルダーの下の ./build フォルダーにあります。

Windows では、バイナリは mosquitto フォルダーの下の .\build\Debug フォルダーにあります。

テレメトリを送信する

mosquitto_telemetry サンプルでは、MQTT ライブラリを使用してデバイスからクラウドへのテレメトリ メッセージを IoT ハブに送信する方法を示します。

サンプル アプリケーションを実行する前に、次のコマンドを実行して、IoT ハブのイベント モニターを起動します。 必ず IoT ハブの名前を使用してください。

az iot hub monitor-events --hub-name my-hub

mosquitto_telemetry サンプルを実行します。 たとえば、Linux では次のようになります。

./build/mosquitto_telemetry

az iot hub monitor-events では、デバイスによって送信されたペイロードを示す次の出力が生成されます。

Starting event monitor, use ctrl-c to stop...
{
    "event": {
        "origin": "mqtt-dev-01",
        "module": "",
        "interface": "",
        "component": "",
        "payload": "Bonjour MQTT from Mosquitto"
    }
}

これで、イベント モニターを停止できます。

コードの確認

次のスニペットは、mosquitto/src/mosquitto_telemetry.cpp ファイルから取ったものです。

次のステートメントでは、接続情報と、テレメトリ メッセージの送信に使用する MQTT トピックの名前を定義しています。

#define HOST IOTHUBNAME ".azure-devices.net"
#define PORT 8883
#define USERNAME HOST "/" DEVICEID "/?api-version=2020-09-30"

#define TOPIC "devices/" DEVICEID "/messages/events/"

関数 main は、IoT ハブで認証するユーザー名とパスワードを設定します。 パスワードは、デバイス用に作成した SAS トークンです。

mosquitto_username_pw_set(mosq, USERNAME, SAS_TOKEN);

このサンプルでは、MQTT トピックを使用して、テレメトリ メッセージを IoT ハブに送信します。

int msgId  = 42;
char msg[] = "Bonjour MQTT from Mosquitto";

// once connected, we can publish a Telemetry message
printf("Publishing....\r\n");
rc = mosquitto_publish(mosq, &msgId, TOPIC, sizeof(msg) - 1, msg, 1, true);
if (rc != MOSQ_ERR_SUCCESS)
{
    return mosquitto_error(rc);
}
printf("Publish returned OK\r\n");

詳細については、「デバイスからクラウドへのメッセージの送信」を参照してください。

クラウドからデバイスへのメッセージを受信する

mosquitto_subscribe サンプルでは、MQTT トピックをサブスクライブし、MQTT ライブラリを使用してクラウドからデバイスへのメッセージを IoT ハブから受信する方法を示しています。

mosquitto_subscribe サンプルを実行します。 たとえば、Linux では次のようになります。

./build/mosquitto_subscribe

次のコマンドを実行して、クラウドからデバイスへのメッセージを IoT ハブから送信します。 必ず IoT ハブの名前を使用してください。

az iot device c2d-message send --hub-name my-hub --device-id mqtt-dev-01 --data "hello world"

mosquitto_subscribe からの出力は次のサンプルのようになります。

Waiting for C2D messages...
C2D message 'hello world' for topic 'devices/mqtt-dev-01/messages/devicebound/%24.mid=d411e727-...f98f&%24.to=%2Fdevices%2Fmqtt-dev-01%2Fmessages%2Fdevicebound&%24.ce=utf-8&iothub-ack=none'
Got message for devices/mqtt-dev-01/messages/# topic

コードの確認

次のスニペットは、mosquitto/src/mosquitto_subscribe.cpp ファイルから取ったものです。

次のステートメントでは、デバイスがクラウドからデバイスへのメッセージを受信するために使用するトピック フィルターを定義します。 # は複数レベルのワイルドカードです。

#define DEVICEMESSAGE "devices/" DEVICEID "/messages/#"

main 関数は、mosquitto_message_callback_set 関数を 使用して、IoT ハブから送信されたメッセージを処理するコールバックを設定し、mosquitto_subscribe 関数を使用してすべてのメッセージをサブスクライブします。 次のスニペットは、このコールバック関数を示しています。

void message_callback(struct mosquitto* mosq, void* obj, const struct mosquitto_message* message)
{
    printf("C2D message '%.*s' for topic '%s'\r\n", message->payloadlen, (char*)message->payload, message->topic);

    bool match = 0;
    mosquitto_topic_matches_sub(DEVICEMESSAGE, message->topic, &match);

    if (match)
    {
        printf("Got message for " DEVICEMESSAGE " topic\r\n");
    }
}

詳細については、「MQTT を使用してクラウドからデバイスへのメッセージを受信する」を参照してください。

デバイス ツインを更新する

mosquitto_device_twin サンプルでは、デバイス ツインで報告されたプロパティを設定し、プロパティを読み取り戻す方法を示します。

mosquitto_device_twin サンプルを実行します。 たとえば、Linux では次のようになります。

./build/mosquitto_device_twin

mosquitto_device_twin からの出力は次のサンプルのようになります。

Setting device twin reported properties....
Device twin message '' for topic '$iothub/twin/res/204/?$rid=0&$version=2'
Setting device twin properties SUCCEEDED.

Getting device twin properties....
Device twin message '{"desired":{"$version":1},"reported":{"temperature":32,"$version":2}}' for topic '$iothub/twin/res/200/?$rid=1'
Getting device twin properties SUCCEEDED.

コードの確認

次のスニペットは、mosquitto/src/mosquitto_device_twin.cpp ファイルから取ったものです。

次のステートメントでは、デバイス がデバイス ツインの更新をサブスクライブし、デバイス ツインを読み取り、デバイス ツインを更新するためにデバイスが使用するトピックを定義しています。

#define DEVICETWIN_SUBSCRIPTION  "$iothub/twin/res/#"
#define DEVICETWIN_MESSAGE_GET   "$iothub/twin/GET/?$rid=%d"
#define DEVICETWIN_MESSAGE_PATCH "$iothub/twin/PATCH/properties/reported/?$rid=%d"

main 関数は、mosquitto_connect_callback_set 関数を 使用して、IoT ハブから送信されたメッセージを処理するコールバックを設定し、mosquitto_subscribe 関数を使用して $iothub/twin/res/# トピックをサブスクライブします。

次のスニペットは、mosquitto_publish を使用してデバイス ツインで報告されたプロパティを設定する connect_callback 関数を示しています。 デバイスはメッセージを $iothub/twin/PATCH/properties/reported/?$rid=%d トピックに発行します。 %d の値は、デバイスがトピックにメッセージを発行するたびにインクリメントされます。

void connect_callback(struct mosquitto* mosq, void* obj, int result)
{
    // ... other code ...  

    printf("\r\nSetting device twin reported properties....\r\n");

    char msg[] = "{\"temperature\": 32}";
    char mqtt_publish_topic[64];
    snprintf(mqtt_publish_topic, sizeof(mqtt_publish_topic), DEVICETWIN_MESSAGE_PATCH, device_twin_request_id++);

    int rc = mosquitto_publish(mosq, NULL, mqtt_publish_topic, sizeof(msg) - 1, msg, 1, true);
    if (rc != MOSQ_ERR_SUCCESS)

    // ... other code ...  
}

デバイスは $iothub/twin/res/# トピックをサブスクライブし、IoT ハブからメッセージを受信すると、message_callback 関数によって処理されます。 サンプルを実行すると、message_callback 関数は2 回呼び出されます。 初回では、デバイスは IoT ハブから報告されたプロパティの更新に対する応答を受け取ります。 その後、デバイスはデバイス ツインを要求します。 2 回目では、デバイスは要求されたデバイス ツインを受信します。 次のスニペットは、message_callback 関数を示しています。

void message_callback(struct mosquitto* mosq, void* obj, const struct mosquitto_message* message)
{
    printf("Device twin message '%.*s' for topic '%s'\r\n", message->payloadlen, (char*)message->payload, message->topic);

    const char patchTwinTopic[] = "$iothub/twin/res/204/?$rid=0";
    const char getTwinTopic[]   = "$iothub/twin/res/200/?$rid=1";

    if (strncmp(message->topic, patchTwinTopic, sizeof(patchTwinTopic) - 1) == 0)
    {
        // Process the reported property response and request the device twin
        printf("Setting device twin properties SUCCEEDED.\r\n\r\n");

        printf("Getting device twin properties....\r\n");

        char msg[] = "{}";
        char mqtt_publish_topic[64];
        snprintf(mqtt_publish_topic, sizeof(mqtt_publish_topic), DEVICETWIN_MESSAGE_GET, device_twin_request_id++);

        int rc = mosquitto_publish(mosq, NULL, mqtt_publish_topic, sizeof(msg) - 1, msg, 1, true);
        if (rc != MOSQ_ERR_SUCCESS)
        {
            printf("Error: %s\r\n", mosquitto_strerror(rc));
        }
    }
    else if (strncmp(message->topic, getTwinTopic, sizeof(getTwinTopic) - 1) == 0)
    {
        // Process the device twin response and stop the client
        printf("Getting device twin properties SUCCEEDED.\r\n\r\n");

        mosquitto_loop_stop(mosq, false);
        mosquitto_disconnect(mosq); // finished, exit program
    }
}

詳細については、「MQTT を使用してデバイス ツインの報告されたプロパティを更新する」および「MQTT を使用してデバイス ツイン プロパティを取得する」を参照してください。

リソースをクリーンアップする

引き続きその他のデバイス開発者の記事に進む場合は、この記事で使用したリソースを保持し、再使用できます。 それ以外の場合、さらに多くの料金が発生するのを防ぐために、この記事で作成したリソースは削除してください。

次の Azure CLI コマンドを使用してリソース グループ全体を削除することで、ハブと登録済みデバイスの両方を一度に削除できます。 ただし、これらのリソースと保持したい他のリソースがリソース グループを共有している場合、このコマンドは使用しないでください。

az group delete --name <YourResourceGroupName>

IoT ハブのみを削除するには、Azure CLI を使用して次のコマンドを実行します。

az iot hub delete --name <YourIoTHubName>

IoT ハブに登録したデバイス ID のみを削除するには、Azure CLI を使用して次のコマンドを実行します。

az iot hub device-identity delete --hub-name <YourIoTHubName> --device-id <YourDeviceID>

また、クローンしたサンプル ファイルをお使いの開発用マシンから削除することもできます。

次の手順

Mosquitto MQTT ライブラリを使用して IoT Hub と通信する方法を学習したところで、次の手順を確認することをお勧めします。