共用方式為


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

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

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

在本教學課程中,您將瞭解如何:

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

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

如尚未擁有 Azure 訂用帳戶,請在開始之前先建立免費帳戶

先決條件

開發電腦必要條件

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

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

  3. 安裝 x64 版Mosquitto

備妥環境以使用 Azure CLI

設定您的環境

如果您還沒有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 中樞中註冊裝置。 下列命令會在名為 my-hub 的 IoT 中樞中註冊一個名為 mqtt-dev-01 的裝置。 請確保使用您 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

這很重要

本文包含使用共用存取簽章 (也稱為對稱金鑰驗證) 連線裝置的步驟。 此驗證方法方便進行測試和評估,但使用 X.509 憑證來驗證裝置是更安全的方法。 若要深入瞭解,請參閱 IoT解決方案 > 連線安全性的安全性最佳做法

複製範例存放庫

使用下列命令,將範例存放庫複製到本機電腦上的適當位置:

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 設定使用者名稱和密碼以進行物聯網中樞的身份驗證。 密碼是您為裝置建立的 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中樞傳送雲端到裝置訊息。 請務必使用您的物聯網中心的名稱:

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函式來設定回呼以處理從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 式會呼叫兩次。 裝置第一次收到來自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 中樞通訊,建議的下一個步驟是檢閱 GitHub 上的 MQTT 應用程式範例