
| 自作マイコンボードとAndroidスマートフォンの無線接続 |
最初に作成したTobaccoアプリケーションを改造して、Bluetoothによるデータ通信ができるようにします。
Bluetoothの予備知識
Bluetoothは、よくこなれた技術なので、優れた解説がネットワークに多数掲載されています。詳しい説明は、検索して調べてください。ここでは、開発で必要な最低限の解説をします。
複数のデバイスとの無線ネットワークを作ることができます。ただし、ここでは、1対1の接続を前提としたアプリケーションを作ります。モジュール1台毎に割り当てられているBluetooth IDを使って、1対多通信に拡張することも可能です。
実機デバッグの準備アプリケーションソフトウエアやファームウエアのデバッグには、Bluetoothターミナルソフト(ASCIIとHEXが表示可能なもの)をインストールしておくと便利です。これらのソフトはGooglePlayで各種無料公開されています。例えば、S2 Terminal for Bluetooth (ターミナルソフト)(無料版)があると便利です。ただし、このターミナルソフトは、SPPのみに対応していますので、他のプロファイルを使う機器とは通信できません。
ユーザインタフェースの変更
センサシステム用に Tobacco の activity_main.xml にテキストとボタンを追加します。
また、editText(入力欄)は、数値だけ入力するように限定していましたが、コマンドには文字を使ったほうが、人間が分かり易いため、文字も入力できるように、Input Typeを変更します。Single Lineというのは文字数が多くなって、画面に表示しきれなくなっても、行を増やさないプロパティ、Ellipsizeは、文字オーバしたときの省略表示方法を決めるプロパティです。

下記のように、プロパティを変更します。
| 属性 | 1行目左テキスト | 1行目右テキスト | 2行目中央エディットテキスト | 2行目右ボタン | 3行目右テキスト | 4行目左ボタン | 4行目右ボタン | 5行目ボタン | 6行目ボタン |
|---|---|---|---|---|---|---|---|---|---|
| id | textAddress | textAddressBox | editTextInputBox | button5 | textOutputBox | button1 | button2 | button3 | button4 |
| text | Msg: | no connection | 変更なし(空欄) | CLR | 0 | Contunuous | One-shot | Command | Stop |
| textSize | 18 | 18 | 18 | 18 | 18 | 18 | 18 | 18 | 18 |
| textStyle | Bold | Bold | Bold | 変更なし | Bold | 変更なし | 変更なし | 変更なし | 変更なし |
| layout_width | 80dp | match_parent | wrap_content | 80 | match_parent | match_parent | match_parent | match_parent | match_parent |
| gravity | 変更なし | left | left | 変更なし | right | 変更なし | 変更なし | 変更なし | 変更なし |
| paddingLeft | 変更なし | 6dp | 変更なし | 変更なし | 変更なし | 変更なし | 変更なし | 変更なし | 変更なし |
| paddingRight | 変更なし | 変更なし | 変更なし | 変更なし | 6dp | 変更なし | 変更なし | 変更なし | 変更なし |
| textColor | 変更なし | #FFFFFF | 変更なし | 変更なし | #00FFFF | 変更なし | 変更なし | 変更なし | 変更なし |
| background | 変更なし | #000000 | 変更なし | 変更なし | #000000 | 変更なし | 変更なし | 変更なし | 変更なし |
| maxLength | 変更なし | 変更なし | 32 | 変更なし | 変更なし | 変更なし | 変更なし | 変更なし | 変更なし |
| inputType | 変更なし | 変更なし | text | 変更なし | 変更なし | 変更なし | 変更なし | 変更なし | 変更なし |
| onClick | 空欄 | 空欄 | 空欄 | 空欄 | 空欄 | 空欄 | 空欄 | 空欄 | 空欄 |
| ellipsize | 変更なし | marquee | 変更なし | 変更なし | marquee | 変更なし | 変更なし | 変更なし | 変更なし |
| singleLine | 変更なし | true | 変更なし | 変更なし | true | 変更なし | 変更なし | 変更なし | 変更なし |
メニューの作成(XMLの入力)
デバイスとの接続、通信エリア内のデバイスのブロードキャスト、接続の終了はいつも使うわけではないので、メニューに表示するようにします。これまでは、Design view画面(GUI)に、文字やボタンを配置してXMLファイルを作成していましたが、ここでは、XMLファイルを編集してみます。メニューを表示するためのメソッドは、MainActivity.javaの中で記述します。
<item
android:id="@+id/scan"
android:title="@string/connected"
android:icon = "@android:drawable/ic_menu_search"
/>
<item
android:id="@+id/discoverable"
android:title="@string/discoverable"
android:icon = "@android:drawable/ic_menu_mylocation"
/>
<item
android:id = "@+id/disconnect"
android:title = "@string/disconnect"
android:icon = "@android:drawable/ic_menu_close_clear_cancel"
/>
接続先選択画面の作成(XMLファイルのコピー)メニューと同様にして、app/src/main/res/layout の下に device_list.xml と device_name.xml を作成します。
| ファイル(ダウンロード) | コピー先 | XMLの内容 |
|---|---|---|
| device_list.xml | app/src/main/res/layout | 接続可能なデバイスリストを作成するための画面 |
| device_name.xml | app/src/main/res/layout | 接続可能なデバイスを表示するための画面 |
| strings.xml | app/src/main/res/values | プログラム中で使用するメッセージやタイトルなどをXMLにまとめたもの |
Bluetoothパーミッションの設定Bluetooth機能をアプリケーションで使用するためには、BLUETOOTHかBLUETOOTH_ADMINのパーミッションをアプリケーションに与える必要があります。アプリケーションが使用するパーミッションやリソースの管理は、マニフェストファイルで行います。
<uses-sdk android:minSdkVersion="8" android:targetSdkVersion="18"/> <uses-permission android:name="android.permission.BLUETOOTH"/> <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/> <uses-permission android:name="android.permission.INTERNET"/>
通信用プログラムの作成通常、通信機能を利用するアプリケーションは、接続/解除手続き、送信処理/受信処理、各種エラー処理を同時にこなすため、複雑な手順になります。Android SDKでは、簡単に開発ができるようになっていますが、それでも長いプログラムになるため、基本的な機能だけを記述したソースファイルをインポートして、必要に応じて手を加えることにしましょう。Javaファイルは新規作成して、コピペしてもかまいません。必要なファイルは、下記の3個です。前に作成した、MainActivity.java は削除してかまいません。尚、これらのソースコードの一部は、Android SDKのサンプルに含まれているBluetoothChatに手を加えたものなので、著作権表示は削除しないでください。(Copyright © 2009 The Android Open Source Project, and revised unnder Apache License 2.0)
[参考] ここでは、Android SDKを使用していますが、Bluetooth通信用のライブラリを公開しているサイトもあります。通信ライブラリを使用すると、非常に簡単に実装できる可能性があります。ただし、自作デバイスとの通信に都合の良い送受信メソッドがライブラリに用意されているとは限りません。
| ファイル(ダウンロード) | ファイルの内容 |
|---|---|
| MainActivity.java | 前に作成したメインアクティビティの内容にBluetooth通信による受信処理と画面操作に対する処理を追加。 |
| DeviceListActivity.java | 接続可能なデバイスリストを作成し、接続するまでの手続きを行うアクティビティ。 |
| BluetoothService.java | 接続手続きおよびサーバソケットを提供するスレッド。各アクティビティと並列に動作させる。 |
<activity android:name=".DeviceListActivity"
android:theme="@android:style/Theme.Dialog" >
</activity>
[参考] メインアクティビティの表示時に、アクションバーまたは画面が表示されない場合は、メインアクティビティクラスの宣言で、継承されているクラス(extends の後)が、app/src/main/res/values/styles.xml に設定されたテーマと整合しているか確認してみてください。また、マニフェストに登録したアクティビティ名が、エラーになる場合は、各アクティビティのjava記述の最初のほうに記述されているパッケージ名が正しいか確認してみてください。
処理内容の概要
ソースプログラムは、時間のあるときに、全体をじっくり読んで理解してもらうことにして、ここでは、大まかな流れと、やや複雑なところに絞って説明します。
BluetoothServiceのスレッドをアクティビティから分けなくてもよさそうに思われますが、通信処理には相手の応答を待つことやデータの受信が含まれるため、この間、他の処理がブロッキングされます。ユーザがアクティビティを利用して、キャンセルのような他の処理を行うためには、スレッドを分ける必要があります。

@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.scan:
// Launch the DeviceListActivity to see devices and do scan
Intent serverIntent = new Intent(this, DeviceListActivity.class);
startActivityForResult(serverIntent, REQUEST_CONNECT_DEVICE);
return true;
case R.id.discoverable:
// Ensure this device is discoverable by others
ensureDiscoverable();
return true;
case R.id.disconnect:
// Close the bluetooth connection
onDestroy();
return true;
}
return false;
}
@Override
public void onStart() {
super.onStart();
// If BT is not on, request that it be enabled.
if (!mBluetoothAdapter.isEnabled()) {
Intent enableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableIntent, REQUEST_ENABLE_BT);
// Otherwise, setup the session
} else {
if (mBluetoothService == null) setupComm();
}
}
public void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) {
case REQUEST_CONNECT_DEVICE:
// When DeviceListActivity returns with a device to connect
if (resultCode == Activity.RESULT_OK) {
// Get the device MAC address
String address = data.getExtras()
.getString(DeviceListActivity.EXTRA_DEVICE_ADDRESS);
// Get the Bluetooth Device object
BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address);
// Attempt to connect to the device
mBluetoothService.connect(device);
}
break;
case REQUEST_ENABLE_BT:
// When the request to enable Bluetooth returns
if (resultCode == Activity.RESULT_OK) {
// Bluetooth is now enabled, so set up a session
setupComm();
} else {
// User did not enable Bluetooth or an error occurred
Log.d(TAG, "BT not enabled");
Toast.makeText(this, R.string.bt_not_enabled_leaving, Toast.LENGTH_SHORT).show();
finish();
}
}
}
Button button1 = (Button) findViewById(R.id.button1);
button1.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
// Send a command using content of the edit text widget
Continuous();
}
});
送り側では、下記のような形で送ることができます。
Message msg = mHandler.obtainMessage(MainActivity.識別子);
mHandler.sendMessage(int 識別子, int 整数, int 整数, msgオブジェクト);
msg.whatで識別子を判定し、識別子がMESSAGE_WRITEまたはMESSAGE_READであった時、msg.objからデータを取り出しています。
private final Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
// Layout Views
TextView addressBox = (TextView) findViewById(R.id.textAddressBox);
TextView outputBox = (TextView) findViewById(R.id.textOutputBox);
switch (msg.what) {
case MESSAGE_WRITE:
byte[] writeBuf = (byte[]) msg.obj;
// construct a string from the buffer
String writeMessage = new String(writeBuf);
outputBox.setText(writeMessage);
break;
case MESSAGE_READ:
byte[] readBuf = (byte[]) msg.obj;
// construct a string from the valid bytes in the buffer
String readMessage = new String(readBuf, 0, msg.arg1);
outputBox.setText(mConnectedDeviceName + "> " + readMessage);
break;
}
}
};
private final BluetoothSocket mmSocket;
private final InputStream mmInStream;
private final OutputStream mmOutStream;
private class ConnectedThread extends Thread {
private final BluetoothSocket mmSocket;
private final InputStream mmInStream;
private final OutputStream mmOutStream;
public ConnectedThread(BluetoothSocket socket) {
mmSocket = socket;
InputStream tmpIn = null;
OutputStream tmpOut = null;
// Get the BluetoothSocket input and output streams
try {
tmpIn = socket.getInputStream();
tmpOut = socket.getOutputStream();
} catch (IOException e) {
Log.e(TAG, "temp sockets not created", e); // Output error log
}
mmInStream = tmpIn;
mmOutStream = tmpOut;
}
public void run() {
byte[] buffer = new byte[1024];
int bytes;
// Keep listening to the InputStream while connected
while (true) {
try {
// Read from the InputStream
bytes = mmInStream.read(buffer);
// Send the obtained bytes to the UI Activity
mHandler.obtainMessage(MainActivity.MESSAGE_READ, bytes, -1, buffer)
.sendToTarget();
} catch (IOException e) {
break;
}
}
}
public void write(byte[] buffer) {
try {
mmOutStream.write(buffer);
// Share the sent message back to the UI Activity
mHandler.obtainMessage(MainActivity.MESSAGE_WRITE, -1, -1, buffer)
.sendToTarget();
} catch (IOException e) { }
}
public void cancel() {
try {
mmSocket.close();
} catch (IOException e) { }
}
変更の際のヒント
各自のアプリケーションの合わせて、手をを加える必要があると予想される部分を抜粋して説明します。
// PSoC Command Table
private static final String Continuous = "meas\r"; // Continuous measurement
private static final String OneShot = "sample\r"; // One-shot measurement
private static final String Stop = "stop\r"; // Stop measurement
private static final String Reset = "reset\r"; // Reset PSoC configuration
private String command = null; // Command Line string
// Intent request codes
private static final int REQUEST_CONNECT_DEVICE = 1000;
private static final int REQUEST_ENABLE_BT = 2000;
// Name for the SDP record when creating server socket
private static final String NAME = "AndroidNexus";
// Unique UUID for SSP(Serial Port Profile)/RFCOMM
private static final UUID MY_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
実機動作の手順Bluetooth通信が使えるPCかスマートフォンがもう1台あれば、双方にターミナルソフトをインストールし、1byte文字か数字を送受信し正しく送れるかテストします。テスト用のPCかスマートフォンが無い場合は、友達にターミナルソフトをインストルしてもらって実験しましょう。
操作手順
MCUボードとの接続が可能で、何らかの応答があるけれども、帰ってくる文字がおかしいという場合は、Bluetoothモジュールが壊れているか、バッテリーが消耗している可能性がありますので、チェックしてみてください。ただし、最初の接続時には、Bluetoothモジュールがマイコンに出力したバイナリメッセージがそのまま返信され、文字化けして表示される可能性があります。うんともすんとも言わないようでしたら。まず回路の配線間違いや半田付けの失敗をテスターの導通チェック機能を使ってチェックしてみてください。

お問い合わせはこちらまで: kitagawa@is.t.kanazawa-u.ac.jp
© Kanazawa Univ., 2013