STM32-笔记33-Wi-Fi遥控风扇项目

一、项目简介

电脑通过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();//接收数据
//}

结果:

相关推荐
懒惰的bit9 天前
STM32F103C8T6 学习笔记摘要(四)
笔记·stm32·学习
zkyqss9 天前
OVS Faucet练习(下)
linux·笔记·openstack
浦东新村轱天乐9 天前
【麻省理工】《how to speaking》笔记
笔记
奔跑的蜗牛AZ9 天前
TiDB 字符串行转列与 JSON 数据查询优化知识笔记
笔记·json·tidb
cwtlw9 天前
Excel学习03
笔记·学习·其他·excel
杭州杭州杭州9 天前
计算机网络笔记
笔记·计算机网络
cyborg9 天前
终于再也不用在notion中写公式了
笔记
循环过三天10 天前
1.2、CAN总线帧格式
笔记·stm32·单片机·嵌入式硬件·学习
循环过三天10 天前
1.1、CAN总线简介
笔记·stm32·单片机·嵌入式硬件·学习
mooyuan天天10 天前
pikachu靶场通关笔记43 SSRF关卡01-CURL(三种方法渗透)
笔记·安全·web安全·ssrf漏洞