自作マイコンボードと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" />
メニューと同様にして、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_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