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编程
- 创建DHT11温湿度传感器驱动文件,请参考STM32CubeMX系列|DHT11温湿度传感器一文
- 创建ESP8266驱动文件esp8266.c和esp8266.h
/************************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开发实例