4. RT-Thread连接阿里云物联网平台
任务目标
在阿里云物联网平台创建产品和设备,使用RT-Thread的MQTT组件完成设备接入,实现设备注册与认证(基于三元组信息的直连认证),并验证设备与平台之间的消息收发。
前置准备
- 阿里云物联网平台账号,已开通公共实例
- RT-Thread Studio或基于RT-Thread的嵌入式工程(支持AT指令或以太网)
- 开发板具备网络连接能力(如ESP8266、以太网或4G模组)
阿里云平台配置
- 登录阿里云物联网平台,创建产品(如“RT-Thread_Demo”),选择直连设备、MQTT协议。
- 在产品下添加设备,获取设备证书三元组:
ProductKey、DeviceName、DeviceSecret。 - 在产品详情页的“Topic类列表”中,默认已有
/sys/{ProductKey}/{DeviceName}/thing/event/property/post和/sys/{ProductKey}/{DeviceName}/thing/service/property/set等Topic,可直接使用。
RT-Thread MQTT接入实现
RT-Thread的MQTT软件包基于paho.mqtt.embedded-c实现,支持阿里云要求的MQTT连接参数。设备认证通过MQTT Connect报文中的username和password字段完成,具体规则如下:
- clientId:
{DeviceName}|securemode=3,signmethod=hmacsha1,timestamp=789 - username:
{DeviceName}&{ProductKey} - password: 使用HMAC-SHA1对
clientId{DeviceName}deviceName{DeviceName}productKey{ProductKey}timestamp789进行签名(注意:实际签名串中clientId的值需去掉竖线后的参数部分)
以下代码在RT-Thread中完成MQTT连接与属性上报:
#include <rtthread.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "mqtt_client.h"
#define ALIYUN_PRODUCT_KEY "a1XXXXXX"
#define ALIYUN_DEVICE_NAME "device01"
#define ALIYUN_DEVICE_SECRET "xxxxxxxxxxxxxxxx"
#define ALIYUN_HOST ALIYUN_PRODUCT_KEY ".iot-as-mqtt.cn-shanghai.aliyuncs.com"
#define ALIYUN_PORT 1883
/* 生成MQTT连接参数 */
static void aliyun_gen_conn_param(char *client_id, char *username, char *password, int buf_len)
{
char sign_content[256];
char timestamp[] = "789";
const char *hmac_key = ALIYUN_DEVICE_SECRET;
/* clientId: DeviceName|securemode=3,signmethod=hmacsha1,timestamp=789 */
rt_snprintf(client_id, buf_len, "%s|securemode=3,signmethod=hmacsha1,timestamp=%s",
ALIYUN_DEVICE_NAME, timestamp);
/* username: DeviceName&ProductKey */
rt_snprintf(username, buf_len, "%s&%s", ALIYUN_DEVICE_NAME, ALIYUN_PRODUCT_KEY);
/* 签名串: clientId{DeviceName}deviceName{DeviceName}productKey{ProductKey}timestamp789 */
rt_snprintf(sign_content, sizeof(sign_content),
"clientId%sdeviceName%sproductKey%stimestamp%s",
ALIYUN_DEVICE_NAME, ALIYUN_DEVICE_NAME, ALIYUN_PRODUCT_KEY, timestamp);
/* 使用HMAC-SHA1计算签名,结果转为十六进制字符串 */
aliyun_hmac_sha1_hex(hmac_key, sign_content, password, buf_len);
}
/* MQTT事件回调 */
static void mqtt_callback(MQTTClient *client, MQTTMessage *msg, void *arg)
{
if (msg->qos == 0)
rt_kprintf("recv msg: %.*s\n", msg->payloadlen, (char *)msg->payload);
}
/* 属性上报 */
static void aliyun_post_property(MQTTClient *client)
{
char topic[128];
char payload[128];
int msg_id = 1;
rt_snprintf(topic, sizeof(topic), "/sys/%s/%s/thing/event/property/post",
ALIYUN_PRODUCT_KEY, ALIYUN_DEVICE_NAME);
rt_snprintf(payload, sizeof(payload),
"{\"id\":%d,\"version\":\"1.0\",\"method\":\"thing.event.property.post\",\"params\":{\"temperature\":25.5}}",
msg_id);
MQTTMessage msg;
memset(&msg, 0, sizeof(msg));
msg.qos = 1;
msg.payload = payload;
msg.payloadlen = strlen(payload);
if (MQTTPublish(client, topic, &msg) < 0)
rt_kprintf("publish failed\n");
else
rt_kprintf("publish ok: %s\n", payload);
}
/* 主线程入口 */
static void aliyun_mqtt_thread(void *param)
{
MQTTClient client;
MQTTString clientId, username, password;
char client_id[128], user_name[128], pass_word[64];
Network n;
int rc;
/* 生成阿里云认证参数 */
aliyun_gen_conn_param(client_id, user_name, pass_word, sizeof(pass_word));
/* 初始化网络与MQTT客户端 */
NewNetwork(&n);
ConnectNetwork(&n, ALIYUN_HOST, ALIYUN_PORT);
MQTTClientInit(&client, &n, 1024, 1024);
clientId.cstring = client_id;
username.cstring = user_name;
password.cstring = pass_word;
MQTTConnectData conn_data = MQTTConnectDataDefault;
conn_data.clientID = clientId;
conn_data.username = username;
conn_data.password = password;
conn_data.keepAliveInterval = 60;
conn_data.cleansession = 1;
/* 建立连接 */
rc = MQTTConnect(&client, &conn_data);
if (rc != 0) {
rt_kprintf("MQTT connect failed, rc=%d\n", rc);
return;
}
rt_kprintf("MQTT connected to Aliyun IoT\n");
/* 订阅云端下行Topic(属性设置) */
char sub_topic[128];
rt_snprintf(sub_topic, sizeof(sub_topic), "/sys/%s/%s/thing/service/property/set",
ALIYUN_PRODUCT_KEY, ALIYUN_DEVICE_NAME);
MQTTSubscribe(&client, sub_topic, 0, mqtt_callback);
/* 循环上报属性 */
while (1) {
aliyun_post_property(&client);
rt_thread_mdelay(5000); /* 5秒上报一次 */
}
}
/* 创建线程 */
static int aliyun_mqtt_start(void)
{
rt_thread_t tid = rt_thread_create("aliyun_mqtt",
aliyun_mqtt_thread, RT_NULL,
4096, 25, 10);
if (tid)
rt_thread_startup(tid);
return 0;
}
INIT_APP_EXPORT(aliyun_mqtt_start);
HMAC-SHA1辅助函数
上述代码中的 aliyun_hmac_sha1_hex 需要实现。RT-Thread的mbedtls软件包提供了现成的HMAC-SHA1实现,在工程中添加该软件包后,实现如下:
#include <mbedtls/md.h>
void aliyun_hmac_sha1_hex(const char *key, const char *data, char *output, int out_len)
{
unsigned char hmac[20];
mbedtls_md_context_t ctx;
mbedtls_md_init(&ctx);
mbedtls_md_setup(&ctx, mbedtls_md_info_from_type(MBEDTLS_MD_SHA1), 1);
mbedtls_md_hmac_starts(&ctx, (const unsigned char *)key, strlen(key));
mbedtls_md_hmac_update(&ctx, (const unsigned char *)data, strlen(data));
mbedtls_md_hmac_finish(&ctx, hmac);
mbedtls_md_free(&ctx);
/* 转为小写十六进制字符串 */
for (int i = 0; i < 20 && i * 2 + 1 < out_len; i++)
rt_snprintf(output + i * 2, 3, "%02x", hmac[i]);
}
编译与运行
- 在RT-Thread Studio中,通过包管理器添加
mbedtls和paho-mqtt软件包。 - 将上述代码保存为
aliyun_mqtt.c,放入工程的applications目录。 - 将代码开头的三元组宏替换为你在阿里云平台创建的实际值。
- 编译并烧录到开发板,确保网络连接正常。
验证方法
- 串口日志:系统启动后,串口输出
MQTT connected to Aliyun IoT,随后每5秒输出一次publish ok,说明连接与上报成功。 - 阿里云控制台:在“设备管理”页面,对应设备状态显示为“在线”;在“日志服务”中可看到设备上报的属性数据(temperature=25.5)。
- 下行验证:在阿里云控制台的“在线调试”中,选择属性设置(如设置temperature为30),开发板串口应打印
recv msg: {...},说明订阅成功。
常见问题
- 连接失败(rc=-2或-3):检查三元组是否正确,设备是否已激活;确认网络能访问
*.aliyuncs.com:1883。 - 签名错误:HMAC-SHA1的key为DeviceSecret,签名串中不要包含clientId的竖线后缀参数。
- 时间戳:示例使用固定值789,实际产品中建议使用当前毫秒时间戳,但需保证签名串与clientId中的timestamp一致。