carcon999のブログ

12年間Y!ブログの記載を移行しました。電子工作関連の記事が多いです。

ESP8266とMilkcocoaでException (28)

■概要

ESP-WROOM-02(ESP8266)Milkcocoaと連携しログを出力しているとたまに、ESPがへそを曲げて例外出力する場合があり、ここ数日解析して対処法が見えてきたので書きとめておくことにしました。もし同様の現象で悩んでいる方の参考になれば幸いです。私の解決方法は、一番下に書いてあります。
探した限り情報が無かったので、あまり困っている人いないかもしれない。

※ちなみに、ESP-WROOM-02Arduino化してMilkcocoaのSDKライブラリを利用している環境になります。

イメージ 1

★例外の出力画面:そうあれです。

ESPで例外が出力されると、

『どうせ電源でしょ?』とあまり例外のスタックトレースダンプをスルーすることが多かったのですが、ダンプの確認方法も紹介します。(ご存じの方も多いと思います。)

■システム構成
今回作っていたのは、ESP-WROOM-02(ESP8266)でMilkcocoaにデータに一定時間毎にpushし、onでデータも監視しているようなシステム構成です。そして、親機と子機の関係も存在し、子機はonだけでデータの変化を監視しているような構成になってます。

イメージ 2

■現象
ログに'Exception (28)'を出力しESP-WROOM-02がリブートしてしまうというものですが、少し面白い?現象が発生しておりました。それは、親機と子機の両方がほぼ同時にリブートするのです。考えられるのは
・USBの電源ノイズ?
・USBのリセット?
・強力な2.4Gが飛んで来てリセット?
なんだろう

傾向として、電源投入直後というよりも少ししばらくしてから発生する頻度が多いような気がしました。

■ヒントは受信データ量にあり!
実は、pushするデータには連番のシーケンス番号(リセットで1~になる)を振っているので、milkcocoa側のデータを確認すると、直前にpushされたデータを確認することができます。

なんとなく、データ量が増えたときに発生するような気がしてきました。

イメージ 3

赤枠で囲った場所が、リセットの直前データ。それ以前のデータよりデータ長が増えている様子が分かると思います。このキャプチャーはその前より2Byte増えている。

イメージ 4

■Exception Decoderを使う
同様な事例を探してみると、あまり見つからないのですが、Exception Decoderなるものがあることを見つけました。例外発生時のスタックトレースをコピペすると、コールスタックを出力してくれる素晴らしいツールです。

ESP-WROOM-02(ESP8266)使う方は、解析用に
絶対に入れておくべき
ツールです。

インストール方法は、他にお任せするのですが、私がインストールする際にはまったのは、Exception Decoderを起動した際に、ELFファイルを要求する選択ダイアログが表示されて困りました。結論としては、IDEを再起動して、ビルドし直せば要求されることなく上手く利用できるようになりました。

シリアル出力されたあの例外をコピペして貼り付けると、なんか出ます。

イメージ 5

一部加工していますが、以下のような出力です。
許可されない領域の不正なアクセスというところでしょうか・・・
※結果としては、後で出てきますが0ポインタのアクセスでした。
Exception 28: LoadProhibited: A load referenced a page mapped with an attribute that does not permit loads

Decoding 8 results
0x40209f22: aJsonClass::getObjectItem(aJsonObject*, char const*) at C:\xxxxxxx\aJSON.cpp line 973
0x40209084: DataElement::DataElement(char*) at C:\xxxxxxx\Milkcocoa.cpp line 159
0x40209309: Milkcocoa::loop(unsigned short) at C:\xxxxxxx\Milkcocoa.cpp line 172
0x402088a4: milkcocoa_main() at xxxxx
0x40208968: loop at xxxxxx
0x40212134: loop_wrapper at xxxxx
0x40100718: cont_norm at xxxxxxx

aJSON.cppのgetObjectItemで、不正アクセスが行われたことが分かりました。
そして、ライブラリの利用側からするとMilkcocoaのloopを呼び出している最中であることも。

■例外をcatchできないか?
私の中の悪魔がささやきました。
『解析するのも面倒だなぁ。データが少しぐらい欠けても問題ないので、リブートさせずにtry-catchでスルーさせるのも良いのでは?』と・・・・

いろいろ調べてみたのですが、Arduino IDEではできないそうですね。
コンパイルオプションの変更を許可してないらしい。残念でした。

■よし!解析するかぁ
シリアルデバッグで気にあるところを出力していきます。今回幸運だったのは、
再現性が高いこと
リブート直前のデータをpushすると、即座に同様の現象が再現できます。

そこで見つけたのは、データ量が増えるとJSONのカッコが消えること。

結果JSON(一部省略)
{xxx,"params":{"seq":99,"dist":12354,"vc":132,"irc":999}}95
×{xxx,"params":{"seq":100,"dist":12355,"vc":133,"irc":100095

×の方は、}}が2つ消えちゃってます。parse処理で失敗するのねぇ。
なんとなく、このバッファサイズが匂います。

■調べるとここで問題発生
カッコが抜けていると判断されて、DataElementのparse処理で失敗して、paJsonObjがNULLで返却されて、getObjectItemにNULLが渡されてしまうことが問題でした。

イメージ 7

■対策方法
結局のところ、バッファサイズが足りないので、MAXサイズを変更することにしました。

Milkcocoa ESP8266 SDKが利用しているAdafruitのMQTTライブラリのヘッダファイルAdafruit_MQTT.h
のMAXBUFFERSIZEを以下のように修正します。
値は、自分の最大データが収まるサイズにすれば256である必要はありません。

イメージ 6

■まとめ
少し長めのデータを受信した場合に、Exception 28が発生する場合は、上記のバッファサイズを変更すると幸せになれるかもしれません。

Milkcocoaも初めて利用させていただきましたが、ありがたいですね。他の会社様に譲渡?されたり今後がきになりますが、無料で継続利用できることを望みます。

対策したことで、非常に安定して連続稼働することができています。
ESPの例外確認は、ひと手間かかりますがException Decoderおすすめです。