基于STM32与ESP8266的太空人WiFi天气时钟(代码开源)

在线wifi跑包 金刚包跑包 cap跑包 hccapx ewsa在线 就来 握手包跑包

各位好 又见面了 我是曹操 今天给大家带来一篇新的教程

希望各位细心学习 低调用网

wifi字典最新

前言:本文介绍了一款基于STM32的太空人WiFi天气时钟项目,与常见的ESP8266版本不同,本项目使用了STM32作为MCU。由于两者芯片的特性(如时钟频率、内存大小等)不同,开发过程中需要进行特殊设计。作者选择使用STM32的原因是,虽然ESP8266在计算能力等方面优于STM32F1xx,但其引脚和外设较少,扩展性一般(而ESP32则兼具二者优点)。另外,网络上已经有很完善的ESP8266太空人WiFi天气时钟开源项目,因此作者尝试使用STM32实现该项目,以便后续拓展开发(代码在文末开源)。

实验硬件:STM32F103ZET6;7针1.3寸TFT-LCD(240×240);ESP8266

硬件实物图:

wifi字典最新wifi字典最新

效果图:

引脚连接:
LCD显示引脚:
VCC –> 3.3V
GND –> GND
CLK –> PA5
DIN –> PA7
RES –> PB0
DC –> PB1
CS –> PA4

ESP8266模块引脚:
VCC –> 3.3V
GND –> GND
RX–> PB10
TX –> PB11
RST –> PB9
EN –> PB7

一、ESP8266简介与使用
1.1 ESP8266简介
ESP8266是一款超低功耗的UART-WiFi透传模块,专为移动设备和物联网应用设计。它可以将物理设备连接到Wi-Fi无线网络上,实现互联网或局域网通信。

ESP8266是上海乐鑫信息科技设计的低功耗WiFi芯片,集成了完整的TCP/IP协议栈和MCU。ESP8266模块是基于ESP8266芯片研发的串口WiFi模块,成本低、使用简便、功能强大。

void esp8266_config(void)
{
		char str[200];
		sprintf(str, "AT+CWJAP="%s","%s"rn", WIFI_NAME, WIFI_PSW);
//		SendATCmd("+++", 500);				// 退出透传模式
		SendATCmd("ATrn", 2000);			// 测试ESP01模块是否存在
//		SendATCmd("AT+GMRrn",3000);	// 查看模块版本信息
		SendATCmd("AT+CWMODE=1rn", 2000);	// 开启STA+AP模式 ==================		
		SendATCmd("AT+RSTrn", 3000);	
		SendATCmd(str, 10000);	// 连接无线路由器或者手机热点,等待10秒 ============
		SendATCmd("AT+CIPMUX=0rn", 2000);	// 关闭多连接
		SendATCmd("AT+CIPSTART="TCP","api.seniverse.com",80rn", 2000);	// 连接心知  天气TCP服务器
		SendATCmd("AT+CIPMODE=1rn", 500);	// 开启透传模式
		SendATCmd("AT+CIPSENDrn", 500);		// 开始透传
}

1.2 硬件与网络的桥梁——ESP8266
ESP8266模块是一种串口WiFi模块,类似于蓝牙模块,可以扩展单片机的功能。它通过串口AT指令与单片机通信,实现串口透传。

透传是指输入即输出,即从WiFi模块串口输入的字符会透传到服务器端,数据不改变,由模块完成不同协议之间的转换。ESP8266模块对于使用者来说是透明的,使用者只需关注使用接口。

通过将硬件连接到网络,我们可以实现更多的功能。配合服务器端的Socket网络编程,可以实现许多有趣的应用。因此,WiFi模块是连接软件(网络编程)与硬件(单片机)的桥梁,将单片机和Web知识联系起来。

ESP8266的出现大大降低了网络开发的难度,促进了技术的下放。通过学习ESP8266/ESP32等模块,可以熟悉TCP/IP等网络协议,对后续的网络开发也非常有意义。

1.3 ESP8266使用——AT指令
AT指令是一些起控制作用的特殊字符串,最早在蓝牙模块上使用。ESP8266模块也支持AT指令,通过AT指令控制模块的功能。

下面是一些常用的AT指令及其用法:

基础AT指令:
AT:测试AT启动
AT+RST:重启模块
AT+GMR:查看版本信息

WiFi功能AT指令:
AT+CWMODE:设置WiFi模式(sta/AP/sta+AP)
AT+CWLAP:扫描附近的AP信息
AT+CWJAP:连接AP
AT+CWQAP:与AP断开连接
AT+CWSAP:设置ESP8266 softAP配置
AT+CWLIF:获取连接到ESP8266 softAP的station的信息

TCP/IP相关AT指令:
AT+CIPSTATUS:查询网络连接信息
AT+CIPMUX:设置多连接模式
AT+CIPSTART:建立TCP连接UDP传输或者SSL连接
AT+CIPCLOSE:关闭TCP/UDP/SSL传输
AT+CIPMODE:设置透传模式
AT+CIPSEND:发送数据

以上是本项目需要使用的一些指令,具体使用方法请参考官方文档:ESP8266 AT指令集。

二、知心天气API使用
本项目需要从网页上读取天气信息,使用了知心天气API。注册并获取API密钥后,可以通过API接口函数获取天气数据。

2.1 登录知心天气官网,注册
如果没有账号,可以自行注册。知心天气提供免费版,注册流程简单。

wifi字典最新wifi字典最新

点击”立即免费试用”,然后点击免费版的”免费申请”。申请成功后,可以查看自己的API密钥(保存好,后面会用到)。

2.2 API函数的使用
大部分网络数据调用都是通过API接口函数实现的。在知心天气官网的API文档中,可以找到各种API接口的地址和返回的数据结果示例(保存好,后面会用到)。

三、UART串口通信
STM32作为MCU与ESP8266之间的通信使用UART(串口)通信。通过串口发送AT指令集,实现与ESP8266的通信。

将STM32的UARTxTX连接到ESP8266的UARTRX,然后通过串口发送AT指令集。ESP8266将从服务器接收到的数据通过UARTTX发送给STM32的UARTxRX。通过解析串口接收到的数据,可以获取所需的信息。

可以使用电脑串口读取STM32接收到的ESP8266返回的信息。

四、CubeMX配置

  1. RCC配置外部高速晶振(HSE)以提高精度。
  2. SYS配置:将Debug设置为Serial Wire,避免芯片自锁。
  3. GPIO配置:配置SPI通信和ESP8266的EN和RST引脚。

wifi字典最新wifi字典最新wifi字典最新wifi字典最新wifi字典最新wifi字典最新wifi字典最新wifi字典最新

  1. RTC配置:配置年月日、时分秒。
  2. UART1和UART3配置:分别与电脑和ESP8266通信(记得开启串口通信中断)。
  3. 时钟树配置。
  4. 工程配置。

五、代码与解析
5.1 TFT-LCD显示代码
LCD显示部分是基础操作,如果不熟悉可以参考其他资料。这里主要指出与其他项目不同的地方。

5.1.1 UI设计
UI设计是WiFi天气时钟的重要部分,需要设计许多界面图标。作者在GitHub和视觉中国找到了符合要求的UI库(需要的话可以在评论区留下邮箱)。

wifi字典最新

5.1.2 GIF动图实现
由于STM32的内存限制,不太适合实现GIF动图。目前主流的方法有两种:使用enWin或Lvgl库实现GIF动图,或从SD卡读取数据显示。

作者使用了一种简单的方法,循环遍历GIF动图的每一帧。

使用GIF分离器分离每一帧的图像。

wifi字典最新wifi字典最新

void showimage4(const unsigned char *p) 
{
  int i; 
	unsigned char picH,picL;
 
	Address_set(180,146,228,195);
	for(i=0;i<49*50;i++)
		{	
			picL=*(p+i*2);	
			picH=*(p+i*2+1);				
			LCD_WR_DATA(picH<<8|picL); 				
		}	
}
 
 
for(int a=0;a<11;a++)
{
    showimage4(gImage_1[a]);
}
/* USER CODE BEGIN 4 */
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
  /* Prevent unused argument(s) compilation warning */
  UNUSED(huart);
  /* NOTE: This function Should not be modified, when the callback is needed,
           the HAL_UART_TxCpltCallback could be implemented in the user file
   */
	if(huart == &huart1)
	{
		g_uart1_rx.buf[g_uart1_rx.size++] = aRxBuffer_rx1;   									
		if((g_uart1_rx.buf[g_uart1_rx.size-1] == 0x0A)&&(g_uart1_rx.buf[g_uart1_rx.size-2] == 0x0D))	
		{
			HAL_UART_Transmit(&huart1, (uint8_t *)&g_uart1_rx.buf, g_uart1_rx.size,0xFFFF);	
					while(HAL_UART_GetState(&huart1) == HAL_UART_STATE_BUSY_TX);			
			g_uart1_rx.size = 0;
			memset(g_uart1_rx.buf,0x00,sizeof(g_uart1_rx.buf));									
		}
		HAL_UART_Receive_IT(&huart1, (uint8_t *)&aRxBuffer_rx1, 1);						
	}
	if(huart == &huart3)
	{
		g_uart3_rx.buf[g_uart3_rx.size++] = aRxBuffer_rx3;   									
	
		if((g_uart3_rx.buf[g_uart3_rx.size-1] == 'K')&&(g_uart3_rx.buf[g_uart3_rx.size-2] == 'O'))	
		{
			HAL_UART_Transmit(&huart1, (uint8_t *)&g_uart3_rx.buf, g_uart3_rx.size,0xFFFF);	
				while(HAL_UART_GetState(&huart1) == HAL_UART_STATE_BUSY_TX);			
			g_uart3_rx.size = 0;
			memset(g_uart3_rx.buf,0x00,sizeof(g_uart3_rx.buf));									
		}
		else if((g_uart3_rx.buf[g_uart3_rx.size-2] == ']')&&(g_uart3_rx.buf[g_uart3_rx.size-1] == '}')
			&&(g_uart3_rx.buf[g_uart3_rx.size-3] == '}'))
		{
			HAL_UART_Transmit(&huart1, (uint8_t *)&g_uart3_rx.buf, g_uart3_rx.size,0xFFFF);	
			while(HAL_UART_GetState(&huart1) == HAL_UART_STATE_BUSY_TX);			//				
			strcpy(Data_buff,(char *)g_uart3_rx.buf);
			temp = 1;
			g_uart3_rx.size = 0;				
			memset(g_uart3_rx.buf,0x00,sizeof(g_uart3_rx.buf));									
 
		}
		HAL_UART_Receive_IT(&huart3, (uint8_t *)&aRxBuffer_rx3, 1);						
	}
	
}
/* USER CODE END 4 */

然后使用Image2Lcd 2.9(破解版)生成图像模式。

将取模代码转换为二维数组,第一维表示帧数,第二维表示每帧图像的取模。

然后循环显示每一帧,实现GIF动图显示。

5.2 ESP8266代码
ESP8266的代码主要是配置和与目标服务器通信,还需要解码服务器返回的信息。

5.2.1 ESP8266配置代码(包括UART处理)
UART回调处理函数:

#ifndef __ESP8266_H
#define __ESP8266_H
 
//#include "stdint.h"
 
//uint8_t aRxBuffer_rx1;			//接收中断缓冲
//uint8_t aRxBuffer_rx3;			//接收中断缓冲
 
//typedef struct {
//	uint16_t size;
//	uint8_t buf[1022]; // 接收缓冲数组
//} UART_RXDATA;
 
//UART_RXDATA g_uart1_rx;
//UART_RXDATA g_uart3_rx;
 
//char Data_buff[1022];
 
//char weather[10];				//存储天气
 
//uint8_t temperature[2]={0,0}; 		//储存最高气温和最低气温
//uint8_t temp = 0;
 
//需要连接的wifi账号和密码,需要修改,且WiFi频段不支持5GHz
#define WIFI_NAME "Wang"
#define WIFI_PSW    "123456"
 
心知天气api,注意key=后面需要替换成自己账号的密钥
//char *get="GET https://api.seniverse.com/v3/weather/daily.json?key=SkV9zIBpwJAOixrJZ&location=chongqing&language=en&unit=crn";
 
//void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart);
void SendATCmd(char *cmd, int waitms); 
void esp8266_config(void);
 
 
#endif
#include "esp8266.h"
#include "usart.h"
#include 
#include 
#include 
#include "lcd.h"
 
 
void SendATCmd(char *cmd, int waitms) 
{ // 发送AT指令给串口3
	if (NULL != cmd)
	{
		HAL_UART_Transmit(&huart3, (uint8_t *)cmd, strlen(cmd), 0xFFFF);	
		if (waitms > 0)
			HAL_Delay(waitms);  // 延时等待ESP01模块应答时间
	}
}
 
void esp8266_config(void)
{
		char str[200];
		sprintf(str, "AT+CWJAP="%s","%s"rn", WIFI_NAME, WIFI_PSW);
//		SendATCmd("+++", 500);				// 退出透传模式
		SendATCmd("ATrn", 2000);			// 测试ESP01模块是否存在
//		SendATCmd("AT+GMRrn",3000);	// 查看模块版本信息
		SendATCmd("AT+CWMODE=1rn", 2000);	// 开启STA+AP模式 ==================		
		SendATCmd("AT+RSTrn", 3000);	
		SendATCmd(str, 10000);	// 连接无线路由器或者手机热点,等待10秒 ============
		SendATCmd("AT+CIPMUX=0rn", 2000);	// 关闭多连接
		SendATCmd("AT+CIPSTART="TCP","api.seniverse.com",80rn", 2000);	// 连接心知天气TCP服务器
		SendATCmd("AT+CIPMODE=1rn", 500);	// 开启透传模式
		SendATCmd("AT+CIPSENDrn", 500);		// 开始透传
	
		SendATCmd("GET https://api.seniverse.com/v3/weather/daily.json?key=SkV9zIBpwJAOixrJZ&location=zhenjiang&language=en&unit=crn", 2000);
}
 

wifi字典最新

ESP8266.h(AT控制):

ESP8266.c:

注意,key=后面尽量换成自己的密钥,location=后面也可以换成自己所在城市的字母。

5.2.2 ESP8266信息解码
作者使用了字符串比较和指针取值的方法进行解码。

strstr()函数:

wifi字典最新

			char *p;
			p =	strstr(Data_buff,"text_day");				//查找天气		
			sscanf(p+11,"%[^"]",weather);	
//			LCD_ShowString(40,80,(uint8_t*)weather);			
			p = strstr(Data_buff,"high");								//查找气温
			temperature[0]=atoi(p+7);
			p = strstr(Data_buff,"low");
			temperature[1]=atoi(p+6);
//			LCD_ShowxNum2(45,40,temperature[1],2,24,0);
			LCD_ShowxNum2(160,207,temperature[0],2,24,0);
 
			//温度
			value = (temperature[1]+temperature[0])/2;
			LCD_ShowxNum2(52,160,value,2,24,0); 			
 
			//湿度
			p = strstr(Data_buff,"humidity");
			humidity=atoi(p+11);
			LCD_ShowxNum2(132,160,humidity,2,24,0);
			LCD_ShowNew(161,160,'%',24,0);
			
			if((strstr(weather,"Overcast")) || (strstr(weather,"Mostly Cloudy")) || (strstr(weather,"Partly Cloudy")) || strstr(weather,"Cloudy"))	
			{
				Overcast();
			}
			if((strstr(weather,"Sunny")) || (strstr(weather,"Clear")) || (strstr(weather,"Fair")))			//ÇçÌì
			{
				Sunny();
			}
			if((strstr(weather,"Shower")))				
			{
				Shower();
			}
			if((strstr(weather,"Thundershower")) || (strstr(weather,"Thundershower with Hail")))		
			{
				Thundershower();
			}
			if((strstr(weather,"Light rain")) || (strstr(weather,"Moderate Rain")))			
			{
				smallrain();
			}
			if((strstr(weather,"Heavy Rain")) || (strstr(weather,"Storm")) || (strstr(weather,"Heavy Storm")) || (strstr(weather,"Severe Storm")))		
			{
				Bigrain();
			}
			if((strstr(weather,"Ice Rain")) || (strstr(weather,"Sleet")) || (strstr(weather,"Snow Flurry")) || (strstr(weather,"Light Snow")) || (strstr(weather,"Moderate Snow")) || (strstr(weather,"Heavy Snow")) || (strstr(weather,"Snowstorm")))		
			{
				snow();
			}			
#ifndef __RTCDISPLAY_H
#define __RTCDISPLAY_H
 
void RTC_display();
 
#endif
#include "rtcdisplay.h"
#include "rtc.h"
#include "lcd.h"
 
RTC_DateTypeDef GetData;  //获取日期结构体
RTC_TimeTypeDef GetTime;   //获取时间结构体
 
void RTC_display()				//RTC DISPLAY
{
	
	  /* Get the RTC current Time */
	  HAL_RTC_GetTime(&hrtc, &GetTime, RTC_FORMAT_BIN);
    /* Get the RTC current Date */
    HAL_RTC_GetDate(&hrtc, &GetData, RTC_FORMAT_BIN);
	
		/* Display date Format : yy/mm/dd */
//		OLED_ShowNum(0,0,2000+GetData.Year,4,16);			//year
//		OLED_ShowStr(35,30,".",2);
//		OLED_ShowNum(45,0,GetData.Month,2,16);				//month
//		OLED_ShowStr(60,30,".",2);
//		OLED_ShowNum(70,0,GetData.Date,2,16);			    //date
	
    /* Display time Format : hh:mm:ss */
		LCD_ShowxNum2(15,75,GetTime.Hours,2,60,0);				//hour
//		LCD_ShowNew(75,65,':',60,0);
		LCD_ShowxNum2(105,75,GetTime.Minutes,2,60,0);			//min
		LCD_ShowxNum2(180,105,GetTime.Seconds,2,32,0);			//seconds
 
}

atoi()函数:

5.3 RTC代码
rtcdisplay.h:

rtcdisplay.c:

RTC的时钟显示使用了专门的LED数字字体,如果需要字体库可以在评论区留言。

六、项目效果
太空人WiFi天气时钟

七、项目代码
代码地址:

赞(0)