基于WioTerminal的联网天气预报仪

概述

本项目基于Wio Terminal开发平台,通过板载WIFI模块联网,使用和风天气提供的免费API获取实时天气、空气质量及未来三天的变化情况,然后通过LVGL GUI在屏幕上进行显示。本文将对该项目的实现思路及关键技术细节进行描述。

项目来源

Funpack 第12期,任务二:

制作一个自动联网的天气预报仪,在设计界面显示温湿度、天气情况、空气质量以及未来三天内的天气变化。

本项目完成效果如下:

tab12

Wio Terminal平台

高度集成的设计

MCU, LCD, WIFI, BT, IMU, 麦克风, 蜂鸣器, microSD Card, 光传感器, 五向开关, 光传感器和红外发射器 (IR 940nm), 加密验证

由Microchip ATSAMD51P19提供支持
  • ARM Cortex-M4F运行速度 120MHz (最高可达200MHz)
  • 4 MB 外置闪存, 192 KB RAM
强大的无线连接
  • 双频 2.4Ghz / 5Ghz Wi-Fi (802.11 a/b/g/n)
  • BLE / BLE 5.0
软件支持
  • Arduino
  • MicroPython

Wio Terminal支持Arduino开发,虽然Arduino我已经听说10几年了,但实际上本项目才真正使用这个平台。我认为Arduino

  • 最大的优点:我无需知道它具体是怎么实现的,但写几行应用代码它就可以跑起来了。

  • 最大的缺点:虽然我只写了几行代码它就跑起来了,但我却完全不清楚它是怎么实现的。

基于Arduino这种特性,很多学生/爱好者都喜欢拿它来开发和学习。我认为是利弊并存的,可能它可以很容易让你实现一个应用而不用过多关心硬件底层,但却容易让你忽略了底层的基础知识学习。

方案设计

Wio Terminal板载资源丰富,无需而外硬件资源,便可以完成联网、显示功能了,因而该项目关键在于找到合适的天气API。选择和风天气API的原因是:

  1. 免费且门槛低,普通免费用户既可有1000次/天的请求额度。
  2. 满足题目需求,普通免费用户既可获取实时天气温湿度、空气质量、未来3天的天气预报。
  3. 有Arduino平台参考软件库,开发简单。
  4. 提供了天气图标字体库,可以很方便的进行天气图标显示。
开发平台
  • 硬件平台:Wio Terminal
  • 软件环境:VS Code / Arduino
联网方案
  • 硬件:板载WIFI模块
  • 软件库:rpcWifi / WiFiClientSecure
天气API
显示方案
  • 硬件:板载2.4寸LCD
  • GUI库:LVGL

环境搭建

Arduino虽然经过了10几年的发展,但是其开发环境仍然是一言难尽,没有跳转到函数实现的功能,没有代码提示、没有智能补全……。好在VS Code有Arduino插件,可以用它来开发、编译、下载。但我实测发现VS Code的Arduino编译下载做得并不好,所以我还是决定VS Code只用于编码,编译、下载、串口终端仍旧使用Arduino官方IDE,互补不足。

  1. VS Code安装Arduino插件

    arduino_plugin

  2. 添加库文件路径

    includepath

    虽然安装Arduino插件之后,用VS Code打开ion工程时,它会自动添加一些Arduino基本的库路径,但可能不全,或者有些库可能是我们后来自行安装的它并不知道。为了让VS Code能够索引到,则需要手动把库路径添加到c_cpp_properties.json文件中。

方案实现

WIFI联网
  1. 更新最新WIFI固件

    按照Seed官方WIKI步骤操作升级最新WIFI固件

  2. 联网代码

    1
    2
    3
    4
    5
    WiFi.mode(WIFI_STA);
    WiFi.disconnect();

    Serial.println("Connecting to wifi...");
    WiFi.begin(ssid,password);
LVGL初始化
  1. TFT初始化

    1
    2
    tft.begin(); /* TFT init */
    tft.setRotation(3); /* Landscape orientation */
  2. LVGL初始化

    • 实现LVGL缓存刷新到TFT函数
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    /* Display flushing */
    void my_disp_flush(lv_disp_drv_t *disp, const lv_area_t *area, lv_color_t *color_p)
    {

    uint16_t c;

    tft.startWrite(); /* Start new TFT transaction */
    tft.setAddrWindow(area->x1, area->y1, (area->x2 - area->x1 + 1), (area->y2 - area->y1 + 1)); /* set the working window */
    for (int y = area->y1; y <= area->y2; y++) {
    for (int x = area->x1; x <= area->x2; x++) {
    c = color_p->full;
    tft.writeColor(c, 1);
    color_p++;
    }
    }
    tft.endWrite(); /* terminate TFT transaction */
    lv_disp_flush_ready(disp); /* tell lvgl that flushing is done */
    }
    • 初始化LVGL相关参数
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    lv_init();

    lv_disp_buf_init(&disp_buf, buf, NULL, LV_HOR_RES_MAX * 10);

    /*Initialize the display*/
    lv_disp_drv_t disp_drv;
    lv_disp_drv_init(&disp_drv);
    disp_drv.hor_res = 320;
    disp_drv.ver_res = 240;
    disp_drv.flush_cb = my_disp_flush;
    disp_drv.buffer = &disp_buf;
    lv_disp_drv_register(&disp_drv);
    • 启动一个5ms定时器中断,用于执行LVGL定时任务。
    1
    2
    TimerTc3.initialize(5000);//5ms
    TimerTc3.attachInterrupt(lv_timer_int_handler);
    • 定时中断中执行如下LVGL任务
    1
    2
    3
    4
    5
    void lv_timer_int_handler(void)
    {

    lv_tick_handler();
    lv_task_handler(); /* let the GUI do its work */
    }

    这样做的好处是,你可以只管更新LVGL的组件、显示内容,在主循环中可以正常使用delay函数,而无需担心影响LVGL的显示刷新。

和风API获取天气

官方提供的参考Arduino库本身提供了获取和解析实时天气、空气质量、未来三天预报功能,但是适用于ESP8266平台的,要想在Wio Terminal上使用,需要做一点简单的修改。

以获取实时天气的WeatherNow.cpp文件为例(其它类似),改动点如下:

weatherlibchanged

  1. 修改WiFi Client的定义

    client必须定义为全局变量,若在get()函数中定义为局部变量,则调用get()函数时会卡死。由于Arduino无法单步调试,我完全不知道发生了什么问题,推测应该是堆栈溢出这类吧。

  2. 初始化配置增加根证书参数

    我看ESP8266的代码,可以将Client设置为非安全模式,不需要服务器根证书。但在Wio Terminal上没找到这个函数,所以还是老老实实在https.get()前进行根证书配置。

  3. 和风服务器根证书获取

    使用Google浏览器,输入API获取一次天气,就可以参考这篇文章导出根证书字符串了。

    rootca

    值得提醒的是,查看和导出证书一定要选到那级,不要选到下面qweather.com这级。

天气图标显示

和风API获取天气时,有个参数是ICON编号,每个编号对应一个图标。和风已在GitHub上提供了所有天气图标的字体库。要想在LVGL上使用该图标库,还需要将字体库转换成C代码。

LVGL官方转换网址为:https://lvgl.io/tools/fontconverter。上传图标字体库ttf文件,填写相关参数,既可得到生成的字体C文件。

fontconv

和风API中得到的ICON是101/102这样的编号,要想根据编号对应上相应的图标,还需要做一些转换。

iconutf

字体库预览,可使用网站:http://blog.luckly-mjw.cn/tool-show/iconfont-preview/index.html。

  1. 图标名称对应UTF-8编码

    1
    2
    3
    4
    5
    #define QW_SYMBOL_100           "\xef\x84\x81"/*U+F101*/
    #define QW_SYMBOL_101 "\xef\x84\x82"/*U+F102*/
    #define QW_SYMBOL_102 "\xef\x84\x83"/*U+F103*/
    #define QW_SYMBOL_103 "\xef\x84\x84"/*U+F104*/
    ...
  2. 图标编号对应图标名称

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    inline const char* qw_get_icon_utf8_code(int iconcode)
    {

    switch(iconcode)
    {
    case 100: return(QW_SYMBOL_100); break;
    case 101: return(QW_SYMBOL_101); break;
    ...
    }
    return (QW_SYMBOL_DUMMY);
    }

这样就可以很容易用LVGL显示天气图标了。

主循环

界面的整体UI,我使用的是LVGL建立了1个TableView,添加了3个标签。其中标签1用于显示当前实时天气、温湿度、空气质量,标签2用于显示未来三天天气变化,标签3则用于显示了项目源代码获取方式。

所以主循环事情则比较简单,主要处理:

  1. 处理按键,切换TableView各个标签的显示
  2. 每隔5分钟调用API更新天气信息,并更新到UI中
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
void loop() {
// put your main code here, to run repeatedly
key_press_handler();
delay(5);
update_timeout_cnt += 5;
if(update_timeout_cnt >= UPDATE_TIMEOUT)
{
update_timeout_cnt = 0;
weatherNow.get();
airquality.get();
weatherForecast.get();
airquality_get = airquality.getAqi();
weathernow_info_update_to_ui(weatherNow.getLastUpdate(),String(weatherNow.getTemp()),...);
weatherforecast_info_update_to_ui(weatherForecast.getFxDate(0),weatherForecast.getIconDay(0),...);
}
}

小结

本文阐述了基于Wio Terminal联网天气预报仪的设计思路及关键实现细节,如仍有描述不清楚的地方,欢迎下载源码共同探讨。项目源码,请扫码关注下方公众号,并回复“SmartWeather”获取。

ETRD