Azure IoT云到设备通信方法详解

本文深入探讨Azure IoT Hub提供的三种云到设备通信方法:C2D消息、直接方法和设备孪生期望属性,包含完整的技术架构说明和TypeScript代码实现示例。

Azure IoT云到设备通信方法

如今,管理云与数百万智能设备之间的通信具有挑战性。假设您正在管理大量设备,需要向所有设备推送关键的状态更新,但许多设备处于离线状态或存在网络连接问题,如何确保消息能够成功传递?

Azure IoT Hub提供三种主要的云到设备通信机制:C2D消息、直接方法和设备孪生中的期望属性。每种方法都针对不同的使用场景设计。本文介绍如何有效选择这些方法来构建可靠、可扩展且高效的IoT解决方案。了解每种方法的适用场景细节将有助于构建稳健可靠的IoT解决方案。

1. 云到设备消息

当消息需要传递到设备但不要求即时性,且需要保证最终交付时,此方法非常理想。

当云发送此消息时,它不会直接推送到设备,而是IoT Hub将该消息存储在消息队列中,等待设备连接并准备接收。这确保了在设备离线或连接不稳定的情况下仍能保证交付。

C2D在需要保证交付但不要求即时性的场景中非常理想。

序列图

2. 直接方法

这些是直接与设备交互的请求和响应。这使其成为需要立即执行操作的场景的完美选择。设想某些设备需要立即执行紧急停止,并且您需要知道何时达到并执行。

直接方法的一个限制是它们具有节流限制。这在设计考虑中很重要,因为如果您有数千台设备突然都需要使用直接方法,可扩展性将成为一个实际问题。

这不适用于设备每几秒调用一次直接方法且扩展到数千台设备的情况。它更适合使用频率较低的命令。

序列图

3. 设备孪生中的期望属性

设备孪生是物理设备在云中的孪生副本。它存储所有设备信息,包括其当前状态和属性。设想您要向所有设备推出新设置,但不想单独定位每个设备的情况,例如在整个设备集上打开或关闭某些功能标志。

设备异步读取期望属性。当设备上线时,它会检查其设备孪生,并按照云希望的方式同步自身配置。这不是即时的,但最终是一致的。

设备可以将其状态报告回云,因此云知道实际发生的情况。

这可以大规模扩展,您只需在云中为设备设置设备孪生中的配置,它最终会同步到设备。

基本上,期望属性用于长期配置管理,使设备保持与云中配置相同的状态。

序列图

以下是我上面解释的方法的服务器端和设备端代码实现,展示如何与IoT Hub通信以发送消息。要向IoT Hub发送任何消息,前提是已设置Azure IoT Hub资源。请参考此链接创建IoT Hub。

IoT Hub云到设备通信的云端实现

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
import { Client, Message, DeviceMethodParams, Registry, Twin } from "azure-iothub";

export class IoTHub {
  private _client: Client | null = null;
  private _registry: Registry | null = null;

  //替换{HubEndpoint}和{key}为您的实际值
  private static readonly CONNECTION_STRING: string =
    "HostName=myhubdeviot.azure-devices.net;SharedAccessKeyName=iothubowner;SharedAccessKey=<yourKeyHere>";

  /**
   * 初始化IoT Hub服务客户端和注册表
   */
  public async init(): Promise<void> {
    if (!this._client) {
      this._client = Client.fromConnectionString(IoTHub.CONNECTION_STRING);
      await this._client.open();
    }
    if (!this._registry) {
      this._registry = Registry.fromConnectionString(IoTHub.CONNECTION_STRING);
    }
  }

  /**
   * 1.发送云到设备消息
   */
  public async sendC2DMessage(
    deviceId: string,
    messageId: string,
    data: any,
    expiryMins: number
  ): Promise<void> {
    if (!this._client) {
      throw new Error("IoTHub客户端未初始化。请调用init()。");
    }

    const message = new Message(JSON.stringify(data));
    message.messageId = messageId;
    message.ack = "none";
    message.expiryTimeUtc = Date.now() + expiryMins * 60 * 1000;

    await this._client.send(deviceId, message);
  }

  /**
   * 2. 在设备上调用直接方法
   */
  public async invokeDirectMethod(
    deviceId: string,
    options: DeviceMethodParams
  ): Promise<any> {
    if (!this._client) {
      throw new Error("IoTHub客户端未初始化。请调用init()。");
    }

    const response = await this._client.invokeDeviceMethod(deviceId, options);
    return response.result;
  }

  /**
   * 3. 更新设备孪生上的期望属性
   */
  public async updateDesiredProperty(
    deviceId: string,
    twinPatch: any,
    etag: string = "*"
  ): Promise<Twin> {
    if (!this._registry) {
      throw new Error("IoTHub注册表未初始化。请调用init()。");
    }

    return await this._registry.updateTwin(deviceId, twinPatch, etag);
  }

  /**
   * 关闭IoT Hub服务客户端
   */
  public async close(): Promise<void> {
    if (this._client) {
      await this._client.close();
      this._client = null;
    }
    this._registry = null;
  }
}

IoT Hub云到设备通信的设备端处理程序实现

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
import { Client as DeviceClient, Message, Twin, DeviceMethodRequest, DeviceMethodResponse } from "azure-iot-device";
import { Mqtt } from "azure-iot-device-mqtt";

// 替换为您的设备连接字符串
const deviceConnectionString = "HostName=myhubdeviot.azure-devices.net;DeviceId=myDeviceId;SharedAccessKey=<deviceKey>";

async function main() {
  const client = DeviceClient.fromConnectionString(deviceConnectionString, Mqtt);

  // 打开连接
  await client.open();
  console.log("设备已连接到IoT Hub。");

  // 1. 处理云到设备消息
  client.on("message", (msg: Message) => {
    const data = msg.getData().toString();
    console.log("收到C2D消息:", data, "消息ID:", msg.messageId);

    // 完成消息以从IoT Hub队列中移除
    client.complete(msg, (err) => {
      if (err) console.error("完成C2D消息时出错:", err);
    });
  });

  // 2. 处理直接方法调用
  client.onDeviceMethod("reboot", async (request: DeviceMethodRequest, response: DeviceMethodResponse) => {
    console.log("直接方法'reboot'被调用,负载为:", request.payload);

    // 模拟设备操作
    const result = { status: "设备将在" + request.payload.delay + "秒后重启" };

    await response.send(200, result, (err) => {
      if (err) console.error("发送方法响应失败:", err);
      else console.log("直接方法响应已发送。");
    });
  });

  // 3. 处理期望属性更新
  client.getTwin((err, twin: Twin) => {
    if (err) {
      console.error("获取孪生时出错:", err);
      return;
    }

    console.log("孪生已初始化。当前期望属性:", twin.properties.desired);

    twin.on("properties.desired", (desiredChange) => {
      console.log("收到期望属性更新:", desiredChange);
    });
  });
}

main().catch((err) => console.error("设备错误:", err));

结论

能够选择正确的IoT通信方法使工程师能够构建真正具有弹性和响应性的系统。下次您考虑设备时,请思考您的系统应支持哪些场景。消息是需要最终到达还是需要立即到达?或者是否需要随时间使用期望属性进行同步?这些问题的答案将决定您的选择,并最终决定IoT系统的成功。

comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计