[STM32+ESP266] 基於STM32單片機控制ESP8266連接服務器(包含C源碼)


關於ESP8266的介紹我已經看到噁心了,相同的內容到處都是,沒有幾個有實際價值的適合新手剛剛入門的內容。博主經過親身實驗,在成功之後在這裏記錄一下。一來方便引導新手,二來方便大家交流學習,最重要的是我時間久了容易忘記內容,方便自己進行查找。目前僅僅是連接通過TCP連接訪問服務器

在這裏說一下,本文只是ESP8266簡單的使用,沒有過於詳細的命令講解關於命令可以查詢相應的手冊,或者網絡資料非常多。我使用的是獨立小模塊ESP-01S,就是這種:

ESP-01S
不是直接又控制芯片的ESP32。
這裏是我購買的產品的公司的AT指令示例
能用到的代碼我會盡量展示全。

1.硬件需求

如果僅僅是測試那隻需要電腦和TTL電平轉USB的轉換器就好了,然後電腦打開串口調試助手,接線完成就可以實驗了。如果沒有合適的串口調試助可以點這個?
幾款好用的串口和網絡調試助手
(恬不知恥地在這爲自己宣傳)

如果你想連接STM32,難麼需要在單片機上留出一個USART,配置成115200波特率的8N1。部分模塊需要增加一個使能管腳,如果你想也可以在爲它留一個外部復位管腳(反正我是都沒有,不過也有這個想法,但是資源不允許呀)。

2.操作流程

1.ESP8266 station模式的操作邏輯流程:

0.檢查AT命令是不是能用
1.配置成Sation模式
2.連接到wifi
2.1可以查詢一下自己的地址
3.連接到服務器
4.發送數據
5.如果是單鏈接模式,想更換服務器,先斷開現在連接的
具體爲啥要單鏈接:因爲透傳模式必須是單鏈接。
啥是透傳模式:就是給啥轉發啥,不管是數據還是命令。也就是說透傳模式不會識別命令,具體怎麼玩後面專門記錄。
發送的數據

2.ESP8266 AP模式的操作邏輯流程

0.檢查AT命令是不是能用
1.配置成AP模式
2.配置AP信息
2.1可以查詢一下自己的地址
3.開啓多連接(如果要開服務器的話,必須是多連接)
4.開啓服務器和設置端口(一個命令)
5.等待接收數據
接收到的數據

3.透傳模式的進去和退出

配置透傳模式需要幾個命令:
建立好連接後:

  1. 設置成單鏈接模式(只能在單鏈接模式)
    AT+CIPMUX=0
  2. 設置爲透傳模式
    AT+CIPMODE=1
  3. 進入透傳模式
    AT+CIPSEND
    具體時先建立連接在設置透傳模式,還是先設置爲透傳模式在建立連接都可以。但是一定在進去透傳模式命令發送前建立連接。在模塊給你發完 ‘>’ 符號時就設置完成了。

然後就可以歡樂的傳數據了,你發啥它轉發啥。如果你在電腦上開了電腦的上的網絡調試助手你可以看到模塊給你發的內容:透傳演示
發現什麼問題沒?
–爲啥我的AT命令也直接發出去了?!
在透傳模式下無論你給ESP8266發啥,它都會無腦轉出去,即使是命令(此刻的模塊已經放飛自我,不受控制了)。如果想要重新可以獲得控制權就需要向它發送退出透傳命令。
–EXM?都說了它已經不聽命令了,咋還說要發命令!垃圾博主,說的屁話,不看了!
大哥留步!這是一條非常特殊的命令!它長得就和人家不一樣!

+++

你沒看錯,就是三個加號。在這裏多說一句,所有的AT命令後面都要加回車換行就是C語言的轉義字符 \r\n ,不然不識別。但是這裏一!定!不!要!加!爲啥? 因爲人家就是不一樣。
這裏就要說到一個坑。退出透傳命令必須要發送前有時間間隔,發送後有時間間隔,根據我實驗,最短200ms即可(有些資料上寫需要1s)。這裏實驗效果不明顯,就不展示了,各位有興趣可以自己親自實驗。

–既然透傳模式設置和退出這麼麻煩,那爲什麼要設置透傳模式?
在發送大量數據,或者我們建立好連接後就需要反反覆覆發數據,而不會控制硬件設備的操作時,透傳就體現除優越性了。至少可以減少每次發數據的命令。

4.C語言程序源碼

扯了這麼多,你是不是差點忘了你來是爲了啥(別以爲我不知道,這也是我曾經整個CSDN找資料的目的)。程序的邏輯和代碼就在這裏(使用的是Keil,所以代碼註釋在這裏顯示亂碼,不要管,我另外有註釋)。
理一下思路先:
1.我們需要控制串口收發命令。具體發送的是ASCLL碼。
2.我們需要通過串口接收數據,接收的也是SACLL碼。
3.我們要能識別什麼時候模塊發送完了。使用定時器計時,需要定義一個全局變量 ESP_RX_STATE 作爲串口接收到數據的標誌
4.接收完能夠處理接收的結果(這裏只講返回數據識別)

1.配置串口

先配置一下USART2,具體配置方法不做多餘解釋,TXD->PA2、RXD->PA3。代碼如下:

//宏定義
//ÍøÂç´®¿ÚʱÖÓ
#define ESP_RCC       RCC_APB1Periph_USART2

//ÍøÂç´®¿Ú·¢ËͶ˿Ú
#define ESP_TX_PIN    GPIO_Pin_2    //PA2
#define ESP_TX_GPIO   GPIOA

//ÍøÂç´®¿Ú½ÓÊն˿Ú
#define ESP_RX_PIN    GPIO_Pin_3   //PA3
#define ESP_RX_GPIO   GPIOA

#define ESP_USART     USART2

//wifi串口初始化 wifi´®¿Ú³õʼ»¯
void ESP_Usart_Config(void)
{
	  GPIO_InitTypeDef  GPIO_InitStructure;
	  USART_InitTypeDef USART_InitStructure;
	  NVIC_InitTypeDef NVIC_InitStructure;
	
	  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);                    //¿ªÆôGPIOAʱÖÓ
	  RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);                     //¿ªÆô¶Ë¿Ú¸´ÓÃʱÖÓ
	
    RCC_APB1PeriphClockCmd(ESP_RCC, ENABLE);                                 //¿ªÆôesp´®¿ÚʱÖÓ

	  USART_DeInit(ESP_USART);																								//¸´Î»esp´®¿Ú
	
//發送端口		·¢ËͶ˿Ú
	  GPIO_InitStructure.GPIO_Pin = ESP_TX_PIN;																				//PA2
	  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;																	//¸´ÓÃÍÆÍìÊä³ö
	  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	  GPIO_Init(ESP_TX_GPIO, &GPIO_InitStructure);
	
//接收端口		½ÓÊն˿Ú
	  GPIO_InitStructure.GPIO_Pin = ESP_RX_PIN;																		  	//PA3
	  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;														//¸´Óø¡¿ÕÊäÈë	
	  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	  GPIO_Init(ESP_RX_GPIO, &GPIO_InitStructure);	
		
	  //ÅäÖô®¿Úģʽ  
		USART_DeInit(ESP_USART);
	  USART_InitStructure.USART_BaudRate = 115200;																			//ÉèÖò¨ÌØÂÊΪ115200bps
	  USART_InitStructure.USART_WordLength = USART_WordLength_8b;												//Êý¾ÝλΪ8λ
	  USART_InitStructure.USART_StopBits = USART_StopBits_1;														//ֹͣλ1λ
	  USART_InitStructure.USART_Parity = USART_Parity_No;																//ÎÞУÑéλ
	  USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;										//·¢ËÍ¡¢½ÓÊÕģʽ´ò¿ª
	  USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;		//ÎÞÓ²¼þ¿ØÖÆÁ÷¿Ø
	
	  USART_Init(ESP_USART, &USART_InitStructure);																		//´«ËÍÅäÖòÎÊý
	
	  USART_ClearFlag(ESP_USART, USART_FLAG_CTS);																			//Çå³ý·¢ËÍÍê³É±ê־λ
    USART_Cmd(ESP_USART, ENABLE);																										//ʹÄÜ´®¿Ú1	  	
	
	  USART_ITConfig(ESP_USART, USART_IT_RXNE, ENABLE);																//¿ªÆô½ÓÊÕÖÐ¶Ï £¨1×Ö½ÚÒ»´ÎÖжϣ©
//		USART_ITConfig(ESP_USART, USART_IT_IDLE, ENABLE);																//¿ªÆô¿ÕÏÐÖжϣ¨8×Ö½ÚÒ»´ÎÖжϣ©
		
		
    //ÉèÖÃNVIC
	  NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
	  
	  NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;																	//ÉèÖô®¿Ú2ÖжÏ
	  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority =0;													//ÇÀÕ¼ÓÅÏȼ¶
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;																//×ÓÓÅÏȼ¶Îª1
	  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;																		//ʹÄÜ
	  NVIC_Init(&NVIC_InitStructure);		

    printf("## ESP_usart_config_over ##\r\n");
}

2.配置定時器

還需要一個定時器,我是用的是TIM3,配置成10ms中斷,並寫中斷函數

//定義一個全局變量用來指示是否接收到數據
//超過10ms沒有收到數據認爲接收結束,則ESP_RX_STATE增加1。即定時器中斷就加1
u8 ESP_RX_STATE = 0;          //³õʼ»¯ÒѽÓÊÕÊý¾ÝΪ0
//在頭文件中聲明外部變量,這樣可以在其他C文件中調用
extern u8 ESP_RX_STATE;            //½ÓÊÕÍê³É״̬

//定時器TIM3中斷優先級配置¶¨Ê±Æ÷TIM3ÖжÏÓÅÏȼ¶ÅäÖÃ
void TIM3_NVIC_config(void)
{	
        NVIC_InitTypeDef NVIC_InitStructure;
    
	    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);           //ÉèÖÃ×éÓÅÏȼ¶
		NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;           //ÉèÖö¨Ê±Æ÷3ÖжÏÏß
		NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; //ÉèÖÃÇÀÕ¼ÓÅÏȼ¶
		NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;        //ÉèÖÃ×ÓÓÅÏȼ¶2
		NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;           //ʹÄÜIRQÖÐ¶Ï 
		NVIC_Init(&NVIC_InitStructure);
}

//定時器TIM3初始化,10ms¶¨Ê±Æ÷TIME3³õʼ»¯,10ms
void TIM3_init(void)
{
      TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;            //TIM3³õʼ»¯½á¹¹Ìå 
	  RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);	    //¿ªÆôʱÖÓ
	  TIM_DeInit(TIM3);                                         //½«ÍâÉèTIM3¼Ä´æÆ÷ÖØÉèΪȱʡֵ
	  
	  TIM_TimeBaseStructure.TIM_Period = (2000-1);                  //×Ô¶¯ÖØ×°ÔؼĴæÆ÷Öµ
	  TIM_TimeBaseStructure.TIM_Prescaler =(3600-1);            //ʱÖÓÔ¤·ÖƵÊý	
	  TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;    //²ÉÑù·ÖƵ£¬Ê¹¼ÆʱÆ÷¹¤×÷ƵÂÊΪ72MHz
	  TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;//ÏòÉϼÆÊýģʽ
	
	  TIM_TimeBaseInit(TIM3,&TIM_TimeBaseStructure);             //ÅäÖö¨Ê±Æ÷
	
	  TIM3_NVIC_config();                                        //ÅäÖÃTIM3ÖжÏÓÅÏȼ¶
	
	  TIM_ClearFlag(TIM3, TIM_FLAG_Update);                      //Çå³ý±ê־λ
	  TIM_ITConfig(TIM3, TIM_IT_Update, ENABLE);                 //ÖжÏÅäÖÃ
//	  TIM_Cmd(TIM3, ENABLE);                                     //ʹÄܶ¨Ê±Æ÷TIM3
}
//TIM3中斷函數TIM3ÖжϺ¯Êý
void TIM3_IRQHandler(void)
{
    if(TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET)          //²úÉúÖжÏ
		{		   

			ESP_RX_STATE ++;             //超過10ms,數據接收完畢,計數+1 ³¬¹ý10ms£¬Êý¾Ý½ÓÊÕÍê±Ï£¬¼ÆÊý+1
        						
		}
  	TIM_ClearITPendingBit(TIM3, TIM_FLAG_Update);  //	Çå³ýÖжϱêÖ¾
		
		TIM_Cmd(TIM3, DISABLE);   //¹Ø±ÕTIM3
}

3.準備接收緩衝區

在準備接收數據之前得先準備一個地方用來存儲數據吧。我們爲了簡潔高效,使用循環隊列作爲數據接收的緩衝區

//宏定義緩衝隊列最大存儲上限
#define ESP_BUF_SIZE   1024     //ESP»º³åÇø×î´óÈÝÁ¿

//循環對列結構體
typedef struct 
{
    unsigned char buf[ESP_BUF_SIZE];
	unsigned short int length;
    unsigned short int fornt;               //ESP¶ÓÁÐÍ·Ö¸Õë
    unsigned short int rear;               //ESP¶ÓÁÐβָÕë
}ESP_BufTypeDef;                     //¶¨ÒåESPÑ­»·¶ÓÁлº³åÇø½á¹¹Ìå

//聲明一個隊列緩衝區,作爲全局變量
ESP_BufTypeDef ESP_RX_BUF;      //Wifi´®¿Ú½ÓÊÕ»º³åÇø
//在頭文件中聲明外部變量,這樣可以在其他C文件中調用
extern ESP_BufTypeDef ESP_RX_BUF;      //Wifi´®¿Ú½ÓÊÕ»º³åÇø
//接收緩衝區初始化ESP½ÓÊÕ»º³åÇø³õʼ»¯
void ESP_Rxbuf_Init(void)
{
	  int i ;
	  memset(ESP_RX_BUF.buf,0,sizeof(ESP_RX_BUF.buf));   //使用memset()函數需要包含頭文件<string.h>
//	for(i=0;i< ESP_BUF_SIZE; i++)
//	  {
//	      ESP_RX_BUF.buf[i] = 0;
//	  }
      ESP_RX_BUF.fornt = 0;
	  ESP_RX_BUF.length = 0;
	  ESP_RX_BUF.rear = 0;
	
	  ESP_RX_STATE = 0;          //ÔÊÐí½ÓÊÕÊý¾Ý
}

到這裏接收的數據存儲位置也準備好了。

4.串口中斷函數

可是定時器中斷函數好了,存儲位置準備好了,沒有數據過來也是不行的呀。那就來寫數據接收過程,即網絡串口接收中斷函數(USART2的中斷函數):

//串口接收中斷函數´®¿Ú½ÓÊÕÖжϺ¯Êý	
void USART2_IRQHandler(void)
{
  	u8 rev_byte;
    if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET)			//Åж϶ȼĴæÆ÷ÊÇ·ñΪ·Ç¿Õ
	 {
	    rev_byte = USART_ReceiveData(USART2);     //接收數據½ÓÊÕÊý¾Ý
										                                         
		if(ESP_RX_BUF.length <= ESP_BUF_SIZE) //如果數據沒有溢出Èç¹ûÊý¾ÝûÓÐÒç³ö
		 {									 
		    ESP_RX_BUF.buf[ESP_RX_BUF.rear] = rev_byte;			//將數據存入緩衝區尾部½«Êý¾Ý´æÈëBUFF»º³åÇøβ²¿
	        ESP_RX_BUF.length ++;                           //緩衝區長度增加»º³åÇø³¤¶ÈÔö¼Ó
			ESP_RX_BUF.rear = (ESP_RX_BUF.rear + 1) % ESP_BUF_SIZE;  //尾指針+1,防止溢出,實現循環隊列·ÀÖ¹Òç³ö
		 }		
		 //這裏的TIM3其實可以用宏定義,這樣可以方便更改使用的定時器
            TIM_SetCounter(TIM3,0);                         //定時器清空¼ÆÊýÆ÷Çå¿Õ
			TIM_Cmd(TIM3,ENABLE);                           //使能定時器ʹÄܶ¨Ê±Æ÷	 
	  }
			
	  USART_ClearITPendingBit(USART2, USART_IT_RXNE);						//清除標誌Çå³ý±êÖ¾
}	

如此,我們就可以將接收到的數據保存起來了。

5.讀取緩衝隊列函數

但是好像還是不行,只是保存起來,卻不能使用,你說難不難受。

//讀取ESP隊列的數據¶ÁÈ¡espÊý¾Ý½ÓÊÕ¶ÓÁеÄÊý¾Ý
u8 ESP_Read_Quene_Data(ESP_BufTypeDef* Rx_buf)
{
    u8 read_data_temp;
	if(Rx_buf->length == 0)    //沒有數據
	{
	    read_data_temp = 0;
	}
	else
	{
	    read_data_temp = Rx_buf->buf[Rx_buf->fornt];  //讀出頭指針所指的數據
		Rx_buf->fornt = (Rx_buf->fornt + 1) % ESP_BUF_SIZE;//頭指針增加
		Rx_buf->length --;  //數據長度減少
	}
	return read_data_temp;  //返回讀取的結果
}

讀取數據是不是很簡單。

6.串口發送數據函數

既然可以接收和讀取了,那怎麼發送數據呢?這裏就要使用到串口發送數據的方法了,如果回使用或者已經寫好的可跳過:

/*-----------------------------------------
 *串口發送字符串函數´®¿Ú·¢ËÍ×Ö·û´®º¯Êý
 * USARTx Ö¸¶¨·¢Ë͵Ĵ®¿Ú
 * *DataÖ¸¶¨´ý·¢Ë͵Ä×Ö·û´®
 * 發送遇到字符串結束符‘\0’結束·¢ËÍÓöµ½×Ö·û´®½áÊø·û'\0'½áÊø
-----------------------------------------*/
//這個函數講真寫的不是太好,有更好的大家可以修改
void USART_Send_String(USART_TypeDef* USARTx, u8 *Data, ...)
{//使用變長參數,具體實現原理可百度
	  //·ÖÅäÄÚ´æ
		 va_list valist;
	  //ΪÊäÈëµÄ²ÎÊý³õʼ»¯
	   va_start(valist, Data);
	
    while(*Data != '\0')
		{
		    USART_SendData(USARTx, *Data);
			  while(USART_GetFlagStatus(USARTx, USART_FLAG_TXE) == RESET); //µÈ´ý´®¿Ú·¢ËÍÍêÊý¾Ý
			  Data++;
		}
		
		//ÇåÀíΪvalist±£ÁôµÄÄÚ´æ
		va_end(valist);
}

至此,我們的準備工作就算完成了,要真正開始操刀對ESP8266下手了。

7.讀取回覆信號函數

根據手冊,我們可以知道,我們可以給ESP8266發送命令,然後它會回給我們相應的結果。我們也可以給它發數據,但是要先發相應的命令,然後跟着發送數據。所以我們先寫發送命令函數,但是好像有個問題:發送完命令緊跟着是得到命令的結果,不然我們也不知道模塊有沒有執行。所以不要着急開始寫CMD函數,先來寫回複函數:

//講真這是非常失敗的函數,但是可以實現
//宏定義返回值
#define ACK_SUCCESS   1
#define ACK_DEFEAT    0

u8 ESP_Check_Ack(char *string)         //¼ì²éÏàÓ¦»Ø¸´ÐźÅ
{
      char *str_temp;               //被檢測的字符串的臨時變量
	  u8 data_temp;
	  u8 rx_buff_temp[150]={0};     //臨時存放接收到的數據ÁÙʱ´æ´¢½ÓÊÕµ½µÄÊý¾Ý
	  u8 pt_temp = 0;               //rx_buff_temp的下標rx_buff_tempµÄϱê
	  u8 length_temp = 0;   //臨時數組的當前長度
		
	  str_temp = string;
	  
	  if(ESP_RX_STATE > 0)    //Èç¹û½ÓÊÕÊý¾Ý½áÊø
		{
			//»ñÈ¡ÊÕµ½µÄ»Ø¸´ ÖÁÁÙʱÊý×飬±ãÓÚÅжÏ		
		    do
			  {
				  if(length_temp < 150)
				  {
				      data_temp = ESP_Read_Quene_Data(&ESP_RX_BUF);//從緩衝區讀取一個值到數組
			          rx_buff_temp[pt_temp] = data_temp;
				      pt_temp ++;  //下標向後移動一位

			          length_temp ++;
			       }
				  else 
					    break;  //³¬³ö´æ´¢ÉÏÏÞ£¬Ã»ÓÐÊÕµ½¡®\0¡¯Ôò»áËÀÑ­»·£¬Ìø³öµ±Ç°Ñ­»·
		  	}
			  while(data_temp != '\0');
			
        ESP_RX_STATE --; //讀取完一個字符串,接收區計數減一¶ÁÈ¡ÍêÒ»¸ö½ÓÊÕµÄ×Ö·û´®£¬¼ÆÊý-1									    
		}	
		//ÅжÏÊÇ·ñ·ûºÏÆÚÍûÐźÅ
		if(strstr((const char *)rx_buff_temp, (const char *)str_temp ) != NULL)  //檢測到有想要的回覆,就能得到不爲空的地址
				
			   return ACK_SUCCESS;   //返回成功Èç¹û·µ»ØµØÖ·²»Îª¿Õ£¬·µ»ØÓÐЧ			
		else
			   return ACK_DEFEAT;    //如果沒有,就是失敗·ñÔò£¬·µ»Øʧ°Ü
}

8.發送命令函數

有檢查回覆信號的函數後就可以寫命令函數了:

/*--------------------------------------------------
 * ·¢ËÍÖ¸ÁϣÍûµÃµ½»Ø¸´
 * *cmd 發送的命令
 * *ack 希望得到的回覆
 * waittime 等待的時長
 * ·µ»ØÖµ ACK_SUCCESS£¬·¢Ëͳɹ¦
 *        ACK_DEFEAT£¬·¢ËÍʧ°Ü
---------------------------------------------------*/
u8 ESP_Send_Cmd(char *cmd, char *ack, u16 waittime )
{//其實這裏也可以寫成邊長參數的,而且會極大的方便後面發送命令,不過博主是寫完功能了才學會的邊長參數,後面我會在我程序中修改
    USART_Send_String(ESP_USART, (u8 *)cmd);  //直接使用發送字符串函數
	  while(waittime-- )
		{
		    if(ESP_Check_Ack(ack) == ACK_SUCCESS)
				{
				   return ACK_SUCCESS;
				}
				delay_ms(10);  //每次等待10ms,所以waittime爲20的時候,實際等待200ms
		}
	  return ACK_DEFEAT;
}

是不是很簡單。

9.發送數據函數

下面就是發送數據函數,之所以最後寫,是因爲發數據之前要發送一個命令,告訴ESP8266我要發數據了:

/*----------------------------------------------------
 * ·¢ËÍÊý¾Ý¸øwifiÄ£¿é£¬
 *  *data爲要發送的數據*dataΪ·¢Ë͵ÄÊý¾Ý
----------------------------------------------------*/
void ESP_Send_Data(u8 *data)
{
      char cmdbuf_temp[32];  //整合命令使用的臨時數組
	  unsigned short len;    //發送數據的長度
	  len = sizeof(data);    
	  sprintf(cmdbuf_temp,"AT+CIPSEND=%d\r\n", len);//格式化的命令寫入命令字符串中,就是把長度加上°Ñ¸ñʽ»¯µÄÊý¾ÝдÈëij¸ö×Ö·û´®ÖÐ
	
	  if(ESP_Send_Cmd(cmdbuf_temp, ">", 200) == ACK_SUCCESS)  //檢測接收數據是否包含‘>’¼ì²é½ÓÊÕÊý¾ÝÊÇ·ñΪ¡°>¡±
		{
		    USART_Send_String(ESP_USART, data);			           //發送數據·¢ËÍÊý¾Ý
		}
}

因爲本篇寫的是ESP8266連接服務器的方法,所以作爲AP時候使用的接收數據解析IPD就不寫了。至此,所有函數寫完了。

10.連接TCP併發送數據演示

在使用的時候,先把ESP8266的端口配置初始化,緩衝區初始化。然後愉快的使用ESP_Send_Cmd函數發送命令,控制模塊就好了。
這裏我展示一個連接TCP並開啓透傳模式的過程。只是簡單演示如何使用,在實際發送命令過程中返回失敗還是很多的,所以要有容錯,讀者自己思考。

#define wifi_information  "AT+CWJAP_DEF=\"wifi名稱\",\"wifi密碼\"\r\n"
#define tcp_information "AT+CIPSTART=\"TCP\",\"TCP地址\",端口號\r\n" 


 if(ESP_Send_Cmd("AT\r\n","OK",20) == ACK_DEFEAT) //測試一下AT命令
 {
      delay_ms(50);
	  ESP_Send_Cmd("+++","",0);    //Í˳ö͸´«Ä£Ê½
	  delay_ms(50);
 }
 
 ESP_Send_Cmd("AT+CWMODE=1\r\n","OK",20);//設置成Station模式
 //一定要重啓!!!
 ESP_Send_Cmd("AT+RST\r\n","ready",20);
 delay_ms(1000);   //一般1s就可以,也可以設置時間長一些
 ESP_Rxbuf_Init(); //往往啓動是時候模塊會發版本信息,清空一下

 printf("Connecting to Wifi.\r\n");
 if(ESP_Send_Cmd("AT+CWJAP_DEF?\r\n","No AP",50) == ACK_SUCCESS)   //返回沒有連接AP·µ»ØûÓÐÁ¬½ÓAP
 {
     for(count_up = 0; count_up < 5; count_up ++ )       //Ñ­»·Á¬½Ó5´Î
     {
         if(ESP_Send_Cmd(wifi_information, "WIFI GOT IP", 600) == ACK_SUCCESS)    //如果連接成功Èç¹ûÁ¬½Ó³É¹¦
         {
              printf("Have conntected.\r\n");
              break;
         }else 
		 {
              printf("Rejoin Wifi.\r\n");
		 }
     }
 }
 //此處可以添加如果重連5次都失敗的處理方法
 
    /*有時候遇到部分8266在鏈接TCP後不能進行單路鏈接設置*/
  ESP_Send_Cmd("AT+CIPMUX=0\r\n","OK",15);      //單路連接模式
  ESP_Send_Cmd(tcp_information, "CONNECT", 50); //連接到TCP
     //這裏其實就可以使用發送數據函數ESP_Send_Data開始發數據了
     //ESP_Send_Data("Hllow word!\n")

  ESP_Send_Cmd("AT+CIPMODE=1\r\n","OK",20)//進去透傳模式
  ESP_Send_Cmd("AT+CIPSEND\r\n","OK",20)//進入透傳模式
//透傳模式直接使用串口發送函數即可
  USART_Send_String(USART2, "Anything!");       

大功告成!

博主寫的已經腦袋暈了,如果有錯誤煩請指正,我再修改

/----------------------------------------------------------------------------------------/
2020.03.23更新
發現朋友們對我的博文很感興趣,我在這裏更新一下相關代碼工程的源文件。完整工程內容駁雜,加之我的代碼水平不是太好,不方便初學者看,會走火入魔的。

MDK工程資源鏈接
https://download.csdn.net/download/qq_40329772/12307863

我把串口2的配置放在了ESP8266源文件裏面了。按照有些人的習慣會將他獨立出來,方到USART的相關文件裏面,條理更清晰,便於代碼管理維護。

以下內容謹慎觀看

/*ESP8266.h*/
#ifndef __ESP8266_H_
#define __ESP8266_H_

#include "stm32f10x.h"

#define ESP_BUF_SIZE   680     //ESP緩衝區長度

#define ESP_RX_END    0x01     //接收數據結束
#define ESP_RX_ALLOW  0x00     //允許接收數據/正在接收數據

#define ACK_SUCCESS   1
#define ACK_DEFEAT    0

/*-------------TCP地址和端口--------------*/
//時間地址
#define ESP_TIME_TCP_ADDRESS "quan.suning.com"
#define ESP_TIME_TCP_POINT "80"

#define ESP_TIME_INFO  "GET http://quan.suning.com/getSysTime.do HTTP/1.1\r\nHost: quan.suning.com\r\n\r\n"   //GET請求時間
//
#define ESP_WEATHER_TCP_ADDRESS "api.seniverse.com"   //心知天氣
#define ESP_WEATHER_TCP_POINT "80"

#define ESP_WEATHER_INFO "GET http://api.seniverse.com/v3/weather/now.json?key=寫入自己的密鑰&location=地區&language=en&unit=c HTTP/1.1\r\nHost: api.seniverse.com\r\n\r\n"  //GET請求,密鑰和地區需要手動寫入,爲實現自動定位

  //這裏是解析時間時使用的便宜地址
#define YERA_ADD_DRES 11
#define MOON_ADD_DRES 15
#define DAYS_ADD_DRES 17
#define HOURS_ADD_DRES 19
#define MINUTES_ADD_DRES 21
#define SECONDS_ADD_DRES 23


#pragma pack(1)
typedef struct 
{
    unsigned char buf[ESP_BUF_SIZE];
	unsigned short int length;
    unsigned short int fornt;               //ESP隊列頭指針
    unsigned short int rear;               //ESP隊列尾指針
}ESP_BufTypeDef;            //定義ESP循環隊列緩衝區結構體

#pragma pack()

extern ESP_BufTypeDef ESP_RX_BUF;      //Wifi串口接收緩衝區
extern ESP_BufTypeDef ESP_TX_BUF;      //Wifi串口發送緩衝區,實際沒使用
extern u8 ESP_RX_STATE;            //接收完成狀態

extern int WEATHER;
extern int TEMPERATURE;

//網絡串口時鐘
#define ESP_RCC       RCC_APB1Periph_USART2

//網絡串口發送端口
#define ESP_TX_PIN    GPIO_Pin_2    //PA2
#define ESP_TX_GPIO   GPIOA

//網絡串口接收端口
#define ESP_RX_PIN    GPIO_Pin_3   //PA3
#define ESP_RX_GPIO   GPIOA

#define ESP_USART     USART2

//wifi模塊使能端口
#define ESP_CS_RCC    RCC_APB2Periph_GPIOA
#define ESP_CS_PIN    GPIO_Pin_4   //PA4
#define ESP_CS_GPIO   GPIOA

#define ESP_CS_H   GPIO_SetBits(ESP_CS_GPIO, ESP_CS_PIN) //ESP CS pin high
#define ESP_CS_L   GPIO_ResetBits(ESP_CS_GPIO, ESP_CS_PIN) //ESP CS pin low



void ESP_Usart_Config(void);
void ESP_Rxbuf_Init(void);     /*初始化緩衝區函數*/

u8 ESP_Connect_TCP(const char *tcp_addr, char *point);
void ESP_Set_Link_Mode(char mode);
u8 ESP_Quit_Trans(void);
u8 ESP_Read_Quene_Data(ESP_BufTypeDef* Rx_buf);
u8 ESP_Check_Ack(char *string);
u8 ESP_Send_Cmd(char *cmd, char *ack, u16 waittime);
void ESP_Send_Data(u8 *data);
void ESP_Init(void);             /*初始化模塊函數,主函數調用*/


u8 ESP_Set_Stationmode(void);
u8 ESP_Set_APmode(char *ap_ssid, char *ap_pwd, char chl, char ecn);
u8 ESP_Connect_AP(const char *wifi_ssid, const char *wifi_password);
void ESP_Set_Link_Mode(char mode);
u8 ESP_Connect_TCP(const char *tcp_addr, char *point);
void ESP_Set_Trans(void);

u8 ESP_Start_Trans(void);

u8 ESP_Init_Time(void);       /*初始化時間函數,主函數調用*/
u8 ESP_Fefresh_NET_Massage(const char *tcp_addr, char *point, void (*function)());

u8 ESP_Get_Wifiinfo(char *wifi_info);

void GET_Net_Time(void);      /*獲取時間函數,主函數調用*/
void Get_NET_Waether(void);   /*獲取天氣函數,主函數調用*/
int Get_Day(char *d);
int Get_Week(char *w);
int Get_Moonth(char *m);
int Get_Year(char *y);
int Get_Times(char *h, char *m, char *s);

void Print_ESP_Rx_Buf(void);

#endif

/*ESP8266.C*/

#include "stm32f10x.h"
#include "esp8266.h"
#include "font.h"  //字體文件,用於顯示屏幕
#include <string.h>
#include <stdlib.h>


ESP_BufTypeDef ESP_RX_BUF;      //Wifi串口接收緩衝區
u8 ESP_RX_STATE = 0;          //初始化已接收數據爲0

/*天氣和溫度變量*/
int WEATHER = 0;
int TEMPERATURE = 0;

/*WIFI連接使用*/
char ESP_WIFI_SSID[125]  = "MN001";
char ESP_WIFI_PASSWORD[125] = "MN001789";

//wifi串口初始化
void ESP_Usart_Config(void)
{
	  GPIO_InitTypeDef  GPIO_InitStructure;
	  USART_InitTypeDef USART_InitStructure;
	  NVIC_InitTypeDef NVIC_InitStructure;
	
	  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);                    //開啓GPIOA時鐘
	  RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);                     //開啓端口複用時鐘
	
      RCC_APB1PeriphClockCmd(ESP_RCC, ENABLE);                                 //開啓esp串口時鐘

	  USART_DeInit(ESP_USART);																								//復位esp串口
	
//		發送端口
	  GPIO_InitStructure.GPIO_Pin = ESP_TX_PIN;																				//PA2
	  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;																	//複用推輓輸出
	  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	  GPIO_Init(ESP_TX_GPIO, &GPIO_InitStructure);
	
//		接收端口
	  GPIO_InitStructure.GPIO_Pin = ESP_RX_PIN;																		  	//PA3
	  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;														//複用浮空輸入	
	  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	  GPIO_Init(ESP_RX_GPIO, &GPIO_InitStructure);	
		
	  //配置串口模式  
		USART_DeInit(ESP_USART);
	  USART_InitStructure.USART_BaudRate = 115200;																			//設置波特率爲115200bps
	  USART_InitStructure.USART_WordLength = USART_WordLength_8b;												//數據位爲8位
	  USART_InitStructure.USART_StopBits = USART_StopBits_1;														//停止位1位
	  USART_InitStructure.USART_Parity = USART_Parity_No;																//無校驗位
	  USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;										//發送、接收模式打開
	  USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;		//無硬件控制流控
	
	  USART_Init(ESP_USART, &USART_InitStructure);																		//傳送配置參數
	
	  USART_ClearFlag(ESP_USART, USART_FLAG_CTS);																			//清除發送完成標誌位
      USART_Cmd(ESP_USART, ENABLE);																										//使能串口1	  	
	
	  USART_ITConfig(ESP_USART, USART_IT_RXNE, ENABLE);																//開啓接收中斷 (1字節一次中斷)		
		
    //設置NVIC
	  NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
	  
	  NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;																	//設置串口2中斷
	  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority =0;													//搶佔優先級
      NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;																//子優先級爲1
	  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;																		//使能
	  NVIC_Init(&NVIC_InitStructure);		

    printf("## ESP_usart_config_over ##\r\n");
}

/*串口接收中斷函數*/	
void USART2_IRQHandler(void)
{
  	unsigned char rev_byte;
    if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET)			//判斷度寄存器是否爲非空
	    {
		       rev_byte = USART_ReceiveData(USART2);     //接收數據
				
//				   if(ESP_RX_STATE == ESP_RX_ALLOW)            //允許接收數據,即接收完的數據已經處理
				   {     /*這裏是入隊函數,可以封裝出來*/                                    
				         if(ESP_RX_BUF.length <= ESP_BUF_SIZE) //如果數據沒有溢出
						 {									 
							 ESP_RX_BUF.buf[ESP_RX_BUF.rear] = rev_byte;			//將數據存入BUFF緩衝區尾部
	                         ESP_RX_BUF.length ++;                           //緩衝區長度增加
						     ESP_RX_BUF.rear = (ESP_RX_BUF.rear + 1) % ESP_BUF_SIZE;  //防止溢出
						 }		
                 		  TIM_SetCounter(TIM3,0);                         //計數器清空
				          TIM_Cmd(TIM3,ENABLE);                           //使能定時器	 
					 }
	    }				
	  USART_ClearITPendingBit(USART2, USART_IT_RXNE);						//清除標誌
}	

//ESP接收緩衝區初始化
void ESP_Rxbuf_Init(void)
{
	  memset(ESP_RX_BUF.buf,0,sizeof(ESP_RX_BUF.buf));

      ESP_RX_BUF.fornt = 0;
	  ESP_RX_BUF.length = 0;
	  ESP_RX_BUF.rear = 0;
	
	  ESP_RX_STATE = 0;          //允許接收數據
}

//讀取esp數據接收隊列的數據
u8 ESP_Read_Quene_Data(ESP_BufTypeDef* Rx_buf)
{
    u8 read_data_temp;
	if(Rx_buf->length == 0)
	{
	    read_data_temp = 0;
	}
	else
	{
	      read_data_temp = Rx_buf->buf[Rx_buf->fornt];
		  Rx_buf->fornt = (Rx_buf->fornt + 1) % ESP_BUF_SIZE;
		  Rx_buf->length --;
	}
	return read_data_temp;
}
u8 ESP_Check_Ack(char *string)         //檢查相應回覆信號
{
      char *str_temp;            
	  u8 data_temp;
	  u8 rx_buff_temp[150]={0};     //臨時存儲接收到的數據
	  u8 pt_temp = 0;               //rx_buff_temp的下標
	  u8 length_temp = 0;
		
	  str_temp = string;
	  
	  if(ESP_RX_STATE > 0)    //如果接收數據結束
		{
			//獲取收到的回覆 至臨時數組,便於判斷		
		    do
			  {
				  if(length_temp < 150)
				  {
				      data_temp = ESP_Read_Quene_Data(&ESP_RX_BUF);
			          rx_buff_temp[pt_temp] = data_temp;
				      pt_temp ++;

			        length_temp ++;
//					printf("## data%d :%c ##\r\n",length_temp,data_temp);
			    }
				  else 
					    break;  //超出存儲上限,沒有收到‘\0’則會死循環,跳出當前循環
		  	}
			  while(data_temp != '\0');
			
        ESP_RX_STATE --; //讀取完一個接收的字符串,計數-1									    
		}	
		//判斷是否符合期望信號
		if(strstr((const char *)rx_buff_temp, (const char *)str_temp ) != NULL)
				
			 return ACK_SUCCESS;   //如果返回地址不爲空,返回有效
			
		else
			 return ACK_DEFEAT;    //否則,返回失敗
}

//發送指令,希望得到回覆
//返回值 ACK_SUCCESS,發送成功
//       ACK_DEFEAT,發送失敗
u8 ESP_Send_Cmd(char *cmd, char *ack, u16 waittime )
{

    USART_Send_String(ESP_USART, (u8 *)cmd);
	  while(waittime-- )
		{
		    if(ESP_Check_Ack(ack) == ACK_SUCCESS)
				{
				   return ACK_SUCCESS;
				}
			delay_ms(10);
		}
	  return ACK_DEFEAT;
}

//發送數據給wifi模塊,
//  *data爲發送的數據
//  len爲發送數據的長度
void ESP_Send_Data(u8 *data)
{
      char cmdbuf_temp[32];
	  unsigned short len;
	  len = sizeof(data);
	  sprintf(cmdbuf_temp,"AT+CIPSEND=%d\r\n", len);//把格式化的數據寫入某個字符串中
	
	  if(ESP_Send_Cmd(cmdbuf_temp, ">", 200) == ACK_SUCCESS)  //檢查接收數據是否爲“>”
		{
		    USART_Send_String(ESP_USART, data);			           //發送數據
		}
}

void ESP_Init(void)
{
     GPIO_InitTypeDef  GPIO_InitStructure;

   	 ESP_Usart_Config();          //初始化串口
	//初始化片選端口
	  RCC_APB2PeriphClockCmd(ESP_CS_RCC, ENABLE);
      GPIO_InitStructure.GPIO_Pin = ESP_CS_PIN;
      GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
      GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
      GPIO_Init(ESP_CS_GPIO, &GPIO_InitStructure);
	  
	  ESP_CS_H;  //WIFI模塊使能
	  
	  ESP_Rxbuf_Init();  //初始化緩衝區	 
}

/*-------------------------------------------------
 * 設置ESP8266爲STATION模式
 * 返回值 ACK_SUCCESS 設置成功
 *       ACK_DEFEAT  設置失敗
-------------------------------------------------*/
u8 ESP_Set_Stationmode(void)
{
	    u8 return_temp = 0;
	
	    if(ESP_Send_Cmd("AT\r\n", "OK", 15) == ACK_DEFEAT)   //先檢查並退出透傳模式
			{
			    ESP_Quit_Trans();
			}
	
      if(ESP_Send_Cmd("AT+CWMODE?\r\n","+CWMODE:1",20) != ACK_SUCCESS)  //如果查詢當前不是不是Station模式
	     {  
				printf("Set ESP mode.\r\n");
				    //設置工作模式爲Station模式
               ESP_Send_Cmd("AT+CWMODE=1\r\n","OK",20); 
                //重啓WIFI
	           ESP_Send_Cmd("AT+RST\r\n","ready",20);
	           delay_ms(1000);
	           ESP_Rxbuf_Init();
	    }			 
			 else
			 {
			     printf("Is station mode.\r\n");
			 }
			
			ESP_Send_Cmd("AT+CWAUTOCONN=1\r\n","",20);   //使能上電自動連接AP
			if(ESP_Send_Cmd("AT+CWMODE?\r\n","+CWMODE:1",20) == ACK_SUCCESS)  //如果查詢當前是Station模式
			{
			     return_temp = ACK_SUCCESS;   //設置成功
			}
			else 
			{
			     return_temp = ACK_DEFEAT;    //設置失敗
			}		
			return return_temp;			
}

/*-------------------------------------------------
 *設置ESP8266爲AP模式
 * *ap_ssid  AP名稱字符串 
 * *ap_pwd   AP密碼字符串
 * chl       通道號
 * ecn       加密方式 0:OPEN\ 1:WEP\ 2:WPA_PASK\ 3:WPA2_PASK\ 4:WPA_WPA2_PSK
 *返回值 ACK_SUCCESS 設置成功
 *       ACK_DEFEAT  設置失敗
-------------------------------------------------*/
u8 ESP_Set_APmode(char *ap_ssid, char *ap_pwd, char chl, char ecn)
{
	    u8 return_temp = 0;
	    char ap_information[150]={0};   //ap熱點臨時數組
			
			if(ESP_Send_Cmd("AT\r\n", "OK", 15) == ACK_DEFEAT)   //先檢查並退出透傳模式
			{
			    ESP_Quit_Trans();
			}
					
      if(ESP_Send_Cmd("AT+CWMODE?\r\n","+CWMODE:2",20) != ACK_SUCCESS)  //如果查詢當是不是AP模式
	     {  
			  printf("Set ESP mode.\r\n");
				    //設置工作模式爲AP模式
              ESP_Send_Cmd("AT+CWMODE=2\r\n","OK",20); 
            //重啓WIFI
	           ESP_Send_Cmd("AT+RST\r\n","ready",20);
	           delay_ms(1000);
	           ESP_Rxbuf_Init();
	    }
			 else
			 {
			     printf("Is AP mode.\r\n");
			 }
			sprintf(ap_information, "AT+CWSAP_DEF=\"%s\",\"%s\",%c,%c\r\n", ap_ssid, ap_pwd, chl, ecn);  //將AP熱點名稱和密碼整合存入字符串	
			ESP_Send_Cmd(ap_information, "OK", 100);         //發送設置命令
			
			ESP_Send_Cmd("AT+CIPMUX=1\r\n", "OK", 20);       //開啓多連接,然後才能開啓服務器
			ESP_Send_Cmd("AT+CIPSERVER=1,80\r\n", "OK", 20);  //開啓服務器,端口號爲80
			
			if(ESP_Send_Cmd("AT+CWMODE?\r\n","+CWMODE:2",20) == ACK_SUCCESS)  //如果查詢當前是AP模式
			{
			     return_temp = ACK_SUCCESS;   //設置成功
				   WIFI_LOGO = SHOW;
			}
			else 
			{
			     return_temp = ACK_DEFEAT;    //設置失敗
				   
			}
			
			Print_ESP_Rx_Buf();
			return return_temp;
}

/*-------------------------------------------------
 * 連接WiFi熱點
 * 返回值 ACK_SUCCESS 連接成功
 *       ACK_DEFEAT  連接失敗
-------------------------------------------------*/
u8 ESP_Connect_AP(const char *wifi_ssid, const char *wifi_password)
{
	 char wifi_information[150]={0};   //WiFi熱點臨時數組
	 short int count_up = 0;     //重連次數	 
	 u8 return_temp = 0;
	 	 
  //建立WiFi連接前先查詢ip是否已經連接,是則不在連接wifi
    if(ESP_Send_Cmd("AT+CIPSTATUS\r\n","STATUS:",20) != ACK_SUCCESS)   //返回沒有連接AP
	{
			    printf("Will conntect to AP %s.\r\n", ESP_WIFI_SSID);				
			    Show_String8(7,0,"Connecting to Wifi:");
				delay_ms(700);
				Show_String8(7,0,(char *)wifi_ssid);
				delay_ms(700);
				
				sprintf(wifi_information, "AT+CWJAP_DEF=\"%s\",\"%s\"\r\n", wifi_ssid, wifi_password);  //將WiFi熱點名稱和密碼整合存入字符串	
   //建立WIFI連接				

	    for(count_up = 0; count_up < 5; count_up ++ )       //循環連接5次
	    {
		      if(ESP_Send_Cmd(wifi_information, "WIFI GOT IP", 600) == ACK_SUCCESS)    //如果連接成功
		       {
				     printf("Have conntected.\r\n");
				     Show_String8(7,0,"Have Connected WiFi.");
              	     delay_ms(700);						
                     return_temp = ACK_SUCCESS;	
                     WIFI_LOGO = SHOW;  
				      break;							
			    }
			  else 
			  {
			        printf("Rejoin Wifi.\r\n");
					Show_String8(7,0,"Rejoin Wifi.");
			  }
	     }
     }
    else
		  {
            		printf("Had conntected.\r\n");
				    return_temp = ACK_SUCCESS;
				    WIFI_LOGO = SHOW; 
			        Show_String8(7,0,"Had Connected WiFi.");
				    delay_ms(700);	
		  } 		
     //Print_ESP_Rx_Buf();
	 if(count_up >=5)    //連接失敗	
	{
		  printf("Conntect failed .\r\n")      
		 return_temp = ACK_DEFEAT; 
		 WIFI_LOGO = HIDE;
	}
	 return return_temp;
}

/*--------------------------------------------------
 * 傳入的數據應該爲 +IPD,設備編號,字節長度:接收內容
 * 將接收到的wifi名稱和密碼數組傳遞給數組
 -------------------------------------------------*/
u8 ESP_Get_Wifiinfo(char *wifi_info)
{
      char *ssid = NULL;
	  char *password = NULL;
	   
	 int ssid_pt  = 0; //WiFi名稱數組下標
	 int pwd_pt   = 0; //WiFi密碼數組下標
	   
	 ssid = strstr((const char *) wifi_info, ":") + 1;  //得到WIFI名稱的第一位地址
	 if(ssid == NULL)
	{
		 return ACK_DEFEAT;
	}
    while(*ssid != ';')   //在沒有遇到密碼符號前	
		{
		     ESP_WIFI_SSID[ssid_pt] = *ssid;
			  
			  ssid ++;
			  ssid_pt ++;
		}
		password = ssid + 1;     // 遇到密碼符號後,地址+1,就是密碼的第一位地址
		while(*password != '\0') //沒有遇到結束符號
		{
		     ESP_WIFI_PASSWORD[pwd_pt] = *password;
			  
			  password ++;
			  pwd_pt ++;
		}
		
	  ESP_WIFI_PASSWORD[pwd_pt] = *password;  //此時*password 是 \0,寫入密碼數組做字符串結束標識符
		
	  return ACK_SUCCESS;	
}


/*-------------------------------------------------
 * 設置ESP8266爲連接模式
 * =0:單路連接模式    =1:多路連接模式
-------------------------------------------------*/
void ESP_Set_Link_Mode(char mode)
{
	  char str1[12] = {0};     //查詢匹配使用的字符串
	  char str2[14] = {0};     //設置使用的字符串
	  sprintf(str1, "+CIPMUX:%c", mode);            //查詢匹配使用的字符串
	  sprintf(str2, "AT+CIPMUX=%c\r\n", mode);      //設置使用的字符串
	
      if((ESP_Send_Cmd("AT+CIPMUX?\r\n",str1,13)) != ACK_SUCCESS)
		 {
		     printf("Set single link.\r\n");
				 ESP_Send_Cmd(str2,"OK",13);
	 	 }
}
/*-------------------------------------------------
 * 連接tcp
 * 返回值 ACK_SUCCESS 連接成功
 *       ACK_DEFEAT  連接失敗
-------------------------------------------------*/
u8 ESP_Connect_TCP(const char *tcp_addr, char *point)
{
	    char tcp_information[64]={0};
		short int count_up = 0;     //重連次數
		u8 return_temp = 0;
		
	  sprintf(tcp_information, "AT+CIPSTART=\"TCP\",\"%s\",%s\r\n", tcp_addr, point);  //將TCP,地址和端口號傳給臨時變量
	//	printf("%s\n",tcp_information);
		for(count_up = 0; count_up < 5; count_up ++ )       //循環連接5次
		{
		    if(ESP_Send_Cmd(tcp_information, "CONNECT", 50) == ACK_SUCCESS)    //如果連接成功
				{
	//				  Print_ESP_Rx_Buf();
				     return_temp = ACK_SUCCESS;
					  break;
				}
		}
		if(count_up >=5)    //連接失敗
		{
		     return_temp = ACK_DEFEAT; 
		}
		
		return return_temp;
}

/*-------------------------------------------------
 * 設置透傳模式命令
-------------------------------------------------*/
void ESP_Set_Trans(void)
{
    if((ESP_Send_Cmd("AT+CIPMODE?\r\n","+CIPMODE:1",10)) != ACK_SUCCESS)   //如果當前不是透傳模式
		{
	      	ESP_Send_Cmd("AT+CIPMODE=1\r\n","OK",10);
		}
	else
    {
		  printf("Is passthrough.\r\n");
	 }
     
    	ESP_Send_Cmd("AT+CIPSEND\r\n","OK",11);          //開啓透傳   至少大於100ms否則不能成功
       printf("ESP passthrough mode set over.\r\n");
}

//wifi模塊退出透傳模式
//返回值 ACK_SUCCESS,退出成功
//       ACK_DEFEAT,退出失敗
u8 ESP_Quit_Trans(void)
{		
//	if( ESP_Send_Cmd("AT\r\n","OK",15) == ACK_DEFEAT)  //發送AT命令
	{
	  delay_ms(20);
	  ESP_Send_Cmd("+++","",0);    //退出透傳模式
	  delay_ms(50);
	}
	  return ESP_Send_Cmd("AT\r\n","OK",15);    //判斷是否退出	  
}
/*---以上ESP8266的驅動函數基本完成 ,下面是我的獲取的時間和天氣並且解析,有許多不好的地方。最好能夠獨立一個C文件------------------------------*/


/*-------------------------------------------------
 * 初始化獲得時間函數
-------------------------------------------------*/
u8 ESP_Init_Time(void)
{	      				 
   //獲取信息
	    Show_image(bishitu);      //界面
      Show_String8(0,0,"Please wait ...");
//		  delay_ms(1000);   //等待WIFI連接穩定
//	    delay_ms(1000);
		 
	    ESP_Quit_Trans();    //退出透傳
		  ESP_Set_Stationmode();	             //設置爲STATION工作模式
	
		  Show_String8(7,0,"Getting time...");
    	if(ESP_Send_Cmd("AT+CWJAP_DEF?\r\n","+CWJAP_DEF:",50) != ACK_SUCCESS)   //返回沒有連接AP
  	 {
					if(ESP_Connect_AP(ESP_WIFI_SSID, ESP_WIFI_PASSWORD) != ACK_SUCCESS)       //開始連接網絡,如果沒有成功
						{
							 OLED_clear();
							 Show_String8(2,0,"Connect to Wifi defeat, please confirm that Wifi exists.");
							 Show_String8(5,0,"And RESET.");
							 while(1)
							 {;}

						}
	  }
				
	    ESP_Set_Link_Mode(0);
	    if(ESP_Connect_TCP(ESP_TIME_TCP_ADDRESS,ESP_TIME_TCP_POINT) != ACK_SUCCESS)//連接時間
				{
					   OLED_clear();
				     Show_String8(0,0,"Getting time defeat,");
					   Show_String8(1,0,"Please RESET.");
						 while(1)
						 {;}
 				}	
			    
	      ESP_Set_Trans();            //開啓透傳模式				
				ESP_Rxbuf_Init();
		
		    GET_Net_Time();             //初始化獲取網絡時間
				ESP_Rxbuf_Init();	
				
				ESP_Quit_Trans() ;          //退出透傳模式        
        ESP_Send_Cmd("AT+CIPCLOSE\r\v", "CLOSED", 20) 	;		//斷開tcp連接
				
        //ESP_Set_Link_Mode(0);
				// Print_ESP_Rx_Buf();  
        if(ESP_Connect_TCP(ESP_WEATHER_TCP_ADDRESS,ESP_WEATHER_TCP_POINT) != ACK_SUCCESS)//連接天氣
				{
					   OLED_clear();
				     Show_String8(0,0,"Getting weather defeat,");
					   Show_String8(1,0,"Please RESET.");
						 while(1)
						 {;}
 				}
				      //  Print_ESP_Rx_Buf();  
				ESP_Set_Trans();     //開啓透傳模式							
				ESP_Rxbuf_Init();
				
			  Get_NET_Waether(); //獲得天氣		          
			
				OLED_clear();
				Show_String8(0,0,"Init over.");
				
				ESP_Quit_Trans() ;                        //退出透傳模式 
        ESP_Send_Cmd("AT+CIPCLOSE\r\v", "CLOSED", 20);		//斷開tcp連接
       
				return ACK_SUCCESS;
	
}

/*-------------------------------------------------
 * 更新網絡時間函數
 * 已經連接WIFI,定期連接TCP更新網絡時間
-------------------------------------------------*/

u8 ESP_Fefresh_NET_Massage(const char *tcp_addr, char *point, void (*function)())
{
	 char wifi_information[150]={0};   //WiFi熱點臨時數組
	 short int count_up = 0;     //重連次數
	 u8 return_temp = 0;
	 
    //檢測是否連接wifi
    
	   //if(ESP_Send_Cmd("AT+CIPSTATUS\r\n","STATUS:",50) != ACK_SUCCESS)   //返回沒有連接AP
		 if(ESP_Send_Cmd("AT+CWJAP_DEF?\r\n","No AP",50) == ACK_SUCCESS)   //返回沒有連接AP
		 {
				Print_ESP_Rx_Buf();
			   Show_String8(7,0,"Rejoin Wifi.");
			   printf("Rejoin Wifi");
	       		sprintf(wifi_information, "AT+CWJAP_DEF=\"%s\",\"%s\"\r\n", ESP_WIFI_SSID, ESP_WIFI_PASSWORD);  //將WiFi熱點名稱和密碼整合存入字符串
				
   //建立WIFI連接				
			   for(count_up = 0; count_up < 5; count_up ++ )       //循環連接5次
				  {
					    if(ESP_Send_Cmd(wifi_information, "OK", 500) == ACK_SUCCESS)    //如果連接成功
				        {
							   printf("Have conntected.\r\n");
							   WIFI_LOGO = SHOW;
							   Show_String8(7,0,"                         ");
				                break;							
						 }
				 }
			  if(count_up >=5)    //連接失敗
		     {
			        Show_String8(7,0," Wifi missing.");
					                      
					   WIFI_LOGO =HIDE;
				      return ACK_DEFEAT; 
		     }		  
		 }
		 
//     Print_ESP_Rx_Buf();
		 
		  ESP_Set_Link_Mode(0);
		  if(ESP_Connect_TCP(tcp_addr,point) == ACK_SUCCESS)//連接時間成功
			{			
				    ESP_Set_Trans();                                             //開啓透傳模式
				    ESP_Rxbuf_Init();
				
				    function();            //可調用獲取網絡時間函數和獲取網絡天氣函數
				    ESP_Rxbuf_Init();	
            
				    ESP_Quit_Trans() ;        //退出透傳模式
				    ESP_Send_Cmd("AT+CIPCLOSE\r\v", "CLOSED", 20);  //斷開TCP連接
			}				
			else
			{
			     return ACK_DEFEAT;     //返回失敗
			}

		  return return_temp;         //更新成功
}



//獲取網絡時間並解析給系統
void GET_Net_Time(void)
{
	   char *data_pt = NULL;
	
	   char *day_string;
	   char *moon_string;
	   char *year_string;
	   char *hour_string;
	   char *minute_string;
	   char *second_string;

			 
	  USART_Send_String(USART2, ESP_TIME_INFO);  //發送GET請求
      while(!(ESP_RX_STATE > 0))    //判斷接收標誌
			{
			     delay_ms(200);
				  break;       //防止卡死
			}  
			 data_pt = strstr((const char *) ESP_RX_BUF.buf,(const char *)"sysTime1");   //尋找到時間結果的地址
			 ESP_RX_STATE --;
			 
			if(data_pt != NULL)
			{
		    	day_string = data_pt + DAYS_ADD_DRES;    //日期地址
	            moon_string = data_pt + MOON_ADD_DRES;   //月份地址
	            year_string = data_pt + YERA_ADD_DRES;   //年份地址
	            hour_string = data_pt + HOURS_ADD_DRES;  //小時地址
	            minute_string = data_pt + MINUTES_ADD_DRES;  //分鐘地址
	            second_string = data_pt + SECONDS_ADD_DRES;   //秒中地址				 
		 
//將時間信息傳遞給全局變量		
	        DAYS = Get_Day(day_string);
		    	MOONS = Get_Moonth(moon_string);
			    YEARS = Get_Year(year_string);
			    TIMES = Get_Times(hour_string, minute_string, second_string);
			}
			else
			{
			     printf("get net time failed!\r\n");
			}
}
//獲得日期函數
//輸入值是日期位置的地址
//返回值是 整型的10進制兩位數
int Get_Day(char *d)
{
    int day_return;
	day_return = atoi(d)/1000000;   //取日期
	
	return day_return;
}

//獲得月份函數
//輸入值是月份位置的地址
//返回值是 整型的10進制兩位數
int Get_Moonth(char *m)
{   
	int moonth_return;
	
    moonth_return = atoi(m)/100000000;   //取月份
    
	return moonth_return;
}

//獲得年函數
//輸入值是年位置的地址
//返回值是 整型的10進制四位數
int Get_Year(char *y)
{
	  int year_return;
	  char *year_temp;
      char year[5] = {0};
	  char i;
//年的獲取需要提取一次字符串,否則無法讀取		
	  year_temp = y;
	  for(i = 0; i<4; i++)
	  {
	      year[i] = *year_temp;
		  year_temp ++;
	  }
	  			
	  year_return =  atoi(&year[0]);
			
	  return year_return;	
}

//獲得時間
//輸入值是時間的位置的地址
//返回值是 整型的10進制的時間總秒數
int Get_Times(char *h, char *m, char *s)
{
     int time_return;
	 int hour_return;
     int min_return;
     int sec_return;	
	
	  hour_return = atoi(h)/10000;  //取小時
	  min_return = atoi(m)/100;   //取分鐘
	  sec_return = atoi(s);   //取秒數
	
	  time_return = hour_return*3600 + min_return*60 + sec_return;   //轉換成總秒數
	
	  return time_return;
}

void Get_NET_Waether(void)
{
	  char *weather_pt = NULL;   //存儲天氣起始地址
	  char *temperature_pt = NULL;  //存儲溫度起始地址
	  int i = 0;
	  char weather[3] = {0};   //存儲天氣代碼字符串
	  char temperature[4] = {0};//存儲溫度字符串
	   
    USART_Send_String(USART2, ESP_WEATHER_INFO); //獲得天氣	

		while(!(ESP_RX_STATE > 0))    //判斷接收標誌
			{
			    delay_ms(200);
				  break;              //防止卡死
			}    
		//	Print_ESP_Rx_Buf();  //測試使用
		weather_pt = strstr((const char *) ESP_RX_BUF.buf,(const char *)"\"code\":") + 8;   //尋找到天氣結果的地址
		temperature_pt = strstr((const char *) ESP_RX_BUF.buf,(const char *)"\"temperature\":") + 15;   //尋找到溫度結果的地址
		
		for(i = 0; i < 2; i++)   //複製天氣代碼到天氣代碼緩衝數組
		{
		    weather[i] = *weather_pt;
			  weather_pt ++;
		}
		WEATHER = atoi(&weather[0]);    //將字符串轉爲整型
		
		for(i = 0; i < 3; i++)   //複製溫度到溫度緩衝數組
		{
		    temperature[i] = *temperature_pt;
			  temperature_pt ++;
		}
		TEMPERATURE = atoi(&temperature[0]);  //將字符串轉成整型
		
		ESP_RX_STATE --;
	  
}

void Print_ESP_Rx_Buf(void)
{
	 int i;
		for(i=0;i<ESP_BUF_SIZE;i++)
		{
			printf("%c",ESP_RX_BUF.buf[i]);
		}
	  	printf("\n");
}

/*main.c*/
#include "sys1.h"
#include "stm32f10x.h"

#include "uart.h"
#include "timer.h"
#include "oled.h"
#include "spi.h"
#include "esp8266.h"
#include "font.h"

void Head_Init(void)
{
	  SystemInit();
	  Systick_init();
	  
	  Print_Usart_Config();      //日誌打印串口初始化
	 // TIM1_init();
      TIM2_init();
	  TIM3_init();
	  OLED_init();           //顯示屏  端口配置和初始化

	  ESP_Init();    //WIFI模塊端口配置
		
	  printf("## Init_over ##\r\n");
}

/*主函數*/
int main(void)
{

	  Head_Init();
	 	
      ESP_Init_Time();    //初始化時間 和天氣
		
	  OLED_clear();
	  Display_mode();  //顯示模板框架

	  Show_char16(0,120,power8,1);
	  Display_days(YEARS, MOONS, DAYS);  //初始化顯示時間與日期
	  TIM1_init();  //獲取時間完成再初始化定時器
	
	  Display_time(TIMES);
	  Display_temperature(TEMPERATURE, WEATHER);             //初始化顯示溫度
		
	//	IWDG_Init();            //看門狗初始化
				
while(1)
{
    //DISPLAYTIME是定時器中斷裏面的一個變量,用來定時刷新
    if(DISPLAYTIME == 1)              //刷新顯示
	{
			//如果達到24小時,則進行日期推演		
			if(TIMES >= 86400)  
			{    
				 TIMES = 0;
				 DAYS ++;
					   
				Times_Change(DAYS,MOONS,YEARS);    //計算當前日期
                Display_days(YEARS, MOONS, DAYS);  //顯示日期
			}		
      	    Display_time(TIMES);	
            DISPLAYTIME = 0;
						
			//			 IWDG_Feed();   //餵狗
	}		
     if(GETNETTIME  >= 1800)   //半小時校準一次網絡時間
     {
			ESP_Fefresh_NET_Massage(ESP_TIME_TCP_ADDRESS, ESP_TIME_TCP_POINT,GET_Net_Time);    //更新時間				  
            Display_temperature(TEMPERATURE, WEATHER); // 每小時刷新顯示天氣
			GETNETTIME = 0;
	}	
	if(GETNETTIME  >= 30)   //每15min校準一次天氣數據
	{
			ESP_Fefresh_NET_Massage(ESP_WEATHER_TCP_ADDRESS, ESP_WEATHER_TCP_POINT,Get_NET_Waether);    //更新天氣					  
            Display_temperature(TEMPERATURE, WEATHER); // 每小時刷新顯示天氣
			GETNETTIME = 0;
    } 		
}




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章