Unity
Edison

[Unity] Intel Edison で Unity を実行する

More than 3 years have passed since last update.

ただし グラフィクスは一切なし です。

使用した Unity のバージョンは 5.1.1.f1 personal です。


Edison をセットアップ

Switch Science さんの解説は各OSごとにまとめてあってすばらしいです。

Intel Edisonを手に入れたらやること

ただし現時点で、一部コマンドの使い方が異なっていました。

configure_edison --setup

の引数 --setup は必要なかったり。

また、そのままだと自動で Wifi に接続してくれないので、

[Intel Edison] 起動したら Wifi につながるようにする

をやっときます。


Unity プロジェクト準備

プロジェクトはなんでもいいですが、実験なので軽いものがよいですね。

とはいえグラフィクスは表示されないので、動作確認がむずかしい。できれば Edison にログインしなくても確認できるのが望ましいので、今回は UDP パケットを投げるサンプルを作りました。(たまたま最近似たようなのを作ったこともあり)。


1秒置きにUDPパケットを送り続けるサンプル


UdpSender.cs

using UnityEngine;

using System.Collections;
using System.Net.NetworkInformation;

public class UdpSender : MonoBehaviour {
public int listenPort = 2002; // ポートはサーバ・クライアントで合わせる必要がある
private static bool sent = false;
private Coroutine coroutine_ = null;

public static void SendCallback(System.IAsyncResult ar)
{
// System.Net.Sockets.UdpClient u = (System.Net.Sockets.UdpClient)ar.AsyncState;
sent = true;
}

IEnumerator send(string server, string message)
{
Debug.Log("sending..");
var u = new System.Net.Sockets.UdpClient();
u.EnableBroadcast = true;
u.Connect(server, listenPort);
var sendBytes = System.Text.Encoding.ASCII.GetBytes(message);
sent = false;
u.BeginSend(sendBytes, sendBytes.Length,
new System.AsyncCallback(SendCallback), u);
while (!sent) {
yield return null;
}
u.Close();
coroutine_ = null;
Debug.Log("done.");
}

IEnumerator sending()
{
for (;;) {
if (coroutine_ == null) {
NetworkInterface[] nicList = NetworkInterface.GetAllNetworkInterfaces();
string sendMsg = "Edison";
foreach( NetworkInterface nic in nicList ) {
IPInterfaceProperties ipInfo = nic.GetIPProperties();
foreach (var addr in ipInfo.UnicastAddresses) {
var bytes = addr.Address.GetAddressBytes ();
// RFC1918
if (bytes[0] == 10 ||
(bytes[0] == 172 && 16 <= bytes[1] && bytes[1] <= 31) ||
(bytes[0] == 192 && bytes[1] == 168)) {
sendMsg += string.Format (":{0}", addr.Address.ToString ());
}
}
}
string server = "192.168.0.1"; // 可能ならブロードキャストが楽
coroutine_ = StartCoroutine(send(server, sendMsg));
yield return new WaitForSeconds(1f);
}
}
}

void Start() {
StartCoroutine(sending());
}

void Update() {
}
}


このスクリプトを適当なオブジェクトにアタッチして、Hierarchy に置きます。

下のほうにある

string server = "192.168.0.1";

ここは適宜書き換えてください。自宅などサブネットを掌握している場合はブロードキャストアドレスを書いちゃうのが楽です。職場とか学校なら迷惑をかけないよう、自分の作業PCのアドレスを入れましょう。

パケットにはアドレスを文字列にして入れておきます。あとでこれが役に立つのです。検出したNICを列挙して、RFC1918 に規定してあるプライベートネットワークアドレスを抽出しています。


Unity でビルド

上記スクリプトの入ったシーンを保存して、BuildSettings Window から


  • Target Platform:Linux

  • Architecture: x86

でビルドします。吐き出し先のフォルダ名は linux にしておきます。(なんでもよいです)

linux という名前にしてあれば、プロジェクトの下に


  • linux.x86

  • linux_Data

というファイル&フォルダが現れています。これらを Edison に送るわけですが、

ディレクトリもあるので、tar で固めます。

$ tar -cvfz linux_Data.tar.gz linux.x86 linux_Data


Edison へ転送

上記のビルド結果を Edison の /var に送ります。(/var は例で、どこでもよいです)

$ scp linux_Data.tar.gz root@192.168.2.15:/var

Edison にログインして /var 以下を確認し、展開します。

root@yuji-edison:~# cd /var

root@yuji-edison:/var# tar -xvfz linux_Data.tar.gz


実行

実行してみましょう。

root@yuji-edison:/var# ./linux.x86

./linux.x86: error while loading shared libraries: libGL.so.1: cannot open shared object file: No such file or directory

エラーが出ますよね。これは予定通り。引数でグラフィクスを無効にすればよいのです。

root@yuji-edison:/var# ./linux.x86 -batchmode -nographics

./linux.x86: error while loading shared libraries: libGL.so.1: cannot open shared object file: No such file or directory

同じエラーが出ましたね。・・・だめじゃん!


ダミーso の作成

本当の戦いはここからです。

Unity のドキュメント見ても、-batchmode -nographics でグラフィクスの初期化しない、と書いてあります。初期化はしない。しないが、so だけ読む。んですね。ならば読み込みにさえ成功させればよいはず。なのでダミーを作ります。

/var で作業するのはなんなので、ホームに移動して、main.cpp というファイルを作成します。


main.cpp

void dummy()

{
}

清々しいコードです。include すらしないコードなんて滅多に書きませんね。

ビルドして、so を作ります。

root@yuji-edison:~/work/dummy# gcc -shared main.cpp -o libGL.so.1

できました。この libGL.so.1 を /usr/lib にコピーします。

root@yuji-edison:~/work/dummy# cp libGL.so.1 /usr/lib

さあ、どうだ。

root@yuji-edison:/var# ./linux.x86 -batchmode -nographics

./linux.x86: error while loading shared libraries: libX11.so.6: cannot open shared object file: No such file or directory

だめ。でも一歩前進。こんどは libX11.so.6 がない。

この調子でダミーを作っていくわけですが、結論からいいますと、ここには複数の関数定義も含める必要がありました。


main.cpp

extern "C" {

typedef void Display;

Display* XOpenDisplay(char* display_name)
{
return 0;
}

typedef void* XIM;
typedef void* XrmDatabase;
XIM XOpenIM(Display*, XrmDatabase, char*, char*)
{
return 0;
}

typedef void* XIC;
XIC XCreateIC(XIM im, ...)
{
return 0;
}

}


これをビルドして、

root@yuji-edison:~/work/dummy# gcc -shared main.cpp -o libX11.so.6

コピーします。

root@yuji-edison:~/work/dummy# cp libX11.so.6 /usr/lib

これでも動きません。必要なダミーso はあと2つありました。

libXcursor.so.1

libXrandr.so.2

これらは空っぽでよいので、libGL.so.1 をコピーします。置き場所は同じく /usr/lib です。

root@yuji-edison:~# cd /usr/lib

root@yuji-edison:/usr/lib# cp libGL.so.1 libXcursor.so.1

root@yuji-edison:/usr/lib# cp libGL.so.1 libXrandr.so.2

さあ、動くかな?

root@yuji-edison:/var# ./linux.x86 -batchmode -nographics

Set current directory to /var

Found path: /var/linux.x86
Mono path[0] = '/var/linux_Data/Managed'
Mono path[1] = '/var/linux_Data/Mono'
Mono config path = '/var/linux_Data/Mono/etc'
PlayerConnection initialized from /var/linux_Data (debug = 0)
PlayerConnection initialized network socket : 0.0.0.0 55014
Multi-casting "[IP] 192.168.0.105 [Port] 55014 [Flags] 2 [Guid] 4221828501 [EditorId] 628846382 [Version] 1048832 [Id] LinuxPlayer(192.168.0.105) [Debug] 0" to [225.0.0.222:54997]...
PlayerConnection already initialized - listening to [192.168.0.105:55014]
displaymanager : trying .X11-unix
Using libudev for joystick management

Importing game controller configs

動いた!・・・のか?

確認するために受信側のプログラムを作成します。


受信プログラムの作成

Unity でプロジェクトを用意して、以下のスクリプトを用意します。


UdpReceiver.cs

using UnityEngine;

using System.Collections;

public class UdpReceiver : MonoBehaviour {
public int listenPort = 2002; // ポートはサーバ・クライアントで合わせる必要がある
private static bool received = false;

private struct UdpState {
public System.Net.IPEndPoint e;
public System.Net.Sockets.UdpClient u;
}

public static void ReceiveCallback(System.IAsyncResult ar) {
System.Net.Sockets.UdpClient u = (System.Net.Sockets.UdpClient)((UdpState)(ar.AsyncState)).u;
System.Net.IPEndPoint e = (System.Net.IPEndPoint)((UdpState)(ar.AsyncState)).e;
var receiveBytes = u.EndReceive(ar, ref e);
var receiveString = System.Text.Encoding.ASCII.GetString(receiveBytes);

Debug.Log(string.Format("Received: {0}", receiveString)); // ここに任意の処理を書く

received = true;
}

IEnumerator receive_loop() {
var e = new System.Net.IPEndPoint(System.Net.IPAddress.Any, listenPort);
var u = new System.Net.Sockets.UdpClient(e);
u.EnableBroadcast = true;
var s = new UdpState();
s.e = e;
s.u = u;
for (;;) {
received = false;
u.BeginReceive(new System.AsyncCallback(ReceiveCallback), s);
while (!received) {
yield return null;
}
}
}

void Start () {
StartCoroutine(receive_loop());
}
}


これを GameObject にアタッチして実行します。実行というかエディタの再生ですね。

Edison は先ほどのを実行させた状態です。

スクリーンショット 2015-08-25 20.58.06.png

見事、受信しています。これで動作確認ができました。めでたしめでたし。


Unity プログラムを Intel Edison で自動起動する

というエントリを近日アップします。こちらのほうが難易度が高いです実は。

これができると、本格的に何かに利用したくなってきます。


追記:書きました。

[Unity] Unity プログラムを Intel Edison で自動起動する