ESP01的接线
ESP826601需要接五根线,TXD,RXD,GND,EN,VCC,从别人的博客拿张图用用
【常用模块】ESP8266 WIFI串口通信模块使用详解(实例:附STM32详细代码)_正点原子wifiesp8266提供代码-CSDN博客
VCC和EN接3.3V就够了,USB转TTL跳键帽把USB_TTL的VCC和5V接在一起,3.3V接在一起就是跟3.3V的单片机通信 ,5V接在一起就是跟5V的通信,我EN和VCC都是接在面包板上,推荐购买01S,那个好像只要四根线,网上还有那种现成的调试
ESP通信个人理解
我看了几篇ESP的文档,个人觉得WIFI通信就是在WIFI连接用户这个圈子中,跟别人建立通信,ESP可以建立圈子,也就是作为热点的AP模式,也可以进别人的圈子,就好像我们是一个圈子的才有共同话题,也就是下面要说的ESP的AP模式和STA模式,在一个圈子是前提,至于是你开热点还是蹭别人的,跟通信的主次无关(跟STA和AP选择无关,跟客户端服务端选择有关),你开了热点也可以作为客户端,你蹭别人的也可以作为服务端, 下面的STA模式和AP模式只是作为示例,并不是一定就要这样子,完全根据自己需要来定,不太懂TCP,理解有限,说错了请谅解
AT指令
AT指令可以到官网查看基础 AT 命令 — ESP-AT 用户指南 文档 (readthedocs-hosted.com),里面有更详细的示例,看下面的这几个就足够了应该,当然调试还要串口调试助手和网络调试助手,野火的多功能调试助手就不错,当我使用时遇到bug,就选了NetAssist网络调试助手
(AP模式)
先发送一个AT作为测试命令,看是否响应OK,再继续往下走
1.AT+CWMODE=2,选择为AP模式,掉电效果不消失
2.AT+RST,复位,其实复位也不一定需要,我看他们是这么做的,自己配置好wifi之后可以省略,而且复位会发送一大串的字符串
3.AT+CWSAP=“WIFI名字”,“WiFi密码”,x<通道号>,x<加密方式>,这个按理来说也是掉电不消失的,但是我只发送下面两个命令不能成功,待会再试试
AT+CWLIF,可以查询有谁连接到你的热点,会显示它的IP地址和mac地址,AT+CWQIF,断开与所有连接者的联系
4.AT+CIPMUX=1,设置为多连接,在AP下,可以让多个人连接你的热点,在STA模式下,你可以连接多个人的热点(下图是官方说明,第二句话不知道对不对,没试过)
5.AT+CIPSERVER=1,<端口号>,建立服务端
别人要想跟你通信需要知道你的IP地址和端口号,端口号前面自己设置了,我也不知道有没有查找端口号的命令,AT+CIPSERVER?我试了我这个模块是报错的,没找到,查IP地址的话用AT+CWSAP?照着下图填应该就可以了
关于透传模式也就是AT+CIPMODE=1,AT+CIPSEND这两条命令先后发送(配置好前面之后),进入透传模式,之后双方发送的都将被当成数据,CIPMUX和CIPSERVER必须为0,ESP作为服务端时不能开启透传,在AP模式下,用网络调试助手的客户端可以随意发送数据给ESP,但是ESP想发送数据要用AT+CIPSEND=<端口号,这个端口号是连接的用户的顺序吧类似,0,1,2,3这样的>,<发送字节的长度>,ESP收到数据还有特别的格式
STA模式
1.AT+CWMODE=1
2.AT+RST
3.AT+CWJAP=" wifi名称","wifi密码",加入wifi,可用AT+CWLAP查看可加入的wifi,加入别人的圈子
如果想要透传就不需要发送AT+CIPMUX=1
4.AT+CIPMUX=0,AT+CIPSERVER=0,嗯。。感觉这两个其实不太需要,前面RST了嘛
5.AT+CIPSTART=”TCP“,"IP地址",端口号,我先用网络调试助手创建服务端,我这里的ip地址是连接手机热点的(忘记截图了不是这个地址),用电脑win+R,输入cmd进入窗口然后输入ipconfig就可以看到IPV4地址,然后端口号自定义
6.开启透传AT+CIPMODE=1,AT+CIPSEND,由此,透传开始
用串口跟ESP通信
有两种判断接收结束的标志,一个是定时器通信,一个是空闲中断,我两种都试过,定时器通信我也不知道是设置超过时间为10ms太长还是什么原因,一旦接收很长字节的数据就会让程序卡死,所以最后选用了空闲中断
以后写代码就这样写吧,原先还想给GPIO和NVIC包装一个函数,但是有时候程序会有些莫名其妙的错误,stdio和stdarg这两个头文件是给串口的printf函数装备的,还有使能中断真的要记得同时清楚标志位,不然一开启就会运行一次中断服务函数
#include "stm32f10x.h"
#include "stdarg.h"
#include "stdio.h"
#include "string.h"
#include "Delay.h"
#include "OLED.h"
void MY_UART1_Init(uint32_t baud)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_9;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStructure);
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IN_FLOATING;
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_10;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStructure);
USART_InitTypeDef USART_InitStructure;
USART_InitStructure.USART_BaudRate=baud;
USART_InitStructure.USART_HardwareFlowControl=USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode=USART_Mode_Tx | USART_Mode_Rx;
USART_InitStructure.USART_Parity=USART_Parity_No;
USART_InitStructure.USART_StopBits=USART_StopBits_1;
USART_InitStructure.USART_WordLength=USART_WordLength_8b;
USART_Init(USART1,&USART_InitStructure);
USART_ITConfig(USART1,USART_IT_RXNE,ENABLE);
USART_ClearFlag(USART1,USART_FLAG_RXNE);
USART_ITConfig(USART1,USART_IT_IDLE,ENABLE);
USART_ClearFlag(USART1,USART_FLAG_IDLE);
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel=USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3;
NVIC_InitStructure.NVIC_IRQChannelSubPriority=0;
NVIC_Init(&NVIC_InitStructure);
USART_Cmd(USART1,ENABLE);
}
USART1的printf函数,可变参不会玩 ,这个最大长度MAX_SIZE最好设大一点?,应该大一点也没关系,下面有长度的判断
#define DATA_MAX_SIZE 100
void u1_printf(char *format,...)
{
char DataBuf[DATA_MAX_SIZE];
va_list start;
va_start(start,format);
vsprintf(DataBuf,format,start);
va_end(start);
uint8_t pDataBuf=0;
while (DataBuf[pDataBuf]!='\0')
{
USART_SendData(USART1,DataBuf[pDataBuf]);
while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)==RESET);
pDataBuf++;
}
}
中断服务函数,中断里接收字节,超过字节最大长度会置标志位,接收完毕也会置标志位,这里的RX_STA是记录字节数和记录接收完毕两个作用的标志位,给STA++后再给对应元素赋字符串结束标志,就不用在每个处理程序中都加结束标志位了,IDLE中断要调用一次接收数据函数,不然清除不掉标志位
#define USART1_RX_MAX_SIZE 40
uint16_t USART1_RX_STA;
uint8_t USART1_RX_BUF[USART1_RX_MAX_SIZE];
void USART1_IRQHandler(void)
{
if (USART_GetITStatus(USART1,USART_IT_RXNE)==SET)
{
if (!(USART1_RX_STA & (1<<15)))
{
USART1_RX_BUF[(USART1_RX_STA&0x7FFF)]=USART_ReceiveData(USART1);
USART1_RX_STA++;
USART1_RX_BUF[USART1_RX_STA&0x7FFF]='\0';
if ((USART1_RX_STA&0x7FFF)>=USART1_RX_MAX_SIZE-1)
{
USART1_RX_STA |= (1<<15);
}
}
USART_ClearITPendingBit(USART1,USART_IT_RXNE);
}
if (USART_GetITStatus(USART1,USART_IT_IDLE)==SET)
{
USART1_RX_STA |= (1<<15);
USART_ReceiveData(USART1);
USART_ClearFlag(USART1,USART_FLAG_IDLE);
}
}
这是测试的函数,在while循环中不断调用,会把接收到的数据返还,不过现在被我注释掉了,因为ESP和单片机连接的话没法看串口,就用oled来显示信息了
void USART_RE_Deal(void)
{
if ((USART1_RX_STA&(1<<15)))
{
USART1_RX_BUF[USART1_RX_STA&0x7FFF]='\0';
OLED_ShowString(3,1,USART1_RX_BUF);
// u1_printf("%s\r\n",USART1_RX_BUF);
USART1_RX_STA=0;
}
}
以上就是串口的相关部分,下面用串口来向ESP发送数据配置ESP
用串口写命令
这个是用来查找ESP响应信息的,strstr函数在string.h中,会查找第二个字符串在第一个字符串中第一次出现的位置,没找到则返回空指针
char *USART1_check_cmd(char * str)
{
char *str_return=NULL;
if (USART1_RX_STA&(1<<15))
{
str_return=strstr(USART1_RX_BUF,str);
}
return str_return;
}
发送命令,记得在printf后面加上\r\n
uint8_t USART1_send_cmd(char *cmd,char *call,uint8_t wait_time)
{
u1_printf("%s\r\n",cmd);
if (call&&wait_time)
{
while (wait_time--)
{
Delay_ms(10);
if (USART1_check_cmd(call)!=NULL)
{
return 1;
}
}
}
USART1_RX_STA=0;
return 0;
}
注意转义字符的使用,有些命令格式要接受字符串,RST后有个几秒的延时,如果先前配置好了前面几句可以注释掉的,AT+CWSAP应该也可以不用第二次配置,但我注释掉的话,能连接到WiFi,但不能通信,很奇怪
2024.3.30
像这种跟串口通信的模块,好像指令不能发的太快,每条指令之间最好间隔一段时间,我没测试,就选了0.5s,效果还行
void USART1_WIFI_START_Trans(void)
{
//下面这些内容在调试的时候配置好了可以删除
//USART1_send_cmd("AT+CWMODE=2","OK",50);
//USART1_send_cmd("AT+RST","ready",20);
//Delay_ms(1000);
//Delay_ms(1000);
//Delay_ms(1000);
USART1_send_cmd("AT+CWSAP=\"ABC\",\"12345678\",11,2","OK",100);
Delay_ms(500);
USART1_send_cmd("AT+CIPMUX=1","OK",20);
Delay_ms(500);
USART1_send_cmd("AT+CIPSERVER=1,8080","OK",50);
}
用ESP作为热点,客户端给ESP发消息,ESP的接受格式类似于+IPD,<用户端口号>,<字节长度>:<内容>,我想用ESP作为一个密码锁,当客户端给他发送数据,他能判断数据的对错
#define PWSD "20210909"
void USART1_check_pwsd(void)
{
if (USART1_RX_STA&(1<<15))
{
char * temp;
temp = strstr(USART1_RX_BUF,PWSD);
if (temp)
{
if (strcmp(temp,PWSD)==0)
{
OLED_ShowString(1,1,"SUCCESS");
}
else
{
OLED_ShowString(2,1,temp);
OLED_ShowString(1,1,"ERROR ");
}
}
USART1_RX_STA=0;
}
}
完整代码
附上我git仓库的链接git@gitee.com:tang-yan-handsome-guy/esp8266-as-the-server.git