Skip to content
まことの手帳
Go back

ESP32 でセンサーデータを AWS に送る #9 気象庁データ比較編

BMP180 から気圧・気温を取得して AWS に送るところまではできました。次に気になったのは「この値、実際どれくらい合っているのか」です。基準になる数値と比べたくなり、気象庁のデータと突き合わせることにしました。

目次

リアルタイム比較は要らない

最初に考えたのは、気象庁のデータもリアルタイムで取得して常時比較する構成でした。ですが、そもそも自分のセンサーは体感の目安が欲しいだけで、秒単位・分単位で追従する必要はありません。1日1回、まとめて取得して並べて見られれば十分という結論になりました。

これで構成がかなり単純になります。ESP32 側は一切変更せず、AWS 側に「1日1回気象庁のデータを取ってきて、既存の仕組みに書き込む」処理を追加するだけで済みます。

気象庁に「公式API」は無い

気象庁は一般開発者向けの公式APIを提供していません。ただし、気象庁のサイト自体が内部で使っている JSON エンドポイントが実質的に公開されており、多くの開発者がこれを利用しています。

最新観測時刻: https://www.jma.go.jp/bosai/amedas/data/latest_time.txt
全地点の観測値: https://www.jma.go.jp/bosai/amedas/data/map/{観測時刻}.json
観測地点一覧(緯度経度付き): https://www.jma.go.jp/bosai/amedas/const/amedastable.json

非公式なので、過去10日分しか遡れず、仕様変更のリスクもあります。ただ、今回のような「参考値としてざっくり比較したい」用途には十分でした。

amedastable.json に全観測地点の緯度経度が入っているので、設置予定地から最も近い「四要素観測所」(気圧・気温・降水・風を観測する地点)を選びました。具体的な観測地点名はここでは伏せますが、緯度経度の差分計算だけで簡単に絞り込めます。

Lambda を1つ追加するだけ

構成は #8 で作った仕組みにそのまま乗せました。新しい Lambda(Python 3.12 / arm64)を1つ追加し、EventBridge で1日1回起動します。

EventBridge (cron, 12:05 JST 1日1回)
    │
    ▼
Lambda (気象庁データ取得)
    │
    ├─→ DynamoDB(#8 と同じテーブル。device_id は地名を含まない専用の識別子)
    └─→ CloudWatch カスタムメトリクス(#8 と同じ namespace)

Lambda の中身はシンプルです。

  1. latest_time.txt で最新の観測時刻を取得
  2. そこから1時間おきに24時間分遡り、map/{時刻}.json を24回取得して対象の観測地点コードの値を取り出す
  3. 気圧・気温を DynamoDB に保存(センサー側と同じテーブル、別の device_id)
  4. CloudWatch にも同じ namespace・別の Dimension でメトリクスを送る

実行自体は1日1回(EventBridge)のままですが、その1回で「過去24時間分を毎時」まとめて取ってくることで、センサー側の粒度に近づけました。device_id と時刻をキーにした書き込みなので、同じ時刻を再取得しても上書きになるだけで重複は発生しません。

センサー側のデータと同じ場所に置いたことで、CloudWatch ダッシュボードのグラフにそのまま重ね描きできるようになりました。

ダッシュボードに重ね描き

#8 で作った気圧・温度のグラフに、気象庁側の系列を追加しました。

metrics = [
  [namespace, "Pressure", "DeviceId", センサーのID, {color: 青, label: "センサー実測"}],
  [namespace, "Pressure", "DeviceId", 気象庁データのID, {color: グレー, label: "気象庁(参考)"}]
]

同じグラフの上に、自分のセンサーの実測値と気象庁の観測値が並んで表示されます。過去10日分は任意の時刻を指定して取得できる仕様を利用し、1週間分をまとめて取り込んで表示を確認しました。

保存項目を増やし、比較地点も2箇所にした

ダッシュボードで使うのは気圧・気温だけですが、DynamoDB には取得できる観測項目を全部保存するように変更しました。湿度・降水量・視程・風向風速なども入っています。今すぐ使う予定はなくても、後から「やっぱり湿度も見たい」となったときに、取得し直さなくて済むようにしておく狙いです。

比較対象の観測地点も1箇所から2箇所に増やしました。設置予定地がちょうど2つの観測所の中間あたりに位置しているため、両側の値を見比べたほうが実態に近い参考値になります。

map/{時刻}.json (全観測地点のデータをまとめて含む)
    │
    ├─→ 観測地点コードA の値を抽出
    └─→ 観測地点コードB の値を抽出

ポイントは、2地点分を取るために気象庁への問い合わせを2倍にしたわけではないことです。map/{時刻}.json はその時刻の全観測地点のデータをまとめて返してくるので、1回のレスポンスから両地点のデータを抜き出せます。リクエスト回数は毎時24回のまま変わりません。

ハマったところ

症状: Lambda を手動実行して DynamoDB には正しく書き込まれているのに、CloudWatch のメトリクスを list-metrics や get-metric-statistics で見ても何も返ってこない。

原因: PutMetricData の呼び出し自体は成功していても、CloudWatch 側でメトリクスが検索可能になるまで数分のタイムラグがある。実行直後に確認すると「反映されていない」ように見えて焦る。

気づきにくい理由: DynamoDB への書き込みは即座に反映されるため、同じ Lambda の中の処理なのに片方だけ遅延するとは想像しにくい。数分待ってから再確認する、という単純な話だが、初回はハマった。

グラフの period とデータ間隔が噛み合わないと、線ではなく点になる

症状: ダッシュボードの表示範囲を「1時間」や「3時間」に絞ると、気象庁側の系列が折れ線ではなくポツポツとした点にしか見えない。「1週間」表示だと綺麗な線に見えるのに、短い範囲だと崩れる。

原因: グラフのウィジェットには集計間隔(period)を5分(300秒)で固定していた。センサー側は1分おきにデータが来るので5分バケットは常に埋まるが、気象庁側は1時間おきにしか来ないため、5分バケットのほとんどが空になる。表示範囲が短いとその「空」がそのまま見えて点だけになり、表示範囲が長いと CloudWatch 側が表示点数の上限に合わせて自動的に period を粗く調整するため、たまたま気象庁の間隔と噛み合って線に見えていた。

解決策: ウィジェットの period を気象庁データの間隔に合わせて3600秒(1時間)に変更した。これで表示範囲によらず気象庁側は途切れず線になる。トレードオフとして、センサー側も5分平均から1時間平均になり、グラフの滑らかさは多少落ちるが、日々の傾向を見る用途では問題ない。

気づきにくい理由: 同じウィジェット内で2本の系列(センサー・気象庁)がまったく違う更新頻度を持っている、という前提を見落としていた。片方に合わせて period を決めると、もう片方の見え方が変わることに、実際にグラフを表示範囲を変えながら眺めるまで気づかなかった。

「四要素観測所」でも気圧が無いことがある

症状: 2箇所目の観測地点を追加したところ、気圧だけがいつまで経っても取得できない。気温・湿度・降水量・風は問題なく取れている。

原因: その観測所には気圧計自体が設置されていない。一時的な欠測ではなく、恒久的な仕様だった。緯度経度だけで最寄りの観測所を選んでいたため、この観測所が何を測っているかまでは確認していなかった。

解決策: amedastable.json の観測地点一覧には、緯度経度に加えて観測項目を示すコードも入っている。距離だけでなく、必要な項目(今回は気圧)を実際に観測しているかも確認してから選定すべきだった。今回は「気温は2地点、気圧は1地点」という構成のまま運用することにした。

気づきにくい理由: 「近くの観測所を選べば大抵の項目は揃っている」と思い込んでいた。全国のアメダスには降水量だけを観測する地点も多く、観測項目の粒度は地点ごとにかなり差がある。

追記: 日照時間も毎時グラフにした

気象庁データには気圧・気温以外に日照時間(sun1h、1時間あたり何分晴れていたか)も含まれています。当初はこれを日次Lambda側で1日分合計してダッシュボードに出していました。

ただ、よく考えると毎時のデータ自体はDynamoDBに既に保存済みで、精度が落ちているわけではありません。単に「1日の合計値」というまとめ方をしていただけでした。日単位の大づかみな傾向を見るには合計値で十分ですが、「午前は晴れていたが午後は曇った」のような日内の推移を見たい場合は、毎時の値をそのままグラフにしたほうが情報量が多くなります。

そこで、気象庁データ取得Lambda(fetch_jma.py)がCloudWatchに送る項目に日照時間(毎時)を追加しました。

DASHBOARD_METRIC_MAP = {
    "pressure_hpa": "Pressure",
    "temperature_c": "Temperature",
    "sunshine_1h_hour": "SunshineHours",  # 追加
}

日次の合計値(大づかみな比較用)と、毎時の時系列(日内の推移用)の両方をダッシュボードに並べる形になりました。用途に応じて使い分けています。

まとめ

項目内容
データ取得元気象庁アメダス(非公式JSON、過去10日分のみ)
実行頻度1日1回(EventBridge、12:05 JST)
取得粒度実行のたびに過去24時間分を毎時取得
比較地点2箇所(設置予定地を挟む形で選定)
保存項目DynamoDB には取得できる項目を全て保存(CloudWatchは気圧・気温・日照時間を使用)
保存先#8 と同じ DynamoDB テーブル・CloudWatch namespace
可視化既存ダッシュボードに重ね描き(センサー実測、気象庁参考1・参考2、period=1時間に統一)
ESP32側の変更なし

ESP32 側を一切触らずに、AWS側にLambdaを1つ足すだけで実現できました。センサー実測値と公式データが同じグラフ上に並ぶと、思ったより答え合わせがしやすくて良い感じです。

次回

しばらく実測値と気象庁データを見比べて、センサーの精度傾向を確認する予定です。並行して、発注済みの電源部材が届き次第、ブレッドボードでの電源回路の検証に進みます。

参考リンク


Share this post on:

Previous Post
ESP32 でセンサーデータを AWS に送る #7 通信方式検討編
Next Post
ESP32 でセンサーデータを AWS に送る #10 ウォッチドッグ編