实战指南:ESP32通过MQTT无缝对接阿里云与华为云IoT平台

2026/03/26 MQTT 共 7679 字,约 22 分钟

实战指南:ESP32通过MQTT无缝对接阿里云与华为云IoT平台

在万物互联的时代,嵌入式设备上云已成为智能硬件开发的标配。ESP32作为一款高性价比的Wi-Fi & Bluetooth双模芯片,是连接云平台的理想选择。而MQTT作为一种轻量级的发布/订阅消息传输协议,则是物联网设备与云端通信的“普通话”。本文将手把手教你如何让ESP32通过MQTT协议,分别连接到国内两大主流云平台:阿里云物联网平台和华为云IoT设备接入服务。

一、准备工作与环境搭建

1.1 硬件与软件准备

  • 硬件:ESP32开发板(如ESP32-DevKitC)、USB数据线、电脑。
  • 软件
    • Arduino IDE(需安装ESP32开发板支持包)。
    • 必要的库:PubSubClient(用于MQTT通信),可通过Arduino库管理器安装。
  • 云平台账户:分别注册阿里云和华为云账号。

1.2 MQTT连接核心概念:三元组

无论是阿里云还是华为云,设备基于MQTT协议连接时,都需要一个唯一的身份标识,通常由“三元组”构成:

  • ProductKey: 产品标识,代表一类设备。
  • DeviceName: 设备名称,在同一个产品下唯一。
  • DeviceSecret: 设备密钥,用于生成连接密码。

连接时,客户端ID、用户名和密码都需要根据三元组和特定规则动态生成,以确保安全。

二、连接阿里云物联网平台

2.1 阿里云平台侧配置

  1. 进入物联网平台:登录阿里云控制台,搜索并进入“物联网平台”。
  2. 创建产品:在“设备管理”->“产品”中,创建一个新产品(例如,选择“自定义品类”,数据格式为“透传/自定义”)。
  3. 创建设备:在刚创建的产品下,添加一个设备,记下系统分配的ProductKeyDeviceNameDeviceSecret
  4. 获取连接信息
    • MQTT服务器地址{ProductKey}.iot-as-mqtt.{region}.aliyuncs.com,其中{region}是你的地域代码,如cn-shanghai
    • 端口1883(非TLS)或8883(TLS)。

2.2 ESP32端代码实现

我们需要根据阿里云的规则生成clientIdusernamepassword。密码的生成较为复杂,通常需要使用HMAC-SHA256算法对设备密钥、客户端ID等信息进行签名。

以下是一个简化示例,实际生产环境中建议使用更安全的密码生成方式(如在后端生成后下发,或在设备端使用加密芯片)。

#include <WiFi.h>
#include <PubSubClient.h>

// WiFi配置
const char* ssid = "Your_WiFi_SSID";
const char* password = "Your_WiFi_Password";

// 阿里云三元组
const char* productKey = "a1**********";
const char* deviceName = "esp32_device_01";
const char* deviceSecret = "your_device_secret_here";

// 阿里云MQTT连接参数
const char* mqttServer = "a1**********.iot-as-mqtt.cn-shanghai.aliyuncs.com";
const int mqttPort = 1883;

// 主题定义 (Pub: 发布, Sub: 订阅)
// 上行(设备->云端)
char pubTopic[100];
// 下行(云端->设备)
char subTopic[100];

WiFiClient espClient;
PubSubClient client(espClient);

void setupWiFi() {
  delay(10);
  Serial.println();
  Serial.print("Connecting to ");
  Serial.println(ssid);
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("");
  Serial.println("WiFi connected");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());
}

// 生成阿里云MQTT连接参数(简化版,密码生成需补充完整HMAC-SHA256逻辑)
void generateAliyunMqttInfo(char* clientId, char* username, char* passwordOut) {
  // 1. 生成clientId
  // 格式: clientId="<deviceName>|<securemode=3,signmethod=hmacsha256,timestamp=xxx>|"
  // securemode: 3表示TLS直连,2表示TCP直连。这里用2。
  unsigned long timestamp = millis(); // 实际应用应使用更精确的UTC时间
  sprintf(clientId, "%s|securemode=2,signmethod=hmacsha256,timestamp=%lu|", deviceName, timestamp);

  // 2. 生成username
  // 格式: <deviceName>&<productKey>
  sprintf(username, "%s&%s", deviceName, productKey);

  // 3. 生成password(此处为示意,实际需要计算签名)
  // 签名原串: clientId<clientId>deviceName<deviceName>productKey<productKey>timestamp<timestamp>
  // password = HMAC-SHA256(deviceSecret, signContent).toHexString();
  // 此处简化,实际项目务必实现正确的签名算法或从安全渠道获取密码。
  String signContent = "clientId" + String(clientId) + "deviceName" + String(deviceName) + "productKey" + String(productKey) + "timestamp" + String(timestamp);
  // 调用HMAC-SHA256计算签名并转为十六进制字符串,赋值给passwordOut
  // strcpy(passwordOut, "计算出的签名");
  strcpy(passwordOut, "your_calculated_password_here"); // 临时占位
}

void callback(char* topic, byte* payload, unsigned int length) {
  Serial.print("Message arrived [");
  Serial.print(topic);
  Serial.print("]: ");
  for (int i = 0; i < length; i++) {
    Serial.print((char)payload[i]);
  }
  Serial.println();
  // 处理云端下发的指令
}

void reconnect() {
  while (!client.connected()) {
    Serial.print("Attempting MQTT connection to Aliyun...");
    char clientId[256];
    char username[128];
    char mqttPassword[128];
    generateAliyunMqttInfo(clientId, username, mqttPassword);

    if (client.connect(clientId, username, mqttPassword)) {
      Serial.println("connected");
      // 构造订阅主题,例如: /sys/{pk}/{dn}/thing/service/property/set
      sprintf(subTopic, "/sys/%s/%s/thing/service/property/set", productKey, deviceName);
      client.subscribe(subTopic);
      Serial.print("Subscribed to: ");
      Serial.println(subTopic);
    } else {
      Serial.print("failed, rc=");
      Serial.print(client.state());
      Serial.println(" try again in 5 seconds");
      delay(5000);
    }
  }
}

void setup() {
  Serial.begin(115200);
  setupWiFi();
  client.setServer(mqttServer, mqttPort);
  client.setCallback(callback);
}

void loop() {
  if (!client.connected()) {
    reconnect();
  }
  client.loop();

  // 示例:每隔10秒发布一条消息
  static unsigned long lastMsg = 0;
  if (millis() - lastMsg > 10000) {
    lastMsg = millis();
    // 构造发布主题,例如: /sys/{pk}/{dn}/thing/event/property/post
    sprintf(pubTopic, "/sys/%s/%s/thing/event/property/post", productKey, deviceName);
    String payload = "{\"id\":\"123\",\"version\":\"1.0\",\"params\":{\"Temperature\":25.5}}";
    client.publish(pubTopic, payload.c_str());
    Serial.println("Message published to Aliyun");
  }
}

三、连接华为云IoT设备接入服务

3.1 华为云平台侧配置

  1. 进入IoTDA:登录华为云控制台,搜索并进入“设备接入IoTDA”。
  2. 创建产品:在“产品”中,创建一个新产品(例如,选择“自定义产品”,数据格式为“JSON”)。
  3. 创建设备:在刚创建的产品下,注册一个设备,记下系统分配的设备ID(即DeviceName)。ProductKeyDeviceSecret在页面上可以找到(通常ProductKey即产品ID)。
  4. 获取连接信息
    • MQTT服务器地址: 在“总览”页面查看“接入信息”,获取“MQTT接入地址”。
    • 端口1883

3.2 ESP32端代码实现

华为云的密码生成规则与阿里云不同,它使用DeviceSecretclientIdDeviceNametimestamp进行加密签名。

// ... 前面的WiFi和库引入部分与阿里云示例相同 ...

// 华为云三元组
const char* hwProductId = "your_product_id_here"; // 相当于ProductKey
const char* hwDeviceId = "your_device_id_here";   // 相当于DeviceName
const char* hwDeviceSecret = "your_device_secret_here";

// 华为云MQTT连接信息
const char* hwMqttServer = "your-mqtt-endpoint.iot-mqtts.cn-north-4.myhuaweicloud.com"; // 请替换为你的接入地址
const int hwMqttPort = 1883;

// 生成华为云MQTT连接参数(简化版,密码生成需补充完整)
void generateHuaweiCloudMqttInfo(char* clientId, char* username, char* passwordOut) {
  // 1. 生成clientId
  // 格式: <deviceId>
  strcpy(clientId, hwDeviceId);

  // 2. 生成username
  // 格式: <deviceId>@<productId>
  sprintf(username, "%s@%s", hwDeviceId, hwProductId);

  // 3. 生成password(此处为示意)
  // 签名原串: <clientId>:<timestamp>
  // password = BASE64_ENCODE( HMAC-SHA256(deviceSecret, signContent) )
  unsigned long timestamp = millis();
  String signContent = String(clientId) + ":" + String(timestamp);
  // 调用HMAC-SHA256和Base64编码计算密码,赋值给passwordOut
  // strcpy(passwordOut, "计算出的Base64密码");
  strcpy(passwordOut, "your_calculated_base64_password_here"); // 临时占位
}

// 华为云主题定义(物模型主题)
char hwCommandTopic[150]; // 用于接收命令
char hwPropertyTopic[150]; // 用于上报属性

void hwCallback(char* topic, byte* payload, unsigned int length) {
  Serial.print("HW Cloud Message arrived [");
  Serial.print(topic);
  Serial.print("]: ");
  for (int i = 0; i < length; i++) {
    Serial.print((char)payload[i]);
  }
  Serial.println();
}

void hwReconnect() {
  while (!client.connected()) {
    Serial.print("Attempting MQTT connection to Huawei Cloud...");
    char clientId[128];
    char username[128];
    char mqttPassword[256];
    generateHuaweiCloudMqttInfo(clientId, username, mqttPassword);

    if (client.connect(clientId, username, mqttPassword)) {
      Serial.println("connected");
      // 订阅命令下发主题
      sprintf(hwCommandTopic, "$oc/devices/%s/sys/commands/#", hwDeviceId);
      client.subscribe(hwCommandTopic);
      Serial.print("Subscribed to: ");
      Serial.println(hwCommandTopic);
    } else {
      Serial.print("failed, rc=");
      Serial.print(client.state());
      Serial.println(" try again in 5 seconds");
      delay(5000);
    }
  }
}

void setup() {
  Serial.begin(115200);
  setupWiFi(); // 使用同一个WiFi连接函数
  client.setServer(hwMqttServer, hwMqttPort);
  client.setCallback(hwCallback); // 设置华为云的回调
}

void loop() {
  if (!client.connected()) {
    hwReconnect();
  }
  client.loop();

  // 示例:每隔15秒上报一次属性
  static unsigned long lastHwMsg = 0;
  if (millis() - lastHwMsg > 15000) {
    lastHwMsg = millis();
    // 构造属性上报主题
    sprintf(hwPropertyTopic, "$oc/devices/%s/sys/properties/report", hwDeviceId);
    String payload = "{\"services\":[{\"service_id\":\"BasicData\",\"properties\":{\"temperature\":26.8}}]}";
    client.publish(hwPropertyTopic, payload.c_str());
    Serial.println("Property reported to Huawei Cloud");
  }
}

四、关键点与调试建议

  1. 密码安全: 上述代码中的密码生成部分是简化的。在实际产品中,切勿将DeviceSecret硬编码在代码中。推荐的做法是:
    • 在首次烧录时通过配网或蓝牙等方式从手机App获取。
    • 使用芯片的安全存储区域(如ESP32的NVS加密分区)。
    • 对于资源受限设备,可考虑在产线预置或通过安全芯片计算。
  2. 主题(Topic): 阿里云和华为云都有自己定义的主题格式,用于区分属性上报、事件上报、服务调用等不同功能。务必参考官方文档使用正确的主题。

  3. 连接保活PubSubClient需要定期调用client.loop()来维持心跳和处理网络流量。确保loop()函数在loop()主循环中被频繁调用。

  4. 调试工具
    • 串口监视器:查看连接状态和错误码(client.state())。
    • 云平台日志:阿里云和华为云控制台都提供“设备日志”或“消息跟踪”功能,可以查看设备上下线、消息收发记录,是排查问题的利器。
    • 桌面MQTT客户端:如MQTT.fx,可以先用电脑客户端测试连接参数和主题是否正确,再移植到ESP32上。

五、总结

通过本文,我们了解了ESP32连接阿里云和华为云IoT平台的核心流程。虽然两者在连接参数生成和主题定义上有所差异,但底层都基于标准的MQTT协议。掌握这些步骤后,你可以根据项目需求灵活选择云平台,并在此基础上实现更复杂的业务逻辑,如OTA升级、影子设备、规则引擎等,从而构建稳定可靠的物联网解决方案。

下一步:尝试实现安全的密码生成算法,探索云平台提供的设备影子、文件上传、定时任务等高级功能,让你的IoT设备更加智能和强大。

文档信息

Search

    Table of Contents