ゆるく楽しむプログラミング
> トップページ > 天気予報 > M5Stackで天気予報表示

M5Stackで天気予報

M5Stack BASIC / Core2で気象庁の天気予報情報を表示してみました。


基本構成

基本構成(基本設計)
・使用するマイコンはM5Stack BASICを前提とする ※Core2でも表示できるように考慮する。
・日本語表示は東雲フォントをSDカードに入れて使用する
・お天気アイコンはSDカードにJPEG画像を入れて使用する
・表示する予報区、Wi-Fiの設定情報はSDカードにJSON形式で保存する

SDカードから定義情報を読み込む

定義情報はJSON形式で記録しますので、JSON形式を扱うためにJSON形式のライブラリをインポートします。
使用するのは Arduino Json by Benoit Blanchon です。
コレを選んだのには特に深い意味はなくドレでも良いようにも思います。



動作環境定義

動作定義 propaty.json ファイルを定義します。
{
  "APLName": "WeatherForecast",
  "Ver": "1",
  "WiFi": {
    "SSID": "SSID",
    "PWD": "xxxxxxx"
  },
  "Forecast": {
    "URL": "https://www.jma.go.jp/bosai/forecast/data/forecast/",
    "RCode": "140000",
    "RName": "神奈川県",
    "ACode": "140010",
    "CName": "横浜市"
  }
}



コレをJSONライブラリを使用して、必要なパラメタを取得します。
ざっくり、大まかな動作はこんな感じで、読めるのが確認できればOK
 if (!SD.exists(PROPATY_FILE_NAME)) {
      return false ;
  }  
  // --- File Load ---
  File file = SD.open(PROPATY_FILE_NAME);
  if (!file) {
    return false ;
  }

  JsonDocument doc;
  DeserializationError error = deserializeJson(doc, file);
  file.close();
  if (error) {
    Serial.print(F("deserializeJson() failed: "));
    Serial.println(error.f_str());
    return false ;
  }

  String appName = doc["APLName"] ;
  String appVer = doc["Ver"] ;
  if (appName != "WeatherForecast" || appVer != "1") {
    return false ;
  }
  
  wifiAP = doc["WiFi"]["SSID"].as<String>() ;        // Wi-Fi アクセスポイント
  wifiPWD = doc["WiFi"]["PWD"].as<String>() ;   // Wi-Fi パスワード
  weatherURL = doc["Forecast"]["URL"].as<String>() ;   // 天気予報取得用URL
  regionCode = doc["Forecast"]["RCode"].as<String>() ; // 地域コード
  areaCode = doc["Forecast"]["ACode"].as<int>() ;     // 予報区コード
  regionName = doc["Forecast"]["RName"].as<String>() ; // 地域コード
  cityName = doc["Forecast"]["CName"].as<String>() ;     // 予報区コード

  Serial.println("--- PROPATY DATA");
  Serial.println(appName) ;
  Serial.println(appVer) ;
//  Serial.println(wifiAP) ;
//  Serial.println(wifiPWD) ;
  Serial.println(weatherURL) ;
  Serial.println(regionCode) ;
  Serial.println(areaCode) ;
  Serial.println(regionName) ;
  Serial.println(cityName) ;

 ※ JSONの読み込みスケッチはChatGPTで次のような質問をすればスケッチ作ってくれますので、ちょっと手直しすれば簡単に出来ます。
   ArduinoJsonでJSONを読み込むスケッチについてはかなり高精度な回答を返してくれるので、読み取り対象のJSONを添付してコードをChatGPTにコードを書いてもらえば
   大きく手間が省けます。
M5StackでSDカードに入れたpropaty.jsonファイルをArduinoJsonを使って読み取るコードを書いて。
読み取るJSONは

{
  "APLName": "WeatherForecast",
  "Ver": "1",
  "WiFi": {
    "SSID": "SSID",
    "PWD": "xxxxxxx"
  },
  "Forecast": {
    "URL": "https://www.jma.go.jp/bosai/forecast/data/forecast/",
    "RCode": "140000",
    "RName": "神奈川県",
    "ACode": "140010",
    "CName": "横浜市"
  }
}
  

天気予報データの取得


気象庁の天気予報サイトで表示されている天気予報のデータはJSON形式で取得できます。
この、JSONデータを取得・加工して天気予報を表示します。
正式に公開されたAPIではなく、Webページ表示用のインターフェイスを勝手に使用するので
ある日突然使えなくなる、突然仕様が変わる可能性があります。

天気予報データ(JSON形式)は▼にアクセスすると取得できます。
https://www.jma.go.jp/bosai/forecast/data/forecast/{天気予報用地域コード}.json

天気予報地区コードの一覧は jma.go.jp/bosai/common/const/area.json から得ることが出来ますが、コレを読み解くのは結構大変なので、
緯度経度から気象庁の天気予報用地域コードを取得するWebAPI から取得すること楽です。
観測地の緯度{lat}経度{lon} の値は、Googlemapで観測地点を右クリックすれば表示されます。


取得した緯度経度を使用して
https://revgeo-forecastcode.herokuapp.com/lat={lat}+lon={lon} にアクセス
forecastcode が 天気予報用地域コード
class10s が 地域コード(西部とか東部とか○○地方とかの区域)
{"forecastcode":"140000","prefname":"神奈川県","cityname":"箱根町","centers":"010300","offices":"140000","class10s":"140020","class15s":"140024","class20s":"1438200"}

天気予報用地域コードを使ってJSONを取得
https://www.jma.go.jp/bosai/forecast/data/forecast/140000.json
返ってきたJSONの "area":{"name":"西部","code":"140020"} が class10s と同じ areaが目的のデータです。

https://www.jma.go.jp/bosai/forecast/data/forecast/140000.json
で得られるJSONは▼です。
この例では神奈川県の東部と西部に分かれていますが、今回必要としているのは class10s:140020(神奈川県 西部)の情報なので 赤字の箇所が対象となります。
後半は週間予報と平年の気温・降水量です。
[
  { "publishingOffice": "横浜地方気象台",
    "reportDatetime": "2025-06-16T05:00:00+09:00",
    "timeSeries": [
      { "timeDefines": ["2025-06-16T05:00:00+09:00","2025-06-17T00:00:00+09:00"],
        "areas": [
          {"area": {"name": "東部", "code": "140010" },
            "weatherCodes": ["201","101"],
            "weathers": ["くもり 時々 晴れ","晴れ 朝晩 くもり"],
            "winds": ["南東の風 後 南の風 海上 では 南西の風 やや強く","南西の風 後 南の風 海上 では 南西の風 やや強く"],
            "waves": ["1メートル 後 1.5メートル","1.5メートル" ]
          },
          {"area": {"name": "西部", "code": "140020" },
            "weatherCodes": ["201","101" ],
            "weathers": ["くもり 時々 晴れ","晴れ 朝晩 くもり"],
            "winds": ["南東の風 後 南の風 海上 では 南西の風 やや強く","南の風 海上 では 南西の風 やや強く"],
            "waves": ["1メートル 後 1.5メートル","1.5メートル"]
          }
        ]
      },
      {"timeDefines": ["2025-06-16T06:00:00+09:00","2025-06-16T12:00:00+09:00","2025-06-16T18:00:00+09:00",
          "2025-06-17T00:00:00+09:00","2025-06-17T06:00:00+09:00","2025-06-17T12:00:00+09:00","2025-06-17T18:00:00+09:00"],
        "areas": [
          {"area": {"name": "東部","code": "140010"},
            "pops": ["10","10","0","0","0","10","10"]
          },
          {"area": {"name": "西部","code": "140020"},
            "pops": ["10","10","0","0","0","10","10"]
          }
        ]
      },
      {"timeDefines": ["2025-06-16T09:00:00+09:00","2025-06-16T00:00:00+09:00","2025-06-17T00:00:00+09:00","2025-06-17T09:00:00+09:00"],
        "areas": [
          {"area": {"name": "横浜","code": "46106"},
            "temps": ["30","30","24","32"]
          },
          { "area": {"name": "小田原","code": "46166"},
            "temps": ["31","31","24","34"]
          }
        ]
      }
    ]
  },
  { "publishingOffice": "横浜地方気象台",
    "reportDatetime": "2025-06-15T17:00:00+09:00",
    "timeSeries": [
      {"timeDefines": ["2025-06-16T00:00:00+09:00","2025-06-17T00:00:00+09:00","2025-06-18T00:00:00+09:00",
          "2025-06-19T00:00:00+09:00","2025-06-20T00:00:00+09:00","2025-06-21T00:00:00+09:00","2025-06-22T00:00:00+09:00"],
        "areas": [
          {"area": {"name": "神奈川県","code": "140000"},
            "weatherCodes": ["210","101","201","201","201","201","201"],
            "pops": ["","20","30","30","30","30","30"],
            "reliabilities": ["","","A","A","A","A","A"]
          }
        ]
      },
      {"timeDefines": ["2025-06-16T00:00:00+09:00","2025-06-17T00:00:00+09:00","2025-06-18T00:00:00+09:00",
          "2025-06-19T00:00:00+09:00","2025-06-20T00:00:00+09:00","2025-06-21T00:00:00+09:00","2025-06-22T00:00:00+09:00"],
        "areas": [
          {"area": {"name": "横浜","code": "46106"},
            "tempsMin": ["","24","25","24","23","23","24"],
            "tempsMinUpper": ["","26","26","25","25","25","25"],
            "tempsMinLower": ["","23","23","23","21","21","22"],
            "tempsMax": ["","33","33","31","30","29","30"],
            "tempsMaxUpper": ["","35","35","33","32","31","32"],
            "tempsMaxLower": ["","31","31","29","28","27","27"]
          }
        ]
      }
    ],
    "tempAverage": {
      "areas": [
        {"area": {"name": "横浜","code": "46106"},
          "min": "19.5",
          "max": "25.6"
        }
      ]
    },
    "precipAverage": {
      "areas": [
        { "area": {"name": "横浜","code": "46106" },
          "min": "28.7",
          "max": "63.3"
        }
      ]
    }
  }
]

この赤字部分を取り出して整理して、こんな形にします。
天気予報データのJSONの読み取りに関しては、ChatGPTにコードを書いてもらうより、ArduinoJsonのサンプルスケッチを参考にした方が手っ取り早いです。
--- 天気予報情報 ---
横浜地方気象台 - 西部
06/16 201
  くもり 時々 晴れ
  降水確率: --  10  10   0 

06/17 101
  晴れ 朝晩 くもり
  降水確率:  0   0  10  10 


お天気アイコンの表示

気象コードと表示アイコンの対応は https://qiita.com/nak435/items/7f3588d3f75beb5890fa などに纏められています。


この対応を見ると 3ケタで各桁をABCとすると
ABCそれぞれが1:;晴れ 2:曇り 3:雨 4:雪 となっています。
Bが0の場合「〇〇 時々(一時) △△」 1の場合「〇〇 のち △△」
Cが0の場合「〇〇(Aのコードに対応)」となります。
ABCに対応するアイコンをJPEG形式で作成し、気象コードに合わせて合成すれば、アイコン表示に対応できます。


M5クラスを使えば M5.Lcd.drawJpgFile(SD,"/image1.jpg"); の1行でJPEG画像を表示できます
ここで、イメージの展開先をLCDではなくメモリーバッファに変更できれば、画像を加工して使用する事ができます。

ArduinoIDEのESP32系マイコンのボードライブラリにはJPEGイメージをBITMAPに展開するオープンスースのライブラリが入っています
 ライブラリ名:TJpgDec - Tiny JPEG Decompressor
 作者:ChaN氏
 ライセンス:BSD
 ソース:http://elm-chan.org/fsw/tjpgd/00index.html
このライブラリの使用方法は Arduino core ESP32 内のライブラリでJPEGファイルをビットマップに変換(デコード)する を参考にしました。

読み込んだJPEG画像の黒(RGB=0,0,0)を透過色として合成しますが、JPEG画像作成時の圧縮ノイズの為に、黒(RGB=0,0,0)がRGB=12,10,8などの黒っぽい色に変換されてしまいます。
そのため、RGBの全てが16以下の黒ぽい色を透過職として合成します。

 RGB=0,0,0で透過  RGBの全てが16以下で透過

予報情報のテキスト表示 ~東雲フォント~

テキスト表示はSDカードに入れた東雲フォント(全角16x16ドット、半角8x16ドット)を使用します。
M5Stackで使用できる東雲フォントデータの作成方法/使用方法は「東雲フォントをSDカードに入れてみた」で説明しています。

コンフィグ機能 ~天気予報表示エリアの選択 ~

M5StackでAreaCodeを選択できるようにします。
天気予報地区コードの一覧は jma.go.jp/bosai/common/const/area.json から得ることが出来ますが、サイズが大きすぎてM5Stackでは扱いきれません。
そこで、M5Stackで扱える大きさに分割してSDカードに入れておきます。
地方/県の一覧 と、都道府県毎の市町村名一覧に分割します。

centers(地域) - offices(都道県) - class10s(予報区) - class15s(予報エリア) - class20s(市町村名)
の階層構造になっているのを
centers(地域) - offices(都道県) : 1ファイル
class10s(予報区) - class20s(市町村名) : 58ファイル
の2種類、59ファイルに分割します。

Visual C++ のコンソールアプリで JSON ファイルを読み込むには、JSON パーサーライブラリを使うのが一般的です。
今回は nlohmann/jsonを使用しました。

GitHub - nlohmann/json: JSON for Modern C++

GitHubから single_include/nlohmann/json.hpp を取得して インクルードパス(プロジェクト内のフォルダで可)にコピーすれば使用できます。
ファイルを1個コピーするだけなので扱いやすいです。

AreaCode一覧作成 (VisualStudio 2022 / C++) :AreaCodeJSON.zip

AreaCode一覧作成 (Windows 64Bit用 実行形式) AreaCodeJSON-EXE.zip

分割した JSONファイルをSDカードにコピーして使用します。

天気予報表示スケッチ


M5Stacl BASIC/CORE2用スケッチ WeatherForecast.zip

SDカードに配置する 設定ファイル(雛形) / お天気ICON / AreaCode JSONファイル / 東雲フォント WeatherForecast-SD.zip

解説動画