基于STM32的家庭气象站

项目地址

Maskbugzero/ESP8266-Weather-2021

一个关于STM32+ESP8266+DHT11的家庭气象站。

硬件资源

  1. 战舰V3\精英STM32F103开发板
  2. ALIENTEK 2.8/3.5/4.3/7寸TFTLCD模块(通过FSMC驱动.FSMC_NE4接LCD片选/A10接RS)
  3. 按键KEY0(PE4)/KEY1(PE3)/KEY_UP(PA0.也称之为WK_UP)
  4. ESP8266-12S WIFI模块1个
  5. DHT11模块1个

实现功能

  1. 微信小程序配网
  2. 室内温湿度测量及显示
  3. 室外天气数据获取及显示
  4. 时间显示及网络校准

连接方式

  • 模块与带有无线网卡的电脑或其他wifi设备连接:采用wifi连接
  • 模块与开发板连接:TTL串口方式

ATK-ESP8266 WIFI模块与精英板连接方式

TXD<————>PB11

RXD<————>PB10

GND<————>GND

VCC<————>3.3V\5V

DHT11模块与精英板连接方式

Data<————>PE11

主要代码

外设初始化

初始化外设时,若DHT11工作不正常则显示屏无法正常显示。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
	LED_Init();		  			//初始化与LED连接的硬件接口
KEY_Init(); //初始化按键
LCD_Init(); //初始化LCD
RTC_Init();
while(DHT11_Init()) //DHT11初始化
{
printf("DHT11 出错!\r\n");
delay_ms(200);
}
W25QXX_Init(); //初始化W25Q128
tp_dev.init(); //初始化触摸屏
usart3_init(115200); //初始化串口3
my_mem_init(SRAMIN); //初始化内部内存池
exfuns_init(); //为fatfs相关变量申请内存
f_mount(fs[0],"0:",1); //挂载SD卡
f_mount(fs[1],"1:",1); //挂载FLASH.

微信小程序配网

微信小程序配网

源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
//一键配网设置
u8 atk_8266_wifi_config(void)
{
int i;

while(atk_8266_send_cmd("AT","OK",20))//检查WIFI模块是否在线
{
atk_8266_quit_trans();//退出透传
atk_8266_send_cmd("AT+CIPMODE=0","OK",200); //关闭透传模式
printf("未检测到模块!!!");
delay_ms(800);
printf("尝试连接模块...");
}
u3_printf("AT+RESTORE"); //恢复出厂设置
delay_ms(1000); //延时3S等待恢复成功
delay_ms(1000);
delay_ms(1000);
printf("恢复出厂设置完成");
atk_8266_send_cmd("AT+RST","OK",20); //软重启
delay_ms(1000); //延时3S等待重启成功
delay_ms(1000);
delay_ms(1000);
delay_ms(1000);
printf("软重启完成");
while(atk_8266_send_cmd("ATE0","OK",20));//关闭回显
atk_8266_send_cmd("AT+CWMODE=1","OK",50); //设置WIFI STA模式
atk_8266_send_cmd("AT+CWAUTOCONN=1","OK",20); //使能上电自动连接AP
delay_ms(300);
atk_8266_send_cmd("AT+CWSTARTSMART=3","OK",20); //支持ESP-Touch和Airkiss智能配网
printf("智能配网已开启 等待30s\r\n");
while(i<=30)
{
delay_ms(1000); //延时30S等待配网成功
i++;
}
// while(atk_8266_check_cmd("smartconfig connected wifi")); //连接目标路由器,并且获得IP
// delay_ms(300);
atk_8266_send_cmd("AT+CWSTOPSMART","OK",20); //释放快连所占的内存

return 0;
}

数据解析

数据解析流程图

天气数据

源码:(以当天天气数据为例,近3天天气数据类似)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
//解析当前天气
u8 parse_now_weather(void)
{
cJSON *root;
cJSON *pSub;
cJSON *arrayItem;
cJSON *pItem;
cJSON *pSubItem;
cJSON *pChildItem;

char *pr,*utf8str,*gbkstr;
u8 size = 0;
int len;
u8 res;
u8 temperature;

root = mymalloc(SRAMIN,sizeof(cJSON));
pSub = mymalloc(SRAMIN,sizeof(cJSON));
pItem = mymalloc(SRAMIN,sizeof(cJSON));
pSubItem = mymalloc(SRAMIN,sizeof(cJSON));
pChildItem = mymalloc(SRAMIN,sizeof(cJSON));
arrayItem = mymalloc(SRAMIN,sizeof(cJSON));

pr = mymalloc(SRAMIN,1000);
utf8str = mymalloc(SRAMIN,50);
gbkstr = mymalloc(SRAMIN,50);

memset(pr,0,1000);
memset(gbkstr,0,50);
memset(utf8str,0,50);

file = mymalloc(SRAMIN,sizeof(FIL));
res=f_open(file,(const TCHAR*)APP_ASCII_5427,FA_READ);//打开文件
if(res==FR_OK)
{
asc2_5427 = mymalloc(SRAMIN,file->fsize);
if(asc2_5427 != NULL)
{
res = f_read(file,asc2_5427,file->fsize,&br);
}
f_close(file);
}

printf("jieshou->1dayjson = %s\r\n",USART3_RX_BUF);

root = cJSON_Parse((const char*)USART3_RX_BUF);
if(root != NULL)
{
pSub = cJSON_GetObjectItem(root,"results");
if(pSub != NULL)
{
// size = cJSON_GetArraySize(pSub);
arrayItem = cJSON_GetArrayItem(pSub,0);
pr = cJSON_Print(arrayItem); //获取jsom数组
pItem = cJSON_Parse(pr); //对数组,进行升级。
if(pItem != NULL)
{
pSubItem = cJSON_GetObjectItem(pItem,"location");
if(pSubItem != NULL)
{
pChildItem = cJSON_GetObjectItem(pSubItem,"name");
if(pChildItem != NULL)
{
utf8str = pChildItem->valuestring;
SwitchToGbk((const u8*)utf8str,strlen(utf8str),(u8 *)gbkstr,&len); //获取城市名称转换为gbk文件
Show_Str(0,0,lcddev.width,lcddev.height,(u8 *)gbkstr,16,0); //显示城市名称。
}
}

memset(utf8str,0,50); //解决阴华
memset(gbkstr,0,50);

pSubItem = cJSON_GetObjectItem(pItem,"now");
if(pSubItem != NULL)
{
pChildItem = cJSON_GetObjectItem(pSubItem,"text"); //获取天气信息。多云
if(pChildItem != NULL)
{
utf8str = pChildItem->valuestring;
SwitchToGbk((const u8*)utf8str,strlen(utf8str),(u8 *)gbkstr,&len);
Show_Str(220,25,lcddev.width,lcddev.height,(u8 *)gbkstr,16,0); //显示多云
}
memset(utf8str,0,50);
memset(gbkstr,0,50);

pChildItem = cJSON_GetObjectItem(pSubItem,"code"); //获取气象代码
if(pChildItem != NULL)
{
gbkstr = pChildItem->valuestring;
show_weather_icon((u8 *)gbkstr,0); //根据气象代码,更新图片
}
memset(gbkstr,0,50);

pChildItem = cJSON_GetObjectItem(pSubItem,"temperature"); //获取温度信息
if(pChildItem != NULL)
{
gbkstr = pChildItem->valuestring;
temperature = str2int((u8 *)gbkstr);
gui_show_num(140,22,2,RED,54,temperature,0x80);
printf("wendu = %d\r\n",temperature);
}
}

pSubItem = cJSON_GetObjectItem(pItem,"last_updated");
if(pSubItem != NULL)
{
gbkstr =pSubItem->valuestring;

LCD_ShowString(0,92,200,20,12,(u8*)gbkstr);
printf("1day_updata_time = %s\r\n",(u8*)gbkstr);
}


}
cJSON_Delete(pItem);
}
}
cJSON_Delete(root);
myfree(SRAMIN,root);
myfree(SRAMIN,pSub);
myfree(SRAMIN,pItem);
myfree(SRAMIN,pSubItem);
myfree(SRAMIN,pChildItem);
myfree(SRAMIN,arrayItem);
myfree(SRAMIN,pr);
myfree(SRAMIN,utf8str);
myfree(SRAMIN,gbkstr);
myfree(SRAMIN,file);
myfree(SRAMIN,asc2_5427);
return 0;
}

时间数据获取和校准

源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
u8 get_beijing_time(void)
{
u8 *p;
u8 res;

u8 *resp;
u8 *p_end;

// u8 ipbuf[16]; //IP缓存
p=mymalloc(SRAMIN,40); //申请40字节内存
sprintf((char*)p,"AT+CIPSTART=\"TCP\",\"%s\",%s",TIME_SERVERIP,TIME_PORTNUM); //配置目标TCP服务器
res = atk_8266_send_cmd(p,"OK",200);//连接到目标TCP服务器
if(res==1)
{
myfree(SRAMIN,p);
return 1;
}
delay_ms(300);
atk_8266_send_cmd("AT+CIPMODE=1","OK",100); //传输模式为:透传
// atk_8266_get_wanip(ipbuf);//获取WAN IP

USART3_RX_STA=0;
atk_8266_send_cmd("AT+CIPSEND","OK",100); //开始透传
printf("start trans...\r\n");
u3_printf("GET http://api.k780.com/?app=life.time&appkey=58173&sign=4e67ab8533b30580e584c8b9f0a6cc50&format=json\n\n");
delay_ms(20);//延时20ms返回的是指令发送成功的状态
// atk_8266_at_response(1);
USART3_RX_STA=0; //清零串口3数据
delay_ms(1000);
// atk_8266_at_response(0);
if(USART3_RX_STA&0X8000) //此时再次接到一次数据,为时间的数据
{
USART3_RX_BUF[USART3_RX_STA&0X7FFF]=0;//添加结束符
}

resp="datetime_2";
p_end = (u8*)strstr((char*)USART3_RX_BUF,(char*)resp);
p = p_end-11; //确定串口数据中时间的起止位,如"datetime_1":"2021-03-31 20:09:07","datetime_2":"2021 中

nwt.hour = ((*p - 0x30)*10 + (*(p+1) - 0x30));
nwt.min = ((*(p+3) - 0x30)*10 + (*(p+4) - 0x30));
nwt.sec = ((*(p+6) - 0x30)*10 + (*(p+7) - 0x30));

nwt.year = ((*(p-11) - 0x30)*1000 + (*(p-10) - 0x30)*100+ (*(p-9) - 0x30)*10+ (*(p-8) - 0x30));
nwt.month = ((*(p-6) - 0x30)*10 + (*(p-5) - 0x30));
nwt.date = ((*(p-3) - 0x30)*10 + (*(p-2) - 0x30));
//使用指针方法获取时分秒年月日
RTC_Set(nwt.year,nwt.month ,nwt.date ,nwt.hour ,nwt.min,nwt.sec); //使用RTC函数设置时间

printf("nwt.year = %d\r\n",nwt.year);
printf("nwt.month = %d\r\n",nwt.month);
printf("nwt.date = %d\r\n",nwt.date);
printf("nwt.hour = %d\r\n",nwt.hour);
printf("nwt.min = %d\r\n",nwt.min);
printf("nwt.sec = %d\r\n",nwt.sec);
// 打印时分秒年月日数据
// parse_now_time();// Cjson方法解析时间数据

atk_8266_quit_trans();//退出透传
atk_8266_send_cmd("AT+CIPCLOSE","OK",50); //关闭连接
myfree(SRAMIN,p);
return 0;
}

实验现象

微信配网实物图

综合调试串口

硬件实物图

注意事项

  1. 4.3寸和7寸屏需要比较大电流,USB供电可能不足,请用外部电源适配器(推荐外接12V 1A电源)。
  2. 本例程在LCD_Init函数里面(在ILI93xx.c),用到了printf,如果不初始化串口1,将导致液晶无法显示!!
  3. 字库更新时,需自备标准SD卡一张(即大卡,也可以用TF卡+卡套),并拷贝光盘:SD卡根目录文件里面的所有内容到SD卡根目录,然后将SD卡插到开发板。
  4. 对于战舰V3开发板,P8需要用跳线短接:PB10(TX)与GBC_RX,PB11(RX)与GBC_TX。
  5. 如果触摸屏不准,请按住KEY0不放,然后按复位,松开复位,进入触摸屏校准。此时松开KEY0,执行校准,即可对屏幕进行校准。

基于STM32的家庭气象站
https://blog.5772447.xyz/posts/c4f182e1/
作者
Maskbugzero
发布于
2021年6月20日
许可协议