ESP32でOpenWeatherMapから天気情報を取得し、成形してみた

ESP32でOpenWeatherMapから天気情報を取得し、成形してみた

こんにちは。寒いですね。
もう天気予報を見るのも億劫ではないですか?ということでESP32に天気を自動で取得させてシリアル通信でデータを送ってもらいました。

参考にしたサイト

この記事の内容はESP32で現在の天気を取得してみたの内容を引き継いでいます。
OpenWeatherMapAPIの使い方などは上記リンクを参照願います。
前回はこのAPIを使ってJSON形式の天気情報をそのままPCにシリアル通信で送っていました。今回はArduinoJSONというライブラリを使い、必要なデータだけ取り出して成形します。

ArduinoJSONのインストール

  1. ライブラリを管理を選択
  2. 検索窓に「json」と入力し、ArduinoJsonというライブラリを見つける。バージョンは6だとESP32ではエラーが出たので、ここでは5.13.4を選択してインストールした(将来的に解決されるかも?)
  3. スケッチ例に「ArduinoJson」があれば成功

ArduinoJSONの簡単な使い方

ほぼスケッチ例のStringExample.inoそのままですが、一応解説します。

#include <ArduinoJson.h>

void setup() {
  //jsonを保存しておくバッファーを設定(Dynamicにすれば自動でサイズを指定してくれて便利)
  DynamicJsonBuffer jsonBuffer;

  String input ="{\"sensor\":\"gps\",\"time\":1351824120,\"data\":[48.756080,2.302038]}";
  //rootに辞書形式のjsonObjectを設定
  JsonObject& root = jsonBuffer.parseObject(input);

  long time = root["time"];

  Serial.println(time);
  
}

void loop() {
}

つまり、

{"key1":value1,"key2":value2,...}

という形式のJSON文字列をinputとすると

  1. DynamicJsonBufferでjsonを格納しておくオブジェクト(ここではjsonBuffer)を作る
  2. jsonBuffer.parseObject(input)として、辞書型(keyを入れるとそれに対応したvalueが返ってくるやつ)に変換
  3. 辞書にkey(“time”など)を入れて、それに対応するvalueを取りだす

ということがこの数行でできます。今回はこのライブラリを使用して、OpenWeatherMapAPIの返り値から天気情報を抜き出します。

ソースコード(コピペで動く)

APIへのアクセス、そしてAPIキーの取得などは前回の記事のESP32で現在の天気を取得してみたを参照願います。
ssid,password,keyをご自分の値に設定すれば動くはずです。

#include <WiFi.h>
#include <HTTPClient.h>
#include <ArduinoJson.h>
 
const char* ssid = "ssid";
const char* password =  "password";
 
const String endpoint = "http://api.openweathermap.org/data/2.5/weather?q=tokyo,jp&APPID=";
const String key = "yourAPIKEY";
 
void setup() {
 
  Serial.begin(115200);
 
  WiFi.begin(ssid, password);
 
  while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
    Serial.println("Connecting to WiFi..");
  }
 
  Serial.println("Connected to the WiFi network");
}
 
void loop() {
 
  if ((WiFi.status() == WL_CONNECTED)) {
 
    HTTPClient http;
 
    http.begin(endpoint + key); //URLを指定
    int httpCode = http.GET();  //GETリクエストを送信
 
    if (httpCode > 0) { //返答がある場合
 
        String payload = http.getString();  //返答(JSON形式)を取得
        Serial.println(httpCode);
        Serial.println(payload);

        //jsonオブジェクトの作成
        DynamicJsonBuffer jsonBuffer;
        String json = payload;
        JsonObject& weatherdata = jsonBuffer.parseObject(json);

        //パースが成功したかどうかを確認
        if(!weatherdata.success()){
          Serial.println("parseObject() failed");
        }

        //各データを抜き出し
        const char* weather = weatherdata["weather"][0]["main"].as<char*>();
        const double temp = weatherdata["main"]["temp"].as<double>();
        Serial.print("weather:");
        Serial.println(weather);
        Serial.print("temperature:");
        Serial.println(temp-273.15);
      }
 
    else {
      Serial.println("Error on HTTP request");
    }
 
    http.end(); //リソースを解放
  }
 
  delay(30000);   //30秒おきに更新
 
}

結果

30秒おきにこのように成形された天気と気温が出力されます。

ちょっと解説

jsonの取得まではESP32で現在の天気を取得してみたで解説したので、その後の成形の部分を少し解説。

//jsonオブジェクトの作成
        DynamicJsonBuffer jsonBuffer;
        String json = payload;
        JsonObject& weatherdata = jsonBuffer.parseObject(json);

DynamicJsonBufferで格納用のjsonBufferを生成。
その後返ってきた生の値であるpayloadをjsonという文字列にコピー。
そして辞書型として使えるweatherdataにjsonオブジェクトをパースします。パースは「解剖」という意味で、「分かりやすい形に成形する」みたいなもんだと思えばいいと思います。
これで便利なweatherdataという名前の辞書ができました。

//パースが成功したかどうかを確認
        if(!weatherdata.success()){
          Serial.println("parseObject() failed");
        }

バッファサイズが足りなかったり、メモリが不足しているとパースに失敗することがあります。失敗した場合はweatherdata.success()がfalseになるので、どうなったかを一応出力しています。ESP8266系や、非常に重たいAPIを処理するときは失敗するかもしれません。

//各データを抜き出し
        const char* weather = weatherdata["weather"][0]["main"].as<char*>();
        const double temp = weatherdata["main"]["temp"].as<double>();
        Serial.print("weather:");
        Serial.println(weather);
        Serial.print("temperature:");
        Serial.println(temp-273.15);

いよいよ抜き出しの部分です。ここで帰ってきた生データをちょっと見てみましょう。

{"coord":{"lon":139.76,"lat":35.68},"weather":[{"id":801,"main":"Clouds","description":"few clouds","icon":"02d"}],"base":"stations","main":{"temp":283.6,"pressure":1019,"humidity":29,"temp_min":283.15,"temp_max":284.15},"visibility":10000,"wind":{"speed":4.1,"deg":340},"clouds":{"all":20},"dt":1546408800,"sys":{"type":1,"id":8077,"message":0.0049,"country":"JP","sunrise":1546379450,"sunset":1546414736},"id":1850147,"name":"Tokyo","cod":200}

例えば”weather”に対応するvalueは

[{"id":801,"main":"Clouds"...}]

という形式で格納されています。これは若干の罠で、

Serial.println(weatherdata["weather"]["main"])

としてもCloudsとは表示されず、何も出てきません。valueに配列が入っているからです。配列と言っても0番目しかないので、

Serial.println(weatherdata["weather"][0]["main"])

とすることでCloudという情報を抜き出せます。これでも出力できますが、何かに使うためにweatherという変数にこの情報を入れてみました。
constにしないとエラーが出て、かつ文字列にする必要があったので、

const char* weather = weatherdata["weather"][0]["main"].as<char*>();

としました。

.as<char*>()

を使えばその中身を文字列に変換できます。
温度に関しても同様で、計算に使えるように

.as<double>()

を使いました。

プログラムの応用

これでweatherとtempにそれぞれ天気と気温の情報が格納できたので、例えば雨の時にはLEDを点灯させる、なんてことが簡単にできるようになりますね。

温度に応じてRGBLEDの色を変えてみただけで外の気温が分かったりすると楽しいかも。

そのようなプロジェクトもやっていく予定なので、twitterをフォローして頂けると報告できると思います。よろしくお願いいたします。

>>ESP32チュートリアル一覧に戻る

「ESP32でOpenWeatherMapから天気情報を取得し、成形してみた」に1件のコメントがあります

  1. ピンバック: ESP32でOpenWeatherMapのAPIを使って現在の天気をLCD「AQM1602A」に表示させる | Wak-tech

コメントする

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です