在分析代码之前,首先介绍 ArduinoJson 库的安装及“心知天气”的ID申请

一、安装 ArduinoJson 库

进入 Arduino 开发环境后,选择菜单栏-->工具-->管理库,搜索“arduinojson”。尽量使用 ArduinoJson 5.x 版本,因为 6.x 版本有很大的改动。

心知天气 esp8266 心知天气api怎么用_数据

二、申请“心知天气”的个人APIKEY

首先进入“心知天气”主页,点击此处访问

  注册并登陆后,点击“立即免费试用”

心知天气 esp8266 心知天气api怎么用_数据_02

  申请免费版

心知天气 esp8266 心知天气api怎么用_心知天气 esp8266_03

  获取自己的API密钥

心知天气 esp8266 心知天气api怎么用_HTTP_04

三、“心知天气”API简介

  本部分内容参考自:https://www.jianshu.com/p/fd8c84e40994

  1. API 请求参数

参数名

参数类型

参数意义

是否必选

key

string

个人API密钥

true

location

string

查询的地理位置

true

language

string

结果表示的语言

false,默认简体中文

unit

string

结果表示的单位

false,默认摄氏度

 

 

 

 

 

 

 

  本实验中,language 的参数值选用“zh-Hans”简体中文,unit 的参数选用“c”。(想了解更多参数范围请看参考链接)

  2. API 响应参数

参数名

参数类型

location

对象:包括 id, name, country, path, timezone, timezone_offset

now

对象:包括 text, code, temperature

last_update

日期

 

 

 

 

 

  参考一个请求的 Json 数据的实例,应该能较直观地理解 Json 数据包的格式:

{
  "results":[
    {
      "location":{
        "id":"WTMKQ069CCJ7",
        "name":"杭州",
        "country":"CN",
        "path":"杭州,杭州,浙江,中国",
        "timezone":"Asia/Shanghai",
        "timezone_offset":"+08:00"
      },
      "now":{
        "text":"晴",
        "code":"1",
        "temperature":"18"
      },
      "last_update":"2020-11-07T21:25:00+08:00"
    }
  ]
}

 引用数据时,用类似数组的引用方式。

  例如,若想引用地区名 “杭州”,则使用:

json["results"][0]["location"]["name"]

四、代码分析

  本次实验代码已上传至本人 github 账号:点击此处查看完整代码

  1.变量设置

#include<ESP8266WiFi.h>
#include<ArduinoJson.h>

const char* ssid ="";//输入热点名称
const char* password ="";//输入热点密码
const char* host ="api.seniverse.com";
const char* APIKEY ="";//输入自己申请的知心天气私钥
const char* city ="hangzhou";//可根据需要改为其余城市的拼音
const char* language ="zh-Hans";

const unsigned long BAUD_RATE=115200;
const unsigned long HTTP_TIMEOUT=5000;
const size_t MAX_CONTENT_SIZE=1000;

struct WeatherData{//存储天气数据的结构体,可根据需要自行添加成员变量
  char city[16];
  char weather[32];
  char temp[16];
  char udate[32];
};

WiFiClient client;//创建了一个网络对象
char response[MAX_CONTENT_SIZE];
char endOfHeaders[]="\r\n\r\n";

  2.初始化

  在这一步中 设置串口的波特率,连接WiFi,设置客户端超时时间

void setup() {
  Serial.begin(BAUD_RATE);
  wifiConnect();//连接WiFi
  client.setTimeout(HTTP_TIMEOUT);
}

  3.循环体

  首先判断 tcp client 是否处于连接状态,若不是,则尝试建立连接。连接成功后,发送 http 请求,并且跳过响应头,直接获取响应 body。

void loop() {
  while(!client.connected()){
    if(!client.connect(host,80)){//尝试建立连接
      Serial.println("connection....");
      delay(500);
    }
  }
  //连接成功,发送GET请求
  if(sendRequest(host,city,APIKEY)&&skipResponseHeaders()){//发送http请求 并且跳过响应头
    clrEsp8266ResponseBuffer();//清除缓存
    readReponseContent(response,sizeof(response));//从HTTP服务器响应中读取正文
    WeatherData weatherData;
    if(parseUserData(response,&weatherData)){//判断Json数据包是否分析成功
      printUserData(&weatherData);//输出读取到的天气信息
    }
  }
 stopConnect();
  delay(5000);
}

 4.自定义函数详解

    (1) 连接到WiFi

void wifiConnect(){
  WiFi.mode(WIFI_STA);//设置esp8266工作模式
  Serial.print("Connecting to");
  Serial.println(ssid);
  WiFi.begin(ssid,password);//连接WiFi
  WiFi.setAutoConnect(true);
  while(WiFi.status()!=WL_CONNECTED){//该函数返回WiFi的连接状态
    delay(500);
    Serial.print(".");
  }
  Serial.println("");
  Serial.println("WiFi connected");
  delay(500);
  Serial.println("IP address:");
  Serial.println(WiFi.localIP());
}

   (2) 发送 http 请求

bool sendRequest(const char* host,const char* cityid,const char* apiKey){
  String GetUrl="/v3/weather/now.json?key=";
  GetUrl+=APIKEY;
  GetUrl+="&location=";
  GetUrl+=city;
  GetUrl+="&language=";
  GetUrl+=language;
  GetUrl+="&unit=c ";
  client.print(String("GET ")+GetUrl+"HTTP/1.1\r\n"+"Host:"+host+"\r\n"+"Connection:close\r\n\r\n");
  Serial.println("creat a request:");
  Serial.println(String("GET ")+GetUrl+"HTTP/1.1\r\n"+"Host:"+host+"\r\n"+"Connection:close\r\n\r\n");
  delay(1000);
  return true;
}

  (3) 跳过响应头

bool skipResponseHeaders(){
  bool ok=client.find(endOfHeaders);
  if(!ok){
    Serial.println("No response of invalid response!");
  }
  return ok;
}

 (4) 读取响应的正文信息

void readReponseContent(char* content,size_t maxSize){
  size_t length=client.readBytes(content,maxSize);
  delay(100);
  Serial.println("Get the data from Internet");
  content[length]=0;
  Serial.println(content);//输出读取到的数据
  Serial.println("Read data Over!");
  client.flush();//刷新客户端
}

 (5) 分析 Json 数据包

bool parseUserData(char* content,struct WeatherData* weatherData){
  DynamicJsonBuffer jsonBuffer;//创建一个动态缓冲区实例
  JsonObject&root=jsonBuffer.parseObject(content);//根据需要解析的数据来计算缓冲区的大小
  if(!root.success()){
    Serial.println("JSON parsing failed!");
    return false;
  }
  //复制数据包中所需的字符串
  strcpy(weatherData->city,root["results"][0]["location"]["name"]);
  strcpy(weatherData->weather,root["results"][0]["now"]["text"]);
  strcpy(weatherData->temp,root["results"][0]["now"]["temperature"]);
  strcpy(weatherData->udate,root["results"][0]["last_update"]);

  return true;
}

 (6) 打印数据

void printUserData(const struct WeatherData* weatherData){
  Serial.println("Print parsed data:");
  Serial.print("City:");
  Serial.print(weatherData->city);
  Serial.print("  Weather:");
  Serial.print(weatherData->weather);
  Serial.print("  Temp:");
  Serial.print(weatherData->temp);
  Serial.print("℃");
  Serial.print("  UpdateTime:");
  Serial.println(weatherData->udate);
}

 (7) 停止客户端访问

void stopConnect(){
  Serial.println("Disconnect");
  client.stop();//停止客户端访问
}

(8) 清除缓存

void clrEsp8266ResponseBuffer(void){
  memset(response,0,MAX_CONTENT_SIZE);
}

五、串口输出样例

心知天气 esp8266 心知天气api怎么用_心知天气 esp8266_05

六、总结

 在本次实践中,我初步理解了 Json 数据包的格式及用法。希望以后可以做出更为复杂的应用。