天気APIのJsonデータをOLEDに表示させる
こんにちは、くろべこです。
ArduinoJsonを知ってから公開APIを漁る日々が続いています。やっぱり、ESP8266は手軽にWifiに接続できるしArduinoで開発できるからライブラリも豊富なので離れられません(笑)。最近は公開APIの種類もかなり豊富なので、IoTをやるにはいい時代になりました、、、
そんな公開APIの中でも超有名な天気予報API【Weather Hacks】について取り上げていきたいと思います。(フォーマットはもちろんJsonです)
リンク:Weather Hacks
目標はHttpClientでWeather HacksのJsonデータをGETして、ArduinoJsonによりデシアライズします。そして、オブジェクト化されたデータから値を抽出し、OLEDに表示させる。といった魂胆になります。
HttpClientの使い方だったり、ArduinoJsonによるデシリアライズのやり方については過去の記事にまとめてありますので参考にどうぞ。
一回使い方さえ慣れてしまえば、Jsonデータの扱いはどのAPIでも同じなのでどんどん情報をGETしてIoTが捗りますよ~
環境準備
今回使用する材料と環境の紹介です
- ESP8266
- OLED(128×64)
- Arduino 1.8.10
- ArduinoJson(verson 6)
※ArduinoJsonはバージョン違いの場合は動かない場合がありますのでご注意ください。
今回のコードは非常に長くなっております。ざっくりコードのブロックを紹介すると
- ヘッダ/定数/変数定義
- 天気データトリミング関数
- HttpClientのGET関数
- Jsonのデシリアライズ関数+OLED表示
- セットアップ(Wifi, OLED, シリアル)
- メインループ
ポイントは2.天気データトリミング関数です。この関数ではUnicodeで表されている天気情報を条件分岐させてアルファベットの天気文字列に変換しています。もしも、条件に合わない文字列が出た場合はUnicodeの文字列をそのまま表示させるようにしています。(ちなみにスペース“¥”と“のち”を表すUnicodeは除去しています。)
Weather Hacksでは晴れ時々曇とか曇のち晴れなど、天気の組み合わせは多岐にわたるので、その都度条件は更新していこうかなと思います。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 |
#include <Arduino.h> #include <ESP8266WiFi.h> #include <ArduinoJson.h> #include <ESP8266HTTPClient.h> #include <WiFiClientSecureBearSSL.h> #include <Wire.h> #include <ACROBOTIC_SSD1306.h> #ifndef STASSID #define STASSID "" #define STAPSK "" #endif const char* ssid = STASSID; const char* password = STAPSK; const String url = "http://weather.livedoor.com/forecast/webservice/json/v1?city=070010"; static String payload; String GetWtr1;//今日の天気予報 String GetWtr2;//明日の天気予報 IPAddress ip; String WtrTrim(String wtr){ //トリミング wtr.replace("¥u306e¥u3061", "");//"のち"を除去 wtr.replace("¥","");//スペースを除去 //お天気処理 if(wtr.indexOf("u6674u66c7")==0) return "SNYtoCDY"; else if(wtr.indexOf("u66c7u6674")==0) return "CDYtoSNY"; else if(wtr.indexOf("u66c7u6642u3005u6674")==0) return "CDYandSNY"; else if(wtr.indexOf("u6674")==0) return "SNY"; else if(wtr.indexOf("u66c7u308a")==0) return "CDY"; else if(wtr.indexOf("u96e8")==0) return "RNY"; else return wtr;//条件が無い場合は値を返す } void HttpClientGet(String url){ WiFiClient client;//WiFiClientオブジェクト生成 HTTPClient http;//HttpClientオブジェクト生成 Serial.print("[HTTP] begin...\n"); if (http.begin(client, url)) { // HTTPClient開始 Serial.print("[HTTP] GET...\n"); // start connection and send HTTP header int httpCode = http.GET();//GETメソッドで接続 // httpCode will be negative on error if (httpCode > 0) {//GET接続出来たら // HTTP header has been send and Server response header has been handled Serial.printf("[HTTP] GET... code: %d\n", httpCode); // file found at server if (httpCode == HTTP_CODE_OK || httpCode == HTTP_CODE_MOVED_PERMANENTLY) {//httpステータスがOKだったら payload = http.getString();//httpのbodyデータを読み取る payload.replace("\\", "¥"); //JSONレスポンス内の"\"をエスケープしないとデシリアライズが失敗する Serial.println(payload); } } else { Serial.printf("[HTTP] GET... failed, error: %s\n", http.errorToString(httpCode).c_str());//エラーコード表示 } http.end();//http接続終了 } else { Serial.printf("[HTTP] Unable to connect\n"); } } void JsonParsing(String json){ // Allocate the JSON document // Use arduinojson.org/v6/assistant to compute the capacity. DynamicJsonDocument doc(10000);//とりあえず読み込み容量以上の容量確保 // Parse JSON object DeserializationError error = deserializeJson(doc, json);//Jsonデータのデシリアライズ if (error) {//デシリアライズにエラーが出た場合 Serial.print(F("deserializeJson() failed: ")); Serial.println(error.c_str());//デシリアライズのエラーメッセージ表示 return; } //値を抽出する JsonArray forecasts = doc["forecasts"];//天気に関するプロパティ JsonObject forecasts_0 = forecasts[0];//今日の天気に関するプロパティ String forecasts_0_date = forecasts_0["date"].as<String>(); // 今日 String forecasts_0_telop = forecasts_0["telop"].as<String>(); // 天気 String forecasts_0_temperature_min_celsius = forecasts_0["temperature"]["min"]["celsius"].as<String>(); // "11" String forecasts_0_temperature_max_celsius = forecasts_0["temperature"]["max"]["celsius"].as<String>(); // "29" JsonObject forecasts_1 = forecasts[1];//明日の天気に関するプロパティ String forecasts_1_date = forecasts_1["date"].as<String>(); // 明日 String forecasts_1_telop = forecasts_1["telop"].as<String>(); // 天気 String forecasts_1_temperature_min_celsius = forecasts_1["temperature"]["min"]["celsius"].as<String>(); // "11" String forecasts_1_temperature_max_celsius = forecasts_1["temperature"]["max"]["celsius"].as<String>(); // "29" //OLEDに表示する形式に加工 Serial.println(F("Response:")); forecasts_0_date="Tdy:"+forecasts_0_date;//今日の日付 forecasts_0_temperature_min_celsius="Min:"+forecasts_0_temperature_min_celsius;//今日の最低気温 forecasts_0_temperature_max_celsius="Max:"+forecasts_0_temperature_max_celsius;//今日の最高気温 GetWtr1=WtrTrim(forecasts_0_telop);//お天気情報条件分岐 GetWtr1="Wtr:"+GetWtr1; forecasts_1_date="Tmr:"+forecasts_1_date;//明日の日付 forecasts_1_temperature_min_celsius="Min:"+forecasts_1_temperature_min_celsius;//明日の最低気温 forecasts_1_temperature_max_celsius="Max:"+forecasts_1_temperature_max_celsius;//明日の最高気温 GetWtr2=WtrTrim(forecasts_1_telop);//お天気情報条件分岐 GetWtr2="Wtr:"+GetWtr2; //シリアル表示 Serial.println(forecasts_0_date); Serial.println(GetWtr1); Serial.println(forecasts_0_temperature_min_celsius); Serial.println(forecasts_0_temperature_max_celsius); Serial.println(forecasts_1_date); Serial.println(GetWtr2); Serial.println(forecasts_1_temperature_min_celsius); Serial.println(forecasts_1_temperature_max_celsius); //OLED表示 oled.setTextXY(0,0); oled.putString(forecasts_0_date); oled.setTextXY(1,0); oled.putString(GetWtr1); oled.setTextXY(2,0); oled.putString(forecasts_0_temperature_min_celsius); oled.setTextXY(3,0); oled.putString(forecasts_0_temperature_max_celsius); oled.setTextXY(4,0); oled.putString(forecasts_1_date); oled.setTextXY(5,0); oled.putString(GetWtr2); oled.setTextXY(6,0); oled.putString(forecasts_1_temperature_min_celsius); oled.setTextXY(7,0); oled.putString(forecasts_1_temperature_max_celsius); //Jsonオブジェクト領域のメモリ開放 doc.clear();//メモリリーク対策 } void setup() { Serial.begin(115200); // We start by connecting to a WiFi network Serial.println(); Serial.println(); Serial.print("Connecting to "); Serial.println(ssid); WiFi.mode(WIFI_STA);//ステーションモードで起動 WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) {//WiFiに接続されなかったら delay(500); Serial.print("."); } Serial.println(""); Serial.println("WiFi connected"); Serial.println("IP address: "); ip=WiFi.localIP(); Serial.println(ip);//ローカルIPアドレス表示 //OLED start Wire.begin(); oled.init(); // Initialze SSD1306 OLED display oled.clearDisplay(); // Clear screen //起動画面 oled.setTextXY(1,0); oled.putString("HttpClirnt Start"); oled.setTextXY(2,0); oled.putString("Get WeaterHacks Json data"); oled.setTextXY(5,0); oled.putString("powered by"); oled.setTextXY(6,0); oled.putString("Kurobeko"); delay(10000); oled.clearDisplay(); } void loop() { HttpClientGet(url); JsonParsing(payload); Serial.println("Wait 10s before next round..."); delay(3000); } |
お天気情報を常に収集しているのでメモリリーク対策が必要です。Jsonオブジェクトの開放は忘れずに行いましょう。コードでいうと【doc.clear()】ですね。
回路接続 / 動作確認
回路はESP8266とOLEDを接続するだけです。OLEDはI2Cタイプを使用しました。
描画スピードが速い場合はSPIが必須ですが、今回は更新頻度が遅いのでI2Cで問題ありません。
ESP8266 | OLED | |
3V3 | => | VCC |
GND | => | GND |
D1 | => | SDA |
D2 | => | SCL |
- CNY:曇り
- RNY:雨
- SNY:晴れ
※天気の条件分岐が合わないときは、Unicodeがそのまま表示されます。なので、その時に応じて条件分岐(WtrTrim)の中に追加してください。
という事で、天気予報API【Weather Hacks】をハックしてOLEDに天気情報を表示することが出来ました。
今回はこんなとこで失礼します!
コメント