09.CoreInkで天気(気象庁)


9.CoreInkで天気(気象庁)修正待ち

気象庁のデータを表示させます。
日本語表示があるのでLovyanGFXモジュール。wifi接続、jsonデータ取得の必要があります。

ライブラリのインストール

Arduino IDE > スケッチ > ライブラリをインクルード > ライブラリを管理にて ArduinoJson by Benoit Blanchon (Ver6.17.3でした)を検索してインストール > 閉じる
(Arduino_JSON by Arduino ではありません)

説明

気象庁の横浜地方気象台のページは、
https://www.jma.go.jp/bosai/forecast/#area_type=class20s&area_code=1410000
で、気象庁の天気予報情報をXMLやJSONP形式で配信してくれている
https://www.drk7.jp/weather/
のサービスを利用します。1日1回AM6:00ごろ更新
しかし、気象庁のホームページがリニューアルしてdrk7.jpの更新待ちなので古いデータで確認します。2021/4更新予定だそうです。そこで、テストとして古いデータで1日のみ表示させてみます。
(4/24更新) https://www.drk7.jp/weather/ を見に行くと、「4月22日現在、一部要素の欠損、確認不足ではありますが、仮対応中です。」となっています。天気,天気マーク,波,最高・最低温度,6H毎の降水確率が7日分見れるようです。更新はもう少し待ってみます。
* 降水確率は、予報区内で一定時間内(ここでは6H)に降水量にして1mm以上の雨or雪の降る確率の平均値で、四捨五入して10%単位で表現しています。
* もし、1時間に1mm以上の雨が降ると、普通に傘が必要です。降水確率30%以上は外出時傘を持った方が良いらしいです。

drk7.jpで出力しているXMLデータ(実際使用するのはjsonデータ)で、一部を抜出すと以下が得られます。

<pref id="神奈川県">
  <area id="東部">  // 横浜
    <geo><long>139.5479</long><lat>35.5142</lat></geo>
    <info date="2021/02/24">
      <weather>くもり時々晴れ</weather>
      <img>http://www.drk7.jp/MT/images/MTWeather/201.gif</img>
      <weather_detail>北の風 海上 では 北の風 やや強く くもり 昼過ぎ から 夕方 晴れ</weather_detail>
      <wave>波 1メートル</wave>
      <temperature unit="摂氏">
        <range centigrade="max">11</range>
        <range centigrade="min">4</range>
      </temperature>
      <rainfallchance unit="%">
        <period hour="00-06">0</period>
        <period hour="06-12">0</period>
        <period hour="12-18">10</period>
        <period hour="18-24">10</period>
      </rainfallchance>
    </info>
    <info date="2021/02/25">  // 2日目
    ・・・(略)
    </info>
    <info date="2021/02/26">  // 3日目
    ・・・(略)
    </info>
    <info date="2021/02/27">  // 4日目
    ・・・(略)
    </info>
    <info date="2021/02/28">  // 5日目
    ・・・(略)
    </info>
    <info date="2021/03/01">  // 6日目
    ・・・(略)
    </info>
    <info date="2021/03/02">  // 7日目
    ・・・(略)
    </info>
  </area>
  <area id="西部">  // 小田原
  ・・・(略)
  </area>
</pref>

予報項目

全ての日(7日間)にある項目
  • 年月日
  • 概略の天気
  • 降水確率(0-6,6-12,12-18,18-24時の1日4回x7日)
  • 最高温度・最低温度
  • 天気マーク(gif)
2日目までの項目
  • 詳細な天気
  • 波の高さ

画像の設定方法

6つの画像は以下の方法で上から順に決めました。
  1. "雨" + "くもり" → "imgKumoriAme[]"
  2. "雨" → "imgAme[]"
  3. "晴" + "くもり" → "imgHareKumori[]"
  4. "晴" → "imgHare[]"
  5. "雪" → "imgYuki[]"
  6. "曇り" → "imgKumori[]"
  7. ""
画面表示内容
年/月/日 ↑最高気温|↓最低気温℃
概略天気
天気マーク
詳細天気
降水確率(0-6時)0 (6-12)0 (12-18)10 (18-24)10%
波の高さ

* 以上が1日に得られるすべてです。

スケッチ

xxxxxは、接続するwifiのSSIDとパスワードを記入します。前回作った天気マークの"icon6.h"も必要です。

// 天気情報 気象庁
// www.drk7.jpからjsonデータを得る。2021/4再開
// 関数一覧
// pushSprite  // LovyanGFXの1画面表示お決まり
// getJson     // jsonを得る
// createJson  // JSONP形式をJSON形式に変換
// drawWeather // 1画面セットする
// inkSet      // coreInkを使う時お決まり
// wifiSet     // wifiを使う時お決まり
// nihonSet    // 日本語を表示する時お決まり
// setup       //
// loop        // 何もしない
#include "icon6.h"  // 天気マーク 同一ホルダに置く
#include <M5CoreInk.h>    // M5Stack CoreInk使用
#include <WiFi.h>         // wifi使用
#include <HTTPClient.h>   // httpと通信
#include <ArduinoJson.h>  // json型式で抽出
#include <efontEnableJaMini.h> // フォント 日本語ミニ
#include <efontFontData.h>     // efontのフォントデータ
#define LGFX_AUTODETECT  // init時に対応機種を自動認識
#include <LovyanGFX.hpp>       // 描画ライブラリ
const char* ssid = "xxxxx";  // 自分のwifiのSSID
const char* password = "xxxxx";     // wifiのパスワード
const char* weatherURL = "https://www.drk7.jp/weather/json/14.js";  //神奈川県
const char* region = "東部";             // 横浜
DynamicJsonDocument weatherInfo(20000);  //
Ink_Sprite InkPageSprite(&M5.M5Ink);     // 定義
static LGFX_Sprite sprite;               // 定義

// LovyanGFXの1画面表示関数
void pushSprite(Ink_Sprite *coreinkSprite, LGFX_Sprite *lgfxSprite) {
  coreinkSprite->clear();
  for (int y = 0; y < 200; y++) {    // 縦方向0-199
    for (int x = 0; x < 200; x++) {  // 横方向0-199
      uint16_t c = lgfxSprite->readPixel(x, y);
      if (c == 0x0000) {
        coreinkSprite->drawPix(x, y, 0);
      }
    }
  }
  coreinkSprite->pushSprite(); // アロー演算子
}

DynamicJsonDocument getJson() {    // 
  DynamicJsonDocument doc(20000);  //
  if ((WiFi.status() == WL_CONNECTED)) {  // wifiが接続されていたら
    HTTPClient http;             // 定義
    http.begin(weatherURL);      // アクセスするURLを登録
    Serial.println(weatherURL);  // シリアルモニタに表示
    int httpCode = http.GET();//登録URLに、GETリクエスト送信
    if (httpCode > 0) {//HTTPのリターンコード。エラーの場合は負数。
      String jsonString = createJson(http.getString());
      //メッセージのボディ部,確保できなかった場合は空文字列,jsonオブジェクトの作成
      deserializeJson(doc, jsonString);
      // 直列化されたデータをDynamicJsonDocumentの形に変換
    } else {
      Serial.println("エラー HTTPリクエスト");  // シリアルモニタに表示
    }
    http.end();                // TCPコネクションを切断しHTTP通信を終了
  }
  return doc;
}

// JSONP形式をJSON形式に getJsonから1回呼ばれる
String createJson(String jsonString) { 
  jsonString.replace("drk7jpweather.callback(", "");
  return jsonString.substring(0, jsonString.length() - 2);
}

void drawWeather(String infoWeather) {  // 1画面セットする
  DynamicJsonDocument doc(20000);       // 
  deserializeJson(doc, infoWeather);    // 

  String myd = doc["date"];                   // 年月日
  sprite.setCursor(0, 1); sprite.print(myd);  // 
  Serial.println(myd);                        // シリアルモニタへ表示

  String maxTmp = doc["temperature"]["range"][0]["content"]; // 最高気温
  Serial.print("最高気温"); Serial.print(maxTmp);     // シリアルモニタへ表示
  String minTmp = doc["temperature"]["range"][1]["content"];  // 最低気温
  Serial.print("/最低気温"); Serial.println(minTmp);  // シリアルモニタへ表示
  sprite.setCursor(130, 1); sprite.printf("↑%02s|↓%02s℃", maxTmp, minTmp);

  String weather = doc["weather"];                 // 概略天気
  sprite.setCursor(0, 21); sprite.print(weather);  // 
  Serial.println(weather);                         // シリアルモニタへ表示
  
  int x = 64, y = 35, W = 72, H = 72;  // 天気マークの表示位置と大きさ
  if (weather.indexOf("雨") != -1) {
    if (weather.indexOf("くもり") != -1) {
      sprite.pushImage(x, y, W, H, imgKumoriAme); // くもり雨
    } else {        //(x, y, W, H, imgデータ)
      sprite.pushImage(x, y, W, H, imgAme); // 雨
    }
  } else if (weather.indexOf("晴") != -1) {
    if (weather.indexOf("くもり") != -1) {
      sprite.pushImage(x, y, W, H, imgHareKumori); // 晴れくもり
    } else {
      sprite.pushImage(x, y, W, H, imgHare); // 晴れ
    }
  } else if (weather.indexOf("雪") != -1) {
    sprite.pushImage(x, y, W, H, imgYuki); // 雪
  } else if (weather.indexOf("くもり") != -1) {
    sprite.pushImage(x, y, W, H, imgKumori); // くもり
  }

  String weather_d = doc["weather_detail"];           // 詳細天気
  sprite.setCursor(0, 105); sprite.print(weather_d);  //
  Serial.println(weather_d);                          // シリアルモニタへ表示

  String rain0_6 = doc["rainfallchance"]["period"][0]["content"];  // 降水量0-6時
  Serial.print("降水確率:"); Serial.print(rain0_6);           // シリアルモニタへ表示
  String rain6_12 = doc["rainfallchance"]["period"][1]["content"]; // 6-12時
  Serial.print(" "); Serial.print(rain6_12);                 // シリアルモニタへ表示
  String rain12_18 = doc["rainfallchance"]["period"][2]["content"];// 12-18時
  Serial.print(" "); Serial.print(rain12_18);                // シリアルモニタへ表示
  String rain18_24 = doc["rainfallchance"]["period"][3]["content"]; // 18-24時
  Serial.print(" "); Serial.println(rain18_24);              // シリアルモニタへ表示
  //sprite.setCursor(0, 140); sprite.print("(0-)(6-)(12)(18)");
  sprite.setCursor(0, 160);
  sprite.printf("(0-)%02s(6)%02s(12)%02s(18)%02s%%"
                , rain0_6, rain6_12, rain12_18, rain18_24);

  String wave = doc["wave"];                      // 波
  sprite.setCursor(0, 180); sprite.print(wave);   //
  Serial.println(wave);                           // シリアルモニタへ表示
}

void inkSet() {                        // お決まり
  M5.begin();               // E-Ink,RTC,I2C,ブザーを初期化
  if (!M5.M5Ink.isInit()) {            // 初期化出来なければ
    Serial.println("エラー Ink Init"); // シリアルモニタに表示
    while (1) delay(100);              // ずっと待つ
  }
  M5.M5Ink.clear();                    // 画像クリア
  delay(1000);                         // 1秒待つ
  InkPageSprite.creatSprite(0, 0, 200, 200, false);
  // 画像領域を作成し、画面ドライバーから画像データバフを取得しない
}
void wifiSet() {                           // お決まり
  Serial.print(ssid);                      // シリアルモニタに表示
  Serial.print("に接続中");                // シリアルモニタに表示
  WiFi.begin(ssid, password);              // ネット設定を初期化
  while (WiFi.status() != WL_CONNECTED) {  // 接続状態が接続完でない時
    delay(500);                            // 0.5秒待つ
    Serial.print(".");                     // シリアルモニタに表示
  }
  Serial.println("ok");                    // シリアルモニタに表示
}
void nihonSet() { // efont日本語16太 お決まり(16or24)
  sprite.setColorDepth(2);        // 2ビットパレットモードに設定
  sprite.createPalette();         // パレットモードにする?
  sprite.createSprite(200, 200);  // 200x200でスプライトを作成
  sprite.clear(TFT_WHITE);        // 全体の背景色を白
  sprite.setFont(&fonts::efontJA_16_b); // efont日本語16ドット太字
  sprite.setTextColor(TFT_BLACK, TFT_WHITE);  // 白地に黒字
}

void setup() {
  inkSet();                          // CoreInkの設定
  wifiSet();                         // wifiの設定
  nihonSet();                        // 日本語設定(efont16太)
  weatherInfo = getJson();           // 関数へ
  WiFi.disconnect(true);             // ネットワークから切断
  WiFi.mode(WIFI_OFF);               // wifiの動作モード
  Serial.print("wifi offか=");       // シリアルモニタに表示
  Serial.println(WiFi.status());     // wifiの接続状態
  // 0(WiFi.beginが呼出され3or4になるまで),1(使用可能なSSIDがない)
  // 2(スキャン完了),3(WiFi接続),4(すべての試行で接続失敗)
  // 5(接続が失われた),255(WiFiシールドがない)
  String today = weatherInfo["pref"]["area"][region]["info"][0];
  //                         神奈川県   area = 東部     info = 今日
  drawWeather(today);                   // 1画面設定関数へ
  pushSprite(&InkPageSprite, &sprite);  // 1画面表示関数へ
}

void loop() {
  delay(15000);                         // 15秒待つ
}