[ESP32]IoTマイコンを動かしてみる11 -JSONをparseする編‐

JSON天気データを
parseして
LCDに出力してみる

OpenWeatherMapからデータを取得できたので、必要なデータを抜き出したり、加工したりする前段階としてparseする。できたら天気データなので太陽とか雲とかのアイコンで表示したいなぁ。


ArduinoJson

Parserについては前回記事参照。

取得したデータをArduinoJson AssistantのInput欄に貼り付けてみる。

すると、JsonBuffer size欄にバッファの容量とマクロを使用した記述方法が現れる。

JsonBuffer size
Expression
3JSON_ARRAY_SIZE(1) + JSON_ARRAY_SIZE(3) + 6JSON_OBJECT_SIZE(1) + 4JSON_OBJECT_SIZE(2) + 4JSON_OBJECT_SIZE(4) + JSON_OBJECT_SIZE(5) + 3JSON_OBJECT_SIZE(7) + 3JSON_OBJECT_SIZE(8)
Additional bytes for input duplication
937
Platform Size
AVR 8-bit 948+937 = 1885
ESP32/ESP8266 1552+937 = 2489
Visual Studio x86 3004+937 = 3941
Visual Studio x64 3304+937 = 4241

なんでもJSON_ARRAY_SIZEとかJSON_OBJECT_SIZEとかのマクロがProcessorに合わせて容量を計算してくれるらしい。深入りしようとしたら頭痛くなってきたので詳細は公式ドキュメントに譲る。(Deserialization tutorialの項にそんな感じのことが書いてある)

で、ESP32のところを見てみると、今回使うJSON天気データを格納するには2489bytes必要みたい。マイコンだと容量も考えないといけん。とりあえず何も考えずコピペしてみて、容量的に厳しい的なエラーが出たら考える方向で。

マシントラブル…

と、少しづつコード書き込んだり休んだり、また書き込んだりを繰り返してたら開発マシンが不調で何も保存できなくなってしまった。。。

心折れたので中断。いつもならこのまま放置しちゃうけど最近進みが悪いので中途半端な状態で一旦公開して自分を追い詰める。

続きはこの記事に追記していきます。寝ます。

———— 再開 ————

JSON Parse 実装

というわけで、基本的にArduinoJson AssistantのParsing program欄のコードをコピペしてゆけば深く考えず実装できる。

最初の方だけ引用してみる。

(1)でメモリのサイズを計算して、(2)でバッファを確保。

長いので略したが、(3)でJSONの実データを入力、そのデータを(4)でJsonObjectにパースする。

あとはパースされたJsonObject(の参照)であるところのrootから種々のデータにアクセス可能。詳細はParsing program欄を参照する。ちなみに今は必要ないが逆パターン(データ要素を入力していってJsonObjectを作成する)はSerializing program欄にコード例が表示される。

で、必要なデータだけ抽出してLCDに表示するプログラムを書いてみる。

#前回書いたプログラムから追加、修正しているので詳細はそちらで。なんか細かいところが気になったりして小細工とかしまくってしまったので却って見づらいかも。。

ソースコード解説

本筋と離れた小細工(こちらに時間とられてしまった。。)が多いので、それらは後回しにして先に肝心のJSON周りの解説、その後詳細を解説する。

要点の解説

・JSON Parse関係 (3) (7) (8) (9) (10) (12)

(3)においてグローバル領域で上述したJson用Bufferサイズを定義している。

一気にloop()に飛んで(7)のあたりで上述した流れと同じくBufferを確保して、getData()(詳細は後述)でjsonデータを取得、jsonObjectにparseする。

jsonデータがうまく取得できなかったりするときのために(8)でparseに成功しているかチェック。ArduinoJsonのJsonObject::success()で確認できる。便利。

#後述するが、当初実装のミスで失敗しまくってた。

(9) (10)のあたりでparseしたjsonObjectから必要なデータをローカル変数に代入。取得するjsonの構造が分かっていれば多次元配列にアクセスするノリで指定すれば問題ない。 ArduinoJson Assistantで確認できるサンプルのように必要であれば要所要所でjsonArrayとかで抜き出した方が事故は起きにくいかも。

(12)でLCDに取得したデータを出力している。確認するだけなら、tft.printf()で文字列を出力するだけでよいのだけど、ついつい色とか位置とかいじりだしてしまう。ここも最終的には関数に分割したいなぁ。

・OpenWeatherMap関係 (9) (10) (11)

基本的には(9)で書いているようにrootから必要なデータを持ってくる。これもArduinoJson Assistantにサンプルコードが出てくるので問題はない。

ここではOpenWeatherMap特有での対応を記す。

取得したデータが所望のものか確認するため、(9)では取得した場所の情報とデータを取得した日時の情報を表示している。ただ、JSONデータに取得日時の情報は入っていないので別の方法で取得している(後述)。

(10)では時間ごとのデータ(3/6/9時間後の各データ)の処理をforループで回している。

前にどこかで書いた気がするが、日時データはGMTなのでJSTに変換する必要がある。その処理をしているのが(11)のあたり。

#間が空いてしまって自分も忘れてしまったので詳細を知りたい方はtime_tとかstruct tmとかでググればわかると思う。

その他小細工の解説

ここからは要点からそれる実装的な話なのでスルー推奨。

・日時データの取扱について (1)(11)

上でも少し書いたが、GMTのUNIXTIMEからJSTに変換する必要がある。取得したUNIXTIMEにJST分のずれ(3600 * 9秒)を足してtm構造体に変換。strftime()で表示形式を時刻だけに変換。しているのだと思う。先述のとおり間が空いてしまって細かいことは忘れた。

・HTTPレスポンスヘッダーからデータ取得日時の取得(2)(5)

こちらも先述のとおり。時間の話が連続してややこしいが、こちらはデータを取得した日時。要はちゃんと最新のデータを取れているかを確認したい。しかし、この日時はJSONには含まれていない。NTPにアクセスしてESP32内部で時間管理するのが正確だろうけど、そこまで正確性をもとめていないので、OpenWeatherMapにアクセスしたときのHTTPレスポンスヘッダーから日時を取得する。

(2)でレスポンスヘッダーから取得したい情報を設定する。レスポンスヘッダーには色々な情報が入っている(下記リンク参照)けど、今回は日時だけほしいので”date”を指定。headerkeys[]で指定すれば他のフィールド情報も取得できると思う。

(5)で先程定義したheaderKeysとnumberOfHeadersを指定してあげると、http.header(“date”)の形で日時情報が取得できる。っぽい。このあたりもどこかの記事を参考にしたはずだが忘れてしまった。申し訳ない。

・getData()関数へのポインタ渡し(4)(6)(13)

データを取得する部分をgetData()として関数にしているのだが、この関数とメインループ関数の間での値のやりとりにかなり苦労した。詳細は省くが、ポイントはchar文字列を関数間でやりとりするときは”ポインタのポインタ”を渡す必要があるということ。このあたりの実装ミスでエラーが多発した。

ただ現状でもgetData()内でnewして(6)、getData()の外でdeleteしている(14)ので不具合が出そうな気はする。

ここも参考リンクを紛失してしまったが、一つだけメモに残っていたので貼っておく。

まとめ

ということで冒頭の画像のように、天気データを加工してLCDに表示するまで完成。しかしアイコンで表示したいと言っておきながらそこまでは未達。

さらに途中まで更新して、一ヶ月以上放置してしまい、自分でも内容がよくわからなくなる始末。

なんかやったらちゃんとすぐ更新しないとダメですね。

記事に関しても解説が雑でついていけんとダメ出しももらってしまったので、一旦検討は置いておいて、次回は簡単に現状をまとめたいと思います。