内容介绍
内容介绍
平台介绍
本次使用的平台,是基于乐鑫官方模块ESP32-S2-Mini-1制作的。该模块由一颗ESP32S2的处理器,和一颗可以运行在80M的QPI上的Flash以及一些外围电路组成。ESP32S2是一颗单核的Xtensa32的处理器,能运行在240MHz的主频上。官方提供了esp-idf和Arduino等开发框架,本次我在使用arduino作为开发框架开发。
任务需求
制作一个本地气象台/温度计
利用OLED显示当前本地的时间、温度和气象信息
设计思路
连接网络,通过网络获取时间和天气情况,处理之后在oled屏幕上进行信息显示。
实现代码
所需库
#include <Arduino.h>
#include <ArduinoJson.h>//进行json的序列化,和转换做数据通讯
#include <WiFi.h>
#include <U8g2lib.h>
#include <SPI.h>
#include <Wire.h>
#include "time.h"
链接wifi
void Wifi_connect()
{
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED)
{
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.println("WiFi connected");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
}
获取数据
void httpRequest()
{
WiFiClient client;
if (!client.connect(host, 80))
{
Serial.println("Connect host failed!");
return;
}
Serial.println("host Conected!");
String httpURL= "/v3/weather/now.json?key=";
httpURL += privateKey;httpURL += "&location=";httpURL += city;httpURL += "&language=";httpURL += language;
client.print(String("GET ") + httpURL + " HTTP/1.1\r\n" + "Host: " + host + "\r\n" + "Connection: close\r\n\r\n");
Serial.println("Get send");
char endOfHeaders[] = "\r\n\r\n";
bool ok = client.find(endOfHeaders);
if (!ok)
{
Serial.println("No response or invalid response!");
}
Serial.println("Skip headers");
String line="";
line += client.readStringUntil('\n');
Serial.println(client.readStringUntil('\n'));
Serial.println(line);
Json(line);
client.stop();
}
void Json(String line)
{
DynamicJsonDocument doc(1400);
DeserializationError error = deserializeJson(doc, line);
struct WeatherData weatherdata = {0};
Serial.println(doc["results"][0]["daily"][0].as<const char*>());
strcpy(weatherdata.city, doc["results"][0]["location"]["name"].as<const char*>());
strcpy(weatherdata.weather, doc["results"][0]["now"]["text"].as<const char*>());
strcpy(weatherdata.temperature, doc["results"][0]["now"]["temperature"].as<const char*>());
strcpy(weatherdata.code,doc["results"][0]["now"]["code"].as<const char*>());
mystr=weatherdata.code;
OLEDDisplay(weatherdata.city, weatherdata.weather, weatherdata.temperature,weatherdata.code);
}
天气图标绘制
#define SUN 0
#define SUN_CLOUD 1
#define CLOUD 2
#define RAIN 3
#define THUNDER 4
//int weathercode;
//weathercode =int(weatherdata.code);
//uint8_t getSymbol(int weathercode)
//{
// if (weathercode == 0)
// return SUN;
//// if (weathercode == "9")
//// return SUN_CLOUD;
//// if (weathercode == "13" || weathercode == "14" || weathercode == "15"|| weathercode == "16" || weathercode == "17"|| weathercode == "18")
//// return RAIN;
//// if (weathercode == "11" || weathercode == "12" )
//// return THUNDER;
//}
void drawWeatherSymbol(u8g2_uint_t x, u8g2_uint_t y, uint8_t symbol)
{
switch(symbol)
{
case SUN:
u8g2.setFont(u8g2_font_open_iconic_weather_6x_t);
Serial.println("its sun");
u8g2.drawGlyph(x, y, 69);
break;
case SUN_CLOUD:
u8g2.setFont(u8g2_font_open_iconic_weather_6x_t);
u8g2.drawGlyph(x, y, 65);
break;
case CLOUD:
u8g2.setFont(u8g2_font_open_iconic_weather_6x_t);
u8g2.drawGlyph(x, y, 64);
break;
case RAIN:
u8g2.setFont(u8g2_font_open_iconic_weather_6x_t);
u8g2.drawGlyph(x, y, 67);
break;
case THUNDER:
u8g2.setFont(u8g2_font_open_iconic_embedded_6x_t);
u8g2.drawGlyph(x, y, 67);
break;
}
}
void OLEDDisplay(String cityName, String cityweather, String citytemperature,String code)
{ struct tm timeinfo;
if(!getLocalTime(&timeinfo)){
Serial.println("Failed to obtain time");
return;
}
u8g2.setCursor(43 , 30 ); u8g2.println("天"); //国内城市白天晴
// u8g2.drawXBM( 0, 0, 38, 24, FACE_Neutral);
u8g2.firstPage();
do{
u8g2.setFont(u8g2_font_wqy12_t_gb2312);
u8g2.setFontDirection(0);
u8g2.clearBuffer();
u8g2.setCursor(5,10);
u8g2.print("城市: ");
u8g2.print(cityName);
// u8g2.drawXBMP(5, 64, 64, 32, sunny);
u8g2.setCursor(5, 23);
u8g2.print("天气: ");
u8g2.print(cityweather);
u8g2.setCursor(5, 36);
u8g2.print("温度: ");
u8g2.print(citytemperature);
u8g2.print("'C");
u8g2.setCursor(5, 50);
u8g2.print(&timeinfo, "%F");
u8g2.setCursor(5, 63);
u8g2.print("时间:");
u8g2.print(&timeinfo, "%T");
u8g2.print(" ");
u8g2.print(&timeinfo, "%A");
u8g2.sendBuffer();
myint=mystr.toInt();
Serial.println("myint");
Serial.println(myint);
uint8_t symbol;//=SUN;
if (myint == 0)
symbol= SUN;
if (myint == 9)
symbol= SUN_CLOUD;
if (myint == 13 || myint == 14 || myint == 15|| myint == 16 || myint == 17|| myint == 18)
symbol= RAIN;
if (myint == 11 || myint == 12 )
symbol= THUNDER;
drawWeatherSymbol(71, 48, symbol);
}while(u8g2.nextPage());
// delay(1000);
}
结果展示
所遇问题及解决过程
- esp32的专业开发环境为idf,在使用idf跑helloworld时就不断报错,最终解决方案为采用纯净版python(python版本问题),以及重新添加path。最后还是放弃idf,用了vscode和arduino,idf在每次编译时都要重新导入,非常慢,在用arduino时,只要编译一次,再修改后编译起来会快很多,新版2.0也能够看一些底层的文件,稍微好用一点点。
- 一开始想查看tm结构体里的信息,没找到。tm结构体在time.c里,time.c在esp32的cores的hal库里。
- 网络时间服务器最常用的主机名是 pool.ntp.org,通过网络时间服务器获得的时间是世界协调时间(UTC)/格林尼治时间(GMT),不同地区的时间可以通过时区换算, gmtOffset_sec 参数就是用来修正时区的,比如对于我们东八区(UTC/GMT+08:00)来说该参数就需要填写 8 * 3600 ;如果使用夏令时 daylightOffset_sec 就填写3600,否则就填写0;u8g2.print(&timeinfo, “%F %A%T”); %F代表获取的年月日,%A代表获取的星期几的全称,%T表示获取的时分秒。
- esp32作客户端向心知天气API发送GET请求;然后,从心知天气响应报文体中的JSON数据解析出天气,气温数据,首先需要注册一个心知天气账号,然后申请一个免费版,然后就可以从网页获取天气了.
- 从心知API获取到一串数据后,内含天气状况代码,阅读心知使用手册对应天气,然后根据天气绘制图标。
- API的地址为api.seniverse.com/v3/weather/now.json?key=私钥(填写自己申请到的)&location=yanji(填写地点)&language=en&unit=c。服务器响应了一个JSON对象:
{"results":[{"location":{"id":"WTSQQYHVQ973","name":"南京","country":"CN",
"path":"南京,南京,江苏,中国","timezone":"Asia/Shanghai","timezone_offset":"+08:00"},
"now":{"text":"晴","code":"0","temperature":"8"},"last_update":"2022-02-24T17:50:05+08:00"}]} - 在绘制天气图标时,遇到了很多问题:
- u8g2有两个库,Adafrui图形化方便,但是字库不圆滑,u8g2显示汉字方便,引脚配置也方便。
- 为了中文显示天气时间方便,用了u8g2lib库,导致画天气图标花了很久,一开始用drawXBMP和drawFrame,最后一个参数取字模的指针没搞明白,最后直接用了drawGlyph绘制字集里的符号(强烈推荐),又快又多,就是需要查一下字库,然后把编
号对应图表。 - 调用函数drawweathersymbol时,一开始什么也不显示,后来发现在draw之前要先设置库,库设置错了。本项目中一共使用了u8g2_font_open_iconic_embedded_6x_t和u8g2_font_open_iconic_weather_6x_t两个库,里面图标特别全,比字模好用多了。
- 因为从知心API抓取到的天气代码是字符串的形式,我又想通过switch来赋值symbol然后draw,所以我直接用保存抓取到的天气数据的数组和天气代号进行比较,但是始终报错,说我没用定义类型或者找不到数组,最后分析应该是数组不能在新写的函数里读到,最后一同操作,连转两次,把天气代码转成了int型,再和天气代号比较,虽然能用了,但是代码实在太丑了。
-
使用u8g2lib库时总是需要在loop里用
u8g.firstPage(); do {undefined draw(); } while( u8g.nextPage() );
才能显示正确。原因是u8glib可能使用了缓存机制,有时候一次循环画不完arduino第一次编译会比较慢,后面再修改后编译就会快单个显示图标之后,怎么和文字信息一起显示又是一个问题。
心得体会
- 阅读examples的能力很重要!
- 多从网上检索、筛选资料。
- 多动手实践,有错误慢慢改。
- 除了敲代码,正确表达自己的错误,正确记录解决问题过程的能力也很重要。
最后附上几个好用的网站和帖子:
天气现象代码说明 | 心知天气文档 (seniverse.com)
【新提醒】【原创】OLED屏-U8glib库 增强版 U8G2库。-Arduino中文社区 - Powered by Discuz!
软硬件
元器件
ESP32-S2-MINI-1
2.4GHz WiFi (802.11 b/g/n) 模组, 内置ESP32S2系列芯片,Xtensa® 单核32位LX7微处理器, 内置芯片叠封4MB flash,可叠封2MB PSRAM, 37个GPIO,丰富的外设, 板载PCB天线或外部天线连接器
电路图
附件下载
weather.rar
团队介绍
南京工业大学计算机科学与技术学院
团队成员
co1
评论
0 / 100
查看更多