Share via


教學課程 - 使用 MQTT 在不使用裝置 SDK 的情況下開發 IoT 裝置用戶端

您應該盡可能使用其中一個 Azure IoT 裝置 SDK 來建置 IoT 裝置用戶端。 不過,在使用記憶體限制裝置等案例中,您可能需要使用 MQTT 連結庫來與 IoT 中樞通訊。

本教學課程中的範例會使用 Eclipse Mosquitto MQTT 連結庫。

在本教學課程中,您會了解如何:

  • 建置 C 語言裝置用戶端範例應用程式。
  • 執行使用 MQTT 連結庫來傳送遙測的範例。
  • 執行使用 MQTT 連結庫來處理從 IoT 中樞傳送的雲端到裝置訊息的範例。
  • 執行使用 MQTT 連結庫來管理裝置對應項的範例。

您可以使用 Windows 或 Linux 開發電腦來完成本教學課程中的步驟。

如果您沒有 Azure 訂用帳戶,請在開始前建立免費帳戶

必要條件

備妥環境以使用 Azure CLI

開發電腦必要條件

如果您使用 Windows:

  1. 安裝 Visual Studio (Community、Professional 或 Enterprise)。 請務必使用 C++ 工作負載啟用桌面開發。

  2. 安裝 CMake。 啟用 [ 將 CMake 新增至所有用戶 的系統 PATH] 選項。

  3. 安裝 x64 版Mosquitto

如果您使用 Linux:

  1. 執行下列命令以安裝建置工具:

    sudo apt install cmake g++
    
  2. 執行下列命令以安裝 Mosquitto 用戶端連結庫:

    sudo apt install libmosquitto-dev
    

設定您的環境

如果您還沒有IoT中樞,請執行下列命令,在名為 mqtt-sample-rg的資源群組中建立免費層IoT中樞。 命令會使用名稱 my-hub 作為要建立之IoT中樞名稱的範例。 選擇 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 中樞註冊裝置。 下列命令會在名為的IoT中樞註冊名為 mqtt-dev-01my-hub的裝置。 請務必使用IoT中樞的名稱:

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

使用下列命令來建立 SAS 令牌,以授與裝置對 IoT 中樞的存取權。 請務必使用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 一個命令中的 選項會將令牌持續時間延長至兩小時。 如果在準備好使用之前到期,請產生新的。 您也可以建立持續時間較長的令牌。 若要深入瞭解,請參閱 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中樞名稱、裝置標識碼和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/#"

函式會mosquitto_message_callback_set使用函main式來設定回呼來處理從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式來設定回呼來處理從IoT中樞傳送的訊息,並使用函mosquitto_subscribe式來$iothub/twin/res/#訂閱mosquitto_connect_callback_set主題。

下列代碼段顯示 connect_callback 用來 mosquitto_publish 在裝置對應項中設定報告屬性的函式。 裝置會將訊息發佈至 $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 式會呼叫兩次。 裝置第一次收到來自IoT中樞對報告屬性更新的回應。 裝置接著會要求裝置對應項。 第二次,裝置會收到要求的裝置對應項。 下列代碼段顯示 函式 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中樞註冊的裝置身分識別,請使用 Azure CLI 執行下列命令:

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

您也可以從開發計算機中移除複製的範例檔案。

下一步

既然您已瞭解如何使用 Mosquitto MQTT 連結庫與 IoT 中樞 通訊,建議的下一個步驟是檢閱: