【NodeMCU实时天气时钟温湿度项目 8】解析天气信息JSON数据并显示在 TFT 屏幕上(和风天气版)

        今天是第8专题,主要内容是:导入ArduinoUZlib功能库,借助该库把从【和风天气】官网返回的经过Gzip压缩的JSON数据,进行解压缩和解析,解析后的数据包括天气实况信息和天气预报信息,并将天气信息显示在 TFT 屏幕上。
        JSON天气信息解压缩功能实现,请参照第 7 专题的内容。本文主要对解析【和风天气】JSON的数据结构和主要函数作个简要介绍。
        最后,我们给出了项目实现的全部源代码和运行结果展示。

一、请求、解压缩和解析JSON信息的数据结构和功能函数

        关于获取和解析天气信息JSON数据的数据结构和函数,都封装在weather_xinzhi.h文件中,其中主要包括 2 个数据结构 和 4 个功能 函数。具体实现代码如下。
        1、实况天气数据结构:struct weather_now_data{},用于保存解析成功后的实况天气信息。

// 实时天气
struct weather_now_data
{
  int code = -1;               // API状态码,具体含义请参考状态码
  String updateTime = "";      // 当前API的最近更新时间
  String now_obsTime = "";     // 数据观测时间
  String now_temp = "0";       // 温度,默认单位:摄氏度
  int now_feelsLike = 0;       // 体感温度,默认单位:摄氏度
  String now_icon = "";        // 天气状况和图标的代码,图标可通过天气状况和图标下载
  String now_text = "";        // 天气状况的文字描述,包括阴晴雨雪等天气状态的描述
  String now_wind360 = "-1";   // 风向360角度
  String now_windDir = "";     // 风向
  String now_windScale = "-1"; // 风力等级
  int now_windSpeed = -1;      // 风速,公里/小时
  int now_humidity = -1;       // 相对湿度,百分比数值
  int now_precip = -1;         // 当前小时累计降水量,默认单位:毫米
  int now_pressure = -1;       // 大气压强,默认单位:百帕
  int now_vis = -1;            // 能见度,默认单位:公里
} wd;

        2、天气预报数据结构:struct weather_tmr_data{},用于保存获取到的天气预报信息。

struct weather_tmr_data
{
  String fxDate = "";         //"预报日期"
  String sunrise = "";        //"日出时间"
  String sunset = "";         //"日落时间"
  String moonrise = "";       //"月升时间"
  String moonset = "";        //"月落时间"
  String moonPhase = "";      //"月相名称"
  String moonPhaseIcon = "";  //"月相图标代码,图标可通过天气状况和图标下载"
  String tempMax = "";        //"预报当天最高温度"
  String tempMin = "";        //"预报当天最低温度"
  String iconDay = "";        //"预报白天天气状况的图标代码,图标可通过天气状况和图标下载"
  String textDay = "";        //"预报白天天气状况文字描述,包括阴晴雨雪等天气状态的描述"
  String iconNight = "";      //"预报夜间天气状况的图标代码,图标可通过天气状况和图标下载"
  String textNight = "";      //"预报晚间天气状况文字描述,包括阴晴雨雪等天气状态的描述"
  String wind360Day = "";     //"预报白天风向360角度"
  String windDirDay = "";     //"预报白天风向"
  String windScaleDay = "";   //"预报白天风力等级"
  String windSpeedDay = "";   //"预报白天风速,公里/小时"
  String wind360Night = "";   //"预报夜间风向360角度"
  String windDirNight = "";   //"预报夜间当天风向"
  String windScaleNight = ""; //"预报夜间风力等级"
  String windSpeedNight = ""; //"预报夜间风速,公里/小时"
  String precip = "";         //"预报当天总降水量,默认单位:毫米"
  String uvIndex = "";        //"紫外线强度指数"
  String humidity = "";       //"相对湿度,百分比数值"
  String pressure = "";       //"大气压强,默认单位:百帕"
  String vis = "";            //"能见度,默认单位:公里"
};

weather_tmr_data wtd[3];

        3、获取实况天气函数:void get_now_Weather(),实现请求和解压缩JSON数据功能。

// 获取实时天气数据
void get_now_Weather()
{
  // 检查WIFI是否连接
  if ((WiFi.status() == WL_CONNECTED))
  {
    // 准备发起请求
    std::unique_ptr<BearSSL::WiFiClientSecure> client(new BearSSL::WiFiClientSecure);
    client->setInsecure();
    Serial.print("[HTTPS] begin...\n");
    HTTPClient https;

    if (https.begin(*client, "https://devapi.qweather.com/v7/weather/now?key=" + key + "&location=" + cityid))
    {
      https.addHeader("Accept-Encoding", "gzip");
      Serial.print("[HTTPS] GET...\n");
      // start connection and send HTTP header
      int httpCode = https.GET();
      if (httpCode > 0)
      {
        // HTTP header has been send and Server response header has been handled
        Serial.printf("[HTTPS] GET... code: %d\n", httpCode);
        // file found at server
        if (httpCode == HTTP_CODE_OK)
        {
          // get length of document (is -1 when Server sends no Content-Length header)
          int len = https.getSize();

          // create buffer for read
          static uint8_t buff[128] = {0};
          // read all data from server
          int offset = 0;
          //  为什么这里分配内存会报错?
          // if(inbuf==NULL) inbuf=(uint8_t*)malloc(sizeof(uint8_t)*128);
          while (https.connected() && (len > 0 || len == -1))
          {
            // get available data size
            size_t size = client->available();

            if (size)
            {
              // read up to 128 byte
              int c = client->readBytes(buff, ((size > sizeof(buff)) ? sizeof(buff) : size));

              memcpy(buffer + offset, buff, sizeof(uint8_t) * c);
              offset += c;
              // write it to Serial
              // Serial.write(buff, c);
              if (len > 0)
              {
                len -= c;
              }
            }
            delay(1);
          }
          readBytesSize = offset;

          delay(1000);
          if (readBytesSize)
          {
            // write it to Serial
            Serial.write(buffer, readBytesSize);
            Serial.println("");
            uint32_t out_size = 0;
            ArduinoUZlib::decompress(buffer, readBytesSize, outbuffer, out_size);

            Serial.write(outbuffer, out_size);

            // 调用解析函数
            JsonDocument doc;
            DeserializationError err = deserializeJson(doc, outbuffer);

            if (err.code() == DeserializationError::Ok)
            {
              get_now_weather_data(doc);
            }
            else
            {
              Serial.println("数据解析出错");
            }
          }
          else
          {
            Serial.println("no avali size!");
          }
          if (outbuffer != NULL)
          {
            free(outbuffer);
            outbuffer = NULL;
          }
        }
      }
      else
      {
        Serial.printf("[HTTPS] GET... failed, error: %s\n", https.errorToString(httpCode).c_str());
      }
      https.end();
    }
    else
    {
      Serial.printf("Unable to connect\n");
    }
  }
}

        4、获取天气预报函数:void get_tmr_Weather(),请求和解压缩天气预报JSON数据。

// 获取天气预报数据
void get_tmr_Weather()
{
  // 检查WIFI是否连接
  if ((WiFi.status() == WL_CONNECTED))
  {
    // 准备发起请求
    std::unique_ptr<BearSSL::WiFiClientSecure> client(new BearSSL::WiFiClientSecure);
    client->setInsecure();
    Serial.print("[HTTPS] begin...\n");
    HTTPClient https;

    if (https.begin(*client, "https://devapi.qweather.com/v7/weather/3d?key=" + key + "&location=" + cityid))
    {
      https.addHeader("Accept-Encoding", "gzip");
      Serial.print("[HTTPS] GET...\n");
      // start connection and send HTTP header
      int httpCode = https.GET();
      if (httpCode > 0)
      {
        // HTTP header has been send and Server response header has been handled
        Serial.printf("[HTTPS] GET... code: %d\n", httpCode);
        // file found at server
        if (httpCode == HTTP_CODE_OK)
        {
          // get length of document (is -1 when Server sends no Content-Length header)
          int len = https.getSize();

          // create buffer for read
          static uint8_t buff[128] = {0};
          // read all data from server
          int offset = 0;
          //  为什么这里分配内存会报错?
          // if(inbuf==NULL) inbuf=(uint8_t*)malloc(sizeof(uint8_t)*128);
          while (https.connected() && (len > 0 || len == -1))
          {
            // get available data size
            size_t size = client->available();

            if (size)
            {
              // read up to 128 byte
              int c = client->readBytes(buff, ((size > sizeof(buff)) ? sizeof(buff) : size));

              memcpy(buffer + offset, buff, sizeof(uint8_t) * c);
              offset += c;
              // write it to Serial
              // Serial.write(buff, c);
              if (len > 0)
              {
                len -= c;
              }
            }
            delay(1);
          }
          readBytesSize = offset;

          delay(1000);
          if (readBytesSize)
          {
            // write it to Serial
            Serial.write(buffer, readBytesSize);
            Serial.println("");
            uint32_t out_size = 0;
            ArduinoUZlib::decompress(buffer, readBytesSize, outbuffer, out_size);

            Serial.write(outbuffer, out_size);

            // 调用解析函数
            JsonDocument doc;
            DeserializationError err = deserializeJson(doc, outbuffer);

            if (err.code() == DeserializationError::Ok)
            {
              get_tmr_weather_data(doc);
            }
            else
            {
              Serial.println("数据解析出错");
            }
          }
          else
          {
            Serial.println("no avali size!");
          }
          if (outbuffer != NULL)
          {
            free(outbuffer);
            outbuffer = NULL;
          }
        }
      }
      else
      {
        Serial.printf("[HTTPS] GET... failed, error: %s\n", https.errorToString(httpCode).c_str());
      }
      https.end();
    }
    else
    {
      Serial.printf("Unable to connect\n");
    }
  }
}

        5、提取实况天气具体数值的函数:void get_now_weather_data(JsonDocument &doc)

void get_now_weather_data(JsonDocument &doc)
{
  // 将数据保存到weahter_data 的结构体,方便后续调用

  Serial.println("");
  wd.code = doc["code"];
  wd.updateTime = doc["updateTime"].as<String>().substring(0, 16);
  wd.now_obsTime = doc["now"]["obsTime"].as<String>().substring(0, 16);
  wd.updateTime.replace("T", " ");
  wd.now_obsTime.replace("T", " ");

  wd.now_temp = doc["now"]["temp"].as<String>();
  wd.now_feelsLike = doc["now"]["feelsLike"].as<int>();
  wd.now_icon = doc["now"]["icon"].as<String>();
  wd.now_text = doc["now"]["text"].as<String>();
  wd.now_wind360 = doc["now"]["wind360"].as<String>();
  wd.now_windDir = doc["now"]["windDir"].as<String>();
  wd.now_windScale = doc["now"]["windScale"].as<String>();
  wd.now_windSpeed = doc["now"]["windSpeed"].as<int>();
  wd.now_humidity = doc["now"]["humidity"].as<int>();
  wd.now_precip = doc["now"]["precip"].as<int>();
  wd.now_pressure = doc["now"]["pressure"].as<int>();
  wd.now_vis = doc["now"]["vis"].as<int>();
}

        6、提取天气预报具体数值的函数:void get_tmr_weather_data(JsonDocument &doc)

// 获取 JSON 解析后的键值,并保存到相应数据结构中
void get_tmr_weather_data(JsonDocument &doc)
{

  Serial.println("");
  Serial.println("");
  String code = doc["code"].as<String>(); // "200"
  Serial.print("code = ");
  Serial.print(code);

  String updateTime = doc["updateTime"].as<String>().substring(0, 16); // "2024-05-15T10:18+08:00"
  wd.now_obsTime = doc["now"]["obsTime"].as<String>().substring(0, 16);
  wd.updateTime.replace("T", " ");

  for (uint8 i = 0; i < 3; i++)
  {
    // 将数据保存到weahter_data 的结构体,方便后续调用
    wtd[i].fxDate = doc["daily"][i]["fxDate"].as<String>();
    wtd[i].sunrise = doc["daily"][i]["sunrise"].as<String>();
    wtd[i].sunset = doc["daily"][i]["sunset"].as<String>();
    wtd[i].moonrise = doc["daily"][i]["moonrise"].as<String>();
    wtd[i].moonset = doc["daily"][i]["moonset"].as<String>();
    wtd[i].moonPhase = doc["daily"][i]["moonPhase"].as<String>();
    wtd[i].moonPhaseIcon = doc["daily"][i]["moonPhaseIcon"].as<String>();

    wtd[i].tempMax = doc["daily"][i]["tempMax"].as<String>();
    wtd[i].tempMin = doc["daily"][i]["tempMin"].as<String>();
    wtd[i].iconDay = doc["daily"][i]["iconDay"].as<String>();
    wtd[i].textDay = doc["daily"][i]["textDay"].as<String>();
    wtd[i].iconNight = doc["daily"][i]["iconNight"].as<String>();
    wtd[i].textNight = doc["daily"][i]["textNight"].as<String>();
    wtd[i].wind360Day = doc["daily"][i]["wind360Day"].as<String>();
    wtd[i].windDirDay = doc["daily"][i]["windDirDay"].as<String>();
    wtd[i].windScaleDay = doc["daily"][i]["windScaleDay"].as<String>();
    wtd[i].windSpeedDay = doc["daily"][i]["windSpeedDay"].as<String>();
    wtd[i].wind360Night = doc["daily"][i]["wind360Night"].as<String>();
    wtd[i].windDirNight = doc["daily"][i]["windDirNight"].as<String>();
    wtd[i].windScaleNight = doc["daily"][i]["windScaleNight"].as<String>();
    wtd[i].windSpeedNight = doc["daily"][i]["windSpeedNight"].as<String>();
    wtd[i].precip = doc["daily"][i]["precip"].as<String>();
    wtd[i].uvIndex = doc["daily"][i]["uvIndex"].as<String>();
    wtd[i].humidity = doc["daily"][i]["humidity"].as<String>();
    wtd[i].pressure = doc["daily"][i]["pressure"].as<String>();
    wtd[i].vis = doc["daily"][i]["vis"].as<String>();
}

二、项目源代码下载

        百度网盘下载链接:WeatherClock_example_8,  提取码:evcb
        友情提示:(1)请务必将 ssid 和 password 修改成您所在环境的名称和密码;(2)请务必将const String key 修改成您自己申请的和风天气API密钥。

三、运行效果展示

WeatherClock_example_8

        下一专题,我们将为这个项目添加智能配网的功能,欢迎关注留言。

         如您需要了解其它专题的内容,请点击下面的链接。
        第一专题内容,请参考:连接点亮SPI-TFT屏幕和UI布局设计
        第二专题内容,请参考:WIFI模式设置及连接
        第三专题内容,请参考:连接SHT30传感器,获取并显示当前环境温湿度数据(I2C)
        第四专题内容,请参考:通过NTPClient库获取实时网络时间并显示在TFT屏幕上
        第五专题内容,请参考:获取关于城市天气实况和天气预报的JSON信息(心知天气版)
        第六专题内容,请参考:解析天气信息JSON数据并显示在 TFT 屏幕上(心知天气版)
        第七专题内容,请参考:和风天气API返回JSON数据信息的解压缩实现

  • 34
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值