【NodeMCU实时天气时钟温湿度项目 7】和风天气API返回JSON数据信息的解压缩实现——ArduinoUZlib功能库

        今天是第七专题,主要内容是:导入ArduinoUZlib功能库,借助该库把从【和风天气】官网返回的经过Gzip压缩的JSON数据,进行解压缩和t解析,在串口监视器上输出解析后的JSON信息。

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

一、【心知天气】官网JSON数据特点

        1、订阅模式。和风天气开发服务采用订阅模式,当你创建项目时,至少需要选择一种订阅。使用和风天气的服务订阅非常简单和自由,免费或者你只需要为你实际使用的部分付费。现有三种订阅模式:免费订阅、标准订阅、高级订阅。
        每种订阅模式均可获取完整的天气信息数据。区别主要体现在数据请求量和更新频率方面。


        2、经过压缩的JSON数据。通过官网API接口返回的JSON数据,是进行Gzip压缩后的JSON数据,客户端需要先进行解压缩,然后再借助ArduinoJson功能库解析出解压缩后JSON数据,比心知天气返回JSON数据增加了一个解压缩过程。
        3、获取和风天气数据的方法。如果需要获得通过官网API接口返回的天气信息数据,需要首先心知天气注册账号、创建项目、选择订阅模式,然后获取API访问密钥KEY。具体操作方法网上有很多文档可供参考,请自行查询,比如:如何获取和风天气Web API的KEY?

二、添加ArduinoUZlib功能库

        1. ArduinoUZlib 的功能。该库是从uzlib移植到Arduino框架的功能库,主要用来解压https请求服务器返回的gzip数据。这个库占用内存比较小。
        2. 添加库方法。该库暂时无法从 PlatformIO 界面  -------> Libraries 加入。
        具体方法是:(1)进入该库 Github 网站,下载 zip 压缩文件。

(2)解压 zip 文件,生 成ArduinoUZlib-main 文件夹。
        (3)将此文件夹复制到 项目目录下的 lib 文件夹内,就可以使用该库的全部功能了。

三、主要功能函数

        该库使用方法简单方便,主要是通过调用 ArduinoUZlib::decompress(inbuff, size, outbuf,outsize) 这个功能函数来实现。

size_t size = stream->available(); // 还剩下多少数据没有读完?
uint8_t inbuff[size]; // 准备一个数组来装流数据,有多少装多少
stream->readBytes(inbuff, size); // 将http流数据写入inbuff中
uint8_t *outbuf=NULL; //解压后的输出流
uint32_t outsize=0; // 解压后多大?在调用解压方法后会被赋值。
// 调用解压函数
int result=ArduinoUZlib::decompress(inbuff, size, outbuf,outsize);
// 输出解密后的数据到控制台。
Serial.write(outbuf,outsize);

        详细内容可参考该库 example 目录下的示例:StreamHttpsClientGzipDemo,用来解压缩返回的 gzip数据。       

/**StreamHTTPClient.inoCreated on: 24.05.2015*/
#include <ArduinoJson.h>
#include <Arduino.h>
#include "ArduinoUZlib.h" 
#include <ESP8266WiFi.h>
#include <ESP8266WiFiMulti.h>#include <ESP8266HTTPClient.h>
ESP8266WiFiMulti WiFiMulti;// ESP.getFreeHeap(), ESP.getMinFreeHeap(), ESP.getHeapSize(), ESP.getMaxAllocHeap()
void heap(){Serial.print("FREE_HEAP[");Serial.print(ESP.getFreeHeap());Serial.print("]\n");
}
void setup() {Serial.begin(115200);// Serial.setDebugOutput(true);Serial.println();Serial.println();Serial.println();for (uint8_t t = 4; t > 0; t--) {Serial.printf("[SETUP] WAIT %d...\n", t);Serial.flush();delay(1000);}WiFi.mode(WIFI_STA);WiFiMulti.addAP("ssid", "password");
}
void log(const char *str) {Serial.println(str);
}static uint8_t buffer[1280]={0};
size_t readBytesSize=0;void fetchBuffer() {if ((WiFiMulti.run() == 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://192.168.2.144:8082/test")) {https.addHeader("Accept-Encoding", "gzip");Serial.print("[HTTPS] GET...\n");// start connection and send HTTP headerint httpCode = https.GET();if (httpCode > 0) {// HTTP header has been send and Server response header has been handledSerial.printf("[HTTPS] GET... code: %d\n", httpCode);// file found at serverif (httpCode == HTTP_CODE_OK) {// get length of document (is -1 when Server sends no Content-Length header)int len = https.getSize();// create buffer for readstatic uint8_t buff[128] = { 0 };// read all data from serverint offset=0;Serial.println("allocate");//  为什么这里分配内存会报错?// if(inbuf==NULL) inbuf=(uint8_t*)malloc(sizeof(uint8_t)*128);while (https.connected() && (len > 0 || len == -1)) {// get available data sizesize_t size = client->available();if (size) {// read up to 128 byteint c = client->readBytes(buff, ((size > sizeof(buff)) ? sizeof(buff) : size));// int c = client->readBytes(buff, size);// Serial.println("memcpy");memcpy(buffer+offset, buff, sizeof(uint8_t)*c);offset+=c;if(c>0 && c!=16) {log("======rb====");Serial.printf("%d,", buff[c-3]);Serial.printf("%d,", buff[c-2]);Serial.printf("%d,", buff[c-1]);log("\n======rb end====");}// write it to Serial// Serial.write(buff, c);if (len > 0) {len -= c;}}delay(1);}readBytesSize=offset;Serial.printf("offset=%d\n", offset);Serial.write(buffer, offset);Serial.print("[HTTPS] connection closed or file end.\n");}} else {Serial.printf("[HTTPS] GET... failed, error: %s\n", https.errorToString(httpCode).c_str());}https.end();} else {Serial.printf("Unable to connect\n");}}
}void loop() {uint8_t *outbuf1=NULL;// wait for WiFi connectionfetchBuffer();Serial.printf("\nAfter fetch, buffer size=%d\n", readBytesSize);delay(1000);if(readBytesSize) {// write it to Seriallog("===buf===");Serial.printf("%d,", readBytesSize-3);Serial.printf("%d,", readBytesSize-2);Serial.printf("%d,", readBytesSize-1);log("\n===buf end===");    Serial.write(buffer,readBytesSize);uint32_t out_size=0;int result=ArduinoUZlib::decompress(buffer, readBytesSize, outbuf1, out_size);printf("outsize=%d, result=\n", out_size,result);parseJSON((char*)outbuf1, out_size);// Serial.write(outbuf,out_size);}else {Serial.println("no avali size!");}
if (outbuf1!=NULL){free(outbuf1);outbuf1=NULL;
}Serial.println("Wait 10s before the next round...");delay(5000);
}void parseJSON(char *input, int inputLength) {// char* input;// size_t inputLength; (optional)DynamicJsonDocument doc(6144);DeserializationError error = deserializeJson(doc, input, inputLength);if (error) {Serial.print(F("deserializeJson() failed: "));Serial.println(error.f_str());return;}const char* code = doc["code"]; // "200"const char* updateTime = doc["updateTime"]; // "2022-12-05T15:35+08:00"const char* fxLink = doc["fxLink"]; // "http://hfx.link/1u0r1"for (JsonObject hourly_item : doc["hourly"].as<JsonArray>()) {// const char* hourly_item_fxTime = hourly_item["fxTime"]; // "2022-12-05T17:00+08:00", ...const char* hourly_item_fxTime = hourly_item["fxTime"]; // "2022-12-05T17:00+08:00", ...const char* hourly_item_temp = hourly_item["temp"]; // "15", "13", "13", "12", "11", "11", "10", "10", ...Serial.printf("%s,", hourly_item_temp);const char* hourly_item_icon = hourly_item["icon"]; // "100", "150", "150", "150", "150", "150", "150", ...const char* hourly_item_text = hourly_item["text"]; // "晴", "晴", "晴", "晴", "晴", "晴", "晴", "多云", "多云", ...const char* hourly_item_wind360 = hourly_item["wind360"]; // "22", "24", "30", "33", "33", "31", "30", ...const char* hourly_item_windDir = hourly_item["windDir"]; // "东北风", "东北风", "东北风", "东北风", "东北风", "东北风", ...const char* hourly_item_windScale = hourly_item["windScale"]; // "3-4", "3-4", "3-4", "3-4", "3-4", ...const char* hourly_item_windSpeed = hourly_item["windSpeed"]; // "16", "16", "16", "16", "14", "14", ...const char* hourly_item_humidity = hourly_item["humidity"]; // "57", "63", "63", "65", "66", "67", "68", ...const char* hourly_item_pop = hourly_item["pop"]; // "1", "3", "6", "6", "6", "6", "6", "6", "7", "7", ...const char* hourly_item_precip = hourly_item["precip"]; // "0.0", "0.0", "0.0", "0.0", "0.0", "0.0", ...const char* hourly_item_pressure = hourly_item["pressure"]; // "1013", "1013", "1012", "1012", "1012", ...const char* hourly_item_cloud = hourly_item["cloud"]; // "5", "5", "4", "4", "7", "9", "11", "33", "54", ...const char* hourly_item_dew = hourly_item["dew"]; // "7", "6", "6", "6", "5", "5", "5", "5", "5", "4", ...}Serial.println();JsonArray refer_sources = doc["refer"]["sources"];const char* refer_sources_0 = refer_sources[0]; // "QWeather"const char* refer_sources_1 = refer_sources[1]; // "NMC"const char* refer_sources_2 = refer_sources[2]; // "ECMWF"const char* refer_license_0 = doc["refer"]["license"][0]; // "CC BY-SA 4.0"}

四、和风天气JSON数据的解压缩实现

        这是个实现解压缩和风天气JSON数据的简单示例:(1)将服务器进行Gzip压缩后返回的JSON数据,接收并保存到缓冲区 buffer 中;(2)调用 ArduinoUZlib::decompress(inbuffer, size, outbuffer,outsize) 解压 buffer 中的经过压缩的JSON数据,同时将解压后JSON数据保存到输出 outbuffer 中;(3)调用 ArduinoJson 库的 deserializeJson(doc, outbuffer) 函数,对明文JSON数据进行解析,并保存到实况天气数据结构 wd 中,然后通过串口监视器输出。
        具体内容,请仔细阅读下面的代码实现。

// 实时天气
struct weather_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;void get_now_weather_data(JsonDocument &doc);size_t readBytesSize = 0;// 用来存放解压前的JSON数据
static uint8_t buffer[1280] = {0};// 用来存放解压后的JSON数据
uint8_t *outbuffer = NULL;// 获取实时天气数据
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 headerint httpCode = https.GET();if (httpCode > 0){// HTTP header has been send and Server response header has been handledSerial.printf("[HTTPS] GET... code: %d\n", httpCode);// file found at serverif (httpCode == HTTP_CODE_OK){// get length of document (is -1 when Server sends no Content-Length header)int len = https.getSize();// create buffer for readstatic uint8_t buff[128] = {0};// read all data from serverint offset = 0;//  为什么这里分配内存会报错?// if(inbuf==NULL) inbuf=(uint8_t*)malloc(sizeof(uint8_t)*128);while (https.connected() && (len > 0 || len == -1)){// get available data sizesize_t size = client->available();if (size){// read up to 128 byteint 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 SerialSerial.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");}}
}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>();Serial.print("wd.code: ");Serial.println(wd.code);Serial.print("wd.updateTime: ");Serial.println(wd.updateTime);Serial.print("wd.now_obsTime: ");Serial.println(wd.now_obsTime);Serial.print("wd.now_temp: ");Serial.println(wd.now_temp);Serial.print("wd.now_feelsLike: ");Serial.println(wd.now_feelsLike);Serial.print("wd.now_icon: ");Serial.println(wd.now_icon);Serial.print("wd.now_text: ");Serial.println(wd.now_text);Serial.print("wd.now_wind360: ");Serial.println(wd.now_wind360);Serial.print("wd.now_windDir: ");Serial.println(wd.now_windDir);Serial.print("wd.now_windScale: ");Serial.println(wd.now_windScale);Serial.print("wd.now_windSpeed: ");Serial.println(wd.now_windSpeed);Serial.print("wd.now_humidity: ");Serial.println(wd.now_humidity);Serial.print("wd.now_precip: ");Serial.println(wd.now_precip);Serial.print("wd.now_pressure: ");Serial.println(wd.now_pressure);Serial.print("wd.now_vis: ");Serial.println(wd.now_vis);
}

五、解压缩JSON数据源代码下载和运行效果展示

        百度网盘下载:UZlib_Qweather_CompressedJsonData_7,  提取码:ivfq
        友情提示:(1)请务必将 ssid 和 password 修改成您所在环境的名称和密码;(2)请务必const String key 修改成您自己申请的和风天气API密钥。
        如果您能在串口监视器看到如下信息,那么恭喜您程序运行成功了。
        

参考文档
1. JSON 基本使用_json怎么用-CSDN博客
2. 如何获取和风天气Web API的KEY?(简要步骤)_天气预报web api key-CSDN博客
3. JSON——概述、JSON语法、序列化和反序列化_所有文档都可以通过json序列化吗-CSDN博客

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/701218.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C++面向对象程序设计-北京大学-郭炜【课程笔记(八)】

C面向对象程序设计-北京大学-郭炜【课程笔记&#xff08;八&#xff09;】 1、虚函数和多态的基本概念1.1、虚函数1.2、多态多态的表现形式一多态的表现形式二 2、多态实例&#xff1a;魔法门之英雄无敌2.1、**非多态的实现方法&#xff1a;**2.2、**多态的实现方法** 3、多态实…

废品回收微信小程序基于FastAdmin+ThinkPHP+UniApp(源码搭建/上线/运营/售后/更新)

一款基于FastAdminThinkPHPUniApp开发的废品回收系统&#xff0c;适用废品回收站、再生资源回收公司上门回收使用的小程序。 一、FastAdmin框架特色功能及优势 模块化开发&#xff1a;控制器、模型、视图、JS一一对应&#xff0c;使用RequireJS进行插件机制&#xff0c;支持插…

5.13网络编程

只要在一个电脑中的两个进程之间可以通过网络进行通信那么拥有公网ip的两个计算机的通信是一样的。但是一个局域网中的两台电脑上的虚拟机是不能进行通信的&#xff0c;因为这两个虚拟机在电脑中又有各自的局域网所以通信很难实现。 socket套接字是一种用于网络间进行通信的方…

JavaEE之线程(5)——Java内存模型、内存可见性、volatile关键字

前言 volatile可以理解成轻量级的 synchronized&#xff0c; 它在多CPU开发中保证了共享变量的“可见性”&#xff0c;可见性我们可以理解成是&#xff1a;当一个线程修改一个共享变量时&#xff0c;另一个线程可以读到这个修改的值。由于它不会引起线程的上下文切换和调度&am…

第四届辽宁省大学生程序设计竞赛

比赛经历&#xff1a;2024.5.14简单vp了一个小时只写出了签到题4个然后跑路了 补题&#xff1a;感觉其他题有点太抽象了主要补了一题&#xff0c;在区间问题中数据结构的使用 比赛链接[点我即可] 目录 A.欢迎来到辽宁省赛 B.胜率 F.隔板与水槽 H.取石子 L.区间与绝对值 …

如何给扫描好的3d模型贴图?---模大狮模型网

在数字化设计领域&#xff0c;3D模型的贴图是提升模型逼真度和视觉效果的重要步骤之一。尤其是对于扫描好的3D模型&#xff0c;通过添加适当的贴图&#xff0c;不仅可以增强模型的细节和真实感&#xff0c;还可以为设计带来更加生动的视觉体验。本文将为您详细介绍如何给扫描好…

Node.js 学习笔记 express框架

express express 使用express下载express 初体验 express 路由什么是路由1路由的使用验证的方法 2获取请求报文参数3获取路由参数4响应设置响应报文 express 中间件5中间件全局中间件路由中间件 6静态资源中间件注意事项案例 7请求体数据8防盗链实现防盗链 9路由模块化router E…

Windows里使用ollama本地大模型部署

下载 ollama官网进行下载 下载完后下一步下一步即可 安装完成后验证是否成功&#xff0c;打开命令行输入ollama&#xff0c;有该指令即安装成功 环境变量配置 配置模型下载位置 看自己电脑硬盘情况配置 打开所有ip接口访问权限 如果想要远程调用ollama接口&#xff0c;…

怎么保护云服务器的安全

在云计算技术迅速发展的今天&#xff0c;云服务器被广泛使用&#xff0c;云服务器已经成为企业、组织和个人不可或缺的重要基础设施。然而&#xff0c;云服务器的普及也伴随着日益严峻的安全挑战。今天德迅云安全就和大家了解云服务器安全的重要性&#xff0c;并分享一些常见的…

3ds Max与Maya不同之处?两者哪个更适合云渲染?

3ds Max 和 Maya 都是知名的3D软件&#xff0c;各有其特色。3ds Max 以直观的建模和丰富的插件生态闻名&#xff1b;Maya 则在动画和角色创作方面更为出色。两者都支持云渲染技术&#xff0c;能帮助用户在云端高效完成项目。 一、3ds Max和Maya之间的主要区别&#xff1a; 3ds…

【初阶数据结构】单链表基础OJ题讲解

前言 &#x1f4da;作者简介&#xff1a;爱编程的小马&#xff0c;正在学习C/C&#xff0c;Linux及MySQL。 &#x1f4da;本文收录与初阶数据结构系列&#xff0c;本专栏主要是针对时间、空间复杂度&#xff0c;顺序表和链表、栈和队列、二叉树以及各类排序算法&#xff0c;持…

【大道至简】官方兼容到android13+的获取系统屏幕高度, statusbar,navBar

android在屏幕高度和app高度&#xff0c;statusbar, navigationbar的高度处理上&#xff0c;迭代了好多版本。 android11&#xff0c; android12都有新的api和过时的api标记。 涉及的api类似如下&#xff1a; windowManager&#xff0c;defaultDisplay, Context.display, Deco…