16.CoreInkで天気(気象庁)2


16.CoreInkで天気(気象庁)2

気象庁の天気予報を表示します。今年の春に気象庁のホームページがリニューアルし、Webの公開APIでは無いですが、気象庁のサイト内でJSONファイルが利用されています。このJSONファイルから表示します。

予報内容

0:今日, 1:明日, 2:明後日,・・・ 7:7日先
No日後 01234567
1日付 〇〇〇〇〇〇〇〇
2降水確率〇〇〇〇〇〇〇〇
3天気マーク 〇〇〇〇〇〇〇〇
4詳細天気〇〇〇-----
5簡易天気---〇〇〇〇〇
6最高温度〇〇〇〇〇〇〇〇
7最低温度〇〇〇〇〇〇〇〇
8 〇〇〇-----
9 〇〇〇-----
10信頼度 ---〇〇〇〇〇
上記内容と、
・平年の最高・最低気温,平年の7日合計降水量
・県内の明後日(or明日)までの予報文
・関東甲信地方の週間予報文
注)
No.2の0-1日は6Hごと「*/*/*/*%」,2-7日は1日「*%」。時刻により過去の表示が"-"になります。降水確率0-6は、AM8以前から。最低気温は、AM8以前から。降水確率6-12は、AM11から"-"。降水確率12-18と最高気温はPM5から"-"になります。降水確率18-24は、常時表示です。(一部推定)
No.6&7の0-1日は「*℃」、2-7日は範囲有り「*(*-*)℃」
No.7は、時刻により過去の表示が"-"になります。
No.9は、表示の無い地域もあります。
* 降水量(mm)の予報は有りません。

予報の資料

予報の更新時刻

・週間予報: AM11,PM5
・明後日までの予報: 上記とAM5。これ以外もあり
・AM5時の更新では明日までと6日先まで、それ以外は、3日と7日先まで
注)時刻により表示数が異なります。AM0-5は、明日の項が当日です。

降水確率

明日までの予報は、0時から6/6から12/12から18/18から24の順
上記以外は、1日
・降水確率は、予報区内で1mm以上の雨の降る確率です。6H毎に10%単位で発表されます。例)18-24時が20%は、その期間に1mm以上の雨の降る可能性が100回中20回あるという意味で、降水量と関係ありません。

最高・最低気温

・明日までは、日中の最高気温, 朝の最低気温
・明後日以降は、1日の最高気温, 1日の最低気温。()範囲に入る確率はおよそ80%です。

信頼度

A (確度高い)
・降水有無の適中率: 明日予報並み(88%)
・翌日に降水有無が変わる可能性: ほとんどない(1%)

B (やや高い)
・降水有無の適中率: 4日先の予報と同程度(73%)
・翌日に降水有無が変わる可能性:低い(6%)

C (やや低い)
・降水有無の適中率: Bより低い(58%)
もしくは
・翌日に降水有無が変わる可能性: Bより高い(16%)

* %値は、5年間の平均値

平年値

・降水量: 予報期間7日間(or 6日間)で合計したの平年並の範囲
・最高・最低気温: 予報4日目の平年値

スケッチに必要な資料

エリアのコード

http://www.jma.go.jp/bosai/common/const/area.json
1都3県のpathCodeは、
・埼玉県 110000 (南部,北部,秩父地方)
・千葉県 120000 (北西部,北東部,南部)
・東京都 130000 (東京地方,伊豆諸島北部,伊豆諸島南部,小笠原諸島)
・神奈川 140000 (東部,西部)

天気マーク

https://www.jma.go.jp/bosai/forecast/img/*.svg
見つけたマークは、
100:晴, 101:晴時々曇, 102:晴一時雨, 110:晴後曇
200:曇, 201:曇時々晴, 202:曇一時雨, 210:曇後時々晴, 212:曇後一時雨
300:雨, 301:雨時々晴, 302:雨時々曇, 303:雨時々雪
400:雪, 401:雪時々晴, 402:雪時々曇, 403:雪時々雨
500:月, 501:月時々曇, 502:月時々雨, 510:月後曇, 610:曇後月
203:曇時々雨 ←ファイルは無いが予報で使用している。?
例)https://www.jma.go.jp/bosai/forecast/img/100.svg は、晴のマークです。

天気マークの決め方

気象庁の天気マークは20以上あるので、以下の方法で6つにまとめました。
No条件yesno
12へ3へ
2曇雨
34へ5へ
4晴曇
56へ
6

HPで3日/8日の予報

https://www.jma.go.jp/bosai/forecast/data/forecast/140000.json
Jsonファイルに注釈はつけられません。字下げをし、注釈を付けました。

{["publishingOffice":"横浜地方気象台",         // 気象台名
"reportDatetime":"2021-06-17T11:00:00+09:00",  // 発表時刻
"timeSeries":[
    {"timeDefines":["2021-06-17T11:00:00+09:00",
                    "2021-06-18T00:00:00+09:00",
                    "2021-06-19T00:00:00+09:00"],//今日から3日の日付
    "areas":[
        {"area":{"name":"東部",
                "code":"140010"},  // 東部(横浜)
        "weatherCodes":["200","200","212"],  // 3日間の天気マーク 曇り,曇り,曇り後雨
        "weathers":["くもり 所により 夜のはじめ頃 まで 雨 で 雷を伴う",
          "くもり 明け方 まで 晴れ",
          "くもり 後 一時 雨"],                   // 3日間の天気
        "winds":["北東の風 後 北の風 海上 では 北東の風 やや強く",
          "北の風 後 南の風",
          "南の風 後 やや強く"],                   // 3日間の風
        "waves":["1.5メートル 後 1メートル",
          "1メートル",
          "1メートル 後 1.5メートル"]},        // 3日間の波
        {"area":{"name":"西部",
                "code":"140020"},  // 西部(小田原) 上記と同様
        "weatherCodes":["200","200","212"],
        "weathers":["くもり 所により 夜のはじめ頃 まで 雨 で 雷を伴う",
          "くもり",
          "くもり 後 一時 雨"],
        "winds":["北の風 海上 では 北東の風 やや強く",
          "北の風 後 南の風",
          "南の風 後 やや強く"],
        "waves":["1.5メートル 後 1メートル",
          "1メートル",
          "1メートル 後 1.5メートル"]}]},

    {"timeDefines":["2021-06-17T12:00:00+09:00",
                    "2021-06-17T18:00:00+09:00",
                    "2021-06-18T00:00:00+09:00",
                    "2021-06-18T06:00:00+09:00",
                    "2021-06-18T12:00:00+09:00",
                    "2021-06-18T18:00:00+09:00"], // 降水確率開始時刻6点
    "areas":[
        {"area":{"name":"東部",
                "code":"140010"},
        "pops":["30","20","0","0","10","10"]},           // 東部 降水確率6点
        {"area":{"name":"西部",
                "code":"140020"},
        "pops":["30","20","10","10","10","10"]}]},       // 西部

    {"timeDefines":["2021-06-17T09:00:00+09:00",
                    "2021-06-17T00:00:00+09:00",
                    "2021-06-18T00:00:00+09:00",
                    "2021-06-18T09:00:00+09:00"],  // ?気温時刻4点
    "areas":[
        {"area":{"name":"横浜",
                "code":"46106"},
        "temps":["26","26","19","27"]},       // 横浜 最低気温 最高気温 明日まで
        {"area":{"name":"小田原",
                "code":"46166"},
        "temps":["26","26","17","26"]}]}]},   // 小田原



{"publishingOffice":"横浜地方気象台",          // 気象台名
"reportDatetime":"2021-06-17T11:00:00+09:00",  // 発表時刻
"timeSeries":[
    {"timeDefines":["2021-06-18T00:00:00+09:00",
                    "2021-06-19T00:00:00+09:00",
                    "2021-06-20T00:00:00+09:00",
                    "2021-06-21T00:00:00+09:00",
                    "2021-06-22T00:00:00+09:00",
                    "2021-06-23T00:00:00+09:00",
                    "2021-06-24T00:00:00+09:00"], // 明日から7日先の日付
    "areas":[{
        "area":{"name":"神奈川県","code":"140000"},      // 神奈川県
        "weatherCodes":["200","212","202","200","202","202","202"],//明日から7日先の天気マーク
        "pops":["","60","50","30","50","60","50"],       // 明後日からの6日先の降水確率
        "reliabilities":["","","C","A","C","B","C"]}]},  // 明々後日から5日先の信頼度

    {"timeDefines":["2021-06-18T00:00:00+09:00",
                    "2021-06-19T00:00:00+09:00",
                    "2021-06-20T00:00:00+09:00",
                    "2021-06-21T00:00:00+09:00",
                    "2021-06-22T00:00:00+09:00",
                    "2021-06-23T00:00:00+09:00",
                    "2021-06-24T00:00:00+09:00"],  // 明日から7日先の日付
    "areas":[{
        "area":{"name":"横浜",
                "code":"46106"},        // 横浜
        "tempsMin":["","21","21","21","18","17","18"], // 明後日から6日先の最低気温
        "tempsMinUpper":["","23","23","22","20","19","20"],     // その範囲H側
        "tempsMinLower":["","19","20","19","15","15","16"],     // その範囲L側
        "tempsMax":["","27","29","27","22","21","23"], // 明後日から6日先の最高気温
        "tempsMaxUpper":["","28","30","30","27","26","28"],     // その範囲H側
        "tempsMaxLower":["","25","26","24","21","19","21"]}]}], // その範囲L側
"tempAverage":{
    "areas":[{
        "area":{"name":"横浜",
                "code":"46106"},
        "min":"19.7",
        "max":"25.9"}]},  // 明日から7日先までの最低気温 最高気温(℃)
"precipAverage":{
    "areas":[{
        "area":{"name":"横浜",
                "code":"46106"},
        "min":"27.0",
        "max":"61.5"}]}}]  //  明日から7日先までの降水量合計範囲(mm)

神奈川県の天気概況

https://www.jma.go.jp/bosai/forecast/data/overview_forecast/140000.json

{"publishingOffice":"横浜地方気象台",
"reportDatetime":"2021-06-17T10:57:00+09:00",
"targetArea":"神奈川県",
"headlineText":"東部では、17日夜のはじめ頃まで土砂災害に注意してください。神奈川県では、17日夜のはじめ頃まで落雷に注意してください。",
"text":" 前線が、華中から東シナ海を通って日本の南へのびています。また、前線上の伊豆諸島の南には低気圧があって、北東へ進んでいます。\n\n
 神奈川県は、おおむね曇りとなっています。\n\n
 17日は、上空の寒気や湿った空気の影響により、曇りで、夜のはじめ頃にかけて雨や雷雨となる所があるでしょう。\n\n 
18日は、高気圧に緩やかに覆われますが、湿った空気の影響により、曇りで、東部では、明け方まで晴れる見込みです。\n\n
 神奈川県の海上では、17日は波がやや高いでしょう。18日は多少波がある見込みです。"}

関東週間天気予報

https://www.jma.go.jp/bosai/forecast/data/overview_week/130000.json

{"publishingOffice":"気象庁",
"reportDatetime":"2021-06-17T10:35:00+09:00",
"headTitle":"関東甲信地方週間天気予報",
"text":"予報期間 6月18日から6月24日まで\n
 向こう一週間は、前線や湿った空気の影響で曇りや雨の日が多いでしょう。\n
 最高気温と最低気温はともに、平年並か平年より低い日が多いですが、期間の前半は平年より高い所もある見込みです。\n
 降水量は、平年並か平年より多いでしょう。"}

概略天気

見つけられなかったので、天気マークから判断しています。

表示内容

・横浜の予報です。
・ボタンを下へ回すと下の項目へ、上へ回すと上の項目へ移動します。
・3秒押すと反応します。
・概略天気は天気マークから判断しているので、ホームページと少し違う場合があります。例)(HP)曇後一時雨 → (M5)曇後雨

[0] 今日の予報
[1] 明日の予報
[2] 明後日の予報
[3] 3日先の予報
[4] 4日先の予報
[5] 5日先の予報
[6] 6日先の予報
[7] 7日先の予報
無い場合は次へスキップします。
[8] 神奈川県の予報
1頁目
2頁目
3頁目
[9] 関東の予報
長くても最初の1頁のみ表示します。
[0]へ戻る。

[7]でデータが無い場合は次に移動します。 [8]は3頁あるのでボタンを押し込むと次の頁へ移動します。
[8]は、3頁で表示できる分のみ表示します。
[9]は、1頁分のみ表示します。
その時のホームページ
朝開いた時(2日/7日)
同日の昼に開いた時(3日/8日)

スケッチ

2ヶ所の*****は自分のwifiのSSIDとそのwifiのパスワードを記入してください。

// 天気情報 気象庁https://www.jma.go.jp/bosai/forecast/#area_type=offices&area_code=140000
// 気象庁ホームページからjsonデータを得てCoreInkに表示
#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 = "*****";     // 自分のwifiのSSID
const char* password = "*****"; // wifiのパスワード
const char* weatherURL = "https://www.jma.go.jp/bosai/forecast/data/forecast/140000.json";
// 気象庁 天気予報 神奈川県 横浜市
const char* dayURL = "https://www.jma.go.jp/bosai/forecast/data/overview_forecast/140000.json";
// 気象庁 神奈川県 天気概況
const char* weekURL = "https://www.jma.go.jp/bosai/forecast/data/overview_week/130000.json";
// 気象庁 関東甲信地方週間天気予報

DynamicJsonDocument weatherInfo(20000);  //
Ink_Sprite InkPageSprite(&M5.M5Ink);     // 定義
static LGFX_Sprite sprite;               // 定義
int wDay = 0;  // 0=今日,1=明日,2=明後日,・・・,7=7日先,8=神奈川県予報,9=関東予報
int bMid = 1, bMidN = 396;  // bunDayの頁(1-3),1頁の文字数(12字x11行x3?=396)
String myd;                 // 日付
String minTmpL, minTmp, minTmpH, maxTmpL, maxTmp, maxTmpH ;//最低気温,最高気温
String rain00_06, rain06_12, rain12_18, rain18_24;         // 降水確率
String weather_d;    // 天気
String reliability;  // 信頼度
String weatherCode;  // 天気マーク
String today;        // json用文字列
String nenTmpMin, nenTmpMax; // 平年値 気温
String nenPreMin, nenPreMax; // 平年値 降水量
char buf1[50];               // 表示用文字列
String bunDay, bunWeek;      // 2文
int hyou = 3;                // 2:2日/6日, 3:3日/7日

// 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(dayURL);        // ◆dayURL アクセスするURLを登録
    //Serial.print("dayULR = "); Serial.println(dayURL);//シリアルモニタに表示
    int httpCodeD = http.GET();//登録URLに、GETリクエスト送信
    if (httpCodeD > 0) {       //HTTPのリターンコード。エラーの場合は負数。
      String jsonStringD = createJson(http.getString());
      //メッセージのボディ部,確保できなかった場合は空文字列,jsonオブジェクトの作成
      Serial.println(jsonStringD);  // 読込みデータ表示
      bunDay = jsonStringD;         // dayURLのjson文字列を保存しておく
    }

    http.begin(weekURL);       // ◆weekURL アクセスするURLを登録
    //Serial.print("weekULR = "); Serial.println(dayURL);//シリアルモニタに表示
    int httpCodeW = http.GET();//登録URLに、GETリクエスト送信
    if (httpCodeW > 0) {//HTTPのリターンコード。エラーの場合は負数。
      String jsonStringW = createJson(http.getString());
      //メッセージのボディ部,確保できなかった場合は空文字列,jsonオブジェクトの作成
      Serial.println(jsonStringW); // 読込みデータ表示
      bunWeek = jsonStringW;       // weekURLのjson文字列を保存しておく
    }

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

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

String mojiHenkan(String moji) { // 文字変換
  moji.replace(" ", "");        // 全角空白を削除
  moji.replace("メートル", "m");  // 波で使用
  moji.replace("0", "0");  // 半角変換 波で使用
  moji.replace("1", "1");  //
  moji.replace("2", "2");  //
  moji.replace("3", "3");  //
  moji.replace("4", "4");  //
  moji.replace("5", "5");  //
  moji.replace("6", "6");  //
  moji.replace("7", "7");  //
  moji.replace("8", "8");  //
  moji.replace("9", "9");  //
  moji.replace(".", ".");  //
  moji.replace("-", "/");   // "-" → "/" 時刻で使用
  moji.replace("T", " ");   // "T" → " " 時刻で使用
  moji.replace("\n", "");   // 予報文で使用
  return moji;
}

void drawWeather(String infoWeather) { // 1画面セットする
  DynamicJsonDocument doc(20000);      //
  deserializeJson(doc, infoWeather);   // 直列化されたデータを
  // DynamicJsonDocumentの形に変換
  sprite.clear(TFT_WHITE); // 全体の背景色を白
  Serial.println();        // 改行

  // 詳細予報表と週間予報表の日数判定
  int i, count = 0;                         // 初期設定
  String timeDefine = doc[0]["timeSeries"][0]["timeDefines"]; //詳細日配列
  for (i = 0; timeDefine[i] != '\0'; i++) { // 文字列の最初から最後まで
    if (timeDefine[i] == ',') {             // ','の数をカウント
      count++;                              // 1カウントする
    }
  }
  hyou = count + 1;               // 2:詳細2日/週間7日,3:3日/8日

  if (wDay < 8) {                 // 天気マーク付画面の場合
    // 日付
    sprite.setCursor(0, 1);       // 表示位置(x,y)
    sprintf(buf1, "[%d] ", wDay); // 何日目
    sprite.print(buf1); Serial.print(buf1);  // 表示

    if (wDay == 0) {                         // ◆日付 今日
      String A = doc[0]["reportDatetime"];   // 発表月日時分
      myd = A; myd = mojiHenkan(myd);        // 文字変換
      sprite.print(myd.substring(5, 16));    // 月日時分
      Serial.println(myd.substring(5, 16)); //4+1文字目から16文字目までを表示
    } else {                                 // ◆日付 明日-7日先
      String A = doc[1]["timeSeries"][0]["timeDefines"][wDay + 2 - hyou]; //予報日
      myd = A; myd = mojiHenkan(myd);        // 文字変換
      sprite.print(myd.substring(0, 10));    // 年月日
      Serial.println(myd.substring(0, 10));  // 0+1文字目から10文字目までを表示
    }

    // 信頼度
    sprite.setCursor(140, 1);          // 表示位置
    if (wDay < 2 || (wDay == 2 && hyou == 3)) { // ◆信頼度 今日,明日,2日でhyou=3の時
      Serial.println("信頼:-");        // シリアルモニタへ表示 画面表示無し
    } else {                           // 2日でhyou=2 or 3-7日先
      String A = doc[1]["timeSeries"][0]["areas"][0]["reliabilities"][wDay + 2 - hyou]; //信頼度
      reliability = A;
      if (reliability == "A") {        // A→高とした
        reliability = "信頼:高";
      } else if (reliability == "B") { // B→中とした
        reliability = "信頼:中";
      } else {                         // C→低とした
        reliability = "信頼:低";
      }
      sprite.print(reliability); Serial.println(reliability); // 表示
    }

    // 天気マーク(天気文に少し消される)
    int x = 64, y = 50, W = 72, H = 72; // 天気マークの表示位置と大きさ
    int wNo;                            // 天気マークNo.
    if (wDay < 2) {                     // ◆マーク 今日と明日
      String A = doc[0]["timeSeries"][0]["areas"][0]["weatherCodes"][wDay];//天気コード
      weatherCode = A;                  // 天気マーク
    } else {                            // ◆マーク 2-7日先
      String A = doc[1]["timeSeries"][0]["areas"][0]["weatherCodes"][wDay + 2 - hyou];//天気コード
      weatherCode = A;                  // 天気マーク
    }
    wNo = weatherCode.toInt();          // String→整数 に変換
    switch (wNo) {
      case 302:  // 雨時々曇
      case 301:  // 雨時々晴
      case 202:  // 曇一時雨
      case 203:  // 曇時々雨
      case 102:  // 晴時々雨
      case 112:  // 晴後雨
      case 502:  // 月時々雨
      case 212:  // 曇後雨
      case 214:  // 曇昼過ぎから雨
        sprite.pushImage(x, y, W, H, imgKumoriAme); // くもり雨
        break;
      case 300:  // 雨
        sprite.pushImage(x, y, W, H, imgAme); // 雨
        break;
      case 101:  // 晴時々曇
      case 501:  // 月時々曇
      case 201:  // 曇時々晴
      case 110:  // 晴後曇
      case 510:  // 月後曇
      case 210:  // 曇後晴
      case 610:  // 曇後月
        sprite.pushImage(x, y, W, H, imgHareKumori); // 晴れくもり
        break;
      case 100:  // 晴
      case 500:  // 月
        sprite.pushImage(x, y, W, H, imgHare); // 晴れ
        break;
      case 400:  // 雪
      case 401:  // 雪時々晴
      case 402:  // 雪時々曇
      case 403:  // 雪時々雨
      case 303:  // 雨時々雪
        sprite.pushImage(x, y, W, H, imgYuki); // 雪
        break;
      case 200:  // 曇
        sprite.pushImage(x, y, W, H, imgKumori); // くもり
        break;
    }

    // 天気文
    sprite.setCursor(0, 20);  // 表示位置
    if (wDay < 2 || (wDay == 2 && hyou == 3)) {  // ◆天気文 今日,明日,2日でhyou=3の時
      String A = doc[0]["timeSeries"][0]["areas"][0]["weathers"][wDay]; // 詳細天気
      weather_d = A; weather_d = mojiHenkan(weather_d);                 // 文字変換
      sprite.print(weather_d); Serial.println(weather_d);               // 表示
    } else {                                     // ◆天気文 2日でhyou=2の時,3-7日先
      String A = doc[1]["timeSeries"][0]["areas"][0]["weatherCodes"][wDay + 2 - hyou];//詳細天気
      weather_d = A;
      if (weather_d == "100") {
        weather_d = "晴";
      } else if (weather_d == "101") {
        weather_d = "晴時々曇";
      } else if (weather_d == "102") {
        weather_d = "晴一時雨";
      } else if (weather_d == "110") {
        weather_d = "晴後曇";
      } else if (weather_d == "112") {
        weather_d = "晴後雨";
      } else if (weather_d == "200") {
        weather_d = "曇";
      } else if (weather_d == "201") {
        weather_d = "曇時々晴";
      } else if (weather_d == "202") {
        weather_d = "曇一時雨";
      } else if (weather_d == "203") {
        weather_d = "曇時々雨";
      } else if (weather_d == "210") {
        weather_d = "曇後晴";
      } else if (weather_d == "212") {
        weather_d = "曇後雨";
      } else if (weather_d == "300") {
        weather_d = "雨";
      } else if (weather_d == "301") {
        weather_d = "雨時々晴";
      } else if (weather_d == "302") {
        weather_d = "雨時々曇";
      } else if (weather_d == "303") {
        weather_d = "雨時々雪";
      } else if (weather_d == "400") {
        weather_d = "雪";
      } else if (weather_d == "401") {
        weather_d = "雪時々晴";
      } else if (weather_d == "402") {
        weather_d = "雪時々曇";
      } else if (weather_d == "403") {
        weather_d = "雪時々雨";
      } else if (weather_d == "500") {
        weather_d = "月";
      } else if (weather_d == "501") {
        weather_d = "月時々曇";
      } else if (weather_d == "502") {
        weather_d = "月時々雨";
      } else if (weather_d == "510") {
        weather_d = "月後曇";
      } else if (weather_d == "610") {
        weather_d = "曇後月";
      } else {
        weather_d = "不明";  // あったら追加していく
      }
      sprite.print(weather_d); Serial.println(weather_d); // 表示
    }

    // ◆場所 (横浜のみ)
    sprite.setCursor(1, 100);                               // 表示位置
    String region = doc[0]["timeSeries"][2]["areas"][0]["area"]["name"]; //横浜
    sprite.printf("(%s)", region); Serial.println(region);  // 表示

    // 降水確率
    sprite.setCursor(0, 120);  // 表示位置
    if (wDay == 0) {           // ◆降水確率 今日
      int i, count = 0;        // 初期設定
      String pop = doc[0]["timeSeries"][1]["areas"][0]["pops"]; // 降水確率配列
      for (i = 0; pop[i] != '\0'; i++) { // 文字列の最初から最後まで
        if (pop[i] == ',') {             // ','の数をカウント
          count++;                       // 1カウントする
        }
      }
      if (count == 7) {                                            // n=8の場合
        String A = doc[0]["timeSeries"][1]["areas"][0]["pops"][0]; // 降水確率00_06
        String B = doc[0]["timeSeries"][1]["areas"][0]["pops"][1]; // 降水確率06_12
        String C = doc[0]["timeSeries"][1]["areas"][0]["pops"][2]; // 降水確率12_18
        String D = doc[0]["timeSeries"][1]["areas"][0]["pops"][3]; // 降水確率18_24
        rain00_06 = A; rain06_12 = B;
        rain12_18 = C; rain18_24 = D;
      } else if (count == 6) {                                     // n=7の場合
        String B = doc[0]["timeSeries"][1]["areas"][0]["pops"][0]; // 降水確率06_12
        String C = doc[0]["timeSeries"][1]["areas"][0]["pops"][1]; // 降水確率12_18
        String D = doc[0]["timeSeries"][1]["areas"][0]["pops"][2]; // 降水確率18_24
        rain00_06 = "--"; rain06_12 = B;
        rain12_18 = C; rain18_24 = D;
      } else if (count == 5) {                                     // n=6の場合
        String C = doc[0]["timeSeries"][1]["areas"][0]["pops"][0]; // 降水確率12_18
        String D = doc[0]["timeSeries"][1]["areas"][0]["pops"][1]; // 降水確率18_24
        rain00_06 = "--"; rain06_12 = "--";
        rain12_18 = C; rain18_24 = D;
      } else if (count == 4) {                                     // n=5の場合
        String D = doc[0]["timeSeries"][1]["areas"][0]["pops"][0]; // 降水確率18_24
        rain00_06 = "--"; rain06_12 = "--";
        rain12_18 = "--"; rain18_24 = D;
      }
      sprintf(buf1, "(0-)%02s(6)%02s(12)%02s(18)%02s%%",
              rain00_06, rain06_12, rain12_18, rain18_24);//
      sprite.print(buf1); Serial.print("降水確率:"); Serial.println(buf1); // 表示
    } else if (wDay == 1) {                                     // ◆降水確率 明日
      int i, count = 0;                                         // 初期設定
      String pop = doc[0]["timeSeries"][1]["areas"][0]["pops"]; // 降水確率配列
      for (i = 0; pop[i] != '\0'; i++) { // 文字列の最初から最後まで
        if (pop[i] == ',') {             // ','の数をカウント
          count++;                       // count=7(n=8)-4(n=5)を想定
        }
      }
      String A = doc[0]["timeSeries"][1]["areas"][0]["pops"][count - 3];//降水確率00_06
      String B = doc[0]["timeSeries"][1]["areas"][0]["pops"][count - 2];//降水確率06_12
      String C = doc[0]["timeSeries"][1]["areas"][0]["pops"][count - 1];//降水確率12_18
      String D = doc[0]["timeSeries"][1]["areas"][0]["pops"][count];    //降水確率18_24
      rain00_06 = A; rain06_12 = B; rain12_18 = C; rain18_24 = D;
      sprintf(buf1, "(0-)%02s(6)%02s(12)%02s(18)%02s%%",
              rain00_06, rain06_12, rain12_18, rain18_24);  //
      sprite.print(buf1); Serial.print("降水確率:"); Serial.println(buf1); // 表示
    } else {                                    // ◆降水確率 2-7日
      String pop = doc[1]["timeSeries"][0]["areas"][0]["pops"][wDay + 2 - hyou]; // 降水確率
      sprintf(buf1, "降水確率:%02s%%", pop);     //
      sprite.print(buf1); Serial.println(buf1); // 表示
    }

    // 気温
    sprite.setCursor(1, 136);  // 表示位置
    if (wDay == 0) {           // ◆気温 今日
      int i, count = 0;        // 初期設定
      String hairetu = doc[0]["timeSeries"][2]["areas"][0]["temps"]; // 気温配列
      for (i = 0; hairetu[i] != '\0'; i++) { // 文字列の最初から最後まで
        if (hairetu[i] == ',') {             // ','の数をカウント
          count++;                           // 1カウントする
        }
      }
      if (count == 3) {                                             // n=4
        String A = doc[0]["timeSeries"][2]["areas"][0]["temps"][0]; // 最低気温
        String B = doc[0]["timeSeries"][2]["areas"][0]["temps"][1]; // 最高気温
        minTmp = A; maxTmp = B;                                     // 最低・最高気温
      } else if (count == 2) {                                      // n=3
        String B = doc[0]["timeSeries"][2]["areas"][0]["temps"][0]; // 最高気温
        minTmp = "--"; maxTmp = B;               // 最低・最高気温
      } else if (count == 1) {                   // n=2
        minTmp = "--"; maxTmp = "--";            // 最低・最高気温
      }
      if (minTmp == maxTmp) {                    // 今日の最低気温=最高気温の場合
        minTmp = "--";                           // 今日の最低気温
      }
      sprintf(buf1, "朝の最低%02s 日中の最高%02s℃", minTmp, maxTmp); //
      sprite.printf(buf1); Serial.println(buf1); // 表示
    } else if (wDay == 1) {                      // ◆気温 明日
      int i, count = 0;                          // 初期設定
      String hairetu = doc[0]["timeSeries"][2]["areas"][0]["temps"];  //気温配列
      for (i = 0; hairetu[i] != '\0'; i++) { // 文字列の最初から最後まで
        if (hairetu[i] == ',') {             // ','の数をカウント
          count++;                           // 1カウントする
        }
      }
      if (count == 3) {                                             // n=4
        String A = doc[0]["timeSeries"][2]["areas"][0]["temps"][2]; // 最低気温
        String B = doc[0]["timeSeries"][2]["areas"][0]["temps"][3]; // 最高気温
        minTmp = A; maxTmp = B;                                     // 最低・最高気温
      } else if (count == 2) {                                      // n=3
        String A = doc[0]["timeSeries"][2]["areas"][0]["temps"][1]; // 最低気温
        String B = doc[0]["timeSeries"][2]["areas"][0]["temps"][2]; // 最高気温
        minTmp = A; maxTmp = B;                                     // 最低・最高気温
      } else if (count == 1) {                                      // n=2
        String A = doc[0]["timeSeries"][2]["areas"][0]["temps"][0]; // 最低気温
        String B = doc[0]["timeSeries"][2]["areas"][0]["temps"][1]; // 最高気温
        minTmp = A; maxTmp = B;                                     // 最低・最高気温
      }
      sprintf(buf1, "朝の最低%02s 日中の最高%02s℃", minTmp, maxTmp);  //
      sprite.printf(buf1); Serial.println(buf1);                    // 表示
    } else {                                                        // ◆気温 2-7日
      String A = doc[1]["timeSeries"][1]["areas"][0]["tempsMin"][wDay + 2 - hyou]; //最低typ
      String B = doc[1]["timeSeries"][1]["areas"][0]["tempsMinUpper"][wDay + 2 - hyou]; //最低H
      String C = doc[1]["timeSeries"][1]["areas"][0]["tempsMinLower"][wDay + 2 - hyou]; //最低L
      minTmp = A; minTmpH = B; minTmpL = C;                                   // 最低気温
      String D = doc[1]["timeSeries"][1]["areas"][0]["tempsMax"][wDay + 2 - hyou]; //最高typ
      String E = doc[1]["timeSeries"][1]["areas"][0]["tempsMaxUpper"][wDay + 2 - hyou]; //最高H
      String F = doc[1]["timeSeries"][1]["areas"][0]["tempsMaxLower"][wDay + 2 - hyou]; //最高L
      maxTmp = D; maxTmpH = E; maxTmpL = F;                                   // 最高気温
      sprintf(buf1, "↓%02s℃(%02s-%02s)  ↑%02s℃(%02s-%02s)",
              minTmp, minTmpL, minTmpH, maxTmp, maxTmpL, maxTmpH);
      sprite.print(buf1); Serial.println(buf1);                               // 表示
    }

    // 風と波
    sprite.setCursor(1, 152);                  // 表示位置
    if (wDay < 2 || (wDay == 2 && hyou == 3)) {//◆風 0-1日,2日でhyou=3の時,3日-は無し
      String wind = doc[0]["timeSeries"][0]["areas"][0]["winds"][wDay];  // 風
      wind = mojiHenkan(wind); wind = "風:" + wind; // 文字変換
      sprite.print(wind); Serial.println(wind);     // 表示

      String wave = doc[0]["timeSeries"][0]["areas"][0]["waves"][wDay]; // 波
      wave = mojiHenkan(wave); wave = "波:" + wave;                     // 文字変換
      sprite.print(" "); sprite.print(wave); Serial.println(wave);      // 表示
    }

    //平年値
    sprite.setCursor(1, 160);                    // 表示位置
    if (wDay > 2 || (wDay == 2 && hyou == 2)) {  // 風と波の表示が無い時
      String A = doc[1]["tempAverage"]["areas"][0]["min"]; // ◆平年 最低温度
      String B = doc[1]["tempAverage"]["areas"][0]["max"]; // ◆平年 最高温度
      String C = doc[1]["precipAverage"]["areas"][0]["min"];//◆平年 降水min
      String D = doc[1]["precipAverage"]["areas"][0]["max"];//◆平年 降水max
      nenTmpMin = A; nenTmpMax = B; nenPreMin = C; nenPreMax = D;
      sprintf(buf1, "平年最低%02.0f℃  最高%02.0f℃",
              nenTmpMin.toFloat(), nenTmpMax.toFloat());
      sprite.print(buf1); Serial.println(buf1);  // 表示
      sprite.setCursor(1, 180);                  // 表示位置
      sprintf(buf1, "平年7日間合計降水%02.0f-%02.0fmm",
              nenPreMin.toFloat(), nenPreMax.toFloat());
      sprite.print(buf1); Serial.println(buf1);  // 表示
    }

  } else if (wDay == 8) {               // ◆神奈川県予報
    sprite.setCursor(0, 0);             // 表示位置
    String A = doc[2]["headlineText"];  // ヘッドライン
    String B = doc[2]["text"];          // text
    A = "[8] " + A + B; A = mojiHenkan(A);            // 文字変換
    A = A.substring((bMid - 1) * bMidN, bMid * bMidN);//bMidN文字分表示
    sprite.print(A); Serial.println(A); // 表示

  } else {                              // ◆関東予報 wDay=9
    sprite.setCursor(0, 0);             // 表示位置
    String A = doc[3]["headTitle"];     // タイトル
    String B = doc[3]["text"];          // text
    A = "[9] " + A + ":" + B; A = mojiHenkan(A); // 文字変換
    sprite.print(A); Serial.println(A); // 表示 144文字のみ
  }
}

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[0] + weatherInfo[1];
  String A = weatherInfo[0];  // 明後日まで
  String B = weatherInfo[1];  // 7日先まで
  today = "[" + A + "," + B + "," + bunDay + "\"}," + bunWeek + "\"}]";
  Serial.println("today = "); Serial.println(today); //シリアルモニタに表示
  drawWeather(today);                   // 1画面設定関数へ
  pushSprite(&InkPageSprite, &sprite);  // 1画面表示関数へ
}

void loop() {
  if (M5.BtnUP.wasPressed()) { // ボタンを上に回したら
    if (wDay > 0) {            // 最初の頁でなければ
      wDay--;                  // 前の頁へ
      if (wDay == 8) {         // 8へ戻ったら
        bMid = 1;              // 1頁目(を表示)
      } else if (hyou == 2 && wDay == 7) { // hyou=2でwDay=7は無いので
        wDay--;                // 1つ前へスキップ
      }
    } else {                   // 最初の頁だったら
      wDay = 9;                // 最後の頁
    }
    drawWeather(today);        // 1画面設定関数へ
    pushSprite(&InkPageSprite, &sprite); // 1画面表示関数へ
  }
  if (M5.BtnDOWN.wasPressed()) { // ボタンを下に回したら
    if (wDay < 10) {             // 最後の頁でなければ
      wDay++;                    // 次の頁へ
      if (wDay == 8) {           // 8へ戻ったら
        bMid = 1;                // 1頁目(を表示)
      } else if (hyou == 2 && wDay == 7) { // hyou=2でwDay=7は無いので
        wDay++;                     // 1つ次へスキップ
      }
    } else {                     // 最後の頁だったら
      wDay = 0;                  // 最初の頁
    }
    drawWeather(today);          // 1画面設定関数へ
    pushSprite(&InkPageSprite, &sprite); // 1画面表示関数へ
  }
  if (M5.BtnMID.wasPressed()) { // ボタンを押し込んだら(8の時のみ使用)
    if (bMid < 3) {             // 1頁か2頁なら
      bMid++;                   // 次の頁へ
    } else {                    // 3頁目なら
      bMid = 1;                 // 1頁へ戻る
    }                           // bMid=1→2→3→1→2→・・・
    drawWeather(today);         // 1画面設定関数へ
    pushSprite(&InkPageSprite, &sprite); // 1画面表示関数へ
  }
  //if (M5.BtnUP.pressedFor(2000)) {   // ボタンを2秒間上に回したら
  //if (M5.BtnDOWN.pressedFor(2000)) { // ボタンを2秒間下に回したら
  //if (M5.BtnEXT.wasPressed()) {      // 上面ボタンを押したら
  //if (M5.BtnPWR.wasPressed()) {      // 電源ボタンを押したら(電源off)
  M5.update();                         // ボタンとブザーを更新
  delay(500);                          // 0.5秒待つ
}