【ソース一式】
http://licheng.sakura.ne.jp/myeongwol/MyeongWol_Android.zip
【スクリーンショット】
操作方法については説明の必要もないでしょう。実際動いてる様子は動作を見てください。
このアプリは、Android SDKに含まれるBluetoothChatを改造して作った。BluetoothChatは、Eclipseで[ファイル]>[新規]>[Androidプロジェクト]>[既存サンプルからプロジェクトを作成]でワークスペースに生成される。BluetoothChatは、BluetoothのSPPを用いるサンプルプログラムである。BluetoothChatのメインのソースは、BluetoothChat.javaであり、これを修正してMyeongWol.javaを作った。以下、BluetoothChat から MyeongWol.java への変更点など説明する。(いや、そんな大層なもんじゃないけど。)
(1) シークバーおよび縦型のシークバー
ラジコンサーボの操作にシークバーを使用。ただし、android.widget.SeekBarは縦にできない。これだと上図のような直感的な操作画面にならない。困ったなと思ったら、balafon氏による縦型シークバーの実装を見つけた。今回はこれを使わせてもらう。
https://groups.google.com/forum/?fromgroups=#!topic/android-group-japan/Tf51tVwJ2NQ
http://560b.sakura.ne.jp/android/VerticalSlidebarExample.zip
// 縦型シークバー、ふつうのシークバー、イベントとリスナー
import com.tokaracamara.android.verticalslidevar.VerticalSeekBar;
import android.widget.SeekBar;
import android.view.MotionEvent;
import android.view.View.OnTouchListener;
(2) レイアウトとオブジェクト宣言
GUIデザイナでボタンとシークバーを上図にようにレイアウトして名前をつける。GUIデザイナはときどきイラッとするので、適宜テキスト編集に切り替えて修正する。できたら、Javaのソースでオブジェクトを宣言してリソースを参照する。いいかげんなレイアウトなので、たぶん機種によってはひどく表示が崩れると思う。要検討。
private Button buttonForward;
private Button buttonBackward;
private Button buttonLeft;
private Button buttonRight;
private SeekBar seekNeck;
private SeekBar seekArmL2;
private SeekBar seekArmR2;
private VerticalSeekBar vseekWaist;
private VerticalSeekBar vseekArmL1;
private VerticalSeekBar vseekArmR1;
...
buttonForward = (Button) findViewById(R.id.buttonForward);
....
seekNeck = (SeekBar) findViewById(R.id.seekNeck);
....
vseekWaist = (VerticalSeekBar) findViewById(R.id.vseekWaist);
(3) イベントリスナの定義
クラス原理主義な言語であるJavaで、コールバック関数の登録を簡潔に記述しようとすると、下記のようないとも珍妙な記述になる。このnew OnTouchListener()以降の記述が無名クラスとか呼ばれるもので、OnTouchListenerのその場かぎりの実装およびインスタンス生成を意味する。
上記では、「前進」ボタンを押したときに前進のコマンドを、離したときに停止のコマンドを送る。(コマンド仕様については後述) 同様に、シークバーについては下記のようになる。ここではバーが動いたときにその値(引数progress)を送る。値は0〜180で、サーボの角度を表す。これをDecimalFormatでゼロ埋め3桁の10進文字列にして送る。
buttonForward.setOnTouchListener(new OnTouchListener() {
public boolean onTouch(View v,MotionEvent event){
int action = event.getAction();
switch(action){
case MotionEvent.ACTION_DOWN:
sendMessage("#F02$");
break;
case MotionEvent.ACTION_UP:
sendMessage("#S$");
break;
}
return false;
}
});
seekNeck.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener(){
public void onProgressChanged(SeekBar seekBar,int progress,boolean fromUser){
if(fromUser){
DecimalFormat zeroformat = new DecimalFormat("000");
sendMessage("#2"+zeroformat.format(progress) +"$");
}
}
public void onStartTrackingTouch(SeekBar seek){;} // does nothing
public void onStopTrackingTouch(SeekBar seekBar){;} // does nothing
});
主な修正は以上で、あとは元のBluetoothChatにあった要らないもの(チャットの入力ボックスとか送信ボタンとかログ表示とか)を削除しただけ。
(4) コマンド仕様
(4.1)コマンド開始/終了コード
一般的にASCII文字でシリアル通信する場合、STX(0x02)/ETX(0x03)を電文の開始/終了コードにする。ただこれはコンソールでデバッグするときに不便だ。キーボードではSTXやETXを打てないし、TeraTermなどのコンソールはSTX/ETXを表示できない。(ハイパーターミナルだとニコニコマークに化ける)
今回は、'#'と'$'を電文の開始/終了コードに割り当てる。電文内で'#'と'$'を使わないという約束があるのなら、これでかまわない。
(4.2)コマンド
コマンドはすべて1文字とし、下記のように定める。
足回り系
前進:'F' 後退:'B' 右回転:'R' 左回転:'L' 停止:'S'
サーボ系
腰:'1' 首:'2' 左肩:'3' 左肘:'4' 右肩:'5' 右肘'6'
(4.3)パラメータ
足回り系
ゼロ埋め2桁の10進文字列で01〜10。値が小さいほど速い。
停止コマンドはパラメータなし。
サーボ系
ゼロ埋め3桁の10進文字列で000〜180。サーボの角度を指令する。