2009/09/23

Java on OSXでWiiリモコン

今さら感は拭えないが、Wiiリモコンとarduinoを組み合わせて遊びたい。
そこでまずは、MacBook上でJavaからWiiリモコンを触れるようにした。
wii remote

準備

JavaとWiiリモコンをBluetoothでつなげてみよう - ブログ: 岡崎 - Okazaki's blog」や「WiiLi.org Wii Linux - JP:WiiremoteJ/ReadMe」などにあるように、必要なものは、Java版Bluetooth API(JSR-82)のオープンソース実装bluecoveと、WiiプロトコルのJavaライブラリWiiRemoteJだけ。2009年9月22日現在での最新バージョン、BlueCove2.1.0とWiiRemoteJ v1.6を使ってみた。

JavaVMの起動オプション

いきなりつまずいた。リモコンを探しに行くところで、
java.lang.IllegalArgumentException: PCM values restricted by JAR82 to minimum 4097
とエラーメッセージが出て止まってしまう。
Googleで検索してみると色々勉強になる(例えばここ)が、結局、「JavaVMの起動オプションに以下を加えることで問題を回避できる」そうだ。
-Dbluecove.jsr82.psm_minimum_off=true
理由は問わない。魔法だと思おう...

実装例 for 安定動作?

とりあえずWiiRemoteJの配布物に含まれているサンプルコードを動かしてみる。
リモコン探して、つないで、ボタンのイベントや加速度センサのデータなどを取得する、一通りの方法はとりあえず分かった。が、このWiiRemoteJ、安定して動作させるのがなかなか難しい、ということも分かった。特にリモコンとの接続確立時に、アプリ側で対処できない(と思われる)エラーがよく起きる。

色々やってみた結果、サンプルコードで使われているfindRemote()でなく、findRemotes(WiiDeviceDiscoveryListener listener)を使った方が、動作が安定する、っぽい。あくまで「っぽい」だけど。WiiDeviceDiscoveryListenerを使ったコードは、例えば以下のようになる。
import wiiremotej.WiiRemote;
import wiiremotej.WiiRemoteJ;
import wiiremotej.event.WRAccelerationEvent;
import wiiremotej.event.WRButtonEvent;
import wiiremotej.event.WRStatusEvent;
import wiiremotej.event.WiiDeviceDiscoveredEvent;
import wiiremotej.event.WiiDeviceDiscoveryListener;
import wiiremotej.event.WiiRemoteAdapter;

public class TestWiiRemote extends WiiRemoteAdapter implements WiiDeviceDiscoveryListener
{
public static void main(String[] args)
{
WiiRemoteJ.setConsoleLoggingAll();
TestWiiRemote test = new TestWiiRemote();
try{
WiiRemoteJ.findRemotes(test,1);
}catch(Exception e){
e.printStackTrace();
}
}

public TestWiiRemote()
{
}

public void findFinished(int numFound)
{
System.out.println(numFound+" remotes has been found!");
}

public void wiiDeviceDiscovered(WiiDeviceDiscoveredEvent evt)
{
System.out.println("remote(#"+evt.getNumber()+") was discovered!");
WiiRemote remote = (WiiRemote)evt.getWiiDevice();

try{
remote.setAccelerometerEnabled(true);
remote.setSpeakerEnabled(true);
remote.setLEDIlluminated(0,true);
}catch(Exception e){
e.printStackTrace();
if(remote!=null && remote.isConnected()){
remote.disconnect();
}
}

final WiiRemote remoteF = remote;
Runtime.getRuntime().addShutdownHook(new Thread(new Runnable(){
public void run(){
if( remoteF!=null && remoteF.isConnected() ){
remoteF.disconnect();
}
}
}));

remote.addWiiRemoteListener(this);
}

public void disconnected()
{
System.out.println("Remote disconnected...");
System.exit(0);
}

public void statusReported(WRStatusEvent evt)
{
System.out.println("Battery level: " + (double)evt.getBatteryLevel()/2+ "%");
System.out.println("Continuous: " + evt.isContinuousEnabled());
System.out.println("Remote continuous: " + evt.getSource().isContinuousEnabled());
}

public void accelerationInputReceived(WRAccelerationEvent evt)
{
double x = evt.getXAcceleration();
double y = evt.getYAcceleration();
double z = evt.getZAcceleration();

System.out.println(x+","+y+","+z);
}

public void buttonInputReceived(WRButtonEvent evt)
{
if (evt.wasPressed(WRButtonEvent.TWO))System.out.println("2");
if (evt.wasPressed(WRButtonEvent.ONE))System.out.println("1");
if (evt.wasPressed(WRButtonEvent.B))System.out.println("B");
if (evt.wasPressed(WRButtonEvent.A))System.out.println("A");
if (evt.wasPressed(WRButtonEvent.MINUS))System.out.println("Minus");
if (evt.wasPressed(WRButtonEvent.HOME))System.out.println("Home");
if (evt.wasPressed(WRButtonEvent.LEFT))System.out.println("Left");
if (evt.wasPressed(WRButtonEvent.RIGHT))System.out.println("Right");
if (evt.wasPressed(WRButtonEvent.DOWN))System.out.println("Down");
if (evt.wasPressed(WRButtonEvent.UP))System.out.println("Up");
if (evt.wasPressed(WRButtonEvent.PLUS))System.out.println("Plus");
}

}
少なくとも自分の環境では、接続時にエラーが出ることはほとんどなくなった。ただ、アプリ終了時のdisconnect()がどうしてもうまくいかない。Ctrl+Cとかでアプリを終了させると、ShutdownHookが利いてdisconnect()が呼ばれるところまでは行くのだが、きちんと切断しきってくれない。アプリが終了した後、Bluetooth接続が残ってしまう。まぁそのままでもいいんだけど、これ、きれいに切りたいなぁ...

0 件のコメント: