先日書いたMQTTでIRKitやPhilips Hueの状況確認・操作を集約が思いのほか読んでいただけているようで、やっぱりIoTってホットなんだなあという感があります。
そこで今回はPepperでMQTTを扱うネタを。今回は**「メモリ(ALMemory)機構とMQTTを相互接続したらクラウド連携とか外部連携アプリを楽に書けるんじゃね」という気がしたのでそういう感じのボックスを作った**という話。
PepperでMQTTを扱うこと自体に関してはSunaさんがすでに試しておられるので参考にさせていただきました。非常に丁寧にまとめてくださっているので、技術的な背景、要素などはこちらを参照していただくのがよいかと。
ついでにPythonボックスでモジュールを利用するものを、ボックスライブラリで簡単に配布するにはどうするか、みたいな個人的Tipsなんかもつらつらと。
あくまでも個人としての開発としてやってるわけですが、一通りできたので使い方や考え方をメモする感じで。
ALMemoryとMQTTとPub/Subモデルと
Pepperのソフトウェア基盤として、NAOqiというものがあるわけですが、このフレームワークの中心には ALMemory というモジュールがあります。これは、イベントを発火したり、そのイベントを購読したり、値をセットしたり取得したりといったことをサポートするもので、アプリケーションからのセンサーの監視や値の取得は基本的にこのモジュールを介しておこなうように設計されています。(詳しくはALMemoryを介したイベント、値の取得あたりを参照)
一方、MQTTは時雨堂さんの MQTTについて詳しく知る あたりを読んでいただくとわかる通り、いろいろなデバイスがメッセージを発行したり、別のデバイスが発行したメッセージを購読したりといったことを実現するプロトコルです。MQTTサーバー(MQTT Broker)というものを通して、さまざまなデバイスがメッセージを発行、購読することができます。
これらの、メッセージを発行したり、それを購読したりといったモデルをPub/Subモデルと呼ぶわけですが、ALMemoryもMQTTもおんなじようなモデルだしきっと親和性高いよねえみたいな気持ちになるわけです。
Pepperとクラウド
イベントなどでPepperでの開発をお手伝いしていて、「Pepperをいろいろな外部のサービスと連携させることで、Pepper自身だけでなくサービスの価値もさらに高めることができるだろう」という気持ちはどんどん強くなります。
各サービスともに非常に便利なクライアントライブラリを提供してくれているわけですが、Pepperの開発ツールChoregrapheが提供するボックスライブラリから直接的にこれらのライブラリを使うことはなかなか難しく、多くの場合コーディングという壁を避けて通ることができません。また、NAOqiはPythonをベースとした実行環境ではあるものの、pipのようなパッケージ管理ツールを自由に使うことができなかったり、「NAOqi固有のお作法」みたいなものがあり、コーディングが簡単な環境かというとそうでもないのが現状です。
しかも、PepperのCPUやメモリは一般販売に向けて強化されているものの、セーフティなどの自律的な制御や感情マップのような部分に計算能力を使わねばならず、アプリで計算をぶん回すのも問題がありそうです。認識処理など重たい計算はクラウドというかPepper外のどこかのサーバに配置するといった配慮をしたりする必要が出てきそうです。
MQTT Adapterボックス: ALMemory-MQTT相互接続でPepper-クラウドの連携
そんなわけで、ALMemoryとMQTTを相互接続することで、ALMemoryを扱うボックスだけで、クラウドなど外部のシステムと連携することが簡単にできるようになるんじゃないかと思ったわけです。
MQTT Adapterボックスにはあらかじめ、 ALMemory中のキー と MQTTのトピック の対応づけを指定しておきます。この対応づけとMQTT Brokerへの接続情報を与えることで、あとはMQTT Adapterボックスを起動しておくだけで
- アプリがRaise Eventボックスで、MQTTのトピックに対応しているイベントを発火すれば、MQTT経由でそのトピックを購読している外部アプリケーションにメッセージを送ることができる
- アプリがSubscribe to Eventボックスなどで、MQTTのトピックに対応しているイベントを購読すれば、MQTT経由で、外部アプリケーションがそのトピックに対して発行したメッセージを受け取ることができる
みたいな感じで動けると面白そうかなと。
MQTT Adapterボックス: 使い方
そんなわけでMQTT Adapterボックスを作りました。
以前のSubscribe to Eventボックスをqi Frameworkで書きなおしてみた話と同じボックスライブラリに追加してみています。GitHubは以下、
web-boxes ディレクトリがボックスライブラリです。ボックスライブラリの読み込み方法、利用方法を説明していきます。
ボックスライブラリの読み込み
アプリにMQTT Adapterボックスを配置するには、まずボックスライブラリをChoregrapheで開く必要があります。以下のようにします。
-
[ボックスライブラリ]パネルから[ボックスライブラリを開く]を選択する
-
GitHubからZIPダウンロードもしくはcloneしたディレクトリのうち、web-boxesディレクトリ を選択する
なお、ファイルの種類に
ボックスライブラリのディレクトリ(Directories)
を選択する必要があります。 -
ボックスライブラリパネルに web-boxes ボックスライブラリが現れるようになります
これでMQTT Adapterボックスを含むweb-boxesボックスライブラリを利用することができるようになります。
MQTT Adapterボックスの配置
web-boxesボックスライブラリの Network > MQTT Adapterボックスは以下のように利用します。
-
web-boxes
ボックスライブラリの Network > MQTT Adapterボックスをフローダイアグラムパネルにドラッグ&ドロップするこのMQTT Adapterボックスでは paho というモジュールを使っていますが、これはMQTT Adapterボックスをドラッグ&ドロップすると自動的にプロジェクト中にインポートされるようになっています。
-
MQTT Adapterボックスの設定ダイアログを開き、以下のパラメータを設定する
以下の値が設定可能です。- Host ... MQTT Brokerのホスト名
- Port ... MQTT Brokerのポート番号
- Username ... MQTT Brokerの接続用ユーザ名
- Password ... MQTT Brokerの接続用パスワード
- Key-Topic Bindings ... ALMemoryのキーとMQTTのトピックとの対応づけ。詳細は後述
-
適当なタイミングでMQTT Adapterボックスを起動するようにする
- onStart入力でMQTT-ALMemory間の相互接続を開始 ... 接続処理実行後、onConnected出力が発火される。以後onStop入力がされるまで、ALMemory-MQTT間のイベント中継が実行されるようになる
- onStop入力で相互接続終了 ... 接続終了するとonStopped出力が発火される
- エラー発生時 ... onError出力がメッセージとともに発火される
以下、定義方法の詳細と、実行例を説明していきます。
キー-トピックの対応関係の定義
MQTT Adapterボックスの[Key-Topic Bindings]設定によって、ALMemoryのどのキーの値とMQTTのどのトピックの値を結びつけるかを指定することができます。
対応関係は、ALMemoryのキー:MQTTのトピック
などとコロンで区切って指定します。;
で区切ることで複数の対応関係を定義することができます。
キー名、トピック名ともに、最後に /#
を付加することで、あるキー・トピック以下のものをまとめて指定することができます。例えば、
-
TestMQTT/Hoge/#:test-yacchin1205/Hoge/#
などとすると、ALMemoryでTestMQTT/Hoge/
からはじまる名前、例えばTestMQTT/Hoge/A
などとイベントを発行すると、MQTTにはtest-yacchin1205/Hoge/A
というトピックにメッセージが発行されます - ALMemoryの
TestMQTT/Hoge/B
というイベントを購読することでMQTTのtest-yacchin1205/Hoge/B
というトピックを購読することが可能です
また、 ALMemoryのキー:MQTTのトピック:型
などとすることで、値の変換を定義することができます。
- 型を指定しない場合、ALMemory-MQTTのイベント変換時に値は変換せず、そのままの型で値を送ろうとします。そのため、MQTTのペイロードに対応していない型の値を持つイベントの場合、発行に失敗する場合があります
- 型が
str
の場合、ALMemory→MQTT変換時に文字列型に変換するようになります。MQTT→ALMemoryも同様です - 型が
json
の場合、ALMemory→MQTT変換時にjsonへのダンプをおこないます。MQTT→ALMemory変換時はjsonの解析をおこないます
以下、使い方の例を・・・
MQTTからPepperに値を渡す例: 別のデバイスからPepperにSay指示
別のデバイスからMQTT Adapterを経由してPepperのALMemoryに値を放り込み、それを使ってSay Textするみたいなことができます。
サンプルはGitHubの samples/mqtt-say-text 参照。
-
MQTT Adapterボックスを配置、設定する。今回は例として、
- MQTTのBrokerには、 test.mosquitto.org を利用する
- MQTTのトピック
test-yacchin1205/sample1/Message
が、ALMemoryのMQTT/Test/Sample1/Message
と相互接続されるように設定する
-
フローダイアグラム中に
MQTT/Test/Sample1/Message
イベントを購読する入力を作成し、Say Textボックスにつなげる
試しに、このアプリを実行した状態で、手元のRaspberry Piにインストールしてあったmosquitto_pubコマンドでMQTTにメッセージを投げてみます。
例えば、 test-yacchin1205/sample1/Message
トピック(ALMemoryでは MQTT/Test/Sample1/Message
)に MQTTからこんにちは
というメッセージを投げる場合は以下のようにします。
pi@raspberrypi ~ $ mosquitto_pub -h test.mosquitto.org -t test-yacchin1205/sample1/Message -m MQTTからこんにちは
すると、ALMemory経由でSay Textボックスが反応します。
このようにMQTTを経由することで、Pepperの外からPepperに対して簡単に指示を投げることができます。
PepperからMQTTに値を渡す例: PepperからHueを制御
MQTTでIRKitやPhilips Hueの状況確認・操作を集約で紹介したように、我が家のHueはMQTT経由で操作できるようになっています。
今回はPepperの頭をなでることでHueのランプの点灯を制御するような例を書いてみます。
MQTTとHueの対応
自宅では以下のように username/hue/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxx(ブリッジのUDN)/light/x(ライトのID)/status
トピックにJSONデータを投げつけることでライトの点灯状態を制御できるようにしています。
username/hue/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxx/light/x/status {"on": true, "saturation": 254, "brightness": 204, "hue": 34297}
ここでは、ALMemoryにHueの点灯状態に関する値を与えると、Hueの状態が変わるようにしたいわけですが、このようなJSONデータ(dict)を与えると、ALMemoryでは [["on", True], ["saturation", 254], ["brightness", 204], ["hue", 34297]]
のような表現に自動的に変換されます。
アプリの作成
アプリは以下のように作成します。ID, パスワードなどの情報が含まれてしまうので、概要のみ・・・
-
MQTT Adapterボックスを配置、設定する
- MQTTのBrokerには、 Sango を利用する。ホスト、ポート、ユーザ名、パスワードにSangoから指定された情報を利用する
-
ユーザ名/hue/
トピック以下にHueのイベントをPublishするようにしているので、このすべてのトピックが、ALMemoryのMQTT/Hue/
から始まるキーと相互接続するように設定する。形式はjson
とする
-
Sensing > Touch > Tactile Headボックスと新規Pythonボックス(Hue On, Hue Off)、Programming > Memory > Raise Eventボックスを配置する
-
Hue Onボックスでは、Hueをオンにするような値を出力するようにする
def onInput_onStart(self): self.onStopped([["on", True], ["saturation", 254], ["brightness", 204], ["hue", 34297]])
-
Hue Offボックスでは、Hueをオフにするような値を出力するようにする
def onInput_onStart(self): self.onStopped([["on", False], ["saturation", 254], ["brightness", 204], ["hue", 34297]])
-
Hue Onボックス、Hue OffボックスにはRaise Eventボックスをつなぎ、Hueのブリッジ、ライトに対応するキーを指定する
これだけで、頭の前方をタッチすると、点灯を指示するデータがMQTT経由でHueブリッジの制御プログラムに伝えられ、ランプがオンになったり、後方をタッチするとオフになったりできるようになります。
小ネタ: ボックスライブラリにPythonコードを含める
Pepper用TweetボックスをつくるではプロジェクトにPythonモジュール(.py)を含め、Pythonボックスからimportするための方法を書きました。
この手順はプロジェクト作成時にファイルコピーなどの手順が必要で、実装に利用するのは少々面倒です。ボックスライブラリ中にこれらのPythonモジュールのファイルを保持しておき、ボックスをプロジェクトにコピーした際に、一緒にPythonコードもコピーできる仕掛けがあるとよいかなと。
今回は、添付ファイルの機構を使うことでこれを実現しています。
MQTT Adapterボックスの中身を見ていただくと、実は中にPythonボックスのMQTTAdapterImplボックスが入っています。
このボックスには、LibFile1, ..., LibFile4という[添付ファイル]タイプの変数を用意してあります。ここで、ボックスライブラリからビヘイビアへとコピーされる際に含めたいファイルを指定してあげています。
このようにすることで、 /paho/...
以下のファイルがボックスライブラリからビヘイビアにコピーされる際にあわせてコピーされるようになるわけです。
わざわざMQTT Adapterボックスの子としてMQTTAdapterImplボックスを作っているのは、これらの添付ファイルに関する項目をボックスの利用者に見せたくなかった(混乱などを防ぐ)ため。
一応、今のところ問題なく動いているように見えますが、ボックスライブラリ作成時、ビヘイビアからボックスライブラリにコピーすると、添付ファイルのパスがプロジェクト中の相対パス(ビヘイビア中の相対パスではない)みたいな落とし穴があったりでなんか別の落とし穴もあるかもしれませんので、今回はあくまでもひとつの試行として。
まとめ
HueやIRKitのMQTT対応を通して、MQTTを使うことでアプリ連携が楽になるなあという感じがすごくしたので、PepperでもMQTTを扱えるようなボックスを作り、ボックスライブラリとして配信できる形にしてみました。
とりあえず暗号化など考えないでやってしまっていますが、やっぱりTLS対応などしたい感じです。GitHubのほうで更新などしていくので見ていただけると。あと、プルリクなど大歓迎です。
ただ、MQTTのようなPubSubモデルはノード間の通信を自由にコーディングできてうれしい感じですが、はしゃいでいろいろコードを書いていたらうっかりメッセージをピンポンするようなコードを作ってしまい、あっという間に10000メッセージとか送信してしまい無料枠を使い果たしそうになる、みたいな悲劇もありました。テストにはMosquittoとか使って自分でMQTT Brokerをたてるのがよいかもという感じもしますねー。