おもちゃラボ

Unityで遊びを作ってます

UnityでArduinoとシリアル通信をする

UnityではC#が使えるので、シリアル通信用のクラス(SerialPortクラス)を使うことが出来ます。ただ、Unity上でシリアル受信をすると速度がでないため、シリアル通信用のプラグインを作ります。

f:id:nn_hokuson:20160726205853j:plain

Arduinoでシリアル送信プログラムを作る

まずはテスト用にArduinoのプログラムを作ります。

f:id:nn_hokuson:20170319152056p:plain:w300

シリアル通信で、アナログピンから得られた値を送信するプログラムです。

void setup() {
  Serial.begin(115200);
}

void loop() {
  int v = analogRead(0);
  Serial.println(v);
}

setpu関数の中でSerialを使う準備をしています。
loop関数の中ではアナログポート0番の電圧値を取得し、それをシリアルで送信しています。

プラグインの作成

続けてシリアルを受信するためのプラグイン(DLL)を作成します。
ここで作成するDLLはUnityのプロジェクトフォルダに入れて使います。

f:id:nn_hokuson:20170319152126p:plain:w300

Macの環境でもMonodevelopを使えばDLLを作ることが出来ます。
まずは/Application/Unity/Monodevelop.appを起動します。メニューバーから「File」→「New」→「Solution」を選択します。

f:id:nn_hokuson:20160725214107p:plain

プロジェクト設定画面でプロジェクト名を入力後「Create」ボタンを押します。ここでは「SerialLib」という名前にしました。

f:id:nn_hokuson:20160725214525p:plain

UnityのC#のバージョンに対応するため、フレームワークを.NET3.5に変更します。
メニューバーから「Project」→「SerialLib Option」を選択してウインドウを開き、左側のペインから「Build」→「General」→「Target framework」の項目を「.NET 3.5」に変更して下さい。

エディタに戻り、下記のシリアル通信の受信側のコードを記述して下さい。
Unity5の環境ではSerialPortクラスのRead関数を呼ぶとフリーズしてしまうため、ReadLine関数を使っています。
メニューバーから「Build」→「Build SerialLib」を選択してコンパイルして下さい。
プロジェクトフォルダの中の/bin/Debug/の中に「serialLib.dll」が作られます。

using System;
using System.Threading;
using System.IO.Ports;
using System.Runtime.InteropServices;

namespace SerialLib
{
    public class UnitySerial
    {
        SerialPort serial;
        byte[] buf;
        string message;
        Thread SerialThread;
        bool isThreading = false;

        public UnitySerial(string portName, int baudRate, int bufSize)
        {
            buf = new byte[bufSize];
            Init ( portName, baudRate );
            SerialThread = new Thread (this.UpdateData);
        }

        public void ThreadStart()
        {
            SerialThread.Start ();
        }

        public void ThreadEnd()
        {            
            isThreading = false;         
            SerialThread.Abort ();
            serial.Close ();
        }

        void Init(string portName, int baudRate)
        {
            serial = new SerialPort (portName, baudRate, Parity.None, 8, StopBits.One);
            try
            {
                serial.Open();
                serial.DtrEnable = true;
                serial.RtsEnable = true;
                serial.DiscardInBuffer();
                serial.ReadTimeout = 5;
            }
            catch( Exception e )
            {
                serial = null;
                return;
            }
        }

        public string GetData()
        {
            return message;
        }

        public void UpdateData()
        {
            isThreading = true;

            while (isThreading)
            {
                int read = 0;
                try
                {
                    message = serial.ReadLine();
                }
                catch(TimeoutException e)
                {
                } 
             }
        }
    }
}

Unityからプラグイン経由でデータを取得する

最後にUnityでシリアル通信のデータを受信する部分を作成しましょう。

f:id:nn_hokuson:20170319152218p:plain:w300

まずはUnityでプロジェクトを作成し、Assetsフォルダの下にPluginsフォルダを作成します。
そのなかに先ほど作成した「SerialLib.dll」を入れて下さい。

f:id:nn_hokuson:20160726200331p:plain

続いてシリアルを受信するためのC#スクリプトを作成します。
プロジェクトビューで「Create」→「C# Script」を選択し「SerialTest」という名前で保存します。
このファイルに次のスクリプトを入力して下さい。

using UnityEngine;
using System.Collections;
using System.IO.Ports;
using System.Runtime.InteropServices;

public class SerialTest : MonoBehaviour {
    public GameObject rocket;
    public static SerialLib.UnitySerial serial;

    void Start()
    {
        serial = new SerialLib.UnitySerial ("/dev/cu.usbmodem1D1141", 115200, 256);
        serial.ThreadStart ();
    }

    void Update()
    {
        string str = serial.GetData ();
        if (str != null) {
            int r = int.Parse (str);

        }
        Debug.Log (serial.GetData ());
    }

    void OnDestroy()
    {
        serial.ThreadEnd ();
    }
}

UnitySerial のコンストラクタに渡している第一引数はポート名、第二引数は通信速度、第三引数は受信バッファのサイズです。
ポート名はArduinoのメニューバーから「ツール」→「シリアルポート」で調べることが出来ます。

Unityエディタに戻ると「The type or namespace name 'Ports' does not exist in the System.IO」というエラーが出ています。
これは、Unityがデフォルトで.Netのサブセットを使うようになっているのが原因です。
.Netのすべての機能が使えるように変更しましょう。

Unityのメニューバーから「Edit」→「Project Settings」→「Player」を選択します。
インスペクタから「Api Compatibility Level」の項目を「.NET 2.0」に設定して下さい。

f:id:nn_hokuson:20160726202538p:plain:w200

作成したスクリプトを「Main Camera」にアタッチ(空のゲームオブジェクトを作って、それにアタッチでももちろんOKです)して動作確認してみてください。

f:id:nn_hokuson:20160726202708p:plain

ちゃんと動いていれば、UnityのコンソールにArduinoから送信されたデータが表示されるはずです。