AliYun平台¶
Copyright © Quectel Wireless Solutions Co., Ltd. 2026. All rights reserved.
AliYun简介¶
阿里云是阿里巴巴旗下自研云计算服务平台,依托自研飞天云计算操作系统,涵盖云计算、存储、物联网、数据库等全栈云产品体系阿里云。平台配套物联网IoT平台,支持MQTT等主流通信协议接入各类嵌入式硬件,实现设备安全鉴权、上下线管理、数据上报与云端指令下发,可无缝联动云服务器、时序数据库等产品,是设备云端接入、数据存储与远程管控的依托载体。
资源准备¶
硬件准备¶
一块移远通信提供的 pico开发板介绍 开发板。(下文以该开发板为例)
USB数据线(USB-A转USB-C)。
PC(Windows 10/Windows 11)。
软件准备¶
unirtos-toolchain.exe:编译工具链安装程序,点此获取 。
Python:用于运行unirtos-cli工具,需使用3.9及更高版本。安装步骤详见 环境搭建。
Git:unirtos-cli使用该工具拉取SDK、依赖库等,需使用2.20及更高版本。安装步骤详见 环境搭建。
unirtos-cli:UniRTOS的命令行工具,用于一键拉取SDK、快速创建工程。安装步骤详见 环境搭建。
USB驱动:用于PC识别模块的USB枚举接口,点此获取 。
QFlash.exe:模块固件烧录程序,用于烧录UniRTOS编译生成的固件,点此获取 。
EPAT工具:芯片厂商提供的日志抓取工具,用于查看模块运行日志以分析应用程序执行情况,点此获取 。
QCOM工具:移远通信提供的串口调试工具,用于执行和验证AT命令,点此获取 。
程序源码:位于UniRTOS的官方github仓库,点此查看,示例可通过unirtos-cli直接拉取 。
关于如何拉取示例程序、编译和烧录固件,请参考 快速上手。
AliYun连接参数获取¶
获取产品证书¶
在AliYun控制台新建一个产品。
进入上一步创建好的产品页面,点击 ProductSecret 右边的“查看”。
获取我们连接需要的两个参数:ProductKey 和 ProductSecret。
获取设备证书¶
创建一个新设备,如图所示。
选择上一节中新建的产品,自定义设备名后点击确认即可。
进入产品页面,点击上方 DeviceSecret 右边的“查看”。
获取 DeviceName 和 DeviceSecret。
获取MQTT连接参数¶
点此产品页面的自定义Topic查看发布Topic和订阅Topic。如图:
点击设备栏“设备信息”页面MQTT连接参数右侧的“查看”,获取MQTT详细连接参数。如图:
主要功能接口¶
参数配置¶
定义云平台MQTT服务器连接地址、服务器连接端口、客户端ID、订阅主题等参数。
定义云平台连接的产品秘钥、设备名称、设备秘钥。
定义程序运行的任务栈大小、日志标签、消息队列大小等系统设置。
示例代码:
#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连接参数,提供给初始化和反初始化等操作使用。
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配置中。示例代码:
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字段。示例代码:
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。示例代码:
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。示例代码:
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() 释放主题缓冲区内存。示例代码:
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 则记录错误日志。示例代码:
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 占用的内存,避免内存泄漏。示例代码:
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。示例代码:
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 将异步通知打包投递到队列,任务取出消息后分发至对应处理函数,驱动状态机按序推进,无需轮询。
程序运行结果¶
程序运行后,进入阿里云物联网平台后台设备页面,设备会显示上线。如图:
点击左侧菜单的“监控运维 -> 日志服务”,选择对应的产品,此时可以看到设备上线、订阅主题和发送消息的3条日志。如图:
点击查看类型为“设备到云”消息的日志,日志内容应为我们源码中编写的消息内容,也就是源码开头定义的 ALIOT_BASIC_DEMO_TEST_CONTENT,默认内容即为“Hello Welcome to test ali mqtt!”。如图:
回到设备页面,点击“Topic列表”,找到源码中订阅的主题,格式为“/k28gt*****N/device_name/user/get”,其中的“device_name”应替换成真实的设备名,点击发布消息,发布自定义内容。如图:
在EPAT工具中抓取日志搜索关键词“unir_mqtt_aliot”,可以看到设备收到了云端下发的消息。
程序收到消息后会主动断开MQTT连接,释放系统资源。