由于之前使用STM32单片机来开发一些物联网的小项目,接触到了WIFI模块ESP8266,所以写下来记录一下。本文主要介绍的是STM32通过发送AT指令集来控制ESP8266 WIFI模块连接WiFi并与心知天气API建立TCP连接获取天气信息。
一、硬件连接
ESP8266我使用的是正点原子的ATK-ESP8266,已经提前烧录好AT指令集固件,至于固件的烧录可以参考正点原子的教程或者去安可信官网查找相关资料即可。STM32单片机设计有USB转串口即可。
STM32 VCC -> ESP8266 VCC
STM32 TXD2 -> ESP8266 RXD
STM32 RXD2 -> ESP8266 TXD
STM32 GND -> ESP8266 GND
这里用到了两组串口,USART1用于打印调试信息到电脑的串口助手中;USART2用于主控与ESP8266的通信。串口配置代码直接沿用了正点原子提供的。在这里就不贴出来了。
二、连接WiFi
ESP8266有三种WiFi模式,分别是station模式,AP模式和station+AP模式。简单来说,station模式就是8266作为连接WiFi的设备;AP模式就是8266作为提供WiFi的设备,相当于手机的热点功能。连接WiFi最简单的步骤就两步:8266为station模式和连接WiFi。
设置WiFi模式
设置WiFi模式的AT指令有AT+CWMODE,AT+CWMODE_CUR和AT+CWMODE_DEF。后面两种指令的区别是后缀为DEF的指令会将设置保存到flash中,重新上电不需要再次设置。第一种不带后缀的指令,官方文档写的是不建议使用,所以就用最后一种就行了。以下是官方文档的具体说明
连接WiFi
连接WiFi指令和设置WiFi模式指令一样有三个版本,这里使用AT+CWJAP_DEF来说明。
ssid是你要连接WiFi的名字,pwd是密码,bssid是当有多个相同名字WiFi区分用的。值得注意的是,WiFi名字或者密码有特殊符号要使用\进行转义。
所以,连接WiFi只需要发送以下指令即可:
//设置为station模式
AT+CWMODE_DEF=1\r\n
//连接WiFi
AT+CWJAP_DEF="你的WiFi名","你的WiFi密码"\r\n
三、建立TCP连接
TCP连接这里以连接心知天气获取天气信息为例。主要为3步:与心知天气网站建立TCP连接,开启透传,发送GET请求获取天气信息。
心知天气
心知天气是提供天气信息的API,可以使用GET请求获取JSON格式的天气信息,有免费的版本可用,非常适合嵌入式应用的开发。具体文档说明和账号申请可自行官网查看(https://www.seniverse.com) 。心知天气会为每个账户提供公钥和私钥,这里我们只用到私钥,只需替代掉接口网址中your_api_key为自己的私钥即可。
https://api.seniverse.com/v3/weather/now.json?key=your_api_key&location=zhaoqing&language=en&unit=c
建立TCP连接
建立TCP,UDP或SSL连接使用指令AT+CIPSTART,具体参数如下:
因为我们要使用透传与网站通信,所以TCP必须设置为单连接;连接类型为TCP;远端IP可以是心知天气的具体IP地址,也可以用域名api.seniverse.com来代替;心知天气提供该服务的端口号是80端口。最后,建立TCP连接的AT指令是:
//建立TCP连接,参数为 连接类型 远端IP 远端端口号
AT+CIPSTART="TCP","api.seniverse.com",80\r\n
开启透传
开启透传首先要使用AT+CIPMODE=mode设置传输模式,mode为0是普通传输模式,mode为1是透传模式。设置完成后使用指令AT+CIPSEND开始传输,具体指令参数说明如下:
开启透传后无需写发送数据的长度,直接发送AT+CIPSEND即可,8266返回>即表示成功进入透传,此时无法再发送AT指令,发送的字符将会被当做数据全部发送给对方。若要退出透传返回AT指令模式,则要单独发送+++,注意这里无需换行。
//设置传输模式为透传模式
AT+CIPMODE=1\r\n
//开始透传
AT+CIPSEND\r\n
//退出透传
+++
GET请求
在进入透传之后就可以发送我们的GET请求。在GET和空格后加上要访问的接口地址就可以获取到网站返回给我们的天气信息了。记得更改私钥和在后面加上换行。
GET https://api.seniverse.com/v3/weather/now.json?key=your_api_key&location=zhaoqing&language=en&unit=c\r\n
四、核心代码
//检测接收到的应答
uint8_t* Check_Command(char *str)
{
char *strx = 0;
if(USART2_RX_STA&0X8000) //接收到一次数据了
{
USART2_RX_BUF[USART2_RX_STA&0X7FFF]=0;//添加结束符
strx=strstr((const char*)USART2_RX_BUF,(const char*)str);
}
return (uint8_t *)strx;
}
//向8266发送命令
//cmd:发送的命令字符串
//ack:期待的应答结果
//waittime:等待时间(单位:10ms)
//返回值:1,发送成功(得到了期待的应答结果)
// 0,发送失败
uint8_t Send_Command(char *cmd, char *ack, uint8_t waittime)
{
uint8_t res=1;
USART2_RX_STA=0;
u2_printf("%s\r\n",cmd); //发送命令
if(ack&&waittime) //需要等待应答
{
while(--waittime) //等待倒计时
{
HAL_Delay(10);
if(USART2_RX_STA&0X8000)//接收到期待的应答结果
{
if(Check_Command(ack))
{
printf("ack:%s\r\n",(uint8_t*)USART2_RX_BUF);
break;//得到有效数据
}
USART2_RX_STA=0;
}
}
if(waittime==0)res=0;
}
return res;
}
//向8266发送数据
void Send_Data(char *data)
{
USART2_RX_STA=0;
u2_printf("%s",data); //发送数据
}
char Rcv_Str[1024] = "NULL";
//建立TCP连接,并开启透传
void TCP_Connect()
{
Send_Command("AT+CIPSTART=\"TCP\",\"api.seniverse.com\",80","OK",50);
HAL_Delay(500);
//查询是否成功建立连接,成功则开启透传发送数据
if(Send_Command("AT+CIPSTATUS","TCP",20))
{
Send_Command("AT+CIPMODE=1","OK",20); //设置为透传模式
Send_Command("AT+CIPSEND",">",20); //开启透传
HAL_Delay(1000);
//发送GET请求
Send_Data("GET https://api.seniverse.com/v3/weather/now.json?key=SJhnSCUV2ASZsxCzh&location=zhaoqing&language=zh-Hans&unit=c\r\n");
HAL_Delay(1000);
strcpy(Rcv_Str,(const char*)USART2_RX_BUF); //将接收到的返回数据复制
}
}
//退出透传,断开TCP连接
void TCP_Disconnect(void)
{
//退出发送模式
while((USART2->SR&0X40)==0); //等待发送空
USART2->DR='+';
HAL_Delay(15); //大于串口组帧时间(10ms)
while((USART2->SR&0X40)==0); //等待发送空
USART2->DR='+';
HAL_Delay(15); //大于串口组帧时间(10ms)
while((USART2->SR&0X40)==0); //等待发送空
USART2->DR='+';
HAL_Delay(1000); //等待1s
while(!Send_Command("AT","OK",20))//退出透传判断.
{
HAL_Delay(200);
}
//关闭透传模式
Send_Command("AT+CIPMODE=0","OK",20);
//断开TCP连接
Send_Command("AT+CIPCLOSE","OK",20);
}
//主函数
int main(void)
{
HAL_Init();
SystemClock_Config();
USART1_Init(115200);
USART2_Init(115200);
HAL_Delay(1000);
//测试ESP8266模块是否正常
if(Send_Command("AT","OK",10))
{
printf("ESP8266 Is Ready\r\n");
}
else
{
printf("ESP8266 Not Ready\r\n");
}
//如果WIFI不是Station模式,则设置为Station模式
if(!Send_Command("AT+CWMODE_DEF?","1",20))
{
Send_Command("AT+CWMODE_DEF=1","OK",20);
}
//查询WIFI是否连接,未连接则连接
if(Send_Command("AT+CWJAP_DEF?","No AP",20))
{
HAL_Delay(500);
Send_Command("AT+CWJAP_DEF=\"BieLai\",\"12345678\"","OK",50);
}
HAL_Delay(1000);
TCP_Connect(); //建立TCP连接
printf("%s",Rcv_Str); //打印网站返回的信息
TCP_Disconnect(); //断开TCP连接
while(1)
{
HAL_Delay(1000);
}