通過ESP8266WiFi模塊調用“心知天氣”接口 獲取天氣信息


在分析代碼之前,首先介紹 ArduinoJson 庫的安裝及“心知天氣”的ID申請

一、安裝 ArduinoJson 庫

  進入 Arduino 開發環境后,選擇菜單欄-->工具-->管理庫,搜索“arduinojson”。盡量使用 ArduinoJson 5.x 版本,因為 6.x 版本有很大的改動。

二、申請“心知天氣”的個人APIKEY

  首先進入“心知天氣”主頁,點擊此處訪問

  注冊並登陸后,點擊“立即免費試用”

  申請免費版

  獲取自己的API密鑰

三、“心知天氣”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);
}

五、串口輸出樣例

六、總結

  在本次實踐中,我初步理解了 Json 數據包的格式及用法。希望以后可以做出更為復雜的應用。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM