paho-MQTT C 使用一型一密连接阿里生活物联网

本文详细介绍了如何在RTOS系统中,不依赖阿里SDK,使用Paho-MQTTC库实现一型一密连接阿里生活物联网平台,包括准备工作、流程、注意事项以及相关代码示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

历经千辛万苦,终于把一型一密的连接阿里生活物联网搞定了

当前网络上能找到的绝大部分连阿里平台都是基于阿里的SDK或者是通信模块的SDK,而今天我要讲的是,在模块的open方案上,不借助阿里的SDK,仅使用paho-MQTT C库在RTOS下自己去实现一型一密连接阿里生活物联网平台。

一、准备条件

1、阿里生活物联网(飞燕平台)的账号,创建好产品。

2、了解设备连接平台的基本流程,了解阿里三要素(如何使用Paho-MQTTC接入物联网平台收发消息_阿里云物联网平台-阿里云帮助中心

3、了解一型一密动态注册,动态注册分两种,一个是免预注册,一个是预注册,可以去阿里官网(如何使用一型一密方式进行设备注册认证_阿里云物联网平台-阿里云帮助中心)查看相关资料。该文着重讲一型一密预注册。

4、了解MQTT.fx工具的使用,可以排除一些问题

5、paho-MQTT 移植,如果模块支持,这一步可以省略。相关移植方法直接参考paht-mqtt github

二、流程及注意事项

1、大致流程

(1)阿里平台上先将设备的IMEI注册好

(2)在设备端烧录设备的productKey和productSecret(所有设备都一样)

(3)设备端通过阿里的组包顺序和加密方式算出MQTT客户端的ID  用户名 密码

(4)创建SSL网络连接,然后使用(3)计算出来的MQTT客户端信息连接阿里云

(5)平台接收到连接,认证通过后会下发三要素(productKey deviceName deviceSecret),设备将三要素写到本地flash永久保存。

(6)断开注册的连接,通过三要素再次发起连接,进行订阅和发布主题

2、注意事项:

(1)如果是非量产状态,平台上有个动态注册的开关,需要开启

(2)量产后,动态注册开关可以不用开启,只要设置产品可动态注册即可

(3)设备动态注册如果成功获取三要素后,再次发起注册会失败(平台不再返回三要素),所以成功获取三要素后需要把三要素写到flash里面。下次通过三要素直接连接平台进行发布和订阅内容。如果需要测试多次注册,可以通过平台的ResetThing接口进行重置注册状态

三、相关代码

 1、动态注册函数

//动态注册函数
static void ALI_Mqtt_regist(void *argv)
{

	MQTTClient regclient = {0};//定义MQTT客户端
	Network network = {0};     //定义网络参数
	unsigned char sendbuf[64] = {0}, readbuf[128] = {0};//mqtt 发送和接收buf
	int rc = 0, count = 0;
	
	/* invoke aiotMqttSign to generate mqtt connect parameters */
	char clientId[150] = {0};
	char username[65] = {0};
	char password[65] = {0};

	printf("++++++++++++++++++++start ALI_Mqtt_regist\n");
	char* device_name = user_app_cfg.imei;                          //使用模块imei作为deviceName
	char* product_key = user_app_cfg.productInfo.productKey; 		//"a1*******";阿里平台上的productKey,user_app_cfg是自定义的一个结构体,里面存放产品信息
	char* product_secret = user_app_cfg.productInfo.productSecret; 	//"w********";阿里平台上的productSecret
	int product_secret_len = strlen(product_secret);
	
	char content[128] = {0};//按阿里的数据结果拼接需要加密的密码
	sprintf(content,"deviceName%sproductKey%srandom123",device_name,product_key);

	char mqttPassword[90] = {0};//记录通过sha256加密后的MQTT密码密码
	uint8_t passwordtemp[32] = {0};//临时变量,加密后的密码的32位的,需要转成64位16进制ASCII

    //utils_hmac_sha256函数在阿里提供的aiot_mqtt_sign.c文件中可以找到,可以自己添加头文件方便引用
	utils_hmac_sha256((const char*)content,strlen((const char*)content),product_secret,product_secret_len,(char *)passwordtemp);
	for(int i=0;i<sizeof(passwordtemp);i++)
		sprintf(mqttPassword+strlen(mqttPassword),"%02X",passwordtemp[i]);
	
	char mqttClientId[128] = {0};//需要按阿里的拼接顺序组件
	sprintf((char *)mqttClientId,"%s|securemode=2,authType=register,random=123,signmethod=hmacsha256|",device_name);//mode=-2时,type=regnwl,表示免预注册;mode=2时type=register,表示预注册。
	
	char mqttUserName[64]={0};
	sprintf((char *)mqttUserName,"%s&%s",device_name,product_key);

	printf("mqttClientID:%s\n",mqttClientId);
	printf("mqttUserName:%s\n",mqttUserName);
	printf("mqttPassword:%s\n",mqttPassword);

	char serip[128] = {0};
	sprintf(serip,"%s.iot-as-mqtt.cn-shanghai.aliyuncs.com",user_app_cfg.productInfo.productKey);

    //注意,动态注册的MQTT连接必须是SSL加密的,如果不加密,无法正常建立连接。
    //另外加密证书rootCA_path,可以使用阿里的证书,也可以用其他的证书但会告警。
    //通信模块的加密方式不一样,配置的SSLConfi参数也不一样,具体看模块。
	SSLConfig SSLConfig = {
		.en = SSL_ENABLE,
	#if SSL_ENABLE
		.profileIdx = PROFILE_IDX,
		.serverName = serip,
		.serverPort = SERVER_PORT,
		.protocol = 0,
		.dbgLevel = 0,
		.sessionReuseEn = 0,
		.vsn = SSL_VSN_ALL,
		.verify = SSL_VERIFY_MODE,
		.cert.from = SSL_CERT_FROM_BUF,
		.cert.path.rootCA = rootCA_path,
		.cert.path.clientKey = NULL,
		.cert.path.clientCert = NULL,
		.cipherList = SSL_CIPHER_LIST,
		.CTRDRBGSeed.data = NULL,
		.CTRDRBGSeed.len = 0
	#endif
	};

	int sta = 1;
	char* address = serip;
    
    //初始化连接
	MQTTPacket_connectData connectData = MQTTPacket_connectData_initializer;
	connectData.willFlag = 0;
	connectData.MQTTVersion = 3;
	connectData.clientID.cstring = mqttClientId;
	connectData.username.cstring = mqttUserName;
	connectData.password.cstring = mqttPassword;
	connectData.keepAliveInterval = 300;//阿里最大支持300sKeepAlike,动态注册的连接可以不设置保活
	connectData.cleansession = 1;

	int ret = 0;
	int cnt = 0;
	while(1)
	{
		switch (sta)
		{
		case M_LOGIC_INIT_NETWORK:
			NetworkInit(&network, &SSLConfig, PROFILE_IDX);//初始化网络
			MQTTClientInit(&regclient, &network, 30000, sendbuf, sizeof(sendbuf), readbuf, sizeof(readbuf));//初始化MQTT客户端
			regclient.defaultMessageHandler = messageArrived;//指定数据接收的回调函数
			if ((rc = NetworkConnect(&network, address, SERVER_PORT)) != 0)
			{
				printf("Return code is %d", rc);
				MQTTClientDeinit(&regclient);
			}
			else
				sta = M_LOGIC_CONNECT_SERVER;
			break;
		case M_LOGIC_CONNECT_SERVER:
			if ((rc = MQTTConnect(&regclient, &connectData)) != 0)
			{
				printf("Return code is %d", rc);
				NetworkDisconnect(&network);
				MQTTClientDeinit(&regclient);
				sta = M_LOGIC_INIT_NETWORK;
				rtos_sleep(10);
			}
			else
			{
				printf("MQTT Connected");
				cnt = 0;
				if ((rc = MQTTStartTask(&regclient)) != 0)
				{
					mqtt_exam_log("Return code is %d", rc);
					NetworkDisconnect(&network);
					MQTTClientDeinit(&regclient);
					sta = M_LOGIC_INIT_NETWORK;
				}
				else
				{
					sta = M_LOGIC_WAIT;
				}
			}
			break;

		case M_LOGIC_WAIT:
		{
			ret = 1;
		}
		break;

		default:
			break;
		}

		if(ret == 0)
		{
			rtos_sleep(1);
			continue;
		}
        //永久等待消息,当MQTT回调函数接收到注册信息后,给消息队列发U_EVENT_REGDATA事件
		memset(&emsg,0,sizeof(UDE_QUEUE_MSG_INFO_T));
		rtos_queue_wait(mqtt_event_queue, &emsg, sizeof(UDE_QUEUE_MSG_INFO_T), QL_WAIT_FOREVER);
		switch (emsg.eType)
		{
		
		case U_EVENT_REGDATA:
		{
			//已经获取注册信息,断开当前连接,发起新连接
			printf("U_EVENT_REGDATA...\n");
			NetworkDisconnect(&network);
		    MQTTClientDeinit(&regclient);
			goto REG_END;
		}
		break;
		
		default:
			break;
		}
	}

REG_END:
	printf("REG_END\n");

}

 2、MQTT回调函数

//MQTT回调接口
static void messageArrived(MessageData* data)
{
    
    printf("recv topic:%s\n", data->topicName->lenstring.data);
	printf("recv paloay:%s\n", data->message->payload);

    //如果是动态预注册,数据通过/ext/register返回,如果是免预注册则是/ext/regnwl
	if(strstr(data->topicName->lenstring.data,"/ext/register") != NULL)
	{
		cJSON* cjson_all = NULL;
    	cJSON* cjson_child = NULL;
		cjson_all = cJSON_Parse((char*) data->message->payload);
	
		cjson_child = cJSON_GetObjectItem(cjson_all, "productKey");
		if(cjson_child != NULL)
		{
			printf("productKey:%s\n",cjson_child->valuestring);
		}

		cjson_child = cJSON_GetObjectItem(cjson_all, "deviceName");
		if(cjson_child != NULL)
		{
			printf("deviceName:%s\n",cjson_child->valuestring);
		}

		cjson_child = cJSON_GetObjectItem(cjson_all, "deviceSecret");
		if(cjson_child != NULL)
		{
			printf("deviceSecret:%s\n",cjson_child->valuestring);
		}

		//需要保存3要素及注册状态
		write_id_key2Flash();
		write_product2Flash(2);
		cJSON_Delete(cjson_all);

        //给消息队列发信号,通知已经注册成功
		UDE_QUEUE_MSG_INFO_T emsg;
		emsg.etime = time(NULL);
		emsg.eType = U_EVENT_REGDATA;
		emsg.data = &reg;
		ql_rtos_queue_release(mqtt_event_queue, sizeof(UDE_QUEUE_MSG_INFO_T), &emsg, QL_NO_WAIT);
		}
}

3、动态注册成功后,无需SSL加密,直接通过3要素连接阿里平台进行数据通信

static void MQTTEchoTask(void *argv)
{

	UDE_QUEUE_MSG_INFO_T emsg;
	MQTTClient client = {0};
	Network network = {0};
	unsigned char sendbuf[512] = {0}, readbuf[512] = {0};//最好使用动态内存,不然占很大的ram
	int rc = 0, count = 0;
	
	/* invoke aiotMqttSign to generate mqtt connect parameters */
	char clientId[150] = {0};
	char username[65] = {0};
	char password[65] = {0};
	char serverip[128] = {0};

	sprintf(serverip,"%s.iot-as-mqtt.cn-shanghai.aliyuncs.com",user_app_cfg.productKey);
	
    //通过注册获取的三要素,及阿里提供的aiot_mqtt_sign.c文件里的aiotMqttSign计算出MQTT的ID、用户名、密码
	if ((rc = aiotMqttSign(user_app_cfg.productKey, user_app_cfg.deviceName, user_app_cfg.deviceSecretKey, clientId, username, password) < 0)) {
		printf("aiotMqttSign -%0x4x\n", -rc);
		return;
	}
	printf("clientid: %s\n", clientId);
	printf("username: %s\n", username);
	printf("password: %s\n", password);

	SSLConfig SSLConfig = {
		.en = 0,
	};

	char* address = serverip;
	MQTTPacket_connectData connectData = MQTTPacket_connectData_initializer;
	connectData.willFlag = 0;
	connectData.MQTTVersion = 3;
	connectData.clientID.cstring = clientId;
	connectData.username.cstring = username;
	connectData.password.cstring = password;
	connectData.keepAliveInterval = 300;
	connectData.cleansession = 1;

	int ret = 0;
    int sta = 0;
	while(1)
	{
        switch (sta)
		{
		case M_LOGIC_INIT_NETWORK:
			NetworkInit(&network, &SSLConfig, PROFILE_IDX);
			MQTTClientInit(&client, &network, 30000, sendbuf, sizeof(sendbuf), readbuf, sizeof(readbuf));
			client.defaultMessageHandler = messageArrived;
			if ((rc = NetworkConnect(&network, address, SERVER_PORT)) != 0)
			{
				printf("Return code is %d", rc);
				MQTTClientDeinit(&client);
                rtos_sleep(5);
			}
			else
				sta = M_LOGIC_CONNECT_SERVER;
			break;
		case M_LOGIC_CONNECT_SERVER:
			if ((rc = MQTTConnect(&client, &connectData)) != 0)
			{
				printf("Return code is %d", rc);
				NetworkDisconnect(&network);
				MQTTClientDeinit(&client);
				sta = M_LOGIC_INIT_NETWORK;
			}
			else
			{
				printf("MQTT Connected");
				user_app_cfg.connectSta = 1;
				if ((rc = MQTTStartTask(&client)) != 0)
				{
					printf("Return code is %d", rc);
					NetworkDisconnect(&network);
					MQTTClientDeinit(&client);
					sta = M_LOGIC_INIT_NETWORK;
				}
				else
				{
					sta = M_LOGIC_SUBSCRIBE_NTP;
				}

			}
			break;

		case M_LOGIC_SUBSCRIBE_NTP:
			{
				char topic[128] = {0};
            sprintf(topic,"/ext/ntp/%s/%s/response",p_productKey,user_app_cfg.keyid.devid);	
				if (rc = MQTTSubscribe(&client, topic, 2, messageArrived) != 0) 
				{
					printf("Return code is %d", rc);
					rc = MQTTDisconnect(&client);
					if(rc == SUCCESS)
						printf("MQTT Disconnected by client");
					else
						printf("MQTT Disconnected failed by client");
					NetworkDisconnect(&network);
					MQTTClientDeinit(&client);
				}
				else
				{
					if(user_app_cfg.timesync == 0)
						sta = M_LOGIC_SYNCTIME;
					else
						sta = M_LOGIC_SUBSCRIBE_SER;
				}
			}
			break;
		
		case M_LOGIC_SYNCTIME:
		{
			MQTTMessage message;
			mqtt_pack_msg_syncTime(&message);//封装阿里的同步时间数据
			char reqtopic[128] = {0};
			sprintf(reqtopic,"/ext/ntp/%s/%s/request",user_app_cfg.productKey,user_app_cfg.deviceName);
			if ((rc = MQTTPublish(&client, reqtopic, &message)) != 0)
			{
				printf("Return code from MQTT subscribe is %d", rc);	
				rc = MQTTDisconnect(&client);
				if(rc == SUCCESS)
					printf("MQTT Disconnected by client");
				else
					printf("MQTT Disconnected failed by client");
				NetworkDisconnect(&network);
				MQTTClientDeinit(&client);
				sta = M_LOGIC_INIT_NETWORK;
			}
			else
				sta = M_LOGIC_SUBSCRIBE_SER;

		}break;
        
        case M_LOGIC_SUBSCRIBE_SER:
        {
        //订阅一些信息
        }break;
    }
    
    if(ret==0)
    {
        rtos_sleep(1);
        continue;
    }
    
    //永久等待消息
    ql_rtos_queue_wait(mqtt_event_queue, &emsg, sizeof(UDE_QUEUE_MSG_INFO_T), QL_WAIT_FOREVER);
    //解析消息数据
    ...
}

四、总结

1、仔细看官方指导文档

2、阿里生活物联网(飞燕平台)和阿里物联网存在一定差异

3、遇到问题多提工单,阿里客户处理速度还是挺快的

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值