目次
ESP32でOpenWeatherMapから天気情報を取得し、成形してみた
こんにちは。寒いですね。
もう天気予報を見るのも億劫ではないですか?ということでESP32に天気を自動で取得させてシリアル通信でデータを送ってもらいました。
参考にしたサイト
この記事の内容はESP32で現在の天気を取得してみたの内容を引き継いでいます。
OpenWeatherMapAPIの使い方などは上記リンクを参照願います。
前回はこのAPIを使ってJSON形式の天気情報をそのままPCにシリアル通信で送っていました。今回はArduinoJSONというライブラリを使い、必要なデータだけ取り出して成形します。
ArduinoJSONのインストール
- ライブラリを管理を選択
- 検索窓に「json」と入力し、ArduinoJsonというライブラリを見つける。バージョンは6だとESP32ではエラーが出たので、ここでは5.13.4を選択してインストールした(将来的に解決されるかも?)
- スケッチ例に「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とすると
- DynamicJsonBufferでjsonを格納しておくオブジェクト(ここではjsonBuffer)を作る
- jsonBuffer.parseObject(input)として、辞書型(keyを入れるとそれに対応したvalueが返ってくるやつ)に変換
- 辞書に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でOpenWeatherMapのAPIを使って現在の天気をLCD「AQM1602A」に表示させる | Wak-tech
ピンバック: M5Stick-Cに天気を画像で表示する | Wak-tech