The jonki

呼ばれて飛び出てじょじょじょじょーんき

[Unity] [Arduino] [OSC] UnityとAruduino間でOSCでハスハスする

前の続きです。前回は導通チェック程度だったけど、今回はちゃんとメッセージをOSCで送受信します。今回通信するのはUnityとArduinoイーサネットシールド付き)の間でやります。

#junki::acoustic UnityでOSCを使う(UnityOSC)

Unity側の準備

セットアップは上の記事を参考に済ませてください。前回と変更した部分を書きます。

OSCHandler.cs

CreateClientのclientID部分の名前をArduinoに変えただけです。

	public void Init(string targetAddr, int outGoingPort, int inComingPort)
	{
		//Initialize OSC clients (transmitters)
		// Sender
		CreateClient("Arduino", IPAddress.Parse(targetAddr), outGoingPort);
		
		//Initialize OSC servers (listeners)
		// Receiver
		CreateServer("SomeOscClient", inComingPort);		
	}
OSCController.cs

OSCを実際に使う部分です。UpdateLogsで受信したパケットをログに突っ込んでます。とりあえずアップデートがあったときだけ表示するように変更しました。また送信部分はマウスのクリック時のXYポジションを送るように変更しました。

注意点としてUpdateLogsはUnity側のアプリのフレームレートで呼ばれるので相手側のフレームレートと合わせないとOSCパケットを取りこぼす可能性があります。あんまりシビアなものでなければとりあえず今のところ気にしなくても良いけど。

using UnityEngine;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using UnityOSC;

public class OSCController : MonoBehaviour {
	#region Network Settings
	public string TargetAddr;
	public int OutGoingPort;
	public int InComingPort;
	#endregion
	private Dictionary<string, ServerLog> servers;
	private int lastPacketIndex;
	
	// Script initialization
	void Start() {	
		OSCHandler.Instance.Init(TargetAddr, OutGoingPort, InComingPort);
		servers = new Dictionary<string, ServerLog>();
	}

	// NOTE: The received messages at each server are updated here
        // Hence, this update depends on your application architecture
        // How many frames per second or Update() calls per frame?
	void Update() {
		// must be called before you try to read value from osc server
		OSCHandler.Instance.UpdateLogs();
		
		// Recieve
		servers = OSCHandler.Instance.Servers;
	        foreach( KeyValuePair<string, ServerLog> item in servers )
		{
			// show the latest osc packet
			if(item.Value.packets.Count  > 0 && lastPacketIndex != item.Value.packets.Count - 1) {
				lastPacketIndex = item.Value.packets.Count - 1;
				UnityEngine.Debug.Log(String.Format("SERVER: {0} ADDRESS: {1} VALUE 0: {2} VALUE 1: {3} VALUE 2: {4}", 
				                                    item.Key, // Server name
				                                    item.Value.packets[lastPacketIndex].Address, // OSC address
				                                    item.Value.packets[lastPacketIndex].Data[0].ToString(), //First data value
				                                    item.Value.packets[lastPacketIndex].Data[1].ToString(),
				                                    item.Value.packets[lastPacketIndex].Data[2].ToString()));
			}
		}		
		
		// Send
		if(Input.GetMouseButtonDown(0)) {
			Debug.Log("SendMessage");
			var clickPos = new List<float>(){Input.mousePosition.x, Input.mousePosition.y};
			OSCHandler.Instance.SendMessageToClient("Arduino", "/click/pos", clickPos);
		}
	}
}

Arduino側の準備

ArdOSCのExample/SimpleRecieve
ArduinoでOSCを使うには最初oscuinoとかいうのを使おうと思ったけど、うまくOSCのアドレスマッチングが動かなかったためこちらの方のOSCライブラリを使った。ありがたや。

github recotana ArdOSC

ArdOSCを使う手順はこんな感じ。

  • これをzipで落として来て、フォルダをArdOSCにリネーム
  • Arduino/libraries/ArdOSC/という感じに直置きする
  • Arduino IDEを再起動して、[Sketch] -> [Import Library...] -> [ArdOSC]
OSCSample.ino

MACアドレスイーサネットシールド裏側に記載があります。myIpはArduinoに設定するIPアドレス、destIpは通信先(今回はUnity側)で設定します。ライブラリがシンプルなので説明すべき部分はほぼないですが、取り扱うOSCのアドレスに対してその処理を行う関数を関連づけておくと、OSCパケットを受信するとその関数がコールされます。パケットをArduino側で受信したらEcho的にUnity側にも"/ard/echo"として適当に3つの値を返すようにしました。

#include <SPI.h>
#include <Ethernet.h>

#include <ArdOSC.h>

byte myMac[] = { 0xXX, 0xXX, 0xXX, 0xXX, 0xXX, 0xXX };
byte myIp[]   = { 192, 168, x, x };
byte destIp[] = { 192, 168, x, x };
int  inPort  = 5555;  // 受信ポート
int  outPort = 6666; // 送信ポート

OSCServer server;
OSCClient client;

void setup(){ 
    Serial.begin(19200);

    Ethernet.begin(myMac ,myIp); 
    server.begin(inPort);

    //set callback function
    server.addCallback("/click/pos", &clickEvent);
}

void loop(){
    if(server.aviableCheck()>0){
        Serial.println("alive! "); 
    }
}

void clickEvent(OSCMessage *_mes){
    Serial.println("clickEvent");
    int posX = _mes->getArgFloat(0);
    int posY = _mes->getArgFloat(1);
    Serial.print(posX);
    Serial.print(", ");
    Serial.println(posY);

    sendMessage();
}

void sendMessage() {
    Serial.println("sendMessage");
    OSCMessage msg;

    msg.setAddress(destIp, outPort);

    // 適当なメッセージを作成
    msg.beginMessage("/ard/echo");
    msg.addArgInt32(10);
    msg.addArgFloat(10.0f);
    msg.addArgString("i am arduino.");

    client.send(&msg);

    msg.flush(); //object data clear
}

実行

Unity側で適当に画面をクリックするとArduino側のシリアルコンソールにマウス座標が飛ばされ、そのエコーとしてサンプルのデータをUnityのコンソールログでも確認できます。これで開発が捗ります。

Unityのコンソール
SendMessage
UnityEngine.Debug:Log(Object)
OSCController:Update() (at Assets/Script/OSCController.cs:68)
SERVER: SomeOscClient ADDRESS: /ard/echo VALUE 0: 10 VALUE 1: 10 VALUE 2: i am arduino.
UnityEngine.Debug:Log(Object)
OSCController:Update() (at Assets/Script/OSCController.cs:57)
Arduinoシリアルコンソール
alive! 
clickEvent
429, 261
sendMessage
alive!