Funpack12基于WioTerminal的自动联网的天气预报仪
使用wio terminal连接远程api获取数据,显示在屏幕上
标签
嵌入式系统
显示
振青666
更新2021-12-21
贵州大学
1195

一、平台简介

Wio Terminal的运行速度为 120MHz (最高可达200MHz), 4MB 外部闪存和 192KB RAM。

Wio Terminal自身配有一个2.4英寸 LCD屏幕, 板载IMU(LIS3DHTR),麦克风,蜂鸣器,microSD卡槽,光传感器和940nm红外发射器。

除了这些它还有两个用于Grove生态系统的多功能Grove接口和兼容Raspberry pi的40个GPIO引脚,用于支持更多附加组件。

FtRoKE1hPrB2AoOKX_kI_MeSaUKS

无线模块由 Realtek RTL8720DN 提供技术支持,双频 2.4 GHz/5 GHz Wi-Fi (802.11 a/b/g/n),BL/BLE 5.0。

可以方便的使用Arduino,MicroPython,ArduPy等方式进行开发。

二、任务介绍

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

三、代码讲解

1、初始化

对系统进行初始化,同时对外输出初始化状态,完成联网等操作。

const char *ssid = "YourWifi";     //填入SSID
const char *password = "YourPassword"; //填入Wifi密码
void setup()
{
  tft.init();
  tft.setRotation(3); //旋转屏幕0-3
  tft.fillScreen(TFT_BLACK);
  tft.drawString("RTL8720 Firmware Version", (320 - tft.textWidth("RTL8720 Firmware Version")) / 2, 100);
  tft.drawString(rpc_system_version(), (320 - tft.textWidth(rpc_system_version())) / 2, 116);
  tft.drawString("Initializing", (320 - tft.textWidth("Initializing")) / 2, 132);
  Serial.begin(115200);
  for (uint8_t t = 2; t > 0; t--)
  {
    Serial.printf("[SETUP] WAIT %d...\n", t);
    Serial.flush();
    delay(1000);
  }
  //Wifi初始化
  Serial.printf("RTL8720 Firmware Version: %s\n", rpc_system_version());
  tft.drawString("Connecting to Wifi", (320 - tft.textWidth("Connecting to Wifi")) / 2, 116);
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED)
  {
    Serial.print(".");
    // wait 1 second for re-trying
    delay(1000);
  }
  Serial.print("WiFi Connected\n");
  tft.drawString("WiFi Connected", (320 - tft.textWidth("WiFi Connected")) / 2, 116);

  while (!SD.begin(SDCARD_SS_PIN, SDCARD_SPI))//初始化SD卡,读取卡内的字体文件
  {
    Serial.println("SD card error!\n");
    while (1)
      ;
  }

  delay(1000);

  pinMode(WIO_KEY_A, INPUT);
  pinMode(WIO_KEY_B, INPUT);
  //payload = jsonStr;//调试用
  getData();
  Serial.println(payload);
  tft.drawString("Get Data OK", (320 - tft.textWidth("Get Data OK")) / 2, 216);
  drawUI();
  decodeJSON();
}

2、获取API数据

本次设计使用的是由itboy.com提供的免费天气预报API(免费天气API,天气JSON API,不限次数获取十五天的天气预报 —技术博客 (sojson.com)),API的调用非常简单,只要拼接在地址 “http://t.weather.itboy.net/api/weather/city/+city_code” 后面即可。city_code可以在 CityCode 中查询获得。

使用http.GET();读取API数据。

String Citycode = "";      //查询所在区域的城市编码填入
                           //https://github.com/baichengzhou/weather.api/blob/master/src/main/resources/citycode-2019-08-23.json
void getData()
{
  // wait for WiFi connection
  if ((WiFi.status() == WL_CONNECTED))
  {

    HTTPClient http;

    Serial.print("[HTTP] begin...\n");
    Serial.println("http://t.weather.itboy.net/api/weather/city/" + Citycode);
    // configure traged server and url
    http.begin("http://t.weather.itboy.net/api/weather/city/" + Citycode); //HTTP

    Serial.print("[HTTP] GET...\n");
    // start connection and send HTTP header
    int httpCode = http.GET();

    // httpCode will be negative on error
    if (httpCode > 0)
    {
      // HTTP header has been send and Server response header has been handled
      Serial.printf("[HTTP] GET... code: %d\n", httpCode);

      // file found at server
      if (httpCode == HTTP_CODE_OK)
      {
        payload = http.getString();
      }
    }
    else
    {
      Serial.printf("[HTTP] GET... failed, error: %s\n", http.errorToString(httpCode).c_str());
    }

    http.end();
  }
  else
  {
    Serial.println("Connect Wifi First");
  }
}

3、解析JSON并显示

使用ArduinoJson Assistant自动生成解析json的代码,然后在代码中加入其他内容实现输出。

void decodeJSON()
{

  forecastCon = 0;
  //JSON解析
  DynamicJsonDocument doc(6144);

  DeserializationError error = deserializeJson(doc, payload);

  if (error)
  {
    Serial.print("deserializeJson() failed: ");
    Serial.println(error.c_str());
    return;
  }

  const char *time = doc["time"];
  Serial.println(time);
  if (modeUI == 0) //当天天气
  {
    JsonObject cityInfo = doc["cityInfo"];
    const char *cityInfo_city = cityInfo["city"];
    const char *cityInfo_parent = cityInfo["parent"];
    const char *cityInfo_updateTime = cityInfo["updateTime"];
    tft.loadFont("simhei24");
    tft.setTextColor(TFT_WHITE, TFT_BLACK);
    tft.drawString(cityInfo_parent, 38, 216);
    tft.drawString(cityInfo_city, 138, 216);
    tft.drawString(cityInfo_updateTime, 238, 216);
    tft.unloadFont();

    JsonObject data = doc["data"];
    const char *data_shidu = data["shidu"];
    int data_pm25 = data["pm25"];
    int data_pm10 = data["pm10"];
    const char *data_quality = data["quality"];
    const char *data_wendu = data["wendu"];
    const char *data_ganmao = data["ganmao"];

    for (JsonObject data_forecast_item : data["forecast"].as<JsonArray>())
    {
      const char *data_forecast_item_date = data_forecast_item["date"];
      const char *data_forecast_item_high = data_forecast_item["high"];
      const char *data_forecast_item_low = data_forecast_item["low"];
      const char *data_forecast_item_ymd = data_forecast_item["ymd"];
      const char *data_forecast_item_sunrise = data_forecast_item["sunrise"];
      const char *data_forecast_item_sunset = data_forecast_item["sunset"];
      int data_forecast_item_aqi = data_forecast_item["aqi"];
      const char *data_forecast_item_fx = data_forecast_item["fx"];
      const char *data_forecast_item_fl = data_forecast_item["fl"];
      const char *data_forecast_item_type = data_forecast_item["type"];

      int length;
      char *data_forecast_item_fx_char;
      length = strlen(data_forecast_item_fx);
      data_forecast_item_fx_char = new char[length + 1];
      strcpy(data_forecast_item_fx_char, data_forecast_item_fx);

      char *data_forecast_item_fl_char;
      length = strlen(data_forecast_item_fl);
      data_forecast_item_fl_char = new char[length + 1];
      strcpy(data_forecast_item_fl_char, data_forecast_item_fl);

      tft.loadFont("qweather-icons50");
      if (strncmp(data_forecast_item_type, "晴", sizeof(data_forecast_item_type)) == 0)
        tft.drawString("\uf101", 35, 46);
      else if (strncmp(data_forecast_item_type, "多云", sizeof(data_forecast_item_type)) == 0)
        tft.drawString("\uf102", 35, 46);
      else if (strncmp(data_forecast_item_type, "阴", sizeof(data_forecast_item_type)) == 0)
        tft.drawString("\uf105", 35, 46);
      else if (strncmp(data_forecast_item_type, "阵雨", sizeof(data_forecast_item_type)) == 0)
        tft.drawString("\uf10a", 35, 46);
      else if (strncmp(data_forecast_item_type, "雷阵雨", sizeof(data_forecast_item_type)) == 0)
        tft.drawString("\uf10c", 35, 46);
      else if (strncmp(data_forecast_item_type, "雨夹雪", sizeof(data_forecast_item_type)) == 0)
        tft.drawString("\uf124", 35, 46);
      else if (strncmp(data_forecast_item_type, "小雨", sizeof(data_forecast_item_type)) == 0)
        tft.drawString("\uf10f", 35, 46);
      else if (strncmp(data_forecast_item_type, "中雨", sizeof(data_forecast_item_type)) == 0)
        tft.drawString("\uf110", 35, 46);
      else if (strncmp(data_forecast_item_type, "大雨", sizeof(data_forecast_item_type)) == 0)
        tft.drawString("\uf111", 35, 46);
      else if (strncmp(data_forecast_item_type, "暴雨", sizeof(data_forecast_item_type)) == 0)
        tft.drawString("\uf112", 35, 46);
      else if (strncmp(data_forecast_item_type, "大暴雨", sizeof(data_forecast_item_type)) == 0)
        tft.drawString("\uf115", 35, 46);
      else if (strncmp(data_forecast_item_type, "特大暴雨", sizeof(data_forecast_item_type)) == 0)
        tft.drawString("\uf116", 35, 46);
      else if (strncmp(data_forecast_item_type, "阵雪", sizeof(data_forecast_item_type)) == 0)
        tft.drawString("\uf12d", 35, 46);
      else if (strncmp(data_forecast_item_type, "小雪", sizeof(data_forecast_item_type)) == 0)
        tft.drawString("\uf120", 35, 46);
      else if (strncmp(data_forecast_item_type, "中雪", sizeof(data_forecast_item_type)) == 0)
        tft.drawString("\uf121", 35, 46);
      else if (strncmp(data_forecast_item_type, "大雪", sizeof(data_forecast_item_type)) == 0)
        tft.drawString("\uf122", 35, 46);
      else if (strncmp(data_forecast_item_type, "暴雪", sizeof(data_forecast_item_type)) == 0)
        tft.drawString("\uf123", 35, 46);
      else if (strncmp(data_forecast_item_type, "小雨-中雨", sizeof(data_forecast_item_type)) == 0)
        tft.drawString("\uf118", 35, 46);
      else if (strncmp(data_forecast_item_type, "中雨-大雨", sizeof(data_forecast_item_type)) == 0)
        tft.drawString("\uf119", 35, 46);
      else if (strncmp(data_forecast_item_type, "大雨-暴雨", sizeof(data_forecast_item_type)) == 0)
        tft.drawString("\uf11a", 35, 46);
      else if (strncmp(data_forecast_item_type, "暴雨-大暴雨", sizeof(data_forecast_item_type)) == 0)
        tft.drawString("\uf11b", 35, 46);
      else if (strncmp(data_forecast_item_type, "大暴雨-特大暴雨", sizeof(data_forecast_item_type)) == 0)
        tft.drawString("\uf11c", 35, 46);
      else if (strncmp(data_forecast_item_type, "小雪-中雪", sizeof(data_forecast_item_type)) == 0)
        tft.drawString("\uf128", 35, 46);
      else if (strncmp(data_forecast_item_type, "中雪-大雪", sizeof(data_forecast_item_type)) == 0)
        tft.drawString("\uf129", 35, 46);
      else if (strncmp(data_forecast_item_type, "大雪-暴雪", sizeof(data_forecast_item_type)) == 0)
        tft.drawString("\uf12a", 35, 46);
      tft.unloadFont();
      tft.loadFont("simhei24");
      tft.drawString(data_forecast_item_type, (120 - tft.textWidth(data_forecast_item_type)) / 2, 106);
      tft.drawString(data_forecast_item_ymd, 0, 162);
      tft.drawString(strcat(data_forecast_item_fx_char, data_forecast_item_fl_char), 120, 190);

      tft.drawString(data_forecast_item_high, 120, 50);
      tft.drawString(data_forecast_item_low, 220, 50);
      //tft.unloadFont();

      break;
    }
    //tft.loadFont("simhei24");
    //tft.setTextColor(TFT_WHITE, TFT_BLACK);
    tft.drawString("温度", 120, 22);
    tft.drawString(data_wendu, 220, 22);
    tft.drawString("PM2.5", 120, 78);
    tft.drawNumber(data_pm25, 220, 78);
    tft.drawString("PM10", 120, 106);
    tft.drawNumber(data_pm10, 220, 106);
    tft.drawString("空气质量", 120, 134);
    tft.drawString(data_quality, 220, 134);
    tft.drawString("湿度", 120, 162);
    tft.drawString(data_shidu, 220, 162);
    tft.unloadFont();
  }
  else if (modeUI == 1) //天气预报
  {
    forecastCon = 0;
    JsonObject data = doc["data"];
    for (JsonObject data_forecast_item : data["forecast"].as<JsonArray>())
    {
      if ((forecastCon++) == 0)
      {
        continue;
      }
      const char *data_forecast_item_date = data_forecast_item["date"];
      const char *data_forecast_item_high = data_forecast_item["high"];
      const char *data_forecast_item_low = data_forecast_item["low"];
      const char *data_forecast_item_ymd = data_forecast_item["ymd"];
      const char *data_forecast_item_sunrise = data_forecast_item["sunrise"];
      const char *data_forecast_item_sunset = data_forecast_item["sunset"];
      int data_forecast_item_aqi = data_forecast_item["aqi"];
      const char *data_forecast_item_fx = data_forecast_item["fx"];
      const char *data_forecast_item_fl = data_forecast_item["fl"];
      const char *data_forecast_item_type = data_forecast_item["type"];

      if (forecastCon == 2 || forecastCon == 4)
      {
        tft.setTextColor(TFT_WHITE, TFT_GREY);
      }
      else
      {
        tft.setTextColor(TFT_WHITE, TFT_BLACK);
      }

      tft.loadFont("qweather-icons50");
      if (strncmp(data_forecast_item_type, "晴", sizeof(data_forecast_item_type)) == 0)
        tft.drawString("\uf101", 5, 4 + (forecastCon - 2) * 80);
      else if (strncmp(data_forecast_item_type, "多云", sizeof(data_forecast_item_type)) == 0)
        tft.drawString("\uf102", 5, 4 + (forecastCon - 2) * 80);
      else if (strncmp(data_forecast_item_type, "阴", sizeof(data_forecast_item_type)) == 0)
        tft.drawString("\uf105", 5, 4 + (forecastCon - 2) * 80);
      else if (strncmp(data_forecast_item_type, "阵雨", sizeof(data_forecast_item_type)) == 0)
        tft.drawString("\uf10a", 5, 4 + (forecastCon - 2) * 80);
      else if (strncmp(data_forecast_item_type, "雷阵雨", sizeof(data_forecast_item_type)) == 0)
        tft.drawString("\uf10c", 5, 4 + (forecastCon - 2) * 80);
      else if (strncmp(data_forecast_item_type, "雨夹雪", sizeof(data_forecast_item_type)) == 0)
        tft.drawString("\uf124", 5, 4 + (forecastCon - 2) * 80);
      else if (strncmp(data_forecast_item_type, "小雨", sizeof(data_forecast_item_type)) == 0)
        tft.drawString("\uf10f", 5, 4 + (forecastCon - 2) * 80);
      else if (strncmp(data_forecast_item_type, "中雨", sizeof(data_forecast_item_type)) == 0)
        tft.drawString("\uf110", 5, 4 + (forecastCon - 2) * 80);
      else if (strncmp(data_forecast_item_type, "大雨", sizeof(data_forecast_item_type)) == 0)
        tft.drawString("\uf111", 5, 4 + (forecastCon - 2) * 80);
      else if (strncmp(data_forecast_item_type, "暴雨", sizeof(data_forecast_item_type)) == 0)
        tft.drawString("\uf112", 5, 4 + (forecastCon - 2) * 80);
      else if (strncmp(data_forecast_item_type, "大暴雨", sizeof(data_forecast_item_type)) == 0)
        tft.drawString("\uf115", 5, 4 + (forecastCon - 2) * 80);
      else if (strncmp(data_forecast_item_type, "特大暴雨", sizeof(data_forecast_item_type)) == 0)
        tft.drawString("\uf116", 5, 4 + (forecastCon - 2) * 80);
      else if (strncmp(data_forecast_item_type, "阵雪", sizeof(data_forecast_item_type)) == 0)
        tft.drawString("\uf12d", 5, 4 + (forecastCon - 2) * 80);
      else if (strncmp(data_forecast_item_type, "小雪", sizeof(data_forecast_item_type)) == 0)
        tft.drawString("\uf120", 5, 4 + (forecastCon - 2) * 80);
      else if (strncmp(data_forecast_item_type, "中雪", sizeof(data_forecast_item_type)) == 0)
        tft.drawString("\uf121", 5, 4 + (forecastCon - 2) * 80);
      else if (strncmp(data_forecast_item_type, "大雪", sizeof(data_forecast_item_type)) == 0)
        tft.drawString("\uf122", 5, 4 + (forecastCon - 2) * 80);
      else if (strncmp(data_forecast_item_type, "暴雪", sizeof(data_forecast_item_type)) == 0)
        tft.drawString("\uf123", 5, 4 + (forecastCon - 2) * 80);
      else if (strncmp(data_forecast_item_type, "小雨-中雨", sizeof(data_forecast_item_type)) == 0)
        tft.drawString("\uf118", 5, 4 + (forecastCon - 2) * 80);
      else if (strncmp(data_forecast_item_type, "中雨-大雨", sizeof(data_forecast_item_type)) == 0)
        tft.drawString("\uf119", 5, 4 + (forecastCon - 2) * 80);
      else if (strncmp(data_forecast_item_type, "大雨-暴雨", sizeof(data_forecast_item_type)) == 0)
        tft.drawString("\uf11a", 5, 4 + (forecastCon - 2) * 80);
      else if (strncmp(data_forecast_item_type, "暴雨-大暴雨", sizeof(data_forecast_item_type)) == 0)
        tft.drawString("\uf11b", 5, 4 + (forecastCon - 2) * 80);
      else if (strncmp(data_forecast_item_type, "大暴雨-特大暴雨", sizeof(data_forecast_item_type)) == 0)
        tft.drawString("\uf11c", 5, 4 + (forecastCon - 2) * 80);
      else if (strncmp(data_forecast_item_type, "小雪-中雪", sizeof(data_forecast_item_type)) == 0)
        tft.drawString("\uf128", 5, 4 + (forecastCon - 2) * 80);
      else if (strncmp(data_forecast_item_type, "中雪-大雪", sizeof(data_forecast_item_type)) == 0)
        tft.drawString("\uf129", 5, 4 + (forecastCon - 2) * 80);
      else if (strncmp(data_forecast_item_type, "大雪-暴雪", sizeof(data_forecast_item_type)) == 0)
        tft.drawString("\uf12a", 5, 4 + (forecastCon - 2) * 80);
      tft.unloadFont();

      int length;
      char *data_forecast_item_fx_char;
      length = strlen(data_forecast_item_fx);
      data_forecast_item_fx_char = new char[length + 1];
      strcpy(data_forecast_item_fx_char, data_forecast_item_fx);

      char *data_forecast_item_fl_char;
      length = strlen(data_forecast_item_fl);
      data_forecast_item_fl_char = new char[length + 1];
      strcpy(data_forecast_item_fl_char, data_forecast_item_fl);

      tft.loadFont("simhei24");
      tft.drawString(data_forecast_item_ymd, 60, 4 + (forecastCon - 2) * 80);
      tft.drawString(data_forecast_item_high, 200, 4 + (forecastCon - 2) * 80);
      tft.drawString(data_forecast_item_sunrise, 60, 30 + (forecastCon - 2) * 80);
      tft.drawString(data_forecast_item_sunset, 60 + tft.textWidth(data_forecast_item_ymd) - tft.textWidth(data_forecast_item_sunset), 30 + (forecastCon - 2) * 80);
      tft.drawString(data_forecast_item_low, 200, 30 + (forecastCon - 2) * 80);
      tft.drawString(data_forecast_item_type, (60 - tft.textWidth(data_forecast_item_type)) / 2, 56 + (forecastCon - 2) * 80);
      tft.drawString(strcat(data_forecast_item_fx_char, data_forecast_item_fl_char), 60, 56 + (forecastCon - 2) * 80);
      tft.drawString("AQI:", 200, 56 + (forecastCon - 2) * 80);
      tft.drawNumber(data_forecast_item_aqi, 260, 56 + (forecastCon - 2) * 80);
      tft.unloadFont();

      if (forecastCon >= 4)
      {
        break;
      }
    }
  }

  ////////////////////////////////////////////////////////////////////////
}

四、实物展示 

Fja5un_NDs5qbmk002B9-gduimnc

初始化页面,显示当前的WiFi模块固件版本号,和WiFi连接情况。

Fqaa-GB-O7rJ_IaOO7rPRe6ShUUT

初始化完成后,自动获取一次天气预报,并显示在屏幕上,包括温度湿度,PM2.5,PM10等信息,按一下右边按键,即可立即更新天气,由于天气API是每八小时更新一次,所以无需频繁更新。

FhEKaK7RKMKnGlaNSKsC3DzcOF9t

按一下中间按键,即可在当天详细天气显示和未来三天天气预报之间进行切换。显示内容包括天气类型,日出日落时间,AQI指数。

五、源文件使用方法

将vlw文件夹内的文件复制到一张小于16g的SD卡中,SD卡需要使用FAT格式,将ino文件中内容进行对应修改。

const char *ssid = "";     //填入SSID
const char *password = ""; //填入Wifi密码
String Citycode = "";      //查询所在区域的城市编码填入
                           //https://github.com/baichengzhou/weather.api/blob/master/src/main/resources/citycode-2019-08-23.json

使用ArduinoIDE对修改后的源文件进行编译,烧录即可使用。

五、不足与展望

没有使用GUI绘制一个漂亮的界面,界面相对简单,未能将字体文件整合写入Flash中,需要使用SD卡,由于对ArduinoJSON的理解不够,JSON处理的方式效率较低。

后续可以基于LVGL实现一个UI界面,优化操作,将字体整合写入Flash,优化JSON解析效率

 

附件下载
Weather.ino
源码
simhei24.vlw
文字字体
qweather-icons50.vlw
天气图标
团队介绍
团队成员
振青666
评论
0 / 100
查看更多
目录
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2024 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号