一、项目简介
电脑通过esp8266模块远程遥控风扇。
PC端的网络调试助手(以服务端的模式连接客户端的esp8266)
二、项目实现
复制项目文件36-编程实现ESP8266连接TCP服务器
重命名为:38-wifi控制风扇项目
重命名为fan
加载文件
main.c
cpp
#include "sys.h"
#include "delay.h"
#include "led.h"
#include "uart1.h"
#include "esp8266.h"
#include "fan.h"
#include "string.h"
int main(void)
{
HAL_Init(); /* 初始化HAL库 */
stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
led_init();//初始化led灯
uart1_init(115200);
fan_init();
esp8266_init(115200);
// printf("hello word!\r\n");
char recv_data[ESP8266_RX_BUF_SIZE];
while(1)
{
esp8266_receive_data(recv_data);//现在是把接收到的数据传到recv_data这里了
if(strstr(recv_data,"ON") != NULL)//判断我们接收到的字符串里面是否有ON这个值,有的话打卡风扇
fan_ON();
else if(strstr(recv_data,"OFF") != NULL)//没有不打开
fan_OFF();
delay_ms(10);
// esp8266_test();
// delay_ms(500);
}
}
esp8266.c
cpp
#include "sys.h"
#include "esp8266.h"
#include "string.h"
#include "stdio.h"
#include "delay.h"
#include "stdarg.h"
uint8_t esp8266_rx_buf[ESP8266_RX_BUF_SIZE];//定义一个数组,用来保存接收的缓冲区
uint8_t esp8266_tx_buf[ESP8266_TX_BUF_SIZE];//定义一个数组,用来保存发送的缓冲区
uint16_t esp8266_cnt = 0,esp8266_cntPre = 0; //定义一个计数器,和保存计数器原本状态的变量
UART_HandleTypeDef esp8266_handle = {0};
void esp8266_uart_init(uint32_t baudrate)
{
esp8266_handle.Instance = USART2;
esp8266_handle.Init.BaudRate = baudrate; //波特率
esp8266_handle.Init.Mode = UART_MODE_TX_RX;//收发模式;
esp8266_handle.Init.Parity = UART_PARITY_NONE;//无校验位
esp8266_handle.Init.WordLength = UART_WORDLENGTH_8B; //字长:8个字长
esp8266_handle.Init.StopBits = UART_STOPBITS_1; //停止位:1个停止位
esp8266_handle.Init.HwFlowCtl = UART_HWCONTROL_NONE; //无硬件流控
HAL_UART_Init(&esp8266_handle);
}
void USART2_IRQHandler(void)
{
uint8_t receive_data = 0;
//这个函数是用来检查特定的UART接口(在这个例子中是esp8266_handle所代表的UART接口)是否有数据可读
if(__HAL_UART_GET_FLAG(&esp8266_handle,UART_FLAG_RXNE) != RESET)//关注RXNE这个标志位的值是不是不为reset(0)
{
if(esp8266_cnt >= sizeof(esp8266_rx_buf))//如果接收的字符长度大于字符缓冲区的长度,则把缓冲区长度置0
esp8266_cnt = 0;
//如果RXNE的值为1,证明有数据,所以需要接收数据
HAL_UART_Receive(&esp8266_handle,&receive_data,1,1000);//句柄,接收的数据存放在哪?接收数据的个数,超时时间
esp8266_rx_buf[esp8266_cnt++] = receive_data;//将接收的数据存放在esp8266rx_buf数组中
//HAL_UART_Transmit(&esp8266_handle,&receive_data,1,1000);//发送数据:句柄,要发送的数据,发送数据的长度,超时
}
}
//这个函数主要用来判断esp8266cnt有没有动,如果没有动证明接收完成了
uint8_t esp8266_wait_receive(void)
{
if(esp8266_cnt == 0)//如果cnt为0证明,出现了错误
return ESP8266_ERROR;//出现错误
if(esp8266_cnt == esp8266_cntPre)//判断当前cnt和上一个cnt是否一致,如果是一致的证明数据不动了,传输完成
{
esp8266_cnt = 0;//cnt清0
return ESP8266_EOK;//数据接收完成
}
esp8266_cntPre = esp8266_cnt;//把当前计数器cnt的值赋给之前计数器
return ESP8266_ERROR;//
}
//把接收寄存器的内容清空
void esp8266_rx_clear(void)
{
//把接收缓冲器清空
memset(esp8266_rx_buf,0,sizeof(esp8266_rx_buf));
//清空长度
esp8266_cnt = 0;
}
//这个函数在while循环里,来一直判断当前数据是否接收完
uint16_t esp8266_receive_data(char *recv_data)
{
if(esp8266_wait_receive() == ESP8266_EOK)//判断数据是否接受完整
{
printf("esp8266 recv: %s\r\n",esp8266_rx_buf);//接收完整,打印数据
//我们把esp8266_rx_buf中的内容通过memcpy的方式,全部copy到recv_data中
memcpy(recv_data,esp8266_rx_buf,strlen((const char *)esp8266_rx_buf));
esp8266_rx_clear();//清除当前接收
//返回esp8266_rx_buf的内容以及长度
return strlen((const char*)recv_data);
}
return 0;
}
//发送数据的函数
void esp8266_send_data(char *fmt,...)
{
va_list ap;
uint16_t len;
va_start(ap,fmt);
vsprintf((char *)esp8266_tx_buf,fmt,ap);
va_end(ap);
len = strlen((const char *)esp8266_tx_buf);
HAL_UART_Transmit(&esp8266_handle,esp8266_tx_buf,len,100);
}
//定义一个发送指令函数,cmd是发送的指令,res是期待的返回值,对应指令接收的日志
uint8_t esp8266_send_command(char *cmd,char *res)
{
uint8_t time_out = 250;
esp8266_rx_clear();//把接收缓冲区的内容清空
HAL_UART_Transmit(&esp8266_handle,(uint8_t *)cmd,strlen(cmd),100);//发送的库函数:句柄,要发生的内容,内容的长度,阻塞的值
//一共等待2.5s的时间,每一次等待10ms
while(time_out--)
{
if(esp8266_wait_receive() == ESP8266_EOK)//表示已经接收到数据
{
if(strstr((const char*)esp8266_rx_buf,res) != NULL)//判断接收到的数据里面有没有我们想要的数据
return ESP8266_EOK;
}
delay_ms(10);
}
return ESP8266_ERROR;
}
//AT指令
uint8_t esp8266_at_test(void)
{
return esp8266_send_command("AT\r\n","OK");
}
//工作模式:1. 是station(设备)模式 2.是AP(路由)模式 3.是双模
uint8_t esp8266_set_mode(uint8_t mode)
{
switch(mode)
{
case ESP8266_STA_MODE:
return esp8266_send_command("AT+CWMODE=1\r\n","OK");
case ESP8266_AP_MODE:
return esp8266_send_command("AT+CWMODE=2\r\n","OK");
case ESP8266_STA_AP_MODE:
return esp8266_send_command("AT+CWMODE=3\r\n","OK");
default:
return ESP8266_EINVAL;//数据非法
}
}
//以设备模式接入家中路由器,账号和密码
uint8_t esp8266_join_ap(char *ssid,char *pwd)
{
char cmd[64];
sprintf(cmd,"AT+CWJAP=\"%s\",\"%s\"\r\n",ssid,pwd);//使用sprintf构造字符串
return esp8266_send_command(cmd,"WIFI GOT IP");
}
//模式:设置单路链接模式(透传只能使用此模式)
uint8_t esp8266_connection_mode(uint8_t mode)
{
char cmd[64];
sprintf(cmd,"AT+CIPMUX=%d\r\n",mode);//使用sprintf构造字符串
return esp8266_send_command(cmd,"OK");
}
//连接服务器
uint8_t esp8266_connect_tcp_server(char *server_ip,char *server_port)
{
char cmd[64];
sprintf(cmd,"AT+CIPSTART=\"TCP\",\"%s\",%s\r\n",server_ip,server_port);//使用sprintf构造字符串
return esp8266_send_command(cmd,"CONNECT");
}
//透传模式
uint8_t esp8266_enter_unvarnished(void)
{
uint8_t ret;
ret = esp8266_send_command("AT+CIPMODE=1\r\n","OK"); //如果数据正常传输结果为0
ret += esp8266_send_command("AT+CIPSEND\r\n",">"); //如果数据正常传输结果为0
if(ret == ESP8266_EOK)
return ESP8266_EOK;
else
return ESP8266_ERROR;
}
void esp8266_init(uint32_t baudrate)
{
printf("esp8266初始化开始...\r\n");
//esp8266串口初始化
esp8266_uart_init(baudrate);
//esp8266的其他初始化
printf("1、测试esp8266是否存在...\r\n");
while(esp8266_at_test())
delay_ms(500);
printf("2、设置工作模式为STA...\r\n");
while(esp8266_set_mode(ESP8266_STA_MODE))
delay_ms(500);
printf("3、设置单路链接模式...\r\n");
while(esp8266_connection_mode(ESP8266_SINGLE_CONNECTION))
delay_ms(500);
printf("4、接wifi,SSID:%s,PWD:%s...\r\n",WIFI_SSID,WIFI_PWD);
while(esp8266_join_ap(WIFI_SSID,WIFI_PWD))
delay_ms(1500);
printf("5、连接TCP服务器,server_ip:%s, server_port:%s\r\n",TCP_SERVER_IP,TCP_SERVER_PORT);
while(esp8266_connect_tcp_server(TCP_SERVER_IP,TCP_SERVER_PORT))
delay_ms(500);
printf("6、进入到透传模式...\r\n");
while(esp8266_enter_unvarnished())
delay_ms(500);
printf("esp8266已连接到TCP服务器,并且进入到透传模式\r\n");
printf("esp8266初始化完成\r\n");
}
定义一个临时的函数,用来测试,判断发送一个指令回应的函数是否正确
//void esp8266_test(void)
//{
// esp8266_send_data("this is from esp8266\r\n");//发送数据
// esp8266_receive_data();//接收数据
//}
结果: