硬件介绍
使用AT指令控制1-ESP8266-AT指令初试化及部分基础知识_ch_pd-CSDN博客
项目需求
通过ESP-01SWIFI模块控制LED状态模拟插座
串口1用于与ESP8266通讯,串口2连接PC,用于打印log,查看系统状态
项目接线
将WIFI模块的TX和RX分别接到单片机的RX1和TX1
将CH340的TXD和RXD分别接到单片机的RX2和TX2
项目注意事项
1. 在这个项目里,烧写完单片机之后,不光要点击复位,还要重启单片机电源,以确保WIFI模块重新启动
2. 在以后的工作中一般不直接在中断服务函数里处理数据,而是在收到数据后直接丢给队列,再处理数据
3. 在中断服务函数里尽量减少使用延时函数及打印函数
4. 2,3条在本项目中并没有实现,因为还没有学到Linux的队列概念,只是先留个印象
5. 由于之前的WIFI模块波特率改成了9600,所以现在连接CH340修改回 115200(使用AT指令修改)
AP模式下控制的实现(透传)
CubeMX
1. 惯例配置(时钟,gpio,系统时钟)
2. 打开两个串口(uart1和uart2),并在WIFI模块对应的串口1打开中断
3. 由于在中断处理函数中使用了Delay函数,得调整中断的优先级(非必要不使用,容易造成未知错误)
4. 惯例配置,导出工程(路径,编程软件)
Keil
要在魔术棒中将MiroLIB勾选,使得可以重写printf
代码:
#include <stdio.h>
#include <string.h>
//串口接收缓存(1字节)
uint8_t buf=0;
//定义最大接收字节数 200,可根据需求调整
#define UART1_REC_LEN 200
// 接收缓冲, 串口接收到的数据放在这个数组里,最大UART1_REC_LEN个字节
uint8_t UART1_RX_Buffer[UART1_REC_LEN];
// 接收状态
// bit15, 接收完成标志
// bit14, 接收到0x0d
// bit13~0, 接收到的有效字节数目
uint16_t UART1_RX_STA=0;
char buffer[12];
char connect_net[] = "AT+CWJAP=\"bbb\",\"56893233\"\r\n"; //入网指令 \r\n新行
char connect_servere[] = "AT+CIPSTART=\"TCP\",\"192.168.93.248\",8880\r\n";//连接服务器指令
char Transparent_mode[] = "AT+CIPMODE=1\r\n"; //透传模式指令
char data_transmission[] = "AT+CIPSEND\r\n"; //数据传输开始指令 //数据传输
char reset[] = "AT+RST\r\n"; //重启模块指令
char AT_OK_flag = 0; //OK返回值的标志位
char AT_Connect_Net_Flag = 0; //WIFI GOT IP返回值的标志位
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
// 判断中断是由哪个串口触发的
if(huart->Instance == USART1)
{
// 判断接收是否完成(UART1_RX_STA bit15 位是否为1)
if((UART1_RX_STA & 0x8000) == 0)
{
// 如果已经收到了 0x0d (回车),
if(UART1_RX_STA & 0x4000)
{
// 则接着判断是否收到 0x0a (换行)
if(buf == 0x0a)
{
// 如果 0x0a 和 0x0d 都收到,则将 bit15 位置为1
UART1_RX_STA |= 0x8000;
//程序运行到此处,代表数据已经完整接收,可以开始判断//
//查看是否收到WIFI GOT IP
if(strcmp(UART1_RX_Buffer,"WIFI GOT IP") == 0){
HAL_UART_Transmit(&huart2, "111\r\n",strlen("111\r\n"), 100);
AT_Connect_Net_Flag = 1;
}
//查看是否收到OK
if(strcmp(UART1_RX_Buffer,"OK") == 0){
HAL_UART_Transmit(&huart2, "222\r\n",strlen("222\r\n"), 100);
AT_OK_flag = 1;
}
//灯控指令
if(strcmp(UART1_RX_Buffer,"open") == 0){
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_8,GPIO_PIN_RESET);
}
if(strcmp(UART1_RX_Buffer,"close") == 0){
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_8,GPIO_PIN_SET);
}
//如果联网失败,收到FAIL
if(!strcmp(UART1_RX_Buffer,"FAIL")){
HAL_UART_Transmit(&huart2, "error\r\n",strlen("error\r\n"), 100);
int i;
for(i = 0;i <5;i++){
HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_8);
HAL_Delay(1000);}
printf(reset);
}
memset(UART1_RX_Buffer,0,UART1_REC_LEN);
UART1_RX_STA = 0;
}
else
// 否则认为接收错误,重新开始
UART1_RX_STA = 0;
}
else // 如果没有收到了 0x0d (回车)
{
//则先判断收到的这个字符是否是 0x0d (回车)
if(buf == 0x0d)
{
// 是的话则将 bit14 位置为1
UART1_RX_STA |= 0x4000;
}
else
{
// 否则将接收到的数据保存在缓存数组里
UART1_RX_Buffer[UART1_RX_STA & 0X3FFF] = buf;
UART1_RX_STA++;
// 如果接收数据大于UART1_REC_LEN(200字节),则重新开始接收
if(UART1_RX_STA > UART1_REC_LEN - 1)
UART1_RX_STA = 0;
}
}
}
// 重新开启中断
HAL_UART_Receive_IT(&huart1, &buf, 1);
}
}
int fputc(int ch, FILE *f)
{//注意,printf的重写是针对串口1的,也就是wifi的,对于串口2的数据发送不能用printf
unsigned char temp[1]={ch};
HAL_UART_Transmit(&huart1,temp,1,0xffff);
return ch;
}
int main(void)
{
HAL_NVIC_SetPriority(SysTick_IRQn,0,0);
// 开启接收中断
HAL_UART_Receive_IT(&huart1, &buf, 1);
HAL_UART_Transmit(&huart2, "START\r\n",strlen("START\r\n"), 100);
printf(connect_net); //发送联网AT指令并等待成功
while(!AT_Connect_Net_Flag) HAL_Delay(50);//此处这个delay必须得加,不然程序会卡死
while(!AT_OK_flag) HAL_Delay(50);//此处这个delay必须得加,不然程序会卡死
HAL_UART_Transmit(&huart2, "5555\r\n",strlen("5555\r\n"), 100);
AT_OK_flag = 0;
//发送连服务器指令并等待成功
printf(connect_servere);
while(!AT_OK_flag) HAL_Delay(50);
HAL_UART_Transmit(&huart2, "6665\r\n",strlen("6665\r\n"), 100);
AT_OK_flag = 0;
//发送透传模式指令并等待成功
printf(Transparent_mode);
while(!AT_OK_flag) HAL_Delay(50);
HAL_UART_Transmit(&huart2, "7775\r\n",strlen("7775\r\n"), 100);
AT_OK_flag = 0;
//发送数据传输指令并等待成功
printf(data_transmission);
while(!AT_OK_flag) HAL_Delay(50);
while (1)
{
printf("hhhhhhhhhhhhaaaaaaaaaa");
HAL_UART_Transmit(&huart2, "hello shion\r\n",strlen("hello shion\r\n"), 100);
HAL_Delay(3000);
}
}
实验效果:
注意!要记得将服务器打开,不然一直连接不上(以至于折磨了我好长时间调试)
在电脑串口和网络调试助手可以看到不断发来的心跳包。
同时,串口1作为单片机和WIFI的通信,而串口2作为日志发回电脑的串口助手,串口1每发送并成功接收了回应,串口2就会返回电脑成功的信息,这样用户就可以可视化的看到连接进行到了哪一步,这算是用两路串口实现了以前白盒测试和黑盒测试的效果。
当在网络助手发送“open”加上回车时,继电器会打开;反之输入“close”加上回车,继电器会闭合
STA模式下控制的实现
在这种模式下,ESP作为路由器,由电脑连接ESP的WIFI,作为Client来接收和发送。
以下是之前学习WIFI模块时总结的,关于ESP作为STA模式下的相关命令
CubeMX
同上
通过白盒测试可知:在ESP作为STA的模式下,WIFI助手发送的数据在串口中显示的是有特定格式的:
如果发送“open”加回车,那么显示的是:+IPD,0,6:open
如果发送“close”加回车,那么显示的是:+IPD,0,7:close
因此,在KEIL中的代码中,字符串的比较不能直接和open close比较,而是和以上两个字符串进行比较!!!
代码
#include <stdio.h>
#include <string.h>
//串口接收缓存(1字节)
uint8_t buf=0;
//定义最大接收字节数 200,可根据需求调整
#define UART1_REC_LEN 200
// 接收缓冲, 串口接收到的数据放在这个数组里,最大UART1_REC_LEN个字节
uint8_t UART1_RX_Buffer[UART1_REC_LEN];
// 接收状态
// bit15, 接收完成标志
// bit14, 接收到0x0d
// bit13~0, 接收到的有效字节数目
uint16_t UART1_RX_STA=0;
char buffer[12];
char connect_net[] = "AT+CWJAP=\"bbb\",\"56893233\"\r\n"; //入网指令 \r\n新行
char mode[] = "AT+CWMODE=2\r\n"; //路由器模式指令
char mul_con[] = "AT+CIPMUX=1\r\n"; //使能多链接
char server[] = "AT+CIPSERVER=1\r\n"; //建立TCPServer default port = 333
char send[] = "AT+CIPSEND=0,5\r\n"; //发送数据
char end[] = "AT+CIPCLOSE=0\r\n"; //断开连接
char AT_Connect_Net_Flag = 0; //WIFI GOT IP返回值的标志位
char AT_OK_flag = 0; //OK返回值的标志位
char Client_Connent_Flag = 0; //客户端接入返回值标志位
// 接收完成回调函数,收到一个数据后,在这里处理
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
// 判断中断是由哪个串口触发的
if(huart->Instance == USART1)
{
// 判断接收是否完成(UART1_RX_STA bit15 位是否为1)
if((UART1_RX_STA & 0x8000) == 0)
{
// 如果已经收到了 0x0d (回车),
if(UART1_RX_STA & 0x4000)
{
// 则接着判断是否收到 0x0a (换行)
if(buf == 0x0a)
{
// 如果 0x0a 和 0x0d 都收到,则将 bit15 位置为1
UART1_RX_STA |= 0x8000;
//程序运行到此处,代表数据已经完整接收,可以开始判断//
//查看是否收到WIFI GOT IP
if(strcmp(UART1_RX_Buffer,"WIFI GOT IP") == 0){
HAL_UART_Transmit(&huart2, "111\r\n",strlen("111\r\n"), 100);
AT_Connect_Net_Flag = 1;
}
//查看是否收到OK
if(strcmp(UART1_RX_Buffer,"OK") == 0){
HAL_UART_Transmit(&huart2, "222\r\n",strlen("222\r\n"), 100);
AT_OK_flag = 1;
}
//灯控指令
if(strcmp(UART1_RX_Buffer,"+IPD,0,6:open") == 0){
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_8,GPIO_PIN_RESET);
}
if(strcmp(UART1_RX_Buffer,"+IPD,0,7:close") == 0){
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_8,GPIO_PIN_SET);
}
//如果联网失败,收到FAIL
if(!strcmp(UART1_RX_Buffer,"0,CONNECT")){
Client_Connent_Flag = 1;
}
memset(UART1_RX_Buffer,0,UART1_REC_LEN);
UART1_RX_STA = 0;
}
else
// 否则认为接收错误,重新开始
UART1_RX_STA = 0;
}
else // 如果没有收到了 0x0d (回车)
{
//则先判断收到的这个字符是否是 0x0d (回车)
if(buf == 0x0d)
{
// 是的话则将 bit14 位置为1
UART1_RX_STA |= 0x4000;
}
else
{
// 否则将接收到的数据保存在缓存数组里
UART1_RX_Buffer[UART1_RX_STA & 0X3FFF] = buf;
UART1_RX_STA++;
// 如果接收数据大于UART1_REC_LEN(200字节),则重新开始接收
if(UART1_RX_STA > UART1_REC_LEN - 1)
UART1_RX_STA = 0;
}
}
}
// 重新开启中断
HAL_UART_Receive_IT(&huart1, &buf, 1);
}
}
int fputc(int ch, FILE *f)
{//注意,printf的重写是针对串口1的,也就是wifi的,对于串口2的数据发送不能用printf
unsigned char temp[1]={ch};
HAL_UART_Transmit(&huart1,temp,1,0xffff);
return ch;
}
int main(void)
{
HAL_NVIC_SetPriority(SysTick_IRQn,0,0);
// 开启接收中断
HAL_UART_Receive_IT(&huart1, &buf, 1);
HAL_UART_Transmit(&huart2, "START\r\n",strlen("START\r\n"), 100);
printf(mode);//发送客户端模式指令并等待成功
while(!AT_OK_flag) HAL_Delay(50);
AT_OK_flag = 0;
HAL_UART_Transmit(&huart1, "mode set success\r\n", strlen("mode set success\r\n"), 100);
printf(mul_con);
while(!AT_OK_flag) HAL_Delay(50);
AT_OK_flag = 0;
printf(server);
while(!Client_Connent_Flag) HAL_Delay(50);
AT_OK_flag = 0;
if(Client_Connent_Flag){
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_8,GPIO_PIN_RESET);
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_9,GPIO_PIN_RESET);
}
while (1)
{
printf(send);//发送数据
HAL_Delay(2000);
printf("wdnmd");//发给串口1的,共2个字节
HAL_Delay(2000);
}
}
实现效果
打开单片机点击复位,使得电脑连上”esp8266“的WIFI,然后在网络助手中输入相关信息之后点击连接:(要提前连好wifi的情况)1-ESP8266-AT指令初试化及部分基础知识_ch_pd-CSDN博客STA模式具体连接再次参考这个
当在网络助手发送“open”加上回车时,LED1会打开;反之输入“close”加上回车,LED1会关闭