← 返回教程库

MQTT 上云:为什么 IoT 都爱用它

最后更新 2026-06-22
L3 · 联网与 IoT ⏱ 约 18 分钟 🟢 软件/低风险
你将学到
  • 理解 MQTT 的发布/订阅模型,以及 Broker、topic、QoS、retain、遗嘱各是什么
  • 用 ESP-IDF 内置的 esp-mqtt 连上公共 Broker,在事件回调里订阅 + 发布 + 控灯
  • 想透 esp-mqtt 为什么不需要 client.loop() 轮询——后台任务驱动、断线自动重连
  • 知道为什么 IoT 偏爱它,以及公共/自建/平台 Broker 怎么选

你做联网设备做到第三个,多半会撞上一个绕不开的缩写:MQTT。第一个项目你可能用 HTTP 自己写了个接口;第二个嫌它笨重换了别的;到第三个,你大概率会掉进 MQTT,然后发现物联网世界几乎都在说这门"语言"——巴法云、OneNET、Home Assistant、各家云平台,底层全是它。

理解 MQTT,你理解的不是一个库,而是"设备到底怎么和云、和彼此说话"这件事。这一节先把原理讲透,再用 ESP32-S3 连一个真实可用的 Broker,做一个能被远程控制的设备。

读这篇前,你得先跑通让 ESP32-S3 连上 WiFi那节——本篇直接复用你那套 wifi_init_sta() 骨架,先连上路由器拿到 IP,再去连 Broker。另外提前打个预防针:ESP-IDF 用的 MQTT 库叫 esp-mqtt(系统内置组件,不用额外装),它和 Arduino 那个 PubSubClient 的写法差很远——esp-mqtt 也是事件驱动的,跟 WiFi 那节一个范式。如果你之前用过 Arduino 的 client.loop(),先把那套"在主循环里反复喂"的肌肉记忆放一边,这正是本篇要讲透的核心。


一个"群聊"的比喻:发布 / 订阅

理解 MQTT 最快的路子,是把它想成一个微信群

普通的 HTTP 是"打电话":A 要找 B,得知道 B 的地址,拨过去,B 接了才能说话。两边必须同时在线、必须互相知道对方是谁。设备一多,这种点对点的关系就乱成一团。

MQTT 换了个思路,核心就四个词——Broker、topic、publish、subscribe

  • Broker(服务器/群主):群里的中间人。所有消息都先发给它,由它转发。设备之间从不直接通话,全靠 Broker 中转。
  • topic(主题/群里的话题标签):消息按"话题"分类,写成一条带斜杠的路径,比如 home/livingroom/temp(客厅温度)、qifudev/demo/led(某个灯的开关)。topic 不用提前注册,发的时候直接写就行。
  • publish(发布/在群里发言):设备把一条消息发到某个 topic。它不关心谁会收到,发完就完事。
  • subscribe(订阅/关注某个话题):设备关注某个 topic,从此只要有人往这个 topic 发消息,Broker 就推给它。

关键在最后这句:发的人和收的人,互相不认识,也不需要认识。温度传感器只管往 home/livingroom/temp 发数字,它不知道是谁在看;你的手机 App 订阅了这个 topic,就开始收数字,它也不知道是哪块板子发的。两边唯一的约定,就是那个 topic 字符串。

这种"只认话题、不认对方"的松耦合,就是 MQTT 的灵魂。


为什么 IoT 偏爱它:轻、省电、解耦

知道了原理,再看它凭什么成为物联网的默认选择。三个理由,每个都直接对应硬件项目的真实痛点。

MQTT 的协议头小到可以忽略——一条最小的消息,协议开销只有 2 个字节。对 ESP32-S3 这种内存只有几百 KB、还要分给 WiFi 栈和 TCP/IP 协议栈的小设备,这点很要命。HTTP 每次请求都要带一大坨 header,在不稳定的家用 WiFi 或 4G 信号下,包越小越不容易出错、重传越快。

省电

MQTT 用的是一条长连接 + 按需推送。设备连上 Broker 后,这根 TCP 连接一直挂着,平时几乎不耗流量;有消息了 Broker 主动推过来。对比之下,HTTP 轮询是"每隔几秒主动去问一次有没有新消息"——大部分时候问了个寂寞,却实打实地唤醒了射频模块、耗了电。对靠电池跑几个月的传感器,长连接推送能把功耗压下一大截。

解耦

这是最被低估、却最影响项目长期演化的一点。传感器端只管 publish,控制端只管 subscribe,中间靠 topic 对接。今天你想再加一块温度板,新板子往同一个 topic 发就行,App 一行代码都不用改;明天你想换个手机 App,订阅同样的 topic 即可,板子那边毫无感知。加设备、换前端、改逻辑,互不牵连——项目越长大,这个好处越值钱。


三个绕不开的术语:QoS、retain、遗嘱

上手代码前,先把 MQTT 这三个高频术语过一遍。它们在 esp-mqtt 的接口里都是一个个参数,你早晚要填,先知道是什么。

  • QoS(消息质量等级):决定一条消息"送达"有多可靠。0 是"发了就不管"(fire and forget,可能丢);1 是"保证至少送到一次"(可能重复);2 是"保证恰好一次"(最重、握手最多)。入门用默认的 0 完全够;等你做的东西"丢一条就出事"(比如远程开门锁)时,再上 12 用得很少。
  • retain(保留消息):发布时把 retain 标记设成 1,Broker 会把这条消息存下来,之后任何新订阅这个 topic 的设备,一连上就立刻收到这条"最后的状态"。典型用途:设备上报"我是开还是关",新上线的 App 不用等下一次上报,立刻知道当前状态。普通的瞬时数据(比如每秒的温度)设 0 就行。
  • 遗嘱(Last Will,LWT):连接时预先告诉 Broker 一条"遗言"——"如果我哪天没打招呼就断了(断电、崩溃、网断),你替我往某个 topic 发这条消息"。典型用途:设备意外掉线时,Broker 自动往 .../status 发一个 offline,订阅方立刻知道"这设备没了",而不用傻等心跳超时。

这三个加上前面的发布/订阅,就是 MQTT 的全部核心概念了。下面动手时你会看到它们具体怎么填。


第一步:先把整段代码跑通

光说不练没用。我们用 ESP-IDF 内置的 esp-mqtt(头文件 mqtt_client.h,不用在 idf_component.yml 里加任何依赖,它是协议组件的一部分),连一个公共测试 Brokerbroker.emqx.io,EMQX 官方免费提供,谁都能连,专门用来练手),做一件完整的事:

ESP32-S3 订阅 qifudev/demo/led 这个 topic,谁往它发 on 就开灯、发 off 就关灯;同时每隔 5 秒往 qifudev/demo/status 报一次"我还活着"。

下面是完整可跑main/main.c。它假设你已经按 WiFi 那节wifi_init_sta() 写好了——这里只把 MQTT 部分摊开,开头那段 WiFi 初始化用一行 wifi_init_sta() 占位(把上一篇的实现贴进来即可,本篇不再重复):

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/gpio.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include "mqtt_client.h"      // esp-mqtt:ESP-IDF 内置,无需额外依赖

#define LED_GPIO 2            // 板载 LED,多数开发板是 GPIO2

static const char *TAG = "mqtt";

#define BROKER_URI  "mqtt://broker.emqx.io:1883"  // 公共测试 Broker,明文 1883
#define TOPIC_SUB   "qifudev/demo/led"            // 订阅:收开关灯命令
#define TOPIC_PUB   "qifudev/demo/status"         // 发布:上报状态

// 上一篇的 wifi_init_sta():连上 2.4G WiFi、拿到 IP 才返回。把它的实现贴进来。
extern void wifi_init_sta(void);

// MQTT 事件回调:连上、收消息、断开,全在这里接住——和 WiFi 那节一个范式
static void mqtt_event_handler(void *arg, esp_event_base_t base,
                               int32_t event_id, void *event_data) {
    esp_mqtt_event_handle_t event = event_data;
    esp_mqtt_client_handle_t client = event->client;

    switch ((esp_mqtt_event_id_t) event_id) {
    case MQTT_EVENT_CONNECTED:
        ESP_LOGI(TAG, "已连上 Broker");
        // 连上后立刻订阅——订阅必须在连接成功之后做
        esp_mqtt_client_subscribe(client, TOPIC_SUB, 0);   // 第三参数是 QoS
        break;

    case MQTT_EVENT_DATA:                                  // 收到订阅的消息
        // event->data 不带结尾 '\0',必须按 data_len 来读
        ESP_LOGI(TAG, "收到 [%.*s]: %.*s",
                 event->topic_len, event->topic,
                 event->data_len, event->data);
        if (event->data_len == 2 && strncmp(event->data, "on", 2) == 0)
            gpio_set_level(LED_GPIO, 1);                   // 开灯
        else if (event->data_len == 3 && strncmp(event->data, "off", 3) == 0)
            gpio_set_level(LED_GPIO, 0);                   // 关灯
        break;

    case MQTT_EVENT_DISCONNECTED:
        ESP_LOGW(TAG, "和 Broker 断开了(esp-mqtt 会自动重连,不用你管)");
        break;

    default:
        break;
    }
}

void app_main(void) {
    ESP_ERROR_CHECK(nvs_flash_init());   // WiFi 校准数据存 NVS,连网前必做
    wifi_init_sta();                     // 先连网拿到 IP,再连 Broker

    gpio_reset_pin(LED_GPIO);
    gpio_set_direction(LED_GPIO, GPIO_MODE_OUTPUT);

    // 5.x 用 .broker.address.uri 一把搞定地址+端口+协议;老式 .host/.port 见下文
    esp_mqtt_client_config_t cfg = {
        .broker.address.uri = BROKER_URI,
    };
    esp_mqtt_client_handle_t client = esp_mqtt_client_init(&cfg);

    // 把回调挂上:ESP_EVENT_ANY_ID 表示这个回调接所有 MQTT 事件
    esp_mqtt_client_register_event(client, ESP_EVENT_ANY_ID,
                                   mqtt_event_handler, NULL);
    esp_mqtt_client_start(client);       // 启动后台任务,开始连 Broker

    // 主循环只管定时上报,收发/心跳/重连全是后台任务在跑,不用你 loop()
    while (1) {
        esp_mqtt_client_publish(client, TOPIC_PUB, "esp32 alive", 0, 0, 0);
        // 参数:client, topic, 数据, len(0=自动strlen), QoS=0, retain=0
        vTaskDelay(pdMS_TO_TICKS(5000)); // 每 5 秒一次
    }
}
📌 说明

别忘了 #include <string.h>strncmp 用到)。另外这段为了聚焦 MQTT,把上一篇的 wifi_init_sta() 实现省成一行 extern 声明——真编译时要么把它的代码贴进同一个文件,要么拆成独立的 wifi.c

在工程目录下一条命令编译、烧录、看日志:

idf.py build flash monitor

你应该看到什么

idf.py monitor 的日志里,先滚出 WiFi 那节那串(拿到 IP: 192.168.x.x),紧接着 esp-mqtt 自己打几条底层握手日志,然后是你回调里那句:

I (3210) mqtt: 已连上 Broker

从这一刻起,板子就在线了——它每 5 秒往 qifudev/demo/status 发一条 esp32 alive,同时竖着耳朵等 qifudev/demo/led 上的命令。等会儿我们用手机往那个 topic 发 on,串口会立刻打出 收到 [qifudev/demo/led]: on,板载灯随即亮起。

如果你迟迟看不到"已连上 Broker",先回头确认 WiFi 那步真的拿到了 IP——没网就连不上 Broker,这是最常见的顺序问题。


第二步:把这套 esp-mqtt 骨架讲透

例子能跑只是第一步。这套代码是几乎所有 ESP-IDF MQTT 设备的骨架,后面 AI 控制、巴法云、网关、远程控制全复用它。把这几处理解透,你换任何项目都能照搬。

配置:.broker.address.uri 一把搞定

esp_mqtt_client_config_t cfg = {
    .broker.address.uri = "mqtt://broker.emqx.io:1883",
};

ESP-IDF 5.x 的 esp-mqtt 用一个 URI 把"协议 + 地址 + 端口"一次性表达完:mqtt:// 是明文,mqtts:// 是 TLS 加密,端口跟在冒号后。这比分开填清爽。

📌 说明

如果你翻到的是 ESP-IDF 4.x 的老教程,会看到 .host = "broker.emqx.io".port = 1883 这种老式分字段写法——那是 5.0 之前的接口,5.x 里已被 .broker.address.uri 取代(老字段虽还能用但会告警)。认准你装的是哪个大版本,别把两套混着填。

初始化 + 注册回调 + 启动:三步走

esp_mqtt_client_handle_t client = esp_mqtt_client_init(&cfg);
esp_mqtt_client_register_event(client, ESP_EVENT_ANY_ID, mqtt_event_handler, NULL);
esp_mqtt_client_start(client);

这三行是 esp-mqtt 的固定流程,对应 Arduino PubSubClient 的"new 一个 client → setServer → setCallback → connect",但范式完全不同:

  • esp_mqtt_client_init(&cfg):拿配置造一个 client 句柄。还没连。
  • esp_mqtt_client_register_event(...):把你的回调挂到这个 client 上。ESP_EVENT_ANY_ID 表示"所有 MQTT 事件都走这个回调",进回调后再用 event_id 分流(连上/收数据/断开)。这和 WiFi 那节 esp_event_handler_instance_register 是同一套事件机制。
  • esp_mqtt_client_start(client)真正开始干活的一步。它在后台起一个 MQTT 任务,由这个任务去连 Broker、握手、保活、收发。连上后系统抛 MQTT_EVENT_CONNECTED,你的回调被叫起来。

关键差异:不需要 client.loop()

这是 esp-mqtt 和 Arduino PubSubClient 最大的不同,也是最值钱的一句:

// Arduino 旧写法(对比用,ESP-IDF 不这么干)
void loop() {
  client.loop();   // 必须每一轮都喂它,否则收发停摆、心跳断、连接被踢
}

Arduino 里你必须在 loop()每一轮都调 client.loop(),MQTT 的收发、心跳保活全靠你这一下下地喂;漏了它连接很快被当成掉线断开。新手最容易栽在这——主循环里干点别的耗时活,没及时回来喂 client.loop(),MQTT 就莫名其妙断了。

esp-mqtt 把这个负担彻底拿掉了。 esp_mqtt_client_start() 之后,收发、心跳、断线重连全在它自己的后台任务里跑,跟你的主循环完全解耦。所以上面代码的 while(1) 里只有"定时发一条状态",没有任何"喂 MQTT"的动作——你的主循环爱干嘛干嘛,MQTT 在背后自顾自地稳稳运转。这跟 WiFi 那节"事件驱动取代 while 轮询"是同一个味道:从"忙着喂"变成"挂上就不用管",这就是产品级写法。

收消息:MQTT_EVENT_DATA 里按 data_len

case MQTT_EVENT_DATA:
    ESP_LOGI(TAG, "收到 [%.*s]: %.*s",
             event->topic_len, event->topic,
             event->data_len, event->data);

所有订阅来的消息都从 MQTT_EVENT_DATA 进。注意 event->data 是一段裸字节、不带结尾 \0,长度在 event->data_len 里。所以打印和比较都得按长度来%.*s.* 就是先吃一个长度参数),千万别把它当 C 字符串直接 strcmp——那会读到内存里的脏数据。判断 on/off 时我用 data_lenstrncmp 双重确认,就是这个道理。

📌 说明

大消息(payload 超过缓冲区)时,esp-mqtt 会把一条消息分多次 MQTT_EVENT_DATA 抛给你,靠 event->current_data_offsetevent->total_data_len 拼。入门发的都是 on/off 这种几字节小消息,不会触发分片;等你以后传大 JSON 再处理这茬。

发布:esp_mqtt_client_publish 六个参数

esp_mqtt_client_publish(client, TOPIC_PUB, "esp32 alive", 0, 0, 0);
//                      client  topic      data           len qos retain

六个参数对着记:句柄、topic、数据指针、数据长度(填 0 让它自动 strlen,发二进制时才要填真实长度)、QoS、retain。比如你要发一条"开机后一直保留的状态",就把最后两个填成 1, 1(QoS 1 + retain),新订阅的 App 一连上就能拿到。

订阅:连上之后,在回调里做

case MQTT_EVENT_CONNECTED:
    esp_mqtt_client_subscribe(client, TOPIC_SUB, 0);  // 第三参数 QoS

订阅一定要放在 MQTT_EVENT_CONNECTED——没连上 Broker,订阅无从谈起。把它写在连接成功事件里还有个隐藏好处:断线重连后会再次触发 MQTT_EVENT_CONNECTED,订阅自动重做一遍。这正好避开了 Arduino 那个经典坑(重连后忘了补订阅、从此收不到消息)——在 esp-mqtt 里你照着这个范式写,重连+重订阅是天然就对的。


topic 怎么设计:几条小建议

topic 是 MQTT 项目的"地图",一开始随手起名,设备一多就会乱。几条经验:

  • 用斜杠分层,从大到小家/房间/设备/属性,比如 home/bedroom/lamp/switch。结构清晰,将来按 home/bedroom/# 一把订阅整个卧室也方便(# 是通配符,订阅这一层及其下所有 topic)。
  • 加个项目前缀:像例子里的 qifudev/,因为公共 Broker 是大家共用的,加前缀能避免你的 topic 和别人撞车、互相收到对方的消息。
  • 命令和状态分开:收命令用一个 topic(.../led),上报状态用另一个(.../status)。别让设备订阅自己发的 topic,否则容易绕成回环。
  • 全小写、不用中文和空格:跨平台兼容最稳。

故障排查:连不上、收不到、老掉线

esp-mqtt 的问题大多集中在这几类,对照查:

现象 / 日志 最可能的原因 怎么办
卡在 WiFi,压根没到"已连上 Broker" 没网,或 wifi_init_sta() 没拿到 IP 先把 l3-wifi 那步跑通,确认日志里有"拿到 IP"
有 IP 了但连不上 Broker,回调进不了 CONNECTED URI 写错,或网络出不去(DNS/防火墙) 核对 mqtt://broker.emqx.io:1883;公司/校园网常封 1883 端口,换网试
连上了,但发命令收不到 没在 MQTT_EVENT_CONNECTED 里订阅,或 topic 拼错 subscribe 放进 MQTT_EVENT_CONNECTED;核对发和订阅的 topic 一字不差
设备每隔几秒反复掉线 clientId 撞了(多设备共用同一 ID) 见下方坑:给每块板子设不同的 client_id
回调里收到的内容是乱码/尾巴有脏字符 data 当成带结尾的字符串读了 event->data_len 读,用 %.*s,别直接当 C 字符串
反复进 MQTT_EVENT_DISCONNECTED 又自动连上 信号弱、Broker 拥堵 这是 esp-mqtt 在自动重连,正常自愈;信号弱就靠近路由器
🚧 避坑

clientId 必须全网唯一。 Broker 用它区分不同设备,两块板子用了同一个 ID,后连上的会把先连的踢下线——你会看到设备莫名其妙反复掉线。esp-mqtt 默认会用芯片信息自动生成一个 client_id,所以单板练手通常撞不上。但你要是手动在 cfg.credentials.client_id 里写死了一个固定字符串,又烧到两块板子上,就必撞。要么别写死、要么每块板拼上芯片唯一 ID(如读 MAC 拼后缀)。

🚧 避坑

端口 1883(URI 里的 mqtt://)是明文的——消息在网络上是裸奔的,谁抓包谁都能看。一旦你的设备要走公网、或传任何不想被看到的内容,就得换成 mqtts://...:8883(MQTT over TLS 加密),并在 cfg.broker.verification.certificate 里配 Broker 的 CA 证书。公共测试 Broker 上无所谓,但只要数据有点价值,明文这条线就不能碰。


公共 Broker、自建、还是平台?

练手时连公共 Broker 最省事,但它有明确的边界,你得心里有数。

  • 公共测试 Broker(如 broker.emqx.io:免费、零配置、连上就能用,做 Demo 和学习的最佳选择。代价是完全公开——任何人都能订阅你的 topic,所以绝对别在上面传敏感数据(位置、密码、能控制门锁的命令)。也没有任何稳定性承诺,随时可能拥堵。

  • 国内 IoT 平台(巴法云、OneNET 等):本质也是托管的 MQTT Broker,但加了账号鉴权、设备管理、数据存储、可视化面板。正式项目、要给别人用的产品,走这条路最省心,国内访问也快。代价是要注册、按设备数可能收费。接巴法云的具体改法见接入巴法云——本篇这套 esp-mqtt 骨架不变,只改 URI 和鉴权字段。

  • 自建 Broker(自己跑一个 EMQX / Mosquitto):数据全在自己手里,topic 想怎么设计怎么设计,没有第三方限制。适合家庭内网、对隐私敏感、或设备量大到平台收费划不来的场景。代价是你得自己运维这台服务器、配鉴权和加密,具体见自建 MQTT Broker

💡 提示

一条务实的成长路径:练手用公共 Broker → 做给自己用的小项目上巴法云/OneNET → 真要规模化或重隐私再自建。别一上来就纠结自建,那是后面的事。让 AI 帮你把"在 MQTT_EVENT_CONNECTED 里订阅、在 MQTT_EVENT_DATA 里判断 on/off 控灯"的事件回调补全,比你从头查文档快得多——但记得让它按 ESP-IDF 5.x 的 esp-mqtt 接口写,别让它给你回 Arduino 的 PubSubClient


动手挑战

代码跑通了,别停在"自己控制自己"。来真正体会一次"远程控制":

  1. 手机当遥控器:在手机上装一个 MQTT 客户端 App(安卓的 "IoT MQTT Panel"、iOS 的 "MQTTool" 都行),连同一个 broker.emqx.io:1883,往 qifudev/demo/led 这个 topic 发 onoff。看着另一个房间的灯随你手指亮灭——这一刻你才真正摸到了物联网的核心体验。
  2. 反过来收状态:在 App 里订阅 qifudev/demo/status,你会看到板子每 5 秒发来的 esp32 alive。把它改成上报一个真实数据——比如接个 UART 串口读来的传感器读数,让手机实时看到设备的状态。
  3. 加 retain,体会"一连上就知道状态":把上报状态那条 publish 的最后一个参数(retain)改成 1,重新烧录跑一会儿,再用手机 App 新订阅 qifudev/demo/status——你会发现刚一订阅就立刻收到最后一条状态,而不用等下一个 5 秒。这就是 retain 的用处。
  4. 加一路:再定义一个 topic 控制第二个 GPIO(接蜂鸣器或另一颗灯),在 MQTT_EVENT_CONNECTED 里多订阅一个、在 MQTT_EVENT_DATA 里多判断一个 topic,体会"加设备只是多订阅一个 topic"的轻松。

卡住了?把你的 main.cidf.py monitor 的完整日志和想实现的效果一起发给 AI,让它帮你改回调和 topic——记得提醒它用 ESP-IDF 5.x 的 esp-mqtt,不是 Arduino。

本篇代码为参考实现,需结合你所用的最新 ESP-IDF 文档自校,尤其是 esp_mqtt_client_config_t 的字段(5.x 的 .broker.address.uri vs 4.x 的 .host/.port)随版本可能微调,以官方 esp-mqtt 文档为准。


小结 · 下一步

  • 你理解了 MQTT 的发布/订阅模型:Broker 中转,设备只认 topic、互不相识,这就是它解耦的本质;也搞清了 QoS、retain、遗嘱三个高频术语各管什么。
  • 你知道了 IoT 偏爱它的三个实打实的理由:轻、省电、解耦
  • 你有了一个完整能跑的 ESP32-S3 esp-mqtt 设备:会在 MQTT_EVENT_CONNECTED 里订阅控灯、定时上报、断线自动重连,还能被手机远程控制。
  • 你拎得清 esp-mqtt 的骨架:init → register_event → start,事件回调里分流连上/收数据/断开,不需要 client.loop() 轮询——后台任务全包了。这跟 WiFi 那节"事件驱动取代 while"是同一个产品级范式。

有了 MQTT 这条"设备说话的通道",下一步把它接进更大的世界。想把设备接进国内 IoT 平台、不折腾鉴权就能远程控制,看接入巴法云;想数据全在自己手里,看自建 MQTT Broker;想让 AI 来解析设备上报的数据、自动决策,看让设备和 AI 用 MQTT 对话。这几篇都直接复用本篇这套 esp-mqtt 骨架——它是你后面所有 MQTT 代码的地基。

往上走,到了 L4 阶梯,你会把这些零散的"会发会收"的设备,组装成一个真正成体系的项目。完整的进阶地图见学习路线

📄 来源 / 自校链接

本文为公开资料整理,非亲测。关键参数与代码请结合实物与下列官方来源验证。

内容有错、看不懂、或想看下一期?告诉我们 →

本文为公开资料的学习整理,非亲测。涉接线/花钱/合规的步骤请结合实物与官方最新资料验证,风险自负。见免责声明