WiFi开发|ESP8266模组AT指令开发二

ESP8266模组AT指令开发二

1. 基于STM32的AT指令开发

ESP8266可以使用官方提供的SDK来进行开发,也可以直接利用官方提供的固件直接使用AT指令配置模块使用。在ESP8266模组AT指令开发一中介绍了常用的AT指令,且基于PC端利用串口调试助手、网络调试助手进行了常用AT指令的测试,包括TCP、UDP、HTTP、智能配网等

本章节将介绍基于STM32的AT指令开发,MCU通过串口与WiFi模块连接并进行数据交互,WiFi模块通过WiFi传输方式与手机进行连接交互,其基本功能连接图如下图示

在这里插入图片描述

2. 硬件设计

本实验要实现的功能是:STM32F103采集DHT11温湿度,通过WiFi模块(TCP Server模式)将温湿度数据传输到手机上(TCP Client),实现通过WiFi对环境温湿度的监控。需要用到的硬件资源如下示:

  • D1指示灯
  • PG11(连接DHT11温湿度传感器)
  • USART1(调试用串口)
  • USART2(连接ESP8266)
  • TIM7(提供us延时)
  • PE6(ESP8266复位管脚)
  • PG7(ESP8266片选管脚)

ESP8266除了需要连接3.3V、GND外,还需要连接串口的RX、TX、RST、EN引脚

3. 软件设计
3.1 STM32CubeMX设置
  • RCC设置外接HSE,时钟设置为72M
  • PC0设置为GPIO推挽输出模式、上拉、高速、默认输出电平为高电平
  • USART1选择为异步通讯方式,波特率设置为115200Bits/s,传输数据长度为8Bit,无奇偶校验,1位停止位
  • USART2选择为异步通讯方式,波特率设置为115200Bits/s,传输数据长度为8Bit,无奇偶校验,1位停止位,开启中断,使能DMA接收功能

在这里插入图片描述

  • PG11设置为GPIO推挽输出模式、上拉、高速
  • PE6、PG7推挽输出模式,默认输出电平为低电平
  • 激活TIM7,预分频因子设为72-1,向上计数,自动重载值为65535;因此计数器CNT_CLK = 1MHz,计数器周期为1us

在这里插入图片描述

  • 输入工程名,选择工程路径(不要有中文),选择MDK-ARM V5;勾选Generated periphera initialization as a pair of ‘.c/.h’ files per IP ;点击GENERATE CODE,生成工程代码
3.2 MDK-ARM编程
/************************esp8266.h************************/
uint8_t esp_rxbuf[128];		//ESP数据接收缓冲区

// AT命令号
typedef enum{	
	AT_IDIE  = 0,
	AT,
	AT_CWMODE,
	AT_CWSAP,
	AT_CIPMUX,
	AT_CIPSERVER,
	AT_CIFSR,
	AT_CIPSEND
}tATCmdNum;
// 命令返回结果的状态
typedef enum{	
	NO_RCV  = 0,
	RCV_SUCCESS,
	RCV_TIMEOUT,
	NO_CONNECT,
}tCmdStatus;
// 命令结构体
typedef struct{			  
	char *CmdSend;         //发送的命令
	char *CmdRcv; 	       //正确返回包含的字符串
	uint16_t TimeOut;      //超时的时间
	tCmdStatus CmdStatus;  //命令返回的状态
}tATCmd; 
/************************esp8266.c**********************************************/
#define AT_CWSAP_STRING   "AT+CWSAP=\"ESP8266_DHT11\",\"12345678\",5,3\r\n"
uint8_t WIFI_CONNECT_FLAG = 0;    //WIFI连接标志位
uint8_t esp_rxbuf[128];			  //ESP数据接收缓冲区
extern uint8_t t_buf[40];
extern uint8_t h_buf[40];
//定义需要用到的命令集合
volatile tATCmd  ATCmds[9]= {
  /*CmdSend,             		*CmdRcv,      TimeOut,   CmdStatus*/  
  {NULL,				  		NULL,		  0,		 NO_RCV},
  {"AT\r\n",             		"OK",         5000,      NO_RCV},   //检测AT指令       
  {"AT+CWMODE=2\r\n",    		"OK" ,        2000,      NO_RCV},   //设置WIFI模式AP
  {AT_CWSAP_STRING,      		"OK" ,     	  2000,      NO_RCV},   //建立MAC相关的AP名称  
  {"AT+CIPMUX=1\r\n",    		"OK" ,        2000,      NO_RCV},   //设置多连接
  {"AT+CIPSERVER=1,8000\r\n", 	"OK" ,   	  2000,      NO_RCV},   //初始化TCP服务器,默认IP(192.168.4.1)设置端口号(8000) 
  {"AT+CIFSR\r\n",   			"CIFSR",   	  2000,      NO_RCV},   //获取IP及MAC地址    
  {"AT+CIPSEND\r\n",   			">" ,         2000,      NO_RCV}    //TCP发送数据
};
/*******************************************************************************
* Function Name  	: Usart2_Data_Send
* Description    	: USART2数据发送函数
* Input          	: str 要发送的字符串; len 字符串长度
* Return         	: none
*******************************************************************************/
void Usart2_Data_Send(uint8_t *str, uint16_t len){
	HAL_UART_Transmit(&huart2, (uint8_t *)str, len,100);
}
/*******************************************************************************
* Function Name  	: ESP_Send_Cmd
* Description    	: MCU向ESP发送命令
* Input          	: ATCmdNum 命令编号
* Return         	: tCmdStatus 命令响应状态 
*******************************************************************************/
tCmdStatus ESP_Send_Cmd(tATCmdNum ATCmdNum){
	uint8_t len;
	//清空接收缓存以及接收状态
	ATCmds[ATCmdNum].CmdStatus = NO_RCV;
	//发送命令
	len = strlen(ATCmds[ATCmdNum].CmdSend);	
	Usart2_Data_Send((uint8_t *)ATCmds[ATCmdNum].CmdSend, len);
	//等待一定时间		
	HAL_Delay(ATCmds[ATCmdNum].TimeOut);
	while(ATCmds[ATCmdNum].CmdStatus != RCV_SUCCESS){
		ESP_Cmd_Rcv(ATCmdNum);
		if(ATCmds[ATCmdNum].CmdStatus == RCV_TIMEOUT)
			return RCV_TIMEOUT;
	}
	return RCV_SUCCESS;	 
}
/*******************************************************************************
* Function Name  	: ESP_Cmd_Rcv
* Description    	: 命令返回值处理函数
* Input          	: ATCmdNum 命令编号
* Return         	: none
*******************************************************************************/
void ESP_Cmd_Rcv(tATCmdNum ATCmdNum){	
	//接收处理命令
	if(strstr((const char*)esp_rxbuf,ATCmds[ATCmdNum].CmdRcv) != NULL){
		ATCmds[ATCmdNum].CmdStatus = RCV_SUCCESS;						
	}			
	else{
		HAL_Delay(ATCmds[ATCmdNum].TimeOut);
		ATCmds[ATCmdNum].CmdStatus = RCV_TIMEOUT;
	}
	memset(&esp_rxbuf,0,sizeof(esp_rxbuf));
}	
/*******************************************************************************
* Function Name  	: ESP_Send_Data
* Description    	: ESP发送数据给WiFi设备
* Input          	: SendBuf 要发送的字符串; len 字符串长度
* Return         	: tCmdStatus 响应状态
*******************************************************************************/
tCmdStatus ESP_Send_Data(uint8_t *SendBuf,uint8_t len){
	uint8_t buf[30] =  {0};
	tATCmdNum ATCmdNum;	
	if(!WIFI_CONNECT_FLAG){  //未连接状态不能发送数据
		printf("No device connected!\r\n");
		return NO_CONNECT;
	}				
	else{ //WIFI模式
		sprintf((char *)buf,"AT+CIPSEND=%d,%d\r\n",0,len);
		ATCmdNum = AT_CIPSEND;	
		Usart2_Data_Send(buf,strlen((char *)buf));   //发送命令
		printf("Send AT_CIPSEND cmd ok!\r\n");	
	}
	//等待一定时间	
	HAL_Delay(ATCmds[ATCmdNum].TimeOut);
	ATCmds[ATCmdNum].CmdStatus = NO_RCV;        //清接收状态		 
	while(ATCmds[ATCmdNum].CmdStatus != RCV_SUCCESS){		 
		ESP_Cmd_Rcv(ATCmdNum);
	  	if(ATCmds[ATCmdNum].CmdStatus == RCV_TIMEOUT)
			return RCV_TIMEOUT;
	}
		
	Usart2_Data_Send(SendBuf,len);              //发送数据
	printf("Send data ok!\r\n");	 
	return RCV_SUCCESS;
}
/*******************************************************************************
* Function Name  	: ESP_Data_Rcv
* Description    	: 接收WiFi设备返回给ESP的数据
* Input          	: none
* Return         	: none
*******************************************************************************/
void ESP_Data_Rcv(void){
    if((!WIFI_CONNECT_FLAG) && (strstr((char *)(esp_rxbuf),"CONNECT")!=NULL)){ 
		printf("WIFI已连接\n");	
		WIFI_CONNECT_FLAG = 1;   //WIFI设备已连接,置位连接标志位
	}
	if(strstr((char *)(esp_rxbuf),"+IPD") != NULL  ){ //收到客户端数据
		printf("WIFI收到上位机数据\n");	
		//提取并处理数据;
		EP32_RcvData_Extract(esp_rxbuf);	
		return ;							
	}	
	if(strstr((char *)(esp_rxbuf),"CLOSED") != NULL){ //客户端断开连接
		printf("WIFI断开连接\n");		 
		WIFI_CONNECT_FLAG = 0;    //清除连接标志位
	}		
	memset(&esp_rxbuf,0,sizeof(esp_rxbuf));
}
/*******************************************************************************
* Function Name  	: EP32_RcvData_Extract
* Description    	: 提取WiFi设备发送给ESP的数据并做处理
* Input          	: rev_buf 接收到的数据
* Return         	: none
*******************************************************************************/
void EP32_RcvData_Extract(uint8_t *rev_buf){
	char *buf = "VALID CMD,please enter TEMPERATURE or HUMIDITY!!!" ;
	if(strstr((char *)(rev_buf),"TEMPERATURE")!=NULL)
		ESP_Send_Data(t_buf,strlen((char *)t_buf));
	else if(strstr((char *)(rev_buf),"HUMIDITY")!=NULL)
		ESP_Send_Data(h_buf,strlen((char *)h_buf));
	else
		ESP_Send_Data((uint8_t *)buf,strlen(buf));
}
/*******************************************************************************
* Function Name  	: ESP_Init
* Description    	: ESP模块初始化
* Input          	: none
* Return         	: none
*******************************************************************************/
void ESP_Init(void){
	ESP_RST_Pin_SetH;
	ESP_CH_PD_Pin_SetH;
	HAL_Delay(1000);	
	tATCmdNum i = AT_IDIE;
	//WIFI模式初始化
	for(i = AT; i<=AT_CIPSERVER ; i++){						
		if( ESP_Send_Cmd(i) != RCV_SUCCESS){
			printf("PES32 Init failed\n");
			return ;
		}
	}
	printf("PES32 Init Success\n");
}
  • 设置DMA在空闲中断方法下接收不定长数据
//使用DMA的空闲中断方式接收不定长数据:/
//RXNE数据接收中断每接收1个字节的数据就会触发一次;/
//IDLE空闲中断在所有数据(N个字节)接收完成后(接收到的数据断流),才会触发
/************main函数里的处理************************************************/
__HAL_UART_ENABLE_IT(&huart2, UART_IT_IDLE);//使能IDLE空闲中断
HAL_UART_Receive_DMA(&huart2, usart2_rxbuf,BUFFER_SIZE);//开启DMA接收
/************中断处理函数里的处理********************************************/
#define BUFFER_SIZE 128
uint8_t rx_len;			//接收一帧数据的长度
uint8_t recv_end_flag;	//一帧数据接收完成标志
uint8_t usart2_rxbuf[128];	//USART2_RMA接收缓冲区
extern uint8_t esp_rxbuf[128];	//ESP数据接收缓冲区
 
void USART2_IRQHandler(void){
  /* USER CODE BEGIN USART2_IRQn 0 */
  uint32_t tmp_flag = 0;
  uint32_t temp;
  recv_end_flag = 0;
  tmp_flag =  __HAL_UART_GET_FLAG(&huart2,UART_FLAG_IDLE);//获取IDLE标志位 
  if((tmp_flag != RESET)){ 		//IDLE标志位被置位(即触发IDLE空闲中断)
	__HAL_UART_CLEAR_IDLEFLAG(&huart2); //清除标志位
	HAL_UART_DMAStop(&huart2);//关闭DMA防止处理数据时接收数据,产生干扰
	temp = hdma_usart2_rx.Instance->CNDTR;//获取DMA中未传输的数据个数
	rx_len =  BUFFER_SIZE - temp;//已经接收的数据个数=总计数-未传输的数据个数 
	recv_end_flag = 1;//接收完成标志置1
	
	uint8_t i;
	for(i=0;i<128;i++)
		esp_rxbuf[i] = usart2_rxbuf[i];
	memset(&usart2_rxbuf,0,sizeof(usart2_rxbuf));

	HAL_UART_Receive_DMA(&huart2,usart2_rxbuf,BUFFER_SIZE);//重启DMA接收	
  }	
  /* USER CODE END USART2_IRQn 0 */
  HAL_UART_IRQHandler(&huart2);
}
  • 在main主函数编写如下代码
uint16_t temperature;
uint16_t humidity;
uint8_t t_buf[40] = {0},h_buf[40] = {0};
extern uint8_t usart2_rxbuf[128];	

int main(void){
  HAL_Init();
  SystemClock_Config();
  MX_GPIO_Init();
  MX_DMA_Init();
  MX_USART1_UART_Init();
  MX_USART2_UART_Init();
  /* USER CODE BEGIN 2 */
  printf("STM32 ESP8266 AT Develop Demo!\n");
  DHT11_Init();
  __HAL_UART_ENABLE_IT(&huart2, UART_IT_IDLE);//使能IDLE空闲中断
  HAL_UART_Receive_DMA(&huart2,usart2_rxbuf,BUFFER_SIZE);//开启DMA接收
  ESP_Init();
  printf("\n");
  /* USER CODE END 2 */
  while (1)
  {
    DHT11_Read_Data(&temperature,&humidity);
	sprintf(t_buf,"DHT11 Temperature = %d.%d degree\r\n",temperature>>8,temperature&0xff);
	sprintf(h_buf,"DHT11 Humidity = %d.%d%%\r\n",humidity>>8,humidity&0xff);		
	ESP_Data_Rcv();
	HAL_GPIO_TogglePin(GPIOC,GPIO_PIN_0);
  }
}
4. 下载验证

编译无误下载到开发板,开发板连接好ESP8266模组和DHT11温湿度传感器,打开串口助手,可以看到ESP8266初始化调试信息;手机连接ESP8266创建的热点后,使用网络调试助手APP建立TCP客户端,在手机端发送相应的指令,ESP8266模组会将DHT11的温湿度数据通过WiFi传输到TCP客户端

在这里插入图片描述

关注我的公众号,在公众号里发如下消息,即可获取相应的工程源代码:

基于STM32的AT指令WiFi开发实例

在这里插入图片描述

  • 5
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

安迪西嵌入式

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值