ここ半年ほど安定して使えているので、HoloLensとNode-REDでMQTTをやり取りするメモを残しておきます。
バージョン状況
- Unityバージョン
- 2017.1.2f1
- VIsual Studio 2017
- 15.8.3
サクッと作るときに自分の手元で安定しているものなので、他のバージョンでも使えるナレッジかと思われます。
ソースコード
MQTTはM2Mqttライブラリを利用して使います。
using System.Collections; using System.Collections.Generic; using UnityEngine; using MiniJSON; #if UNITY_UWP using System; using System.Text; using System.Threading.Tasks; using System.Windows; // M2Mqtt using uPLibrary.Networking.M2Mqtt; using uPLibrary.Networking.M2Mqtt.Messages; #endif public class MQTTManager : MonoBehaviour { #if UNITY_UWP MqttClient client; string clientId; string topicPublishPath; string topicSubscribePath; string BrokerAddress; #endif // Use this for initialization void Start() { Debug.Log(string.Format("[DISPLAY] MQTTManager START")); #if UNITY_UWP // 接続先のアドレス BrokerAddress = "<BrokerAddress>"; clientId = Guid.NewGuid().ToString(); client = new MqttClient(BrokerAddress); client.ProtocolVersion = MqttProtocolVersion.Version_3_1; client.MqttMsgPublishReceived += client_MqttMsgPublishReceived; // publish先のtopic topicPublishPath = "pub/HoloLens"; // subscribe先のtopic topicSubscribePath = "sub/HoloLens"; try { client.Connect(clientId); } catch (Exception e) { Debug.Log(string.Format("Exception has occurred in connecting to MQTT {0} ", e )); throw new Exception("Exception has occurred in connecting to MQTT", e.InnerException); } Debug.Log("Connect OK"); // Subscribe client.Subscribe(new string[] { topicSubscribePath }, new byte[] { 2 }); // Publish try { // JSONデータを作る var data = new Dictionary<string, object>(); data["status"] = "HoloLens connected"; string json = Json.Serialize(data); // publish a message with QoS 0 client.Publish(topicPublishPath, Encoding.UTF8.GetBytes(json), MqttMsgBase.QOS_LEVEL_AT_MOST_ONCE, true); } catch (Exception e) { Debug.Log("Exception has occurred in publishEvent"); throw new Exception("Exception has occurred in publishEvent ", e); } #endif } // Update is called once per frame void Update() { } #if UNITY_UWP // this code runs when a message was received void client_MqttMsgPublishReceived(object sender, MqttMsgPublishEventArgs e) { string ReceivedMessage = Encoding.UTF8.GetString(e.Message); Debug.Log("client_MqttMsgPublishReceived"); Debug.Log(ReceivedMessage); Task.Run(async () => { // Unityの描画に処理を戻す UnityEngine.WSA.Application.InvokeOnAppThread(() => { var jsonDataTree = Json.Deserialize(ReceivedMessage) as Dictionary<string, object>; // JSONデータのmessage値を取得する Debug.Log(jsonDataTree["message"]); // ここでGameObjectなど描画に関する処理を書く }, true); await Task.Delay(100); }); } #endif void OnClosed() { #if UNITY_UWP client.Disconnect(); #endif } }
こちらを同名のGameObjectを作って適用しておきます。
MiniJSONもインストール
上記のソースでは、JSONデータを扱いやすくするため、MiniJSONもインストールします。
Unityからビルドすると
UnityからVisual Studioプロジェクトとして書き出します。
Visual Studioプロジェクト(~.sln)開いてみると以下のようなエラーが出ます。
ひとつひとつ解決します。
一旦 x86 でリビルド
書き出されたプロジェクトによるかもしれませんが、私の場合、初期設定がARMのビルドになっているのか、いろいろとエラーが出ます。
まず、x86に指定してから、
ソリューションのリビルドをします。
M2Mqttライブラリが無いAssembly-CSharpエラーを解決
重大度レベル コード 説明 プロジェクト ファイル 行 抑制状態
エラー CS0103 現在のコンテキストに ‘MqttProtocolVersion’ という名前は存在しません。 Assembly-CSharp F:\Creative\workspace\hololens\holo_mqtt_blog_0911\Assets\Scripts\MQTTManager.cs 40 アクティブ
のように、がっつり、
using uPLibrary.Networking.M2Mqtt; using uPLibrary.Networking.M2Mqtt.Messages;
あたりでM2Mqttライブラリがないとエラーが出ています。
パッケージマネージャータブを選択します。
既存のプロジェクトをAssembly-CSharpにします。
Install-Package M2Mqtt
で、パッケージマネージャーでコマンドを打ってインストールします。
PM> Install-Package M2Mqtt GET https://api.nuget.org/v3/registration3-gz/m2mqtt/index.json OK https://api.nuget.org/v3/registration3-gz/m2mqtt/index.json 190 ミリ秒 C:\ \holo_mqtt_blog_0911\build\GeneratedProjects\UWP\Assembly-CSharp\project.json のパッケージを復元しています... 復元をコミットしています... Writing lock file to disk. Path: C:\Creative\workspace\git\holo_mqtt_blog_0911\build\GeneratedProjects\UWP\Assembly-CSharp\project.lock.json C:\ \holo_mqtt_blog_0911\build\GeneratedProjects\UWP\Assembly-CSharp\Assembly-CSharp.csproj の復元が 505.67 ms で完了しました。 'M2Mqtt 4.3.0' が Assembly-CSharp に正常にインストールされました C:\ \holo_mqtt_blog_0911\build\HoloSimple1.1\project.json のパッケージを復元しています... 復元をコミットしています... Writing lock file to disk. Path: C:\Creative\workspace\git\holo_mqtt_blog_0911\build\HoloSimple1.1\project.lock.json C:\ \holo_mqtt_blog_0911\build\HoloSimple1.1\HoloSimple1.1.csproj の復元が 538.04 ms で完了しました。 NuGet の操作の実行に 1.29 sec かかりました 経過した時間: 00:00:04.5622172 PM>
このようにインストールされるはずです。
無事エラーが解消されました。
動かしてみる
Node-REDでやりとりしてみます。
の記事のように、Node-RED上でMQTTブローカーも作っている前提で進めます。
フローのJSONはこちらです。
[{"id":"5770fe87.186e9","type":"mqtt in","z":"88d61bce.c3bcd8","name":"","topic":"pub/HoloLens","qos":"2","broker":"f732bc1c.35538","x":260,"y":200,"wires":[["cf09c3a5.55a11"]]},{"id":"cf09c3a5.55a11","type":"debug","z":"88d61bce.c3bcd8","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":450,"y":200,"wires":[]},{"id":"9b69e751.b39ad8","type":"inject","z":"88d61bce.c3bcd8","name":"","topic":"","payload":"{\"message\":\"Hello HoloLens!!\"}","payloadType":"json","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":250,"y":260,"wires":[["bd2f11d.55d13f"]]},{"id":"bd2f11d.55d13f","type":"mqtt out","z":"88d61bce.c3bcd8","name":"","topic":"sub/HoloLens","qos":"","retain":"","broker":"f732bc1c.35538","x":460,"y":260,"wires":[]},{"id":"f732bc1c.35538","type":"mqtt-broker","z":"","broker":"127.0.0.1","port":"1883","clientid":"","usetls":false,"compatmode":true,"keepalive":"60","cleansession":true,"willTopic":"","willQos":"0","willPayload":"","birthTopic":"","birthQos":"0","birthPayload":""}]
トピック pub/HoloLens でHoloLensからデータを受け取り、トピック sub/HoloLens でHoloLensにデータを送るシンプルなやり取りです。
トピック pub/HoloLens でHoloLensからデータを受け取る
実際に起動してみると、MQTTManagerは起動時に動き出すので、
このようにNode-REDのデバッグタブにメッセージが表示されています。
// JSONデータを作る var data = new Dictionary<string, object>(); data["status"] = "HoloLens connected"; string json = Json.Serialize(data); // publish a message with QoS 0 client.Publish(topicPublishPath, Encoding.UTF8.GetBytes(json), MqttMsgBase.QOS_LEVEL_AT_MOST_ONCE, true);
この部分が動作していてMiniJSONでSerializeされたJSON文字列が送信されています。
注意ポイントは、Oculus GoとNode-REDでMQTTをやり取りするメモで言及した QOS を 0 で送るところですね。QOS 2 で、なぜうまく行かないかが理論的に分かってないのですが、経験則に寄せています。
トピック sub/HoloLens でHoloLensにデータを送る
また、injectノードでは「Hello HoloLens!!」という文字列をメッセージを送ります。
injectノードを押してみると、無事、Visual Studioのコンソールでメッセージが表示されます。
void client_MqttMsgPublishReceived(object sender, MqttMsgPublishEventArgs e) { string ReceivedMessage = Encoding.UTF8.GetString(e.Message); Debug.Log("client_MqttMsgPublishReceived"); Debug.Log(ReceivedMessage); Task.Run(async () => { // Unityの描画に処理を戻す UnityEngine.WSA.Application.InvokeOnAppThread(() => { var jsonDataTree = Json.Deserialize(ReceivedMessage) as Dictionary<string, object>; // JSONデータのmessage値を取得する Debug.Log(jsonDataTree["message"]); // ここでGameObjectなど描画に関する処理を書く }, true); await Task.Delay(100); }); }
HoloLensがデータを受け取ったときに反応するのはこのあたり。
string ReceivedMessage で文字列として受け取りつつ、Json.Deserializeをかけて、JSONデータツリーのmessage値をDebug.Logで出力しています。
UnityEngine.WSA.Application.InvokeOnAppThread に関する使い方は、
の記事のような、Unityの描画に処理を戻してアニメーションをさせる流れを参考にしてください。
実際使っていて
HoloLensからサーバーにデータを送る場合はHTTPでも事足りるのですが、やはり、HoloLensへ何かしらを制御する場合、このMQTTのナレッジは重宝しています。
たとえば、こちらのコンテンツで、IoTでHoloLens内の映像を変化させるときに活躍しました。もちろん、HTTPやWebSocketを使う場合もあるので、このあたりは、案件の状況に合わせて使い分けています。
それでは、よき MQTT & HoloLens Lifeを。