如果可能,应使用 Azure IoT 设备 SDK 之一以构建 IoT 设备客户端。 但是,在某些情形下(如使用内存有限的设备),可能需要使用 MQTT 库与 IoT 中心进行通信。
本教程中的示例使用 Eclipse Mosquitto MQTT 库。
本教程中,您将学习如何:
- 生成 C 语言设备客户端示例应用程序。
- 运行使用 MQTT 库发送遥测数据的示例。
- 运行使用 MQTT 库以处理从 IoT 中心发送的云到设备消息的示例。
- 运行使用 MQTT 库管理设备上的设备孪生的示例。
可以使用 Windows 或 Linux 开发计算机以完成本教程中的步骤。
如果没有 Azure 订阅,请在开始之前创建一个免费帐户。
先决条件
开发计算机先决条件
安装 Visual Studio(Community、Professional 或 Enterprise 版)。 请务必启用 以 C++ 进行桌面开发 工作负载。
安装 CMake。 启用“将 CMake 添加到所有用户的系统路径”选项。
安装 x64 版本 的 Mosquitto。
为 Azure CLI 准备环境
在 Azure Cloud Shell 中使用 Bash 环境。 有关详细信息,请参阅开始使用 Azure Cloud Shell。
如需在本地运行 CLI 参考命令,请安装 Azure CLI。 如果在 Windows 或 macOS 上运行,请考虑在 Docker 容器中运行 Azure CLI。 有关详细信息,请参阅如何在 Docker 容器中运行 Azure CLI。
如果使用的是本地安装,请使用 az login 命令登录到 Azure CLI。 若要完成身份验证过程,请遵循终端中显示的步骤。 有关其他登录选项,请参阅 使用 Azure CLI 向 Azure 进行身份验证。
出现提示时,请在首次使用时安装 Azure CLI 扩展。 有关扩展详细信息,请参阅使用和管理 Azure CLI 的扩展。
运行 az version 以查找安装的版本和依赖库。 若要升级到最新版本,请运行 az upgrade。
设置环境
如果尚无 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 中心内注册设备。 以下命令在名为 mqtt-dev-01
的 IoT 中心内注册名为 my-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。
重要
本文包括使用共享访问签名(也称为对称密钥身份验证)连接设备的步骤。 此身份验证方法便于测试和评估,但使用 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 中心名称、设备 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 文件。
要生成示例,请在 shell 中运行以下命令:
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"
来自 mosquito to_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/#
主题。
以下代码片段展示了 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 中心通信,建议接下来查看 GitHub 上的 MQTT 应用程序示例。