两块STM32分别控制一块ESP8266,实现两机信息交互
1.前言
2019年上半年,为了准备一个机器人比赛,就去研究了一下ESP8266 WIFI模块。模块本身已经被封装得很好了,可是在搭载到单片机上出现了很多问题,借此机会总结一下:单片机配置流程和中间可能遇到的一些坑。
2.思路
- 做什么:单片机 控制 ESP8266。
- 怎么做:a.STM32核心板提供ESP8266所需要的硬件环境;
b.程序中写好特定格式的命令,通过串口发送给ESP8266。 - 技术路线:
3.硬件
我用的是下面这块裸板,为的是能直接焊在板子上,节省空间。
根据模块提供的手册,对引脚进行配置,一般来说我们使用运行模式。
模式 | CH_PD(EN) | RST | GPIO15 | GPIO0 | GPIO2 | TXD0 |
运行模式 | 高 | 高 | 低 | 高 | 高 | 高 |
下载模式 | 高 | 高 | 低 | 低 | 高 | 高 |
对应的电路设计可以参考下面这张图
注意:
1.模块最终能有效运行的关键因素就是上图中的引脚是否电平配置到位,如果模块接入电路,通电时,模块上面自带的蓝色LED小灯没有闪烁一下,那就说明模块硬件环境没有配置好(或者模块烧毁)。建议挨个检查引脚的电压,可能遇到的情况有a.电压不足b.完全没有电压c.过压。那么你就要根据电路图,原理图找找是否存在断路,分压和短路等情况了。
2.我的是裸板,我的是裸板,没有VCC,GND,RX,TX等六个引脚直接引出。像下面这样:
图中的裸板已经搭载在一个配套电路上了,实现了我上面说的电路设计,拿来通上电直接用,对刚接触的人来说特别友好,不过我是为了集成化,节约空间,就直接拿裸板焊自己板子上,所以需要自己配置引脚(特别坑,当初也是直接VCC,GND直接怼,结果没卵用),所以你买的什么模块,就看店家提供的资料,别人的不一定适合自己。
3.模块GND要和STM32的GND相连。
4.软件
先利用电脑通过串口发送信息给 ESP8266模块,熟悉一下流程。
选择正确的 COM 号(WIFI与你电脑相连的串口),然后设置波特率为 115200,勾选发送新行 (必选!即自动添加回车换行功能)然后发送 AT指令 到 ESP8266模块。
然后理清楚一下服务器(主机)和客户端(从机)分别需要执行什么AT指令。
服务器(主机) | AT指令 | 客户端(从机) | AT指令 |
检测模块是否在线 | AT | 检测模块是否在线 | AT |
修改服务器WIFI名和密码 | AT+CWSAP=“ALIENTEK”,“159020203”,11,3 | 设置 WIFI 模式1 | AT+CWMODE=1 |
设置 WIFI 模式2 | AT+CWMODE=2 | 重启生效 | AT+RST |
重启生效 | AT+RST | 连接服务器的WIFI | AT+CWJAP=“ALIENTEK”,“159020203” |
建立服务器指令 | AT+CIPSERVER=1,8089 | 连接服务器 | AT+CIPSTART=“TCP”,“你的服务器 IP 地址”,8089 |
获取服务器IP地址 | AT+CIFSR | 开启透传模式 | AT+CIPMODE=1 |
开始透传 | AT+CIPSEND |
说明:
a. 我修改服务器WIFI名为ALIENTEK,密码159020203,修改完后复位,可以在电脑上找到这个WIFI名,这也是后面客户端要连接服务器时所用到的账号密码。
b. WIFI模式分为3种:1为station(客户端);2为access point(服务器);3为1和2的兼容模式
c. 如果想要一个服务器与多个客户端通信,可以在服务器复位后插入AT+CIPMUX=1(启动多连接)即可。那问题来了,服务器怎么知道自己要通信的客户端是谁呢?可以这样,先让客户端连接上服务器WIFI,对客户端使用AT+CIPSTATUS,就会返回当前模块的拦截状态和连接参数 STATUS: +CIPSTATUS:,,<remote_ip>,, <local_port>, 。id就是我们要的值,然后AT+CIPSND=0,5就可以针对0号客户端发送5个字节的数据了。
d. 结束透传的话需要发送"+++"(该指令必须在开启透传模式下使用 )
关于AT指令的使用细则网上已经有很多资料,我不再赘述
多操作两遍,摸清楚流程,在电脑上能实现两者互相通信后,接下来就是在单片机上写好程序了。在单片机上写好上面AT指令,通过串口发送给ESP8266模块。上面我说到硬件最大的问题就是电压有没有给到位,而代码中最关键的问题有两个,一是格式,二是速度。
软件我是这样写的:
/*************************************************
服务器逻辑代码
*************************************************/
void atk_8266_server(void)
{
atk_8266_send_cmd("AT","OK",20); //检查WIFI模块是否在线
atk_8266_send_cmd("AT+CWMODE=2","OK",20); //1.Station模式2.Ap模式3.兼容1.2两个模式
atk_8266_send_cmd("AT+RST","OK",200); //重启复位,模式生效
atk_8266_send_cmd("AT+CIPMUX=1","OK",20); //启动多连接指令
atk_8266_send_cmd("AT+CIPSERVER=1,8089","OK",20);//建立服务器server指令
USART2_RX_STA=0;
while(1) //等待倒计时
{
delay_ms(100);
if(USART2_RX_STA&0X8000) //接收到期待的应答结果
{
if(atk_8266_check_cmd("start"))//服务器接收数据"start"
{
actions();
break;
}
USART2_RX_STA=0;
}
}
}
/*************************************************
向ESP8266发送命令
cmd:发送的命令字符串
ack:期待的应答结果,如果为空,则表示不需要等待应答
waittime:等待时间(单位:10ms)
返回值:0,发送成功(得到了期待的应答结果)
1,发送失败
*************************************************/
u8 atk_8266_send_cmd(u8 *cmd,u8 *ack,u16 waittime)
{
u8 res=0;
USART2_RX_STA=0;
USART2_printf("%s\r\n",cmd); //发送命令
// delay_ms(100);
// USART2_printf("%s\r\n",cmd);如果碰到长指令发送无效,可以尝试发送两遍,亲测有效!!!
if(ack&&waittime) //需要等待应答
{
while(--waittime) //等待倒计时
{
delay_ms(10);
if(USART2_RX_STA&0X8000)//接收到期待的应答结果
{
if(atk_8266_check_cmd(ack))
{
//printf("ack:%s\r\n",(u8*)ack);
break; //得到有效数据
}
USART2_RX_STA=0;
}
}
if(waittime==0)res=1;
}
return res;
}
/*************************************************
ESP8266发送命令后,检测接收到的应答
str:期待的应答结果
返回值:0,没有得到期待的应答结果
其他,期待应答结果的位置(str的位置)
*************************************************/
u8* atk_8266_check_cmd(u8 *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 (u8*)strx;
}
/*************************************************
ESP8266退出透传模式
返回值:0,退出成功;
1,退出失败
*************************************************/
u8 atk_8266_quit_trans(void)
{
while((USART2->SR&0X40)==0); //等待发送空
USART2->DR='+';
delay_ms(15); //大于串口组帧时间(10ms)
while((USART2->SR&0X40)==0); //等待发送空
USART2->DR='+';
delay_ms(15); //大于串口组帧时间(10ms)
while((USART2->SR&0X40)==0); //等待发送空
USART2->DR='+';
delay_ms(500); //等待500ms
return atk_8266_send_cmd("AT","OK",20);//退出透传判断.
}
/*************************************************
客户端代码
*************************************************/
void atk_8266_test(void)
{
atk_8266_send_cmd("AT+CWMODE=1","OK",20);//1.Station模式 2.Ap模式 3.兼容1.2两个模式
atk_8266_send_cmd("AT+RST","OK",200); //重启复位
atk_8266_send_cmd("AT+CWJAP=\"ALIENTEK\",\"159020203\"","OK",2000); //连接WIFI
atk_8266_send_cmd("AT+CIPSTART=\"TCP\",\"192.168.4.1\",8089","OK",1000);//连接服务器
atk_8266_send_cmd("AT+CIPSEND=5","OK",20);//发送5个字节数据
atk_8266_send_cmd("start","OK",100); //发送"start"
}
注意:
a.格式问题基本如上,上面的AT指令可以通过串口发送,ESP8266也能识别。
b.速度问题,我必须强调一下,单片机发送命令的速度是非常快的,远快于8266模块接收到指令并返回给单片机这个过程。有可能一连串指令中有一个反应慢了点(客户端WIFI连接服务器WIFI时耗用的时间在1秒到10秒不等),单片机就可能会略过这个指令,导致WIFI连接不上。但对于单片机来说,你又无法直接看到到底是哪个指令失效,我的做法比较笨,有两个思路:一是在程序中每个指令若执行成功就会有返回值,并通过串口打印在电脑上;二是将单片机控制的ESP8266和电脑串口控制的ESP8266进行通信,这样你能确保电脑控制的一定不会出错,同时连接时产生的一些信息会打印在串口助手窗口上,有利于排查错误。总之,每个指令的等待时间不一样,需要自己不断调整。
c.如果单独发一条格式正确的长指令(比如修改WIFI名指令),也可能会出现没有接收成功的问题。有一个不清楚 问题在哪的办法:延时一两百毫秒,再发送一遍。(惭愧,没搞清楚为什么这样弄一下就会有用,如果有知道的大佬麻烦在评论区指出。)
5.总结
a.凡是不能打败你的,都使你更加强大,但也不能太折磨自己
终于理解嵌入式为什么坑了,配个WIFI花了大半时间在硬件上,板子设计缺陷,焊接错误,8266电路配置不到位,整得我死去活来的,说多了都是自闭。最终还是对硬件,串口有了更好的掌握。可能先买模块组装比较好,或者有人带带上手比较快,不然太折磨人了。
b.由易入难,循序渐进
没有在电脑上通过串口控制ESP8266模块的经验,会对在单片机上配置带来没有可参照标准现象,8266实际反应速度等 相对精准的参考依据,就像在黑暗中只能用手摸着路回家了,其难度可想而知。所以还是要借用别人成熟的项目经验,自己模仿着来操作,会少踩很多坑,也会少做很多无用功。
买开发板搭配视频学习>寻找自己感兴趣且别人有教程的项目>积累足够经验开始自己开发。像我这个新手直接硬刚真的是遭罪啊。
需要源代码的同学请到Github自取
https://github.com/Iamliuguo