Posted at

Wio3G Soracom EditionやWioLTE JP Versionでmruby/c動くようにした


レポジトリ

githubのリポジトリは以下

https://github.com/hiroeorz/mrubyc_for_Wio_cell_lib

使い方とかはREADMEに書いたので、関心のある方はそれをみていただければ。


全体の構成

サーバーサイド含めてなんだかんだやってるうちに、全体の構成はこんなかんじになった。今流行りの(?)サーバーレスアーキテクチャー。


Wio3G SoracomEdition、 WioLTE JP Version とは

Wio3G、WioLTEはSeeed社が開発したマイコンモジュールです。

特徴はこのサイズで3G,LTEの通信モジュールを搭載しており、Groveと呼ばれるインターフェイスを通してデジタル、アナログI/Oはもとより、UART、I2Cなどの入出力をもっている点です。








経緯

モンスターラボのhasumiさんが講師をされた、ESP32×mruby/c IoTハンズオンセミナーに参加したのがきっかけでmruby/cに関心を持ち、ググったらkishimaさんという方がすでにWioLTEでmruby/cを動かしておられることがわかり、手元にあったWio 3G SORACOM Editionでも動かないかなと思ったのがきっかけです。

ハンズオンがあったのが3月でしたが、4月、5月は出張や会議が多く、なんとなく気持ちもせかせかしてたので見送り、6月に少し落ち着いたので腰を据えて取り組んでみました。

やったことは以下の通り。


  1. とりあえずmruby/c library for Wio LTEがそのまま使えないか試してみたけど、流石にWio3GとはCのライブラリも異なるので動かなかった。

  2. 気を取り直してkishimaさんの書いておられるmruby/cの小さな世界の電子書籍を購入して読む。

  3. Wio3G向けのライブラリとしてコードを書き始める。mruby/cのリポジトリからソースをコピーし、halディレクトリ以下をWio3G用に作る。

  4. Wio3G SORACOM Edition用のArduinoライブラリWio_3G_for_ArduinoをラップするRubyのクラスWioクラスのコードを書く。

  5. 途中でWio_3G_for_Arduinoはすでにメンテされていなことを知り、Wio_cell_lib_for_Arduinoに切り替える。この時点でWio3Gの基本的な機能はmruby/cから実行できるようになった。


  6. Wio_cell_lib_for_Arduinoをラップするようにしたことで、LTE-M版であるWio LTE M1/NB1も(たぶん)カバーできたことから、どうせならWioLTEでも動くようにするか、と思いWio LTE JP Versionもカバーした。

  7. 実戦で使うにはサーバーとの通信や、送受信するデータのパーサ、ジェネレータも欲しかったのでMQTTクライアントJSONパーサ・ジェネレータも使えるようにした。


現在


  • 自社製品(工場に設置される設備)の制御基板とUARTでシリアル通信してみたところ正常に通信でき、基板から情報を取得したり書き込んだりできた。

  • Soracom BeamとMQTTで接続し、BeamからAWS IoTへデータを送り、DynamoDBに保存するのも特に問題なくできた。

  • 逆にWebインターフェイスからAWS IoT、そしてUARTシリアルを通してデータを書き込み遠隔設定もできた。


ハマったところなど


  1. そもそもC、C++の経験がゼロなので人のコード読みながら書いてる感じ。 

  2. mruby/cの self がブロック中で自身のオブジェクトを指さないことがあってハマった。

  3. CのコードからRubyのコードの呼び出し方がよくわからなかった。 mrbc_funcall(struct VM *vm, const char *name, mrbc_value *v, int argc); がそれっぽかったが、結局自分のところではうまく動かなかった。

2に関してはgithubのイシューに上げておいた。自分で解決してプルリク投げられればいいんだけど、ちょっとどうしていいのかわからなかった。

この問題に対しては、一旦変数に myself = self のような形でとっておいて、ブロック内では変数( myself )の方を使うような書き方にしたらとりあえず意図した動きになった。

3に関しては、MQTTでサブスクライブしてるデータを受信した際に一旦Cのコールバックで受け取ってそこからRubyのブロックなどに投げたかったんだけどうまくいかなかった。

ただ、CからRubyコードのインスタンス変数には値を代入できたので、インスタンス変数を使って受け渡しすることにした。リアルタイム性は落ちるけど、サブスクライブしたデータで割り込まれるのも予測できない部分があって怖いので、Rubyコード側からインスタンス変数を確認しにいく形の方がいいと思いそうしている。


感想とか

やはり使い慣れているRubyのシンタックスで書けるのは楽でいいです。設計も慣れた感覚で行える。

それと、mruby/c特有の機能であるマルチタスクが、IoT特有の、「I/Oなめつつサーバーとも通信する」という使い方にマッチしていると思った。おそらくマルチタスク使うとリアルタイム性は低下するんだろうけど、そもそもそういうところではmruby/c使うことないだろうから問題にはならないだろう。

機能面だけでなく、マルチタスクを意識した設計にすることによって、サーバーとの通信とI/O通信のコードを明確に分けることができるから全体にすっきりとした構成にできた。

あとは、わからないことがあってもmruby/cのコードが小さいのでコード見てある程度どうすればいいか自分で判断できた。(とはいえ、スコープがどう実装されてるのかは自分のゼロに等しいC力ではさっぱりわからんかった)

現在はmrubycで組んだコードをWioLTEで動かして(結局WioLTEも購入した)継続して稼働させてみてるけど、今のところクラッシュなどは起こっていない。しばらくは継続して稼働させてみて、問題なさそうなら社内プロジェクトに使うことを提案してみようと思う。