加入星計劃,您可以享受以下權(quán)益:

  • 創(chuàng)作內(nèi)容快速變現(xiàn)
  • 行業(yè)影響力擴散
  • 作品版權(quán)保護
  • 300W+ 專業(yè)用戶
  • 1.5W+ 優(yōu)質(zhì)創(chuàng)作者
  • 5000+ 長期合作伙伴
立即加入

STM32連接-OneNET,阿里云(MQTT協(xié)議)詳細教程

06/19 10:03
7004
服務(wù)支持:
技術(shù)交流群

完成交易后在“購買成功”頁面掃碼入群,即可與技術(shù)大咖們分享疑惑和經(jīng)驗、收獲成長和認同、領(lǐng)取優(yōu)惠和紅包等。

虛擬商品不可退

當(dāng)前內(nèi)容為數(shù)字版權(quán)作品,購買后不支持退換且無法轉(zhuǎn)移使用。

加入交流群
掃碼加入
獲取工程師必備禮包
參與熱點資訊討論
放大
實物圖
相關(guān)方案
  • 方案介紹
    • MQTT協(xié)議
    • 關(guān)于如何連接OneNET
    • 平臺迭代?
    • 微信公眾號--星之援工作室 發(fā)送關(guān)鍵字(OneNet公共賬號)
    • 例程文件
    • 關(guān)于如何連接阿里云
    • 補充信息
  • 相關(guān)文件
  • 推薦器件
  • 相關(guān)推薦
  • 電子產(chǎn)業(yè)圖譜
申請入駐 產(chǎn)業(yè)圖譜

MQTT協(xié)議

什么是物聯(lián)網(wǎng)

物聯(lián)網(wǎng)一詞在2009年由Kevin Ashton首次使用。物聯(lián)網(wǎng)指的是通過互聯(lián)網(wǎng)來連接物理設(shè)備。物理設(shè)備可以相互之間通過互聯(lián)網(wǎng)交換數(shù)據(jù)或由其他設(shè)備控制。物聯(lián)網(wǎng)設(shè)備可以是冰箱、交通信號燈、工業(yè)控制系統(tǒng)等電子裝置或電氣設(shè)備。最常見的用例之一是傳感器數(shù)據(jù)的收集,傳輸,分析和顯示。工作人員可利用物聯(lián)網(wǎng)數(shù)據(jù)實現(xiàn)遠程監(jiān)控臺,或者對超過閾值的數(shù)據(jù)設(shè)置警報。

什么是MQTT

MQTT是一種輕量級消息傳輸協(xié)議,它為物聯(lián)網(wǎng)設(shè)備提供了一種簡單的方法來傳輸數(shù)據(jù)信息。由于MQTT占用網(wǎng)絡(luò)資源小,且適用于遠程信息傳輸,MQTT在物聯(lián)網(wǎng)(IoT)領(lǐng)域起著重要作用。

MQTT協(xié)議的第一版由Andy Stanford-Clark(IBM)和Arlen Nipper(Cirrus Link)于1999年建立的。該協(xié)議最早應(yīng)用于監(jiān)控穿越沙漠的石油管道。因為設(shè)備是通過衛(wèi)星鏈路連接的,所以當(dāng)時MQTT所運行的網(wǎng)絡(luò)帶寬很小,且十分不穩(wěn)定。而MQTT協(xié)議的設(shè)計目標(biāo)也正是為了適用于這類傳輸距離遠,帶寬小,不穩(wěn)定的網(wǎng)絡(luò)環(huán)境。

MQTT運行機制

MQTT協(xié)議的中央通信中樞是MQTT服務(wù)器,它負責(zé)發(fā)送方和接收方間的信息通訊。每個向MQTT服務(wù)器發(fā)布消息的客戶端都會在發(fā)布消息中包含一個主題。每個想要接收該消息的客戶端都會訂閱該主題。MQTT服務(wù)器在收到客戶端向主題發(fā)布的信息后,會將信息發(fā)送給所有訂閱該主題的客戶端。這種體系結(jié)構(gòu)可實現(xiàn)高度可擴展的解決方案,而數(shù)據(jù)生產(chǎn)者和數(shù)據(jù)使用者之間沒有依賴關(guān)系。


關(guān)于如何連接OneNET

OneNETicon-default.png?t=N7T8https://open.iot.10086.cn/console/

平臺迭代?

現(xiàn)在OneNet4.14后不再維護多協(xié)議了
大家可以轉(zhuǎn)戰(zhàn) 阿里云 或者 華為云

這個文章沒寫華為云,后續(xù)空了我會添加上去~~~

現(xiàn)在多協(xié)議只對老用戶開發(fā),新注冊的賬號已經(jīng)沒有多協(xié)議選項了

若需要測試OneNet,可以私聊博主,博主可以提供一個公共賬號進行測試?。?!

阿里云還是可以直接放心使用的 ~ ~ ~

微信公眾號--星之援工作室 發(fā)送關(guān)鍵字(OneNet公共賬號)

????

452c0cf75b1d4e4895194df8a5022c34.png (865×328)

例程文件

鏈接:MQTT-Onenet例程文件icon-default.png?t=N7T8https://pan.baidu.com/s/11KlzByF5sEBxxFpcOCyp7w?pwd=xzy0%C2%A0
提取碼:xzy0

搭建云平臺設(shè)備

1.1.協(xié)議選擇

?1.2.添加產(chǎn)品

?1.3.添加設(shè)備

?到這里我們就基本快完成云平臺的搭建了

?

?到此云平臺已搭建完成

2.查看設(shè)備參數(shù)(MQTT連接使用)

產(chǎn)品id

設(shè)備id

設(shè)備鑒權(quán)信息

4.連接測試

4.1.準備物件

1.stm32F103系列開發(fā)板

2.ESP8266模塊

4.2.連線圖與修改參數(shù)

ESP8266連接引腳

TX -- PA3? ? ? ? VCC -- 3.3V

RX -- PA2? ? ? ?GND -- GND

RST -- PB9

修改OenNET.c的連接參數(shù),把之前保存的參數(shù)填寫上去

使用的熱點名稱為 hhh? 密碼 12345678 (大家自行打開手機熱點或者修改一下WiFi參數(shù)哦)

WIFi參數(shù)修改在esp8266.c
#define ESP8266_WIFI_INFO?? ??? ?"AT+CWJAP="hhh","12345678"rn"

#define ESP8266_ONENET_INFO?? ??? ?"AT+CIPSTART="TCP","183.230.40.39",6002rn"

4.3.連接成功

顯示平臺設(shè)備在線,只需要在添加另一臺設(shè)備訂閱相同主題即可實現(xiàn)消息的收發(fā)

?到此OneNET連接教程完成??

關(guān)于如何連接阿里云

1.搭建云平臺設(shè)備

阿里云icon-default.png?t=N7T8https://www.aliyun.com/?accounttraceid=5df262f1b2db40f2b26ca21fd1f026bdgwje

鏈接:創(chuàng)建視頻以及物理模型icon-default.png?t=N7T8https://pan.baidu.com/s/1AsllLqDd1MljFonIWpKUxw?pwd=XZY0

1.1.平臺選擇

1.2.創(chuàng)造產(chǎn)品

1.3.創(chuàng)造設(shè)備

2.查看設(shè)備參數(shù)(MQTT連接使用)

2.1.設(shè)備連接參數(shù)保存

2.2.主題的發(fā)布訂閱

3.下位機代碼編寫

3.1.復(fù)用onenet的底層配置

4.連接測試

4.1.準備物件

1.stm32F103系列開發(fā)板

2.ESP8266模塊

4.2.連線圖與修改參數(shù)

ESP8266連接引腳

TX -- PA3? ? ? ? VCC -- 3.3V

RX -- PA2? ? ? ?GND -- GND

RST -- PB9

修改OenNET.c的連接參數(shù),把之前保存的參數(shù)填寫上去

使用的熱點名稱為 hhh? 密碼 12345678 (大家自行打開手機熱點或者修改一下WiFi參數(shù)哦)

WIFi參數(shù)修改在esp8266.c
#define ESP8266_WIFI_INFO?? ??? ?"AT+CWJAP="hhh","12345678"rn"

#define ESP8266_ONENET_INFO?? ???

"AT+CIPSTART="TCP","iot-06z00e3auli0497.mqtt.iothub.aliyuncs.com",1883rn"

4.3.連接成功

顯示平臺設(shè)備在線,只需要在添加另一臺設(shè)備訂閱相同主題即可實現(xiàn)消息的收發(fā)

?到此阿里云連接教程完成??

?3.下位機代碼編寫

3.1.ESP8266.h

#ifndef _ESP8266_H_
#define _ESP8266_H_

#include "sys.h"



#define REV_OK		0	//接收完成標(biāo)志
#define REV_WAIT	1	//接收未完成標(biāo)志


void ESP8266_Init(void);
void Usart2_Init(unsigned int baud);
void ESP8266_Clear(void);

void ESP8266_SendData(unsigned char *data, unsigned short len);

unsigned char *ESP8266_GetIPD(unsigned short timeOut);
void Usart2_SendString(unsigned char *str, unsigned short len);

#endif

3.2.ESP8266.c

//單片機頭文件
#include "stm32f10x.h"

//網(wǎng)絡(luò)設(shè)備驅(qū)動
#include "esp8266.h"

/* FreeRTOS頭文件 */
#include "bsp_SysTick.h"
#include "usart.h"

//C庫
#include <string.h>
#include <stdio.h>

#define ESP8266_WIFI_INFO		"AT+CWJAP="hhh","12345678"rn"

#define ESP8266_ONENET_INFO		"AT+CIPSTART="TCP","183.230.40.39",6002rn"


unsigned char esp8266_buf[128];
unsigned short esp8266_cnt = 0, esp8266_cntPre = 0;


//==========================================================
//	函數(shù)名稱:	ESP8266_Clear
//
//	函數(shù)功能:	清空緩存
//
//	入口參數(shù):	無
//
//	返回參數(shù):	無
//
//	說明:		
//==========================================================
void ESP8266_Clear(void)
{

	memset(esp8266_buf, 0, sizeof(esp8266_buf));
	esp8266_cnt = 0;

}

//==========================================================
//	函數(shù)名稱:	ESP8266_WaitRecive
//
//	函數(shù)功能:	等待接收完成
//
//	入口參數(shù):	無
//
//	返回參數(shù):	REV_OK-接收完成		REV_WAIT-接收超時未完成
//
//	說明:		循環(huán)調(diào)用檢測是否接收完成
//==========================================================
_Bool ESP8266_WaitRecive(void)
{

	if(esp8266_cnt == 0) 							//如果接收計數(shù)為0 則說明沒有處于接收數(shù)據(jù)中,所以直接跳出,結(jié)束函數(shù)
		return REV_WAIT;
		
	if(esp8266_cnt == esp8266_cntPre)				//如果上一次的值和這次相同,則說明接收完畢
	{
		esp8266_cnt = 0;							//清0接收計數(shù)
			
		return REV_OK;								//返回接收完成標(biāo)志
	}
		
	esp8266_cntPre = esp8266_cnt;					//置為相同
	
	return REV_WAIT;								//返回接收未完成標(biāo)志

}

//==========================================================
//	函數(shù)名稱:	ESP8266_SendCmd
//
//	函數(shù)功能:	發(fā)送命令
//
//	入口參數(shù):	cmd:命令
//				res:需要檢查的返回指令
//
//	返回參數(shù):	0-成功	1-失敗
//
//	說明:		
//==========================================================
_Bool ESP8266_SendCmd(char *cmd, char *res, u16 time)
{	
	Usart2_SendString((unsigned char *)cmd, strlen((const char *)cmd));

	while(time--)
	{
		if(ESP8266_WaitRecive() == REV_OK)							//如果收到數(shù)據(jù)
		{
			if(strstr((const char *)esp8266_buf, res) != NULL)		//如果檢索到關(guān)鍵詞
			{
				ESP8266_Clear();									//清空緩存
				
				return 0;
			}
		}
		
		Delay_ms(10);
	}
	
	return 1;

}

//==========================================================
//	函數(shù)名稱:	ESP8266_SendData
//
//	函數(shù)功能:	發(fā)送數(shù)據(jù)
//
//	入口參數(shù):	data:數(shù)據(jù)
//				len:長度
//
//	返回參數(shù):	無
//
//	說明:		
//==========================================================
void ESP8266_SendData(unsigned char *data, unsigned short len)
{

	char cmdBuf[32];
	
	ESP8266_Clear();								//清空接收緩存
	sprintf(cmdBuf, "AT+CIPSEND=%drn", len);		//發(fā)送命令
	if(!ESP8266_SendCmd(cmdBuf, ">", 200))				//收到‘>’時可以發(fā)送數(shù)據(jù)
	{
		Usart2_SendString(data, len);		//發(fā)送設(shè)備連接請求數(shù)據(jù)
	}

}

//==========================================================
//	函數(shù)名稱:	ESP8266_GetIPD
//
//	函數(shù)功能:	獲取平臺返回的數(shù)據(jù)
//
//	入口參數(shù):	等待的時間(乘以10ms)
//
//	返回參數(shù):	平臺返回的原始數(shù)據(jù)
//
//	說明:		不同網(wǎng)絡(luò)設(shè)備返回的格式不同,需要去調(diào)試
//				如ESP8266的返回格式為	"+IPD,x:yyy"	x代表數(shù)據(jù)長度,yyy是數(shù)據(jù)內(nèi)容
//==========================================================
unsigned char *ESP8266_GetIPD(unsigned short timeOut)
{

	char *ptrIPD = NULL;
	
	do
	{
		if(ESP8266_WaitRecive() == REV_OK)								//如果接收完成
		{
			ptrIPD = strstr((char *)esp8266_buf, "IPD,");				//搜索“IPD”頭
			if(ptrIPD == NULL)											//如果沒找到,可能是IPD頭的延遲,還是需要等待一會,但不會超過設(shè)定的時間
			{
				//printf(""IPD" not foundrn");
			}
			else
			{
				ptrIPD = strchr(ptrIPD, ':');							//找到':'
				if(ptrIPD != NULL)
				{
					ptrIPD++;
					return (unsigned char *)(ptrIPD);
				}
				else
					return NULL;
				
			}
		}
		
		Delay_ms(5);													//延時等待
	} while(timeOut--);
	
	return NULL;														//超時還未找到,返回空指針

}
//==========================================================
//	函數(shù)名稱:	USART2_IRQHandler
//
//	函數(shù)功能:	串口2收發(fā)中斷
//
//	入口參數(shù):	無
//
//	返回參數(shù):	無
//
//	說明:		
//==========================================================
void USART2_IRQHandler(void)
{

	if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET) //接收中斷
	{
		if(esp8266_cnt >= sizeof(esp8266_buf))	esp8266_cnt = 0; //防止串口被刷爆
		esp8266_buf[esp8266_cnt++] = USART2->DR;
		
		USART_ClearFlag(USART2, USART_FLAG_RXNE);
	}

}
//==========================================================
//	函數(shù)名稱:	ESP8266_Init
//
//	函數(shù)功能:	初始化ESP8266
//
//	入口參數(shù):	無
//
//	返回參數(shù):	無
//
//	說明:		
//==========================================================
void ESP8266_Init(void)
{
	GPIO_InitTypeDef GPIO_Initure;
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);

	//ESP8266復(fù)位引腳
	GPIO_Initure.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIO_Initure.GPIO_Pin = GPIO_Pin_9;					//GPIOB1-復(fù)位
	GPIO_Initure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_Initure);
	
	GPIO_WriteBit(GPIOB, GPIO_Pin_9, Bit_RESET);
	Delay_ms(100);
	GPIO_WriteBit(GPIOB, GPIO_Pin_9, Bit_SET);
	Delay_ms(100);
	
	ESP8266_Clear();

	printf("ATrn");
	while(ESP8266_SendCmd("ATrnr", "OK", 200))
		Delay_ms(500);
	
	printf("CWMODErn");
	while(ESP8266_SendCmd("AT+CWMODE=1rn", "OK", 200))
		Delay_ms(500);
	
	printf("AT+CWDHCPrn");
	while(ESP8266_SendCmd("AT+CWDHCP=1,1rn", "OK", 200))
		Delay_ms(500);
	
	printf("CWJAPrn");
	while(ESP8266_SendCmd(ESP8266_WIFI_INFO, "GOT IP", 200))
		Delay_ms(500);
	
	printf("CIPSTARTrn");
	while(ESP8266_SendCmd(ESP8266_ONENET_INFO, "CONNECT", 200))
		Delay_ms(500);
	
	printf("ESP8266 Init OKrn");
}


/*
************************************************************
*	函數(shù)名稱:	Usart2_Init
*
*	函數(shù)功能:	串口2初始化
*
*	入口參數(shù):	baud:設(shè)定的波特率
*
*	返回參數(shù):	無
*
*	說明:		TX-PA2		RX-PA3
************************************************************
*/
void Usart2_Init(unsigned int baud)
{

	GPIO_InitTypeDef gpio_initstruct;
	USART_InitTypeDef usart_initstruct;
	NVIC_InitTypeDef nvic_initstruct;
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);
	
	//PA2	TXD
	gpio_initstruct.GPIO_Mode = GPIO_Mode_AF_PP;
	gpio_initstruct.GPIO_Pin = GPIO_Pin_2;
	gpio_initstruct.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &gpio_initstruct);
	
	//PA3	RXD
	gpio_initstruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;
	gpio_initstruct.GPIO_Pin = GPIO_Pin_3;
	gpio_initstruct.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &gpio_initstruct);
	
	usart_initstruct.USART_BaudRate = baud;
	usart_initstruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;		//無硬件流控
	usart_initstruct.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;						//接收和發(fā)送
	usart_initstruct.USART_Parity = USART_Parity_No;									//無校驗
	usart_initstruct.USART_StopBits = USART_StopBits_1;								//1位停止位
	usart_initstruct.USART_WordLength = USART_WordLength_8b;							//8位數(shù)據(jù)位
	USART_Init(USART2, &usart_initstruct);
	
	USART_Cmd(USART2, ENABLE);														//使能串口
	
	USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);									//使能接收中斷
	
	nvic_initstruct.NVIC_IRQChannel = USART2_IRQn;
	nvic_initstruct.NVIC_IRQChannelCmd = ENABLE;
	nvic_initstruct.NVIC_IRQChannelPreemptionPriority = 0;
	nvic_initstruct.NVIC_IRQChannelSubPriority = 0;
	NVIC_Init(&nvic_initstruct);

}





/*
************************************************************
*	函數(shù)名稱:	Usart_SendString
*
*	函數(shù)功能:	串口數(shù)據(jù)發(fā)送
*
*	入口參數(shù):	USARTx:串口組
*				str:要發(fā)送的數(shù)據(jù)
*				len:數(shù)據(jù)長度
*
*	返回參數(shù):	無
*
*	說明:		
************************************************************
*/
void Usart2_SendString(unsigned char *str, unsigned short len)
{

	unsigned short count = 0;
	
	for(; count < len; count++)
	{
		USART_SendData(USART2, *str++);									//發(fā)送數(shù)據(jù)
		while(USART_GetFlagStatus(USART2, USART_FLAG_TC) == RESET);		//等待發(fā)送完成
	}
}




3.3.onenet.h

#ifndef _ONENET_H_
#define _ONENET_H_





_Bool OneNet_DevLink(char *Tip);

void OneNet_SendData(char *Tip);
void OneNet_SendCmd(void);
void OneNet_RevPro(unsigned char *cmd);
_Bool OneNet_Subscribe(const char *topics[], unsigned char topic_cnt);
void OneNet_PacketPing(void);
_Bool OneNet_Publish(const char *topic, const char *msg);
void OneNet_HeartBeat(void);
#endif

3.4.onenet.c

//單片機頭文件
#include "stm32f10x.h"

//網(wǎng)絡(luò)設(shè)備
#include "esp8266.h"

//協(xié)議文件
#include "onenet.h"
#include "mqttkit.h"
#include "./SysTick/bsp_SysTick.h"

//硬件驅(qū)動
#include "usart.h"

//C庫
#include <string.h>
#include <stdio.h>

//全局變量
extern char *Tips ; //主題

#define PROID		"515377"   //產(chǎn)品ID

#define AUTH_INFO	"123"		//鑒權(quán)信息

#define DEVID		"943294381"	//設(shè)備ID


extern unsigned char esp8266_buf[128];

extern unsigned char esp8266_buf[128];

//==========================================================
//	函數(shù)名稱:	OneNet_DevLink
//
//	函數(shù)功能:	與onenet創(chuàng)建連接
//
//	入口參數(shù):	無
//
//	返回參數(shù):	1-成功	0-失敗
//
//	說明:		與onenet平臺建立連接
//==========================================================
_Bool OneNet_DevLink(char *Tip)
{
	
	MQTT_PACKET_STRUCTURE mqttPacket = {NULL, 0, 0, 0};					//協(xié)議包

	unsigned char *dataPtr;
	
	_Bool status = 1;
	
	printf("OneNet_DevLinkrnPROID: %s,	AUIF: %s,	DEVID:%srn", PROID, AUTH_INFO, DEVID);
	
	if(MQTT_PacketConnect(PROID, AUTH_INFO, DEVID, 256, 0, MQTT_QOS_LEVEL0, NULL, NULL, 0, &mqttPacket) == 0)
	{
		ESP8266_SendData(mqttPacket._data, mqttPacket._len);			//上傳平臺
		
		dataPtr = ESP8266_GetIPD(250);									//等待平臺響應(yīng)
		if(dataPtr != NULL)
		{
			if(MQTT_UnPacketRecv(dataPtr) == MQTT_PKT_CONNACK)
			{
				switch(MQTT_UnPacketConnectAck(dataPtr))
				{
					case 0:printf("Tips:%srn",Tip);
					status = 0;
					//LED2_ON;						//入網(wǎng)成功
					;break;
					
					case 1:printf("WARN:	連接失?。簠f(xié)議錯誤rn");break;
					case 2:printf("WARN:	連接失?。悍欠ǖ腸lientidrn");break;
					case 3:printf("WARN:	連接失?。悍?wù)器失敗rn");break;
					case 4:printf("WARN:	連接失敗:用戶名或密碼錯誤rn");break;
					case 5:printf("WARN:	連接失?。悍欠ㄦ溄?比如token非法)rn");break;
					
					default:printf("ERR:	連接失敗:未知錯誤rn");break;
				}
			}
		}
		
		MQTT_DeleteBuffer(&mqttPacket);								//刪包
	}
	else
		printf("WARN:	MQTT_PacketConnect Failedrn");
	
	return status;
	
}
u8 velue0 = 0;
u8 velue1 = 0;
unsigned char OneNet_FillBuf(char *buf)
{
	char text[32];
	
	memset(text, 0, sizeof(text));
	
	strcpy(buf, ",;");
		
	memset(text, 0, sizeof(text));
	sprintf(text, "Blue_Led,%d;", velue0);
	strcat(buf, text);
	
	memset(text, 0, sizeof(text));
	sprintf(text, "Yellow_Led,%d;", velue1);
	strcat(buf, text);
	
	return strlen(buf);
}

//==========================================================
//	函數(shù)名稱:	OneNet_SendData
//
//	函數(shù)功能:	上傳數(shù)據(jù)到平臺
//
//	入口參數(shù):	type:發(fā)送數(shù)據(jù)的格式
//
//	返回參數(shù):	無
//
//	說明:		
//==========================================================
void OneNet_SendData(char *Tip)
{
	
	MQTT_PACKET_STRUCTURE mqttPacket = {NULL, 0, 0, 0};												//協(xié)議包
	
	char buf[128];
	
	short body_len = 0, i = 0;
	
	//printf("Tips:%srn",Tip);
	
	memset(buf, 0, sizeof(buf));
	
	body_len = OneNet_FillBuf(buf);																	//獲取當(dāng)前需要發(fā)送的數(shù)據(jù)流的總長度
	
	if(body_len)
	{
		if(MQTT_PacketSaveData(DEVID, body_len, NULL, 5, &mqttPacket) == 0)							//封包
		{
			for(; i < body_len; i++)
				mqttPacket._data[mqttPacket._len++] = buf[i];
			
			ESP8266_SendData(mqttPacket._data, mqttPacket._len);									//上傳數(shù)據(jù)到平臺
			//printf("Send %d Bytesrn", mqttPacket._data);
			
			MQTT_DeleteBuffer(&mqttPacket);															//刪包
		}
		else
			printf("WARN:	EDP_NewBuffer Failedrn");
	}
	
}
//==========================================================
//	函數(shù)名稱:	OneNet_Publish
//
//	函數(shù)功能:	發(fā)布消息
//
//	入口參數(shù):	topic:發(fā)布的主題
//				msg:消息內(nèi)容
//
//	返回參數(shù):	0-成功	1-需要重送
//
//	說明:		
//==========================================================
_Bool OneNet_Publish(const char *topic, const char *msg)
{

	//Delay_ms(100);
	MQTT_PACKET_STRUCTURE mqttPacket = {NULL, 0, 0, 0};							//協(xié)議包
	
	//printf( "Publish Topic: %s, Msg: %srn", topic, msg);
	
	if(MQTT_PacketPublish(MQTT_PUBLISH_ID, topic, msg, strlen(msg), MQTT_QOS_LEVEL2, 0, 1, &mqttPacket) != 1)
	{

		ESP8266_SendData(mqttPacket._data, mqttPacket._len);					//向平臺發(fā)送訂閱請求
		MQTT_DeleteBuffer(&mqttPacket);											//刪包
	}
	return 0;

}
//==========================================================
//	函數(shù)名稱:	OneNet_Subscribe
//
//	函數(shù)功能:	訂閱
//
//	入口參數(shù):	topics:訂閱的topic
//				topic_cnt:topic個數(shù)
//
//	返回參數(shù):	SEND_TYPE_OK-成功	SEND_TYPE_SUBSCRIBE-需要重發(fā)
//
//	說明:		
//==========================================================
_Bool OneNet_Subscribe(const char *topics[], unsigned char topic_cnt)
{
	
	unsigned char i = 0;
	
	MQTT_PACKET_STRUCTURE mqttPacket = {NULL, 0, 0, 0};							//協(xié)議包
	
	for(; i < topic_cnt; i++)
		printf( "Subscribe Topic: %srn", topics[i]);
	
	if(MQTT_PacketSubscribe(MQTT_SUBSCRIBE_ID, MQTT_QOS_LEVEL2, topics, topic_cnt, &mqttPacket) == 0)
	{
		ESP8266_SendData(mqttPacket._data, mqttPacket._len);					//向平臺發(fā)送訂閱請求
		
		MQTT_DeleteBuffer(&mqttPacket);											//刪包
	}
  return 0;
}

//==========================================================
//	函數(shù)名稱:	OneNet_HeartBeat
//
//	函數(shù)功能:	心跳檢測
//
//	入口參數(shù):	無
//
//	返回參數(shù):	無
//
//	說明:		
//==========================================================
void OneNet_HeartBeat(void)
{
	
	MQTT_PACKET_STRUCTURE mqttPacket = {NULL, 0, 0, 0};
	
	unsigned char sCount = 3;
	
//---------------------------------------------步驟一:組包---------------------------------------------
	if(MQTT_PacketPing(&mqttPacket))
		return;
	
	while(sCount--)
	{
//---------------------------------------------步驟二:發(fā)送數(shù)據(jù)-----------------------------------------
		ESP8266_SendData(mqttPacket._data, mqttPacket._len);
	  //while(OneNet_DevLink(Tips))
---------------------------------------------步驟三:解析返回數(shù)據(jù)-------------------------------------
//		if(MQTT_UnPacketRecv(cmd) == MQTT_PKT_PINGRESP)
//		{
//			printf( "Tips:	HeartBeat OKrn");
//			
//			break;
//		}
//		else
//		{
//			//ESP8266_Init();					//初始化ESP8266
//			printf("Check Devicern");
//		}
		
		Delay_ms(10);
	}
	
//---------------------------------------------步驟四:刪包---------------------------------------------
	MQTT_DeleteBuffer(&mqttPacket);

}
/**
  * @brief  檢測字符長度并提取透傳
  * @param  無
  * @retval 無
  */
char my_strlen(int8 **payload)//?const?????
{
	static char cmd[200];
	int i=0;
	while(**payload)
	{
		cmd[i] = **payload;
		i++;
		payload++;
		printf("%dn",cmd[i]);
	}

	printf("%cn",cmd[100]);
	printf("%c%c%c%cn",cmd[111],cmd[112],cmd[113],cmd[114]);
	return cmd[11];
}
//==========================================================
//	函數(shù)名稱:	OneNet_RevPro
//
//	函數(shù)功能:	平臺返回數(shù)據(jù)檢測
//
//	入口參數(shù):	dataPtr:平臺返回的數(shù)據(jù)
//
//	返回參數(shù):	無
//
//	說明:		
//==========================================================
void OneNet_RevPro(unsigned char *cmd)
{
	
	MQTT_PACKET_STRUCTURE mqttPacket = {NULL, 0, 0, 0};								//協(xié)議包
	
	char *req_payload = NULL;
	char *cmdid_topic = NULL;
	
	unsigned short topic_len = 0;
	unsigned short req_len = 0;
	
	unsigned char type = 0;
	unsigned char qos = 0;
	static unsigned short pkt_id = 0;
	
	short result = 0;

	char *dataPtr = NULL;
	char numBuf[10];
	int num = 0;
	uint16_t ID = 0;
	type = MQTT_UnPacketRecv(cmd);
	switch(type)
	{

		case MQTT_PKT_CMD:															//命令下發(fā)
			
			result = MQTT_UnPacketCmd(cmd, &cmdid_topic, &req_payload, &req_len);	//解出topic和消息體
			if(result == 0)
			{
				printf( "cmdid: %s, req: %s, req_len: %drn", cmdid_topic, req_payload, req_len);
				
				if(MQTT_PacketCmdResp(cmdid_topic, req_payload, &mqttPacket) == 0)	//命令回復(fù)組包
				{
					printf( "Tips:	Send CmdResprn");
					
					ESP8266_SendData(mqttPacket._data, mqttPacket._len);			//回復(fù)命令
					MQTT_DeleteBuffer(&mqttPacket);									//刪包
				}
			}
		
		break;
			
		case MQTT_PKT_PUBLISH:														//接收的Publish消息
		
			result = MQTT_UnPacketPublish(cmd, &cmdid_topic, &topic_len, &req_payload, &req_len, &qos, &pkt_id);
			if(result == 0)
			{
				printf( "topic: %s, topic_len: %d, payload: %s, payload_len: %drn",
																	cmdid_topic, topic_len, req_payload, req_len);
	
				switch(qos)
				{
					case 1:															//收到publish的qos為1,設(shè)備需要回復(fù)Ack
					
						if(MQTT_PacketPublishAck(pkt_id, &mqttPacket) == 0)
						{
							printf( "Tips:	Send PublishAckrn");
							ESP8266_SendData(mqttPacket._data, mqttPacket._len);
							MQTT_DeleteBuffer(&mqttPacket);
						}
					
					break;
					
					case 2:															//收到publish的qos為2,設(shè)備先回復(fù)Rec
																					//平臺回復(fù)Rel,設(shè)備再回復(fù)Comp
						if(MQTT_PacketPublishRec(pkt_id, &mqttPacket) == 0)
						{
							printf( "Tips:	Send PublishRecrn");
							ESP8266_SendData(mqttPacket._data, mqttPacket._len);
							MQTT_DeleteBuffer(&mqttPacket);
						}
					
					break;
					
					default:
						break;
				}
			}
		
		break;
			
		case MQTT_PKT_PUBACK:														//發(fā)送Publish消息,平臺回復(fù)的Ack
		
			if(MQTT_UnPacketPublishAck(cmd) == 0)
				printf( "Tips:	MQTT Publish Send OKrn");
			
		break;
			
		case MQTT_PKT_PUBREC:														//發(fā)送Publish消息,平臺回復(fù)的Rec,設(shè)備需回復(fù)Rel消息
		
			if(MQTT_UnPacketPublishRec(cmd) == 0)
			{
				printf( "Tips:	Rev PublishRecrn");
				if(MQTT_PacketPublishRel(MQTT_PUBLISH_ID, &mqttPacket) == 0)
				{
					printf( "Tips:	Send PublishRelrn");
					ESP8266_SendData(mqttPacket._data, mqttPacket._len);
					MQTT_DeleteBuffer(&mqttPacket);
				}
			}
		
		break;
			
		case MQTT_PKT_PUBREL:														//收到Publish消息,設(shè)備回復(fù)Rec后,平臺回復(fù)的Rel,設(shè)備需再回復(fù)Comp
			
			if(MQTT_UnPacketPublishRel(cmd, pkt_id) == 0)
			{
				printf( "Tips:	Rev PublishRelrn");
				if(MQTT_PacketPublishComp(MQTT_PUBLISH_ID, &mqttPacket) == 0)
				{
					printf( "Tips:	Send PublishComprn");
					ESP8266_SendData(mqttPacket._data, mqttPacket._len);
					MQTT_DeleteBuffer(&mqttPacket);
				}
			}
		
		break;
		
		case MQTT_PKT_PUBCOMP:														//發(fā)送Publish消息,平臺返回Rec,設(shè)備回復(fù)Rel,平臺再返回的Comp
		
			if(MQTT_UnPacketPublishComp(cmd) == 0)
			{
				printf( "Tips:	Rev PublishComprn");
			}
		
		break;
			
		case MQTT_PKT_SUBACK:														//發(fā)送Subscribe消息的Ack
		
			if(MQTT_UnPacketSubscribe(cmd) == 0)
				printf( "Tips:	MQTT Subscribe OKrn");
			else
				printf( "Tips:	MQTT Subscribe Errrn");
		
		break;
			
		case MQTT_PKT_UNSUBACK:														//發(fā)送UnSubscribe消息的Ack
		
			if(MQTT_UnPacketUnSubscribe(cmd) == 0)
				printf( "Tips:	MQTT UnSubscribe OKrn");
			else
				printf( "Tips:	MQTT UnSubscribe Errrn");
		
		break;
		
		default:
			result = -1;
		break;
	}
	
	ESP8266_Clear();									//清空緩存
	
	if(result == -1)
		return;
	
		dataPtr = strchr(req_payload, ':');					//搜索':'

	if(dataPtr != NULL && result != -1)					//如果找到了
	{
		dataPtr++;
		printf("%s",Tips);
		while(*dataPtr >= '0' && *dataPtr <= '9')		//判斷是否是下發(fā)的命令控制數(shù)據(jù)
		{
			numBuf[num++] = *dataPtr++;
		}
		numBuf[num] = 0;
		
		num = atoi((const char *)numBuf);				//轉(zhuǎn)為數(shù)值形式
		printf( "num:%drn",num);
		if(strstr((char *)req_payload, "LED"))		//搜索"redled"
		{
			//LED
		
			//Mqtt_LED(Tips,num);
		}
		else if(strstr((char *)req_payload, "TOUCH"))
		{
			//指紋
			dataPtr = strchr(req_payload, '-');					//搜索'-'
			if(dataPtr != NULL && result != -1)					//如果找到了
			{
				dataPtr++;
				while(*dataPtr >= '0' && *dataPtr <= '9')		//判斷是否是下發(fā)的命令控制數(shù)據(jù)
				{
					numBuf[ID++] = *dataPtr++;
				}
				numBuf[ID] = 0;
				ID = atoi((const char *)numBuf);				//轉(zhuǎn)為數(shù)值形式
				printf("ID:%drn",ID);
				//Mqtt_Task( Tips, num, ID);
			}else{
				//Mqtt_Task(Tips, num, ID);
			}
			
		}
		else if(strstr((char *)req_payload, "DOOR"))
		{
			//步進電機
			//Door(Tips,num);
		}
		

	}

	if(type == MQTT_PKT_CMD || type == MQTT_PKT_PUBLISH)
	{
		MQTT_FreeBuffer(cmdid_topic);
		MQTT_FreeBuffer(req_payload);
	}

}

最后MQTT協(xié)議使用引用他人

3.5.MqttKit.h

#ifndef _MQTTKIT_H_
#define _MQTTKIT_H_


#include "Common.h"


//=============================配置==============================
//===========可以提供RTOS的內(nèi)存管理方案,也可以使用C庫的=========
//RTOS
#include <stdlib.h>

#define MQTT_MallocBuffer	malloc

#define MQTT_FreeBuffer		free
//==========================================================


#define MOSQ_MSB(A)         (uint8)((A & 0xFF00) >> 8)
#define MOSQ_LSB(A)         (uint8)(A & 0x00FF)


/*--------------------------------內(nèi)存分配方案標(biāo)志--------------------------------*/
#define MEM_FLAG_NULL		0
#define MEM_FLAG_ALLOC		1
#define MEM_FLAG_STATIC		2


typedef struct Buffer
{
	
	uint8	*_data;		//協(xié)議數(shù)據(jù)
	
	uint32	_len;		//寫入的數(shù)據(jù)長度
	
	uint32	_size;		//緩存總大小
	
	uint8	_memFlag;	//內(nèi)存使用的方案:0-未分配	1-使用的動態(tài)分配		2-使用的固定內(nèi)存
	
} MQTT_PACKET_STRUCTURE;


/*--------------------------------固定頭部消息類型--------------------------------*/
enum MqttPacketType
{
	
    MQTT_PKT_CONNECT = 1, /**< 連接請求數(shù)據(jù)包 */
    MQTT_PKT_CONNACK,     /**< 連接確認數(shù)據(jù)包 */
    MQTT_PKT_PUBLISH,     /**< 發(fā)布數(shù)據(jù)數(shù)據(jù)包 */
    MQTT_PKT_PUBACK,      /**< 發(fā)布確認數(shù)據(jù)包 */
    MQTT_PKT_PUBREC,      /**< 發(fā)布數(shù)據(jù)已接收數(shù)據(jù)包,Qos 2時,回復(fù)MQTT_PKT_PUBLISH */
    MQTT_PKT_PUBREL,      /**< 發(fā)布數(shù)據(jù)釋放數(shù)據(jù)包, Qos 2時,回復(fù)MQTT_PKT_PUBREC */
    MQTT_PKT_PUBCOMP,     /**< 發(fā)布完成數(shù)據(jù)包, Qos 2時,回復(fù)MQTT_PKT_PUBREL */
    MQTT_PKT_SUBSCRIBE,   /**< 訂閱數(shù)據(jù)包 */
    MQTT_PKT_SUBACK,      /**< 訂閱確認數(shù)據(jù)包 */
    MQTT_PKT_UNSUBSCRIBE, /**< 取消訂閱數(shù)據(jù)包 */
    MQTT_PKT_UNSUBACK,    /**< 取消訂閱確認數(shù)據(jù)包 */
    MQTT_PKT_PINGREQ,     /**< ping 數(shù)據(jù)包 */
    MQTT_PKT_PINGRESP,    /**< ping 響應(yīng)數(shù)據(jù)包 */
    MQTT_PKT_DISCONNECT,  /**< 斷開連接數(shù)據(jù)包 */
	
	//新增
	
	MQTT_PKT_CMD  		 /**< 命令下發(fā)數(shù)據(jù)包 */
	
};


/*--------------------------------MQTT QOS等級--------------------------------*/
enum MqttQosLevel
{
	
    MQTT_QOS_LEVEL0,  /**< 最多發(fā)送一次 */
    MQTT_QOS_LEVEL1,  /**< 最少發(fā)送一次  */
    MQTT_QOS_LEVEL2   /**< 只發(fā)送一次 */
	
};


/*--------------------------------MQTT 連接請求標(biāo)志位,內(nèi)部使用--------------------------------*/
enum MqttConnectFlag
{
	
    MQTT_CONNECT_CLEAN_SESSION  = 0x02,
    MQTT_CONNECT_WILL_FLAG      = 0x04,
    MQTT_CONNECT_WILL_QOS0      = 0x00,
    MQTT_CONNECT_WILL_QOS1      = 0x08,
    MQTT_CONNECT_WILL_QOS2      = 0x10,
    MQTT_CONNECT_WILL_RETAIN    = 0x20,
    MQTT_CONNECT_PASSORD        = 0x40,
    MQTT_CONNECT_USER_NAME      = 0x80
	
};


/*--------------------------------消息的packet ID,可自定義--------------------------------*/
#define MQTT_PUBLISH_ID			10

#define MQTT_SUBSCRIBE_ID		20

#define MQTT_UNSUBSCRIBE_ID		30


/*--------------------------------刪包--------------------------------*/
void MQTT_DeleteBuffer(MQTT_PACKET_STRUCTURE *mqttPacket);

/*--------------------------------解包--------------------------------*/
uint8 MQTT_UnPacketRecv(uint8 *dataPtr);

/*--------------------------------登錄組包--------------------------------*/
uint8 MQTT_PacketConnect(const int8 *user, const int8 *password, const int8 *devid,
						uint16 cTime, uint1 clean_session, uint1 qos,
						const int8 *will_topic, const int8 *will_msg, int32 will_retain,
						MQTT_PACKET_STRUCTURE *mqttPacket);

/*--------------------------------斷開連接組包--------------------------------*/
uint1 MQTT_PacketDisConnect(MQTT_PACKET_STRUCTURE *mqttPacket);

/*--------------------------------連接響應(yīng)解包--------------------------------*/
uint8 MQTT_UnPacketConnectAck(uint8 *rev_data);

/*--------------------------------數(shù)據(jù)點上傳組包--------------------------------*/
uint1 MQTT_PacketSaveData(const int8 *devid, int16 send_len, int8 *type_bin_head, uint8 type, MQTT_PACKET_STRUCTURE *mqttPacket);

/*--------------------------------二進制文件上傳組包--------------------------------*/
uint1 MQTT_PacketSaveBinData(const int8 *name, int16 file_len, MQTT_PACKET_STRUCTURE *mqttPacket);

/*--------------------------------命令下發(fā)解包--------------------------------*/
uint8 MQTT_UnPacketCmd(uint8 *rev_data, int8 **cmdid, int8 **req, uint16 *req_len);

/*--------------------------------命令回復(fù)組包--------------------------------*/
uint1 MQTT_PacketCmdResp(const int8 *cmdid, const int8 *req, MQTT_PACKET_STRUCTURE *mqttPacket);

/*--------------------------------訂閱主題組包--------------------------------*/
uint8 MQTT_PacketSubscribe(uint16 pkt_id, enum MqttQosLevel qos, const int8 *topics[], uint8 topics_cnt, MQTT_PACKET_STRUCTURE *mqttPacket);

/*--------------------------------訂閱主題回復(fù)解包--------------------------------*/
uint8 MQTT_UnPacketSubscribe(uint8 *rev_data);

/*--------------------------------取消訂閱組包--------------------------------*/
uint8 MQTT_PacketUnSubscribe(uint16 pkt_id, const int8 *topics[], uint8 topics_cnt, MQTT_PACKET_STRUCTURE *mqttPacket);

/*--------------------------------取消訂閱回復(fù)解包--------------------------------*/
uint1 MQTT_UnPacketUnSubscribe(uint8 *rev_data);

/*--------------------------------發(fā)布主題組包--------------------------------*/
uint8 MQTT_PacketPublish(uint16 pkt_id, const int8 *topic,
						const int8 *payload, uint32 payload_len,
						enum MqttQosLevel qos, int32 retain, int32 own,
						MQTT_PACKET_STRUCTURE *mqttPacket);

/*--------------------------------發(fā)布消息回復(fù)解包--------------------------------*/
uint8 MQTT_UnPacketPublish(uint8 *rev_data, int8 **topic, uint16 *topic_len, int8 **payload, uint16 *payload_len, uint8 *qos, uint16 *pkt_id);

/*--------------------------------發(fā)布消息的Ack組包--------------------------------*/
uint1 MQTT_PacketPublishAck(uint16 pkt_id, MQTT_PACKET_STRUCTURE *mqttPacket);

/*--------------------------------發(fā)布消息的Ack解包--------------------------------*/
uint1 MQTT_UnPacketPublishAck(uint8 *rev_data);

/*--------------------------------發(fā)布消息的Rec組包--------------------------------*/
uint1 MQTT_PacketPublishRec(uint16 pkt_id, MQTT_PACKET_STRUCTURE *mqttPacket);

/*--------------------------------發(fā)布消息的Rec解包--------------------------------*/
uint1 MQTT_UnPacketPublishRec(uint8 *rev_data);

/*--------------------------------發(fā)布消息的Rel組包--------------------------------*/
uint1 MQTT_PacketPublishRel(uint16 pkt_id, MQTT_PACKET_STRUCTURE *mqttPacket);

/*--------------------------------發(fā)布消息的Rel解包--------------------------------*/
uint1 MQTT_UnPacketPublishRel(uint8 *rev_data, uint16 pkt_id);

/*--------------------------------發(fā)布消息的Comp組包--------------------------------*/
uint1 MQTT_PacketPublishComp(uint16 pkt_id, MQTT_PACKET_STRUCTURE *mqttPacket);

/*--------------------------------發(fā)布消息的Comp解包--------------------------------*/
uint1 MQTT_UnPacketPublishComp(uint8 *rev_data);

/*--------------------------------心跳請求組包--------------------------------*/
uint1 MQTT_PacketPing(MQTT_PACKET_STRUCTURE *mqttPacket);


#endif

3.6.MqttKit.c

補充信息

?關(guān)于main函數(shù)的使用

//硬件初始化
static void Hardware_Init(void)
{
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);//設(shè)置中斷優(yōu)先級分組為組2:2位搶占優(yōu)先級,2位響應(yīng)優(yōu)先級
	
	SysTick_Init();			//延時函數(shù)初始化
	
	USART_Config();	//串口1初始化為115200
	Usart2_Init(115200); 	//串口2,驅(qū)動ESP8266用
//	BEEP_Config();
//	DHT11_Init();
//	ADCx_Init();

}

static void Net_Init()
{
	ESP8266_Init();					//初始化ESP8266

	Delay_ms(10);
	while(OneNet_DevLink(Tips))			//接入OneNET
		Delay_ms(100);
	OneNet_Subscribe(topics, 1);
   
}
/**
  * @brief  主函數(shù)
  * @param  無
  * @retval 無
  */
int main(void)
{
	unsigned char *dataPtr = NULL;
	unsigned short timeCount = 0;	//發(fā)送間隔變量
	unsigned short timeDoor = 0;	//發(fā)送間隔變量
	Hardware_Init();				//初始化外圍硬件
	
	Delay_ms(500);   /* 延時500個tick */

	Net_Init();						//網(wǎng)絡(luò)初始化

	
	
	while(1)
	{

		timeDoor ++;

		dataPtr = ESP8266_GetIPD(0);
		if(dataPtr != NULL)
		{
			//從收到信息開始算起
			timeCount = 0;
			OneNet_RevPro(dataPtr);
		
		}else if(timeCount >= 100)	//發(fā)送間隔
		{

			//每兩秒詢問指紋狀態(tài),自動關(guān)門
			sprintf((char*)string,"temp:%s humi:%s MQ_2:%s MQ_4:%s MQ_135:%s",temp,humi,MQ_2,MQ_4,MQ_135);
			printf("%sn",string);
			OneNet_Subscribe(topics, 1);
			//心跳包
			while(OneNet_Publish( Tips , string));
		
			timeCount = 0;
		}

		



	}	

 
  
}

關(guān)于如何實現(xiàn)Web端訂閱可以查看這篇文章

WebSocket連接MQTT方案之阿里云及其它平臺通解:https://blog.csdn.net/C2391494354/article/details/124918013?spm=1001.2014.3001.5502

注:轉(zhuǎn)載請標(biāo)明出處!

聯(lián)系方式 微信號:13648103287

  • 聯(lián)系方式.docx

推薦器件

更多器件
器件型號 數(shù)量 器件廠商 器件描述 數(shù)據(jù)手冊 ECAD模型 風(fēng)險等級 參考價格 更多信息
SN74LVC2G74DCURE4 1 Texas Instruments Single Positive-Edge-Triggered D-Type Flip-Flop with Clear and Preset 8-VSSOP -40 to 125

ECAD模型

下載ECAD模型
$0.62 查看
AFBR-57B4APZ 1 Broadcom Limited Transceiver,
$69.51 查看
KSZ9031RNXIC-TR 1 Microchip Technology Inc IC TXRX ETHERNET

ECAD模型

下載ECAD模型
$3.71 查看

相關(guān)推薦

電子產(chǎn)業(yè)圖譜

方案定制,程序設(shè)計方案、單片機程序設(shè)計與講解、APP定制開發(fā)。本公眾號致力于向讀者傳遞關(guān)于程序設(shè)計和開發(fā)的相關(guān)知識,并分享一些關(guān)于軟件開發(fā)的最佳實踐。如果您有什么問題或建議,請隨時聯(lián)系我們。我們將竭誠為您服務(wù)