一. 任务目的
根据任务二的要求,使用Wio Termina实现一个自动联网的天气预报仪,显示温湿度、天气情况、空气质量以及未来三天内的天气变化。
在该任务中,我们使用了arduino平台来实现各种所需功能。
二. 硬件介绍
Wio Terminal 是基于SAMD51的微控制器,具有 Realtek RTL8720DN 支持的无线连接,与Arduino和MicroPython兼容。它的运行速度为 120MHz (最高可达200MHz), 4MB 外部闪存和 192KB RAM。它同时支持蓝牙和Wi-Fi,为物联网项目提供了骨架。Wio Terminal自身配有 a 2.4” LCD屏幕, 板载IMU(LIS3DHTR),麦克风,蜂鸣器,microSD卡槽,光传感器和红外发射器(IR 940nm)。 最重要的是它还有两个用于Grove生态系统 的多功能Grove端口和40个Raspberry pi兼容的GPIO引脚,用于支持更多附加组件。
三. 项目实现
根据要求,该项目主要由网络获取天气信息和界面显示两部分组成。具体实现方案是,我们利用心知天气获取天气数据,然后利用LVGL来显示结果。下面分别说明。
a) 天气数据获取
考虑到功能需求、使用方便和获取成本等多方面因素,我们选用了心知天气。
1. 注册账号。
心知天气提供了很多类型的账号。有免费版本和试用版,也有收费的专业版。由于我们需要获取温湿度、天气情况、空气质量等数据,免费版本只能提供天气现象文字、代码和气温3项天气实况数据,因此我们申请了试用版服务,可以在14天内免费访问包括天气实况、空气质量、生活指数等许多有价值的数据。
2. 访问api
我们分别调用了天气实况和空气质量两个api接口。
https://api.seniverse.com/v3/weather/now.json?key=your_api_key&location=beijing&language=zh-Hans&unit=c
{
"results": [
{
"location": {
"id": "C23NB62W20TF",
"name": "西雅图",
"country": "US",
//...
},
"now": {
"text": "多云", //天气现象文字
"code": "4", //天气现象代码
"temperature": "14", //温度,单位为c摄氏度或f华氏度
"feels_like": "14", //体感温度,单位为c摄氏度或f华氏度
"pressure": "1018", //气压,单位为mb百帕或in英寸
"humidity": "76", //相对湿度,0~100,单位为百分比
// ...
},
"last_update": "2015-09-25T22:45:00-07:00" //数据更新时间(该城市的本地时间)
}
]
}
https://api.seniverse.com/v3/air/now.json?key=your_api_key&location=beijing&language=zh-Hans&scope=city
{
"results": [{
"location": {
"id": "WX4FBXXFKE4F",
"name": "北京",
"country": "CN",
"path": "北京,北京,中国",
"timezone": "Asia/Shanghai",
"timezone_offset": "+08:00"
},
"air": {
"city": { //城市综合空气质量数据
"aqi": "239", //空气质量指数(AQI)是描述空气质量状况的定量指数
"pm25": "189", //PM2.5颗粒物(粒径小于等于2.5μm)1小时平均值。单位:μg/m³
"pm10": "216", //PM10颗粒物(粒径小于等于10μm)1小时平均值。单位:μg/m³
"so2": "14", //二氧化硫1小时平均值。单位:μg/m³
"no2": "90", //二氧化氮1小时平均值。单位:μg/m³
"co": "1.350", //一氧化碳1小时平均值。单位:mg/m³
"o3": "44", //臭氧1小时平均值。单位:μg/m³
"primary_pollutant": "PM2.5", //首要污染物
"quality": "优", //空气质量类别,有“优、良、轻度污染、中度污染、重度污染、严重污染”6类
"last_update": "2015-09-23T13:00:00+08:00" //数据发布时间
},
"stations": [{ //该城市所有监测站数组
"aqi": "248",
"pm25": "198",
"pm10": "237",
"so2": "15",
"no2": "112",
"co": "1.5",
"o3": "23",
"primary_pollutant": "PM2.5", //首要污染物
"station": "万寿西宫", //监测站名称
"latitude": "39.865927", //监测站纬度
"longitude": "116.359805", //监测站经度
"last_update": "2015-09-23T13:00:00+08:00" //数据发布时间
}, {
... //更多监测站
}]
},
"last_update": "2015-09-23T22:45:48+08:00"
}]
}
3. 网络访问
我们使用了rpcWiFi来实现网络访问
#include "rpcWiFi.h"
// Set WiFi to station mode and disconnect from an AP if it was previously connected
WiFi.mode(WIFI_STA);
WiFi.disconnect();
Serial.println("Connecting to WiFi..");
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.println("Connecting to WiFi..");
WiFi.begin(ssid, password);
}
之前查询得知心知天气的ip地址是116.62.81.138,那么连接服务器
const uint16_t port = 80; // Default port
const char* host = "116.62.81.138"; // IP Address of api.seniverse.com
// Use WiFiClient class to create TCP connections
WiFiClient client;
if (!client.connect(host, port)) {
Serial.println("Connection failed.");
Serial.println("Waiting 5 seconds before retrying...");
delay(5000);
return;
} else {
Serial.println("Connection succeed. ");
}
成功之后就可以通过访问api接口来获取天气数据了。简单例子如下:
// This will send a request of weather to the server
client.println("GET /v3/weather/daily.json?key=your-api-key&location=shenzhen&language=zh-Hans&unit=c&start=0&days=5");
client.println();
String line_daily = client.readString(); // Read from the server response
client.println("GET /v3/air/now.json?key=your-api-key&location=shenzhen&language=zh-Hans&scope=city");
client.println();
String line_air = client.readString(); // Read from the server response
值得注意的是,在使用WiFi访问网络之前,需要更新Wio Terminal的固件
4. 解析数据
由于我们在api接口中请求了json数据结构,我们使用ArduinoJson来解析返回数据。相关代码片段如下。
#include <ArduinoJson.h>
DynamicJsonDocument doc_daily(4096);
deserializeJson(doc_daily, line_daily);
JsonObject obj_daily = doc_daily.as<JsonObject>();
DynamicJsonDocument doc_air(4096);
deserializeJson(doc_air, line_air);
JsonObject obj_air = doc_air.as<JsonObject>();
这样子我们就得到了所需要的天气和空气质量的具体数值了。
b) 图形界面实现
LVGL的作者是来自匈牙利的Gabor Kiss-Vamosikisvegabor,LVGL用C语言编写,以实现最大的兼容性(与C ++兼容),模拟器可在没有嵌入式硬件的PC上启动嵌入式GUI设计,同时LVGL作为一个图形库,它自带着接近三十多种小工具可以供开发者使用。这些强大的构建块按钮搭配上带有非常丝滑的动画以及可以做到平滑滚动的高级图形,同时兼具着不高的配置要求以及开源属性,显著的优势使得LVGL蔚然成风,成为广大开发者在选择GUI时的第一选择。
一块能驱动显示屏且Flash大于64KB,RAM大于20KB的单片机都可以支持运行LVGL。这样也就说明只需要是我们经常用于开发的单片机几乎都可以支持(16bit及以上)LVGL,LVGL能够同时被这么多平台支持的主要原因是LVGL对硬件的要求并不算太高。
Wio Terminal的硬件规格远远超出上述最低要求,因此LVGL表现得相当好。同时由于我们的天气预报小应用并没有太多的可交互动作,因此我们基本上使用了label和table来显示信息。
#include <lvgl.h>
#include <TFT_eSPI.h>
#define LVGL_TICK_PERIOD 5
TFT_eSPI tft = TFT_eSPI(); /* TFT instance */
static lv_disp_buf_t disp_buf;
static lv_color_t buf[LV_HOR_RES_MAX * 10];
在标签上显示字符
// display settings
lv_obj_clean(lv_scr_act());
lv_obj_t* page = lv_page_create(lv_scr_act(), NULL);
lv_obj_set_size(page, LV_HOR_RES, LV_VER_RES);
// LOGO: Wio Weather
lv_obj_t * logo = lv_btn_create(page, NULL);
lv_btn_set_fit(logo, LV_FIT_TIGHT);
lv_obj_align(logo, NULL, LV_ALIGN_IN_TOP_RIGHT, 0, 0);
lv_obj_t * label = lv_label_create(logo, NULL);
lv_label_set_text(label, "Wio\nWeather");
利用表格显示数据
lv_obj_t * table = lv_table_create(parent, NULL);
lv_obj_clean_style_list(table, LV_TABLE_PART_BG);
lv_obj_set_click(table, false);
lv_table_set_col_cnt(table, 2);
lv_table_set_row_cnt(table, 4);
lv_coord_t w = lv_page_get_width_fit(parent) - 10;
lv_table_set_col_width(table, 0, 3 * w / 10);
lv_table_set_col_width(table, 1, w / 4);
lv_obj_set_pos(table, 0, 0);
lv_obj_set_size(table, 60, 40);
// get today's weather info
// ...
lv_table_set_cell_value(table, 0, 1, get_weather_type(code));
lv_table_set_cell_value(table, 1, 1, str_temp);
lv_table_set_cell_value(table, 2, 1, str_hum);
lv_table_set_cell_value(table, 3, 1, str_aqi);
这样子效果就出来了
完成,收工。
四. 总结感想
Wio Terminal非常好玩,功能全面,颜值也高。而且容易上手,跟arduino结合起来,很适合做个小东西。
五. 意见建议
这个活动很有趣,非常期待下一次。