1. 程式人生 > >菜鳥江濤帶你學最小物聯網系統之模組篇(02)——STM32通過串列埠傳送AT指令控制ESP模組連線伺服器

菜鳥江濤帶你學最小物聯網系統之模組篇(02)——STM32通過串列埠傳送AT指令控制ESP模組連線伺服器

接著上一篇繼續,這篇部落格我將帶大家使用STM32的串列埠來發送AT指令給ESP模組連線伺服器。當然目前測試使用的是區域網,自己的電腦當伺服器使用。使用TCP連線伺服器,STM32通過ESP12F模組透傳上傳溫溼度資料到伺服器。看下效果圖片

好了,看下主要的實現程式碼:

#include "stm32f10x.h"
#include "delay.h"
#include "oled.h"
#include "bmp.h"
#include "dth11.h"
#include "usart.h"
#include "esp_at_cmd.h"
#include "string.h"

unsigned char buffer[5];
unsigned int hum , temp ;
unsigned char t1[5];
unsigned char t2[5];

char tx1[] = "TEMP:";
char tx2[] = "HUM:";

/************
 * [ESP8266_SendCmd 傳送命令到ESP8266]
 * @param  cmd   [需要傳送的AT指令]
 * @param  reply [期望模組回顯的內容]
 * @param  wait  [等待的時間(ms)]
 * @return       [期望內容等於實際回顯內容返回1,否則0]
 *************/
int ESP8266_SendCmd(u8* cmd, char* reply, int wait) 
{
	
	USART1_Send_String(cmd);		// 傳送指令,等待回顯接收
	DelayMs(wait);							// 等待接收回顯內容
	
	if(strcmp(reply , "") == 0) return 0 ; // 不進入回顯判斷
	
	if((USART_RX_STA&0x8000) == 1) 					// 接收完成
	{
		USART_RX_STA = 0 ;							// 重置標誌位
		
		if(strstr((char*)USART_RX_BUF , reply))			// 如果返回的字串中包含有期望的返回值
		{
			return 1 ;
		}
	}
	return 0 ;
}

/**************
 * 初始化
 *************/
int Esp_mode() 
{
	int res = 0 ;
	
	res = ESP8266_SendCmd(AT_INIT , "OK" , 500);	// 初始化
	
	return res ;

}

/**************
 * 連線WIFI
 *************/
int Esp_joinAp() 
{
	int res = 0 ;
	
	res = ESP8266_SendCmd(AT_WIFI , "WIFI CONNECTED" , 5000);	// 連線WIFI
	
	return res ;

}

/**************
 * 連線TCP
 *************/
int Esp_connectTcp() 
{
	int res = 0 ;
	
	res = ESP8266_SendCmd(AT_TCP , "CONNECT" , 5000);	// 連線TCP
	
	return res ;

}

/**************
 * 設定透傳模式
 *************/
int Esp_sendMode() 
{
	int res = 0 ;
	
	res = ESP8266_SendCmd(AT_MODE , "OK" , 500);	// 設定模組的透傳模式
	
	return res ;

}

/**************
 * 啟動傳送
 *************/
int Esp_sendStart() 
{
	int res = 0 ;
	
	res = ESP8266_SendCmd(AT_START , "OK" , 500);	// 啟動傳送
	
	return res ;

}

/**************
 * 退出
 *************/
void Esp_exit() 
{
	ESP8266_SendCmd(AT_EXIT , "" , 500);	// 關閉
}

int ESP_Init()
{
	if(Esp_mode())									// 初始化成功
	{
		if(Esp_joinAp())							// 連線WIFI成功
		{
			if(Esp_connectTcp())				// 連線TCP伺服器
			{
				if(Esp_sendMode())				// 設定透傳模式
				{
					if(Esp_sendStart())			// 啟動傳送
					{
						return 1 ;						// 所有初始化成功之後返回1
					}
				}
			}
		}	
	}
	
	return 0;	
}

int main(void)
{

	u8 count = 0 ;

	DelayInit();
	
	OLED_Init();			//初始化OLED  
	OLED_Clear()  	; 
	
	uart_init(115200);    // 一定要注意使用115200波特率
	DelayMs(100);
	
	ESP_Init();			// 初始化ESP
	
	while(1){
		
		GPIO_WriteBit(GPIOD,GPIO_Pin_2,(BitAction)(1-GPIO_ReadOutputDataBit(GPIOD, GPIO_Pin_2)));    //IO的電平翻轉
		
		if (dht11_read_data(buffer) == 0)
		{
				hum = buffer[0]*10 + buffer[1];
				temp = buffer[2]*10 + buffer[3];
			
				t1[0] = hum/100 + '0';
				t1[1] = hum%100/10 + '0';
				t1[2] = 0x2e;
				t1[3] = hum%10 + '0';
			
				t2[0] = temp/100 + '0';
				t2[1] = temp%100/10 + '0';
				t2[2] = 0x2e;
				t2[3] = temp%10 + '0';

				OLED_ShowString(0,2,(u8*)"hum: ");
				OLED_ShowString(8*6,2,t1);
				OLED_ShowString(0,4,(u8*)"temp: ");
				OLED_ShowString(8*6,4,t2);

		}
			
		DelayS(2);
		
		count ++ ;
		if(count == 5)		// 計時10秒鐘傳送一次資料
		{
			count = 0 ;
			USART1_Send_String((u8*)tx1);
			USART1_Send_String(t1);
			USART1_Send_String((u8*)" -- ");
			USART1_Send_String((u8*)tx2);
			USART1_Send_String(t2);
		}
			
	}
  
		
}

再貼下串列埠的 .c檔案和 .h檔案

#ifndef __USART_H
#define __USART_H
#include "stdio.h"	
#include "stm32f10x.h"

#define USART_REC_LEN  			200  	//定義最大接收位元組數 200
#define EN_USART1_RX 			1		//使能(1)/禁止(0)串列埠1接收
	  	
extern u8  USART_RX_BUF[USART_REC_LEN]; //接收緩衝,最大USART_REC_LEN個位元組.末位元組為換行符 
extern u16 USART_RX_STA;         		//接收狀態標記	

void uart_init(u32 bound);				// 初始化串列埠
void USART1_Send_Byte(u8 Data);		// 傳送一個位元組	
void USART1_Send_String(u8 *Data);			// 傳送字串

#endif
#include "usart.h"	  

//////////////////////////////////////////////////////////////////
//加入以下程式碼,支援printf函式,而不需要選擇use MicroLIB	  
#if 1
#pragma import(__use_no_semihosting)             
//標準庫需要的支援函式                 
struct __FILE 
{ 
	int handle; 

}; 

FILE __stdout;       
//定義_sys_exit()以避免使用半主機模式    
_sys_exit(int x) 
{ 
	x = x; 
} 
//重定義fputc函式 
int fputc(int ch, FILE *f)
{      
	while((USART1->SR&0X40)==0);//迴圈傳送,直到傳送完畢   
    USART1->DR = (u8) ch;      
	return ch;
}
#endif 

/*使用microLib的方法*/
 /* 
int fputc(int ch, FILE *f)
{
	USART_SendData(USART1, (uint8_t) ch);

	while (USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET) {}	
   
    return ch;
}
int GetKey (void)  { 

    while (!(USART1->SR & USART_FLAG_RXNE));

    return ((int)(USART1->DR & 0x1FF));
}
*/
 
#if EN_USART1_RX   //如果使能了接收
//串列埠1中斷服務程式
//注意,讀取USARTx->SR能避免莫名其妙的錯誤   	
u8 USART_RX_BUF[USART_REC_LEN];     //接收緩衝,最大USART_REC_LEN個位元組.
//接收狀態
//bit15,	接收完成標誌
//bit14,	接收到0x0d
//bit13~0,	接收到的有效位元組數目
u16 USART_RX_STA=0;       //接收狀態標記	  
  
void uart_init(u32 bound){
	//GPIO埠設定
	GPIO_InitTypeDef GPIO_InitStructure;
	USART_InitTypeDef USART_InitStructure;
	NVIC_InitTypeDef NVIC_InitStructure;
	 
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA|RCC_APB2Periph_AFIO, ENABLE);	//使能USART1,GPIOA時鐘以及複用功能時鐘
	 //USART1_TX   PA.9
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA.9
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;	//複用推輓輸出
	GPIO_Init(GPIOA, &GPIO_InitStructure);
 
	//USART1_RX	  PA.10
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空輸入
	GPIO_Init(GPIOA, &GPIO_InitStructure);  

   //Usart1 NVIC 配置
  NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1 ;//搶佔優先順序3
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;		//子優先順序3
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;			//IRQ通道使能
	NVIC_Init(&NVIC_InitStructure);	//根據指定的引數初始化VIC暫存器
  
   //USART 初始化設定

	USART_InitStructure.USART_BaudRate = bound;//一般設定為9600;
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字長為8位資料格式
	USART_InitStructure.USART_StopBits = USART_StopBits_1;//一個停止位
	USART_InitStructure.USART_Parity = USART_Parity_No;//無奇偶校驗位
	USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//無硬體資料流控制
	USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;	//收發模式

	USART_Init(USART1, &USART_InitStructure); //初始化串列埠
	USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//開啟中斷
	USART_Cmd(USART1, ENABLE);                    //使能串列埠 

}

void USART1_Send_Byte(u8 Data) 								//傳送一個位元組;
{
	USART_SendData(USART1,Data);
	while( USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET );
}

void USART1_Send_String(u8 *Data) 						//傳送字串;
{
	while(*Data)
	USART1_Send_Byte(*Data++);
}

void USART1_IRQHandler(void)                	//串列埠1中斷服務程式
{
	u8 Res;

	if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)  //接收中斷(接收到的資料必須是0x0d 0x0a結尾)
		{
		Res =USART_ReceiveData(USART1);//(USART1->DR);	//讀取接收到的資料
		
		if((USART_RX_STA&0x8000)==0)//接收未完成
			{
			if(USART_RX_STA&0x4000)//接收到了0x0d
				{
				if(Res!=0x0a)USART_RX_STA=0;//接收錯誤,重新開始
				else USART_RX_STA|=0x8000;	//接收完成了 
				}
			else //還沒收到0X0D
				{	
				if(Res==0x0d)USART_RX_STA|=0x4000;
				else
					{
					USART_RX_BUF[USART_RX_STA&0X3FFF]=Res ;
					USART_RX_STA++;
					if(USART_RX_STA>(USART_REC_LEN-1))USART_RX_STA=0;//接收資料錯誤,重新開始接收	  
					}		 
				}
			}   		 
     } 

} 
#endif	

我使用的是dth11溫溼度感測器來測量溫溼度的,使用OLED來顯示,這部分不是最重要的,所以就不貼出來佔篇幅了,大家有需要的可以留言,看到我會發你的。