# AliYun平台 ***Copyright © Quectel Wireless Solutions Co., Ltd. 2026. All rights reserved.*** --- # AliYun简介 阿里云是阿里巴巴旗下自研云计算服务平台,依托自研飞天云计算操作系统,涵盖云计算、存储、物联网、数据库等全栈云产品体系阿里云。平台配套物联网IoT平台,支持MQTT等主流通信协议接入各类嵌入式硬件,实现设备安全鉴权、上下线管理、数据上报与云端指令下发,可无缝联动云服务器、时序数据库等产品,是设备云端接入、数据存储与远程管控的依托载体。 # 资源准备 ### **硬件准备** - 一块移远通信提供的 [pico开发板介绍](../../../%E5%BC%80%E5%8F%91%E6%9D%BF%E4%BB%8B%E7%BB%8D/pico%E5%BC%80%E5%8F%91%E6%9D%BF%E4%BB%8B%E7%BB%8D/pico%E5%BC%80%E5%8F%91%E6%9D%BF%E4%BB%8B%E7%BB%8D.md) 开发板。(下文以该开发板为例) - **USB数据线**(USB-A转USB-C)。 - **PC**(Windows 10/Windows 11)。 ### **软件准备** - **unirtos-toolchain.exe**:编译工具链安装程序,[点此获取]() 。 - **Python**:用于运行unirtos-cli工具,需使用3.9及更高版本。安装步骤详见 [环境搭建](../../../%E5%BF%AB%E9%80%9F%E4%B8%8A%E6%89%8B/%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA/%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA.md)。 - **Git**:unirtos-cli使用该工具拉取SDK、依赖库等,需使用2.20及更高版本。安装步骤详见 [环境搭建](../../../%E5%BF%AB%E9%80%9F%E4%B8%8A%E6%89%8B/%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA/%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA.md)。 - **unirtos-cli**:UniRTOS的命令行工具,用于一键拉取SDK、快速创建工程。安装步骤详见 [环境搭建](../../../%E5%BF%AB%E9%80%9F%E4%B8%8A%E6%89%8B/%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA/%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA.md)。 - **USB驱动**:用于PC识别模块的USB枚举接口,[点此获取]() 。 - **QFlash.exe**:模块固件烧录程序,用于烧录UniRTOS编译生成的固件,[点此获取]() 。 - **EPAT工具**:芯片厂商提供的日志抓取工具,用于查看模块运行日志以分析应用程序执行情况,[点此获取]() 。 - **QCOM工具**:移远通信提供的串口调试工具,用于执行和验证AT命令,[点此获取]() 。 - **程序源码**:位于UniRTOS的官方github仓库,[点此查看](),示例可通过unirtos-cli直接拉取 。 - 关于如何拉取示例程序、编译和烧录固件,请参考 [快速上手](../../../%E5%BF%AB%E9%80%9F%E4%B8%8A%E6%89%8B/%E5%BF%AB%E9%80%9F%E4%B8%8A%E6%89%8B.md)。 # AliYun连接参数获取 ### 获取产品证书 1. 在AliYun控制台新建一个产品。 ```{image} images/image_J5p5bxQAQoJaM7xcRQlcS7U8nwc.webp :width: 1795px :height: 836px :align: center ``` 2. 进入上一步创建好的产品页面,点击 **ProductSecret** 右边的“**查看**”。 ```{image} images/image_SjsDbqcP0octZrxglJTc64UFnrd.webp :width: 1822px :height: 675px :align: center ``` 3. 获取我们连接需要的两个参数:**ProductKey** 和 **ProductSecret。** ```{image} images/image_QXpHbDCqyoDLlrxkbNKchcZGnUd.webp :width: 630px :height: 383px :align: center ``` ### 获取设备证书 1. 创建一个新设备,如图所示。 ```{image} images/image_M6bsbHrjWoUVUQxZxM8cp0Fjnrb.webp :width: 1716px :height: 770px :align: center ``` 2. 选择上一节中新建的产品,自定义设备名后点击确认即可。 ```{image} images/image_PVHNbtpCzoEPNfx7D2wcnDwxnSg.webp :width: 476px :height: 425px :align: center ``` 3. 进入产品页面,点击上方 **DeviceSecret** 右边的“**查看**”。 ```{image} images/image_F7hzbndeJoNJbJxVKQxc8i0UnZc.webp :width: 1699px :height: 724px :align: center ``` 4. 获取 **DeviceName** 和 **DeviceSecret**。 ```{image} images/image_KLSGbjSCiooU7fxddk3c4n89nef.webp :width: 634px :height: 304px :align: center ``` ### 获取MQTT连接参数 1. 点此产品页面的自定义Topic查看发布Topic和订阅Topic。如图: ```{image} images/image_L04AbGSJZoF7QuxUbiccQCKpn8e.webp :width: 1905px :height: 619px :align: center ``` 2. 点击设备栏“设备信息”页面MQTT连接参数右侧的“**查看**”,获取MQTT详细连接参数。如图: ```{image} images/image_W4lhb7mBQo18qkxx7Mbc1vgpntf.webp :width: 1859px :height: 687px :align: center ``` # 主要功能接口 ### 参数配置 - 定义云平台MQTT服务器连接地址、服务器连接端口、客户端ID、订阅主题等参数。 - 定义云平台连接的产品秘钥、设备名称、设备秘钥。 - 定义程序运行的任务栈大小、日志标签、消息队列大小等系统设置。 示例代码: ```c #define ALIOT_BASIC_DEMO_SERVER_ADDR "iot-0**********9q90x.mqtt.iothub.aliyuncs.com" /*!< Aliot server address */ #define ALIOT_BASIC_DEMO_SERVER_PORT 1883 /*!< Aliot server port */ #define ALIOT_BASIC_DEMO_TOPIC_SUB "/k28*****qN/device_name/user/get" /*!< MQTT test topic for subscribing messages */ #define ALIOT_BASIC_DEMO_CLIENTID "k28*****qN.device_name" /*!< Aliot client unique identifier */ #define ALIOT_BASIC_DEMO_PRODUCT_KEY "k28*****qN" /*!< Aliot product key */ #define ALIOT_BASIC_DEMO_DEVICE_NAME "" /*!< Aliot device name */ #define ALIOT_BASIC_DEMO_DEVICE_SECRET "" /*!< Aliot device secret */ #define ALIOT_BASIC_DEMO_TEST_CONTENT "Hello Welcome to test ali mqtt!" /*!< Test message content */ #define ALIOT_BASIC_DEMO_TOPIC_PUB "/k28*****qN/device_name/user/update" /*!< MQTT test topic for publishing messages */ #define ALIOT_BASIC_DEMO_MQTTS 0 /*!< Test MQTT or MQTTS */ #define QOS_LOG_TAG LOG_TAG_DEMO #define ALIOT_BASIC_DEMO_TASK_STACK_SIZE 4096 /*!< ALIOT basic demo task stack size in bytes */ #define ALIOT_BASIC_DEMO_MSG_MAX_CNT 5 #define ALIOT_BASIC_DEMO_SIMID 0 /*!< SIM ID */ #define ALIOT_BASIC_DEMO_PDPID 1 /*!< PDP context ID for network connection */ #define ALIOT_BASIC_DEMO_KEEP_ALIVE 60 /*!< Keep alive time in seconds */ #define ALIOT_BASIC_DEMO_DELIVERY_TIME 5 /*!< Retransmission interval in seconds */ #define ALIOT_BASIC_DEMO_DELIVERY_CNT 3 /*!< Retransmission count */ #define ALIOT_BASIC_DEMO_LEN_MAX 257 ``` ### 结构体定义 - **mqtt_aliot_demo_msg_t**:作为本示例中消息队列的消息结构。 - **mqtt_aliot_demo_ctx_t**:全局保存MQTT连接参数,提供给初始化和反初始化等操作使用。 ```c typedef struct { int msg_id; /*!< Event ID / int param1; /!< Data */ void *argv; /*!< Data */ } mqtt_aliot_demo_msg_t; typedef struct { qosa_uint8_t client_idx; /*!< MQTT client handle / qosa_uint32_t port; /!< MQTT server port / int msg_id; /!< Packet identifier / qosa_msgq_t msgq; /!< Message queue */ qcm_ssl_config_t *ssl_config; /*!< SSL configuration */ char *clientID; /*!< MQTT client identifier */ char *username; /*!< MQTT client username */ char *password; /*!< MQTT client password */ } mqtt_aliot_demo_ctx_t; ``` ### unir_mqtt_aliot_demo_ctx_init - 功能描述: 初始化MQTT配置上下文,为结构体字段分配内存并填充MQTT连接选项。 - 具体步骤: 通过 *qosa_malloc()* 为全局上下文 *g_mqtt_aliot_ctx* 分配内存,并用 *qosa_memset()* 清零。 调用 *qosa_msgq_create()* 创建消息队列,用于异步接收MQTT事件,队列深度为 *ALIOT_BASIC_DEMO_MSG_MAX_CNT*(5条)。 分别为clientID、username、password字段各分配512字节缓冲区,并清零。 初始化基础字段:*msg_id* 置为1(QoS > 0时报文标识符不得为0)、*port* 置为服务器端口号。 向 *mqtt_aliot_option* 结构体填写MQTT协议版本、SIM卡ID、PDP上下文ID、保活时间、重传间隔、重传次数及会话清除标志等参数。 若启用了MQTTS(*ALIOT_BASIC_DEMO_MQTTS* 为非零且编译了 *CONFIG_QCM_VTLS_FUNC*),则分配并配置 *qcm_ssl_config_t* 结构体,设置TLS版本、认证模式、传输协议、握手超时时间(30秒)及SNI开关,并将SSL配置绑定到MQTT配置中。 - 示例代码: ```c static int unir_mqtt_aliot_demo_ctx_init(qcm_mqtt_config_t *mqtt_aliot_option) { int ret = 0; if (mqtt_aliot_option == QOSA_NULL) { return -1; } if (g_mqtt_aliot_ctx == QOSA_NULL) { g_mqtt_aliot_ctx = qosa_malloc(sizeof(mqtt_aliot_demo_ctx_t)); if (g_mqtt_aliot_ctx == QOSA_NULL) { return -1; } } qosa_memset(g_mqtt_aliot_ctx, 0, sizeof(mqtt_aliot_demo_ctx_t)); if (g_mqtt_aliot_ctx->msgq == QOSA_NULL) { ret = qosa_msgq_create(&g_mqtt_aliot_ctx->msgq, sizeof(mqtt_aliot_demo_msg_t), ALIOT_BASIC_DEMO_MSG_MAX_CNT); if (ret != QOSA_OK) { return -1; } } if (g_mqtt_aliot_ctx->clientID == QOSA_NULL) { g_mqtt_aliot_ctx->clientID = qosa_malloc(QOSA_ARRAY_BYTE_512); if (g_mqtt_aliot_ctx->clientID == QOSA_NULL) { return -1; } qosa_memset(g_mqtt_aliot_ctx->clientID, 0, QOSA_ARRAY_BYTE_512); } if (g_mqtt_aliot_ctx->username == QOSA_NULL) { g_mqtt_aliot_ctx->username = qosa_malloc(QOSA_ARRAY_BYTE_512); if (g_mqtt_aliot_ctx->username == QOSA_NULL) { return -1; } qosa_memset(g_mqtt_aliot_ctx->username, 0, QOSA_ARRAY_BYTE_512); } if (g_mqtt_aliot_ctx->password == QOSA_NULL) { g_mqtt_aliot_ctx->password = qosa_malloc(QOSA_ARRAY_BYTE_512); if (g_mqtt_aliot_ctx->password == QOSA_NULL) { return -1; } qosa_memset(g_mqtt_aliot_ctx->password, 0, QOSA_ARRAY_BYTE_512); } g_mqtt_aliot_ctx->msg_id = 1; g_mqtt_aliot_ctx->port = ALIOT_BASIC_DEMO_SERVER_PORT; mqtt_aliot_option->version = QCM_MQTT_VERSION_V3_1_1; mqtt_aliot_option->sim_id = ALIOT_BASIC_DEMO_SIMID; mqtt_aliot_option->pdp_cid = ALIOT_BASIC_DEMO_PDPID; mqtt_aliot_option->kalive_time = ALIOT_BASIC_DEMO_KEEP_ALIVE; mqtt_aliot_option->delivery_time = ALIOT_BASIC_DEMO_DELIVERY_TIME; mqtt_aliot_option->delivery_cnt = ALIOT_BASIC_DEMO_DELIVERY_CNT; mqtt_aliot_option->clean_session = QOSA_TRUE; #ifdef CONFIG_QCM_VTLS_FUNC if (ALIOT_BASIC_DEMO_MQTTS) { mqtt_aliot_option->ssl_enable = QOSA_TRUE; if (g_mqtt_aliot_ctx->ssl_config == QOSA_NULL) { g_mqtt_aliot_ctx->ssl_config = qosa_malloc(sizeof(qcm_ssl_config_t)); if (g_mqtt_aliot_ctx->ssl_config == QOSA_NULL) { return -1; } } qosa_memset(g_mqtt_aliot_ctx->ssl_config, 0x00, sizeof(qcm_ssl_config_t)); g_mqtt_aliot_ctx->ssl_config->ssl_version = QCM_SSL_VERSION_3; g_mqtt_aliot_ctx->ssl_config->auth_mode = QCM_SSL_VERIFY_NULL; g_mqtt_aliot_ctx->ssl_config->transport = QCM_SSL_TLS_PROTOCOL; g_mqtt_aliot_ctx->ssl_config->ssl_negotiate_timeout = 30; g_mqtt_aliot_ctx->ssl_config->ssl_log_debug = 4; g_mqtt_aliot_ctx->ssl_config->sni_enable = QOSA_TRUE; mqtt_aliot_option->ssl_config = g_mqtt_aliot_ctx->ssl_config; } #endif /* CONFIG_QCM_VTLS_FUNC */ return 0; } ``` ### unir_mqtt_aliot_demo_generate_ali_login_info - 功能描述: 按照阿里云物联网平台的签名规范,使用HMAC-SHA1算法生成MQTT登录所需的clientID、username和password。 - 具体步骤: 调用 *qosa_get_system_time_seconds()* 获取当前系统时间戳,通过位运算保证其为正数,用作签名防重放参数。 按照阿里云物联网平台规定的拼接格式,构造待签名内容字符串:*clientId{CLIENTID}deviceName{DEVICE_NAME}productKey{PRODUCT_KEY}timestamp{timestamp}*。 将clientID格式化为 *{CLIENTID}|securemode=3,signmethod=hmacsha1,timestamp={ts}|*。 将username格式化为 *{DEVICE_NAME}&{PRODUCT_KEY}*。 以 *ALIOT_BASIC_DEMO_DEVICE_SECRET* 为密钥,调用 *qcm_hmac_sha1()* 对待签名内容进行HMAC-SHA1运算,得到20字节摘要。 将摘要格式化为40位十六进制字符串,作为MQTT连接的password字段。 - 示例代码: ```c static void unir_mqtt_aliot_demo_generate_ali_login_info(void) { qosa_time_t tv_sec; qosa_int32_t secure_mode = 3; char *content = QOSA_NULL; unsigned char digset[20] = {0}; tv_sec = qosa_get_system_time_seconds(); tv_sec &= 0x7FFFFFFFFFFFFFFF; content = qosa_malloc(QOSA_ARRAY_BYTE_1024); if (content == QOSA_NULL) { return; } qosa_memset(content, 0, QOSA_ARRAY_BYTE_1024); char tv_sec_buf[65] = {0}; qcm_utils_int64_to_text(tv_sec, tv_sec_buf, 10); qosa_snprintf( (char *)content, 1024, "clientId%sdeviceName%sproductKey%stimestamp%s", ALIOT_BASIC_DEMO_CLIENTID, ALIOT_BASIC_DEMO_DEVICE_NAME, ALIOT_BASIC_DEMO_PRODUCT_KEY, tv_sec_buf ); qosa_snprintf(g_mqtt_aliot_ctx->clientID, 512, "%s|securemode=%d,signmethod=hmacsha1,timestamp=%s|", ALIOT_BASIC_DEMO_CLIENTID, (int)secure_mode, tv_sec_buf); qosa_snprintf((char *)g_mqtt_aliot_ctx->username, 512, "%s&%s", ALIOT_BASIC_DEMO_DEVICE_NAME, ALIOT_BASIC_DEMO_PRODUCT_KEY); qcm_hmac_sha1( (unsigned char *)ALIOT_BASIC_DEMO_DEVICE_SECRET, qosa_strlen((char *)ALIOT_BASIC_DEMO_DEVICE_SECRET), (unsigned char *)content, qosa_strlen((char *)content), (unsigned char *)digset ); qosa_snprintf( (char *)g_mqtt_aliot_ctx->password, 512, "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02X", digset[0], digset[1], digset[2], digset[3], digset[4], digset[5], digset[6], digset[7], digset[8], digset[9], digset[10], digset[11], digset[12], digset[13], digset[14], digset[15], digset[16], digset[17], digset[18], digset[19] ); } ``` ### unir_mqtt_aliot_demo_open - 功能描述: 完成MQTT客户端的完整初始化,并建立与阿里云物联网平台的TCP连接(对应MQTT协议中网络层连接建立阶段)。 - 具体步骤: 调用 *qcm_mqtt_client_default_config()* 获取MQTT客户端默认配置作为基础参数。 调用 *unir_mqtt_aliot_demo_ctx_init()* 初始化上下文并将业务相关参数覆写至配置结构体。 调用 *qcm_mqtt_client_create()* 创建MQTT客户端实例,获取 *client_idx* 句柄。 调用 *qcm_mqtt_client_init()* 将MQTT配置和事件回调函数 *unir_mqtt_aliot_demo_event_cb* 绑定到该客户端实例。 调用 *qcm_mqtt_client_open()* 发起TCP连接,目标地址为 *ALIOT_BASIC_DEMO_SERVER_ADDR*,端口为上下文中已配置的端口号。上述步骤任意一步失败均通过 *goto exit* 跳过后续操作并返回-1。 - 示例代码: ```c static int unir_mqtt_aliot_demo_open(void) { int ret = 0; qcm_mqtt_config_t mqtt_aliot_option = {0}; ret = qcm_mqtt_client_default_config(&mqtt_aliot_option); if (ret != QCM_MQTT_RES_OK) { QLOGE("default config failed!"); goto exit; } ret = unir_mqtt_aliot_demo_ctx_init(&mqtt_aliot_option); if (ret != 0) { QLOGE("init failed!"); goto exit; } g_mqtt_aliot_ctx->client_idx = qcm_mqtt_client_create(); QLOGI("init client_idx=%d", g_mqtt_aliot_ctx->client_idx); ret = qcm_mqtt_client_init(g_mqtt_aliot_ctx->client_idx, &mqtt_aliot_option, unir_mqtt_aliot_demo_event_cb, QOSA_NULL); if (ret != QCM_MQTT_RES_OK) { QLOGE("init failed!"); goto exit; } ret = qcm_mqtt_client_open(g_mqtt_aliot_ctx->client_idx, ALIOT_BASIC_DEMO_SERVER_ADDR, g_mqtt_aliot_ctx->port); if (ret != QCM_MQTT_RES_OK) { QLOGE("open failed!"); goto exit; } return 0; exit: return -1; } ``` ### unir_mqtt_aliot_demo_connect - 功能描述: 在TCP连接建立成功后,发送MQTT CONNECT报文,完成与阿里云物联网平台的应用层鉴权握手。 - 具体步骤: 调用 *unir_mqtt_aliot_demo_generate_ali_login_info()* 生成符合阿里云物联网平台签名规范的clientID、username和password。 调用 *qcm_mqtt_client_connect()* 传入上述三组凭据及其长度,发起MQTT协议层的CONNECT请求。 检查返回值,若失败则记录错误日志并返回-1。 - 示例代码: ```c static int unir_mqtt_aliot_demo_connect(void) { qcm_mqtt_eercode_e ret = 0; unir_mqtt_aliot_demo_generate_ali_login_info(); ret = qcm_mqtt_client_connect( g_mqtt_aliot_ctx->client_idx, g_mqtt_aliot_ctx->clientID, qosa_strlen(g_mqtt_aliot_ctx->clientID), g_mqtt_aliot_ctx->username, qosa_strlen(g_mqtt_aliot_ctx->username), g_mqtt_aliot_ctx->password, qosa_strlen(g_mqtt_aliot_ctx->password) ); if (ret != QCM_MQTT_RES_OK) { QLOGE("connect fail=%x", ret); return -1; } return 0; } ``` ### unir_mqtt_aliot_demo_subscribe - 功能描述: 向阿里云物联网平台订阅指定主题,以便接收云端下发的消息。 - 具体步骤: 调用 *qcm_mqtt_client_get_state_info()* 检查客户端当前状态,仅在 *QCM_MQTT_STATE_MQTT_CONNECTED* 状态下才执行订阅操作。 调用 *qcm_mqtt_malloc_data()* 为订阅主题 *ALIOT_BASIC_DEMO_TOPIC_SUB* 分配内存,QoS级别设置为 *QCM_MQTT_AT_LEAST_ONCE_DELIVERY*(QoS 1)。 自增报文标识符后,调用 *qcm_mqtt_client_subscribe()* 发起订阅请求。 订阅操作完成后,调用 *qcm_mqtt_free_data()* 释放主题缓冲区内存。 - 示例代码: ```c static void unir_mqtt_aliot_demo_subscribe(void) { qcm_mqtt_client_info_t client_info = {0}; qcm_mqtt_sub_topic_t sub_topic[QCM_MQTT_MAX_TOPIC_NUM] = {0}; int i, topic_cnt = 0; qcm_mqtt_eercode_e ret = 0; qcm_mqtt_sub_config_t sub_info = {0}; if (qcm_mqtt_client_get_state_info(g_mqtt_aliot_ctx->client_idx, &client_info) != QCM_MQTT_RES_OK || client_info.client_state != QCM_MQTT_STATE_MQTT_CONNECTED) { QLOGE("state err"); return; } topic_cnt = 0; qcm_mqtt_malloc_data(&sub_topic[topic_cnt].topic, qosa_strlen(ALIOT_BASIC_DEMO_TOPIC_SUB), ALIOT_BASIC_DEMO_TOPIC_SUB); sub_topic[topic_cnt].qos = QCM_MQTT_AT_LEAST_ONCE_DELIVERY; topic_cnt += 1; sub_info.msg_id = g_mqtt_aliot_ctx->msg_id++; sub_info.topics = sub_topic; sub_info.topic_cnt = topic_cnt; ret = qcm_mqtt_client_subscribe(g_mqtt_aliot_ctx->client_idx, &sub_info); QLOGD("topic_cnt=%d,err=%x", topic_cnt, ret); for (i = 0; i < topic_cnt; i++) { qcm_mqtt_free_data(&sub_topic[i].topic); } if (ret != QCM_MQTT_RES_OK) { QLOGE("ret=%x", ret); } } ``` ### unir_mqtt_aliot_demo_publish - 功能描述: 向阿里云物联网平台的指定发布主题上报消息内容。 - 具体步骤: 构造 *qcm_mqtt_pub_config_t* 发布配置结构,填写自增的报文标识符、QoS级别(QoS 1)、保留标志(不保留)、发布主题 *ALIOT_BASIC_DEMO_TOPIC_PUB* 及消息载荷 *ALIOT_BASIC_DEMO_TEST_CONTENT*。 调用 *qcm_mqtt_client_publish()* 发送消息,若返回值非 *QCM_MQTT_RES_OK* 则记录错误日志。 - 示例代码: ```c static void unir_mqtt_aliot_demo_publish(void) { qcm_mqtt_quality_of_service_e qos = QCM_MQTT_AT_LEAST_ONCE_DELIVERY; qosa_bool_t retain = QOSA_FALSE; qcm_mqtt_eercode_e ret = 0; qcm_mqtt_pub_config_t pub_info = {0}; pub_info.msg_id = g_mqtt_aliot_ctx->msg_id++; pub_info.qos = qos; pub_info.retain = retain; pub_info.topic.data_ptr = ALIOT_BASIC_DEMO_TOPIC_PUB; pub_info.topic.data_len = qosa_strlen(ALIOT_BASIC_DEMO_TOPIC_PUB); pub_info.payload.data_ptr = (char *)ALIOT_BASIC_DEMO_TEST_CONTENT; pub_info.payload.data_len = qosa_strlen(ALIOT_BASIC_DEMO_TEST_CONTENT); ret = qcm_mqtt_client_publish(g_mqtt_aliot_ctx->client_idx, &pub_info); if (ret != QCM_MQTT_RES_OK) { QLOGE("publish err=%x", ret); } } ``` ### unir_mqtt_aliot_demo_event_handle - 功能描述: 处理来自MQTT客户端的各类异步事件,根据事件类型驱动连接状态机的状态转移,是整个连接流程的核心调度函数。 - 具体步骤: 从消息结构的 *argv* 字段取得 *qcm_mqtt_common_resp_t* 响应结构指针,获取 *client_id*。 按 *msg_id* 分支处理各类事件: **QCM_MQTT_CLIENT_OPEN_EVENT**:TCP连接结果回调。连接成功时调用 *unir_mqtt_aliot_demo_connect()* 发起MQTT鉴权握手。 **QCM_MQTT_CLIENT_CONNECT_EVENT**:MQTT CONNACK回调。连接成功时调用 *unir_mqtt_aliot_demo_subscribe()* 订阅目标主题。 **QCM_MQTT_CLIENT_PUBLISH_EVENT**:消息发布结果回调,记录报文标识符和发送结果。 **QCM_MQTT_CLIENT_SUBSCRIBE_EVENT**:订阅请求发送结果回调。 **QCM_MQTT_CLIENT_SUBACK_EVENT**:SUBACK回调。订阅成功后释放QoS列表内存,随即调用 *unir_mqtt_aliot_demo_publish()* 上报测试消息。 **QCM_MQTT_CLIENT_UNSUBSCRIBE_EVENT**:取消订阅请求发送结果回调。 **QCM_MQTT_CLIENT_UNSUBACK_EVENT**:UNSUBACK回调。取消订阅成功后调用 *unir_mqtt_aliot_demo_disconnect()* 断开MQTT连接。 **QCM_MQTT_CLIENT_STATE_EVENT**:连接状态变更事件,可从 *qcm_mqtt_close_cause_t* 中获取断连原因。 **QCM_MQTT_CLIENT_DISCONNECT_EVENT**:DISCONNECT事件回调,记录协议层返回码。 **QCM_MQTT_CLIENT_CLOSE_EVENT**:TCP连接关闭事件回调。 处理完毕后,释放 *resp_ptr->data* 和 *resp_ptr* 占用的内存,避免内存泄漏。 - 示例代码: ```c static int unir_mqtt_aliot_demo_event_handle(mqtt_aliot_demo_msg_t *msg) { qosa_uint8_t client_idx = 0; int ret = 0; if (msg == QOSA_NULL) { return -1; } qcm_mqtt_common_resp_t *resp_ptr = (qcm_mqtt_common_resp_t *)msg->argv; client_idx = resp_ptr->client_id; QLOGV("msg->msg_id=%d, client_idx=%d", msg->msg_id, client_idx); if (msg->msg_id == QCM_MQTT_CLIENT_OPEN_EVENT) { if (resp_ptr->result == QCM_MQTT_RES_OK) { ret = unir_mqtt_aliot_demo_connect(); if (ret != 0) { QLOGE("connect err"); } } } else if (msg->msg_id == QCM_MQTT_CLIENT_CONNECT_EVENT) { if (resp_ptr->result == QCM_MQTT_RES_OK) { unir_mqtt_aliot_demo_subscribe(); } } else if (msg->msg_id == QCM_MQTT_CLIENT_SUBACK_EVENT) { qcm_mqtt_suback_resp_t *suback_ptr = (qcm_mqtt_suback_resp_t *)resp_ptr->data; if (resp_ptr->result == QCM_MQTT_RES_OK) { if (suback_ptr != QOSA_NULL) { QLOGI("qos_cnt:%d", suback_ptr->qos_cnt); qosa_free(suback_ptr->qoss); //!!! need free } unir_mqtt_aliot_demo_publish(); } } else if (msg->msg_id == QCM_MQTT_CLIENT_UNSUBACK_EVENT) { if (resp_ptr->result == QCM_MQTT_RES_OK) { unir_mqtt_aliot_demo_disconnect(); } } else if (msg->msg_id == QCM_MQTT_CLIENT_STATE_EVENT) { qcm_mqtt_close_cause_t *close_cause_ptr = (qcm_mqtt_close_cause_t *)resp_ptr->data; if (close_cause_ptr != QOSA_NULL) { QLOGV("close cause = %d", close_cause_ptr->close_cause); } } else if (msg->msg_id == QCM_MQTT_CLIENT_DISCONNECT_EVENT) { QLOGE("state disconnect = %d", resp_ptr->pocotrol_code); } if (resp_ptr->data != QOSA_NULL) { qosa_free(resp_ptr->data); } qosa_free(resp_ptr); return ret; } ``` ### unir_mqtt_aliot_demo_recv_new_message - 功能描述: 从MQTT客户端接收缓冲中读取云端下发的订阅消息,并根据主题执行相应的业务处理。 - 具体步骤: 从消息结构的 *argv* 字段取得 *qcm_mqtt_common_resp_t* 指针,进而获取 *qcm_mqtt_new_msg_notify_t* 通知结构中的 *store_id*(消息存储索引)。 调用 *qcm_mqtt_client_read_subcribe_message()*,通过 *store_id* 读取完整的发布消息,包含主题和载荷。 比较接收到的主题与预设的订阅主题 *ALIOT_BASIC_DEMO_TOPIC_SUB*,若匹配则调用 *unir_mqtt_aliot_demo_unsubscribe()* 取消该主题的订阅。 调用 *qcm_mqtt_free_data()* 释放主题和载荷的内存,随后释放 *resp_ptr->data* 和 *resp_ptr*。 - 示例代码: ```c static void unir_mqtt_aliot_demo_recv_new_message(mqtt_aliot_demo_msg_t *msg) { qosa_uint8_t client_idx = 0; qosa_uint32_t payload_len = 0; qcm_mqtt_recv_pub_t recv_pub = {0}; qcm_mqtt_new_msg_notify_t *new_msg_ptr = QOSA_NULL; qosa_uint8_t store_id = 0; qcm_mqtt_common_resp_t *resp_ptr = (qcm_mqtt_common_resp_t *)msg->argv; client_idx = resp_ptr->client_id; new_msg_ptr = (qcm_mqtt_new_msg_notify_t *)resp_ptr->data; store_id = new_msg_ptr->store_id; QLOGD("client_idx=%d, new_message=%d", client_idx, store_id); if (qcm_mqtt_client_read_subcribe_message(client_idx, store_id, &recv_pub) == QCM_MQTT_RES_OK) { QLOGD("topic = %s, payload_len = %d", recv_pub.topic.data_ptr, payload_len); if (qosa_memcmp(ALIOT_BASIC_DEMO_TOPIC_SUB, recv_pub.topic.data_ptr, qosa_strlen(ALIOT_BASIC_DEMO_TOPIC_SUB)) == 0) { unir_mqtt_aliot_demo_unsubscribe(); } qcm_mqtt_free_data(&recv_pub.topic); qcm_mqtt_free_data(&recv_pub.payload); } if (resp_ptr->data != QOSA_NULL) { qosa_free(resp_ptr->data); } qosa_free(resp_ptr); } ``` # 应用逻辑流程图 基于UniRTOS的MQTT客户端连接阿里云物联网平台的完整流程采用全异步消息驱动模式:任务在消息队列上永久阻塞等待,MQTT驱动层通过事件回调 *unir_mqtt_aliot_demo_event_cb* 将异步通知打包投递到队列,任务取出消息后分发至对应处理函数,驱动状态机按序推进,无需轮询。 ```{figure} images/board_XU1OwJuTVhquMVbDtcEcwddYnoe.jpg :align: center :alt: image ``` # 程序运行结果 1. 程序运行后,进入阿里云物联网平台后台设备页面,设备会显示上线。如图: ```{image} images/image_YkVMbyf33okkYSxTWLCckBHOnAf.webp :width: 1920px :height: 904px :align: center ``` 2. 点击左侧菜单的“监控运维 -> 日志服务”,选择对应的产品,此时可以看到设备上线、订阅主题和发送消息的3条日志。如图: ```{image} images/image_ELAbbp2MooUIyKxj3MIcEEznnsg.webp :width: 1902px :height: 910px :align: center ``` 3. 点击查看类型为“设备到云”消息的日志,日志内容应为我们源码中编写的消息内容,也就是源码开头定义的 **ALIOT_BASIC_DEMO_TEST_CONTENT**,默认内容即为“Hello Welcome to test ali mqtt!”。如图: ```{image} images/image_QIFObYOLNoCg5VxSt55ciPadnld.webp :width: 1304px :height: 651px :align: center ``` 4. 回到设备页面,点击“Topic列表”,找到源码中订阅的主题,格式为“/k28gt*****N/device_name/user/get”,其中的“device_name”应替换成真实的设备名,点击发布消息,发布自定义内容。如图: ```{image} images/image_PboObsVCUoAHMDxKnXActEBCnIe.webp :width: 1898px :height: 705px :align: center ``` ```{image} images/image_Y9tibpNcFobta3xSuWUcA8GJnRg.webp :width: 1038px :height: 590px :align: center ``` 5. 在EPAT工具中抓取日志搜索关键词“unir_mqtt_aliot”,可以看到设备收到了云端下发的消息。 ```{image} images/image_RoQQbj4N4ocOjCxrIylcM6MTnab.webp :width: 1833px :height: 172px :align: center ``` 6. 程序收到消息后会主动断开MQTT连接,释放系统资源。