HaloCodeのCloudMessageを自前のMQTTブローカー経由で外部へ投げる

# 要旨
[HaloCode](https://www.makeblock.com/jp/steam-kits/halocode)のCloud MessageはMQTTプロトコルのメッセージ通信であると仮定し、Heroku上へ構築した自前のMQTTブローカー経由で、[HaloCode](https://www.makeblock.com/jp/steam-kits/halocode)－他のMQTTクライアント間におけるメッセージ送受信を試してみた、という実験メモです。

結論からいうと送受信できました。

# 経緯
Makeblock社の[HaloCode](https://www.makeblock.com/jp/steam-kits/halocode)というデバイスはWi-Fiモジュールを内蔵しており、単独で無線通信できるところが魅力のひとつです。

しかしWi-Fiモジュールを使った通信は、公式の開発環境[mBlock](https://www.mblock.cc/?noredirect=en_US)にてあらかじめ用途が限定[^1]されており、例えばサードパーティーの外部システムのAPIを直接HTTP通信で叩くようなプログラムは書くことができません[^2]。

Wi-Fi経由のメッセージ通信については通常、PCまたは他のMakeblock社のWi-Fi対応デバイスとだけ通信できるようになっている[HaloCode](https://www.makeblock.com/jp/steam-kits/halocode)ですが、その他のデバイスやサービスとも直接つなげることができれば活用の幅が広がりそうだと考え、2019/6/23時点で実現できる手段を探してみることにしました。

# 手がかり
公式ドキュメント[^3]のAPI仕様のうち、cloud_message.startメソッドの項目を参照していたところ、[mBlock](https://www.mblock.cc/?noredirect=en_US)内でCloud Messageと呼ばれている通信は単なるMQTTプロトコルでのメッセージ通信なのではないかと推測しました。
（特にサブドメインがmq.*になっているところ、デフォルトのポート番号が1883であるところを見て）

```python
cloud_message.start(topic_head, client_id=None, server="mq.makeblock.com", port=1883, user=None, password=None, keepalive=60, ssl=False)
```

上記のcloud_message.startメソッドは複数のパラメータを指定できる仕様になっているため、ここに手を加えるだけで、外部のMQTTブローカーを経由して[HaloCode](https://www.makeblock.com/jp/steam-kits/halocode)とメッセージ通信できるようになる可能性を感じました。

# 実験の準備
MQTTブローカー（MQTT通信を媒介するサーバー）を作り、そのMQTTブローカーを利用するプログラムを[HaloCode](https://www.makeblock.com/jp/steam-kits/halocode)へアップロードして確かめてみることにしました。

## MQTTブローカーを用意する
今回は他の方の記事[^4]を参考にクラウドサービスのHerokuを用いて構築しましたので、ここでは詳細を割愛します。

（構築後の画面。ここで表示されている情報を後で使用する）
<img width="874" alt="" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2760/61f6edda-7f77-6cf5-7184-3d0fb618e548.png">

## HaloCodeのプログラムを作る
まず次のようにブロックエディタで実装しました。次の３点が要点です。

1. 本体のタクトスイッチを押せば"fromHaloCode"というCloud Messageを送信する
1. "fromHeroku"というCloud Messageを受信したら本体のLEDが光る
1. 標準のサーバーではなく、自分で用意したMQTTブローカーへ接続する

<img width="299" alt="" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2760/0021970b-5112-8054-0d2d-04356a1dc795.png">

前述の3.を実現するのはこのままではダメなので、次にこのプログラムのPythonコードを表示し、Pythonエディタへ（コピペなどで）貼り付けます。

（これを）
<img width="337" alt="" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2760/9bf8bca8-a7c1-faf8-f949-2f941a50ed75.png">
（こちらへ）
<img width="624" alt="" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2760/1adca130-b782-c9ac-8737-3440b5b9c89e.png">

最後に、cloud_message.startメソッドのパラメータを、先ほど構築したMQTTブローカーの情報に合わせて書き換えました。

<img width="463" alt="" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2760/d5fb5b26-9254-a485-a477-663abac8fa77.png">

# 実験とその結果
次の順に実験しました。

## HaloCodeの電源を入れてMQTTブローカーに接続させてみる

Wi-Fi通信が有効になると、MQTTブローカー側の管理画面にてMQTTクライアントからのコネクション数が *1* と表示されるようになりました。

<img width="896" alt="" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2760/ff0123ba-3163-ff29-4190-11321672bd78.png">
↓
<img width="569" alt="" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2760/2dad1589-a90b-9fa8-d0d6-2cfcf1a8c578.png">

## タクトスイッチを押してCloud Messageを送信してみる

スイッチを押すことで[HaloCode](https://www.makeblock.com/jp/steam-kits/halocode)が送信した ```fromHaloCode``` というメッセージを、他のMQTTクライアント（ここではMQTTブローカーの管理画面に内蔵されたWEBクライアント）で検出することができました。

メッセージに含まれる情報のうち、 *Topic* とはHaloCodeのプログラムのうちcloud_message.startメソッドで ```{TOPIC}``` と指定した部分のことです。 

この ```{TOPIC}``` という文字列はPythonコードを自動生成された時点ですでに書き込まれているもので、このまま利用するとmBlock用のユーザー情報?を指す文字列へ勝手に変換されます。そのため下記画像ではその部分をマスクしてあります。

<img width="914" alt="" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2760/3f0b4d21-dc5a-b54c-1d4e-9b5f34054cce.png">

## 他のMQTTクライアントからHaloCode向けのメッセージを送信してみる

他のMQTTクライアント（ここではMQTTブローカーの管理画面に内蔵されたWEBクライアント）を使って ```fromHeroku``` というメッセージを送信したところ、[HaloCode](https://www.makeblock.com/jp/steam-kits/halocode)にそのメッセージが届いたことを確認[^5]できました。

（下図はメッセージを送信しようという場面。図左側の *Send Message* 欄へ送信したいメッセージ内容を指定して *Send* ボタンを押した。）
<img width="722" alt="" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2760/29f8d29c-0dc2-fe63-07fb-2a5231e5b3b4.png">

実験にあたっての注意点は２つあります。

まず *Topic* はcloud_message.startメソッドの第一パラメータで指定した文字列と同じにする必要があります。前述した ```{TOPIC}``` を使用した場合、変換後の文字列を指定しなければなりません。

もう一つは *Message* を ```{"message":"", "value":""}``` というJSON形式で表現しなければならない点です。ここではHaloCodeのプログラムでfromHerokuメッセージであると認識してもらうためには、 ```{"message":"fromHeroku", "value":""}``` という文字列を送信する必要があります。

# まとめ

以上の実験から、Cloud Messageの実態はMQTT通信であり、[mBlock](https://www.mblock.cc/?noredirect=en_US)によって自動生成されたPythonコードを書き換えることによって、自分で用意したMQTTブローカーを通じて外部のMQTTクライアントとも通信できることを確認できました。

今回の通信手段を応用して何かシステムを作ってみることにも関心ありますが、機会を改めます。

こうして外部システムと通信する方法はいずれ公式にも用意される気がします[^6]が、取り急ぎ[HaloCode](https://www.makeblock.com/jp/steam-kits/halocode)を既存のシステムと連携して利用したいときに、このメモが役立つかもしれません。

[^1]: 公式の開発環境[mBlock](https://www.mblock.cc/?noredirect=en_US)において、Makeblock社のサーバーを使ってインターネット経由でデバイス間メッセージ通信を行う「Cloud Message」と、スター型ネットワークをアドホックに構成して直接デバイス間メッセージ通信を行う「LAN」の２種類しか2019/6/23時点で用意されていないことを指しています。

[^2]: Pythonが使えると聞いて、本当はrequestsモジュールまたはurllibモジュールをimportして直接http通信したかったのですが、mBlock v5.0.1のPythonエディタではそれぞれ ```ImportError: no module named requests ``` 、```ImportError: no module named urllib``` となり利用できませんでした。Pythonのモジュールを新たにインストールする方法は、本記事記載時点ではまだ見つけることができていません。

[^3]: [Python API Reference](http://docs.makeblock.com/halocode/en/python-api/python-api.html)

[^4]: [爆速でMQTTのパブリックBroker環境を作る方法、Raspberry Pi からアクセスできる各パブリッククラウドの比較](https://iot-plus.net/make/raspi/mqtt-broker-on-public-cloud/)

[^5]: [HaloCode](https://www.makeblock.com/jp/steam-kits/halocode)のLEDがアニメーション点滅したことによって目視で確認。実物の写真は割愛。

[^6]: https://forum.makeblock.com/t/openness-of-the-makeblock-app-towards-3rd-party-cloud-platforms-and-or-apps/12551