ただし グラフィクスは一切なし です。
使用した 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パケットを送り続けるサンプル
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 というファイルを作成します。
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 がない。
この調子でダミーを作っていくわけですが、結論からいいますと、ここには複数の関数定義も含める必要がありました。
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 でプロジェクトを用意して、以下のスクリプトを用意します。
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 は先ほどのを実行させた状態です。
見事、受信しています。これで動作確認ができました。めでたしめでたし。
Unity プログラムを Intel Edison で自動起動する
というエントリを近日アップします。こちらのほうが難易度が高いです実は。
これができると、本格的に何かに利用したくなってきます。