d.sunnyone.org
sunnyone.org

ページ

2013-09-25

AndroidからBluetooth機器にSPP (Serial Port Profile) で接続する

作ったデバイスにAndroidからつないでみようと思ってやってみた。まっとうに使ってもらおうとすると、他にもけっこういろいろ必要なのだけど、自分で使うくらいならわりと簡単にできる。

やりかた

  1. BluetoothAdapter.getDefaultAdapter()を使って、アダプタを得る。
  2. アダプタにisEnabled()を呼び出して、Bluetooth設定が有効なことを確認する。
    • インテントを出してユーザーに有効にしてもらうことも可能だけど、ここではしない。
  3. アダプタにgetBondedDevices()を呼び出して、ペアリングされたデバイスの一覧を得る。
    • 探索してペアリングされたデバイス以外から探すことも可能だけど、ここではしない。
  4. デバイスのプロパティを見て、欲しいデバイスを探し出す。
    • 本当はUI出して選んでもらうのがいいと思うけど、ここではしない。
  5. 欲しいデバイスにcreateRfcommSocketToServiceRecord()を呼び、必要なサービスのBluetoothSocketを得る。
    • SPP (Serial Port Profile)のUUID(00001101-0000-1000-8000-00805F9B34FB)を渡す必要があるので、定数定義しておく。
  6. BluetoothSocketにconnect()を呼ぶ。
  7. BluetoothSocketにgetInputStream() / getOutputStream()を呼んで、適当に操作する。
  8. BluetoothSocketにclose()を呼ぶ。


このへんのページのほうが詳しいので、「ここではしない」ことがしたければこちらを参考に...

罠1) getDefaultAdapter()は、Gingerbread (2.3)では、UIスレッドでしか呼べない。


いかにもUIなんか関係なさげなメソッドなのに、Handlerが取れなくて例外が出る。4.1では起きないらしいけど、2.3じゃダメだと…ねぇ?
cf: https://groups.google.com/forum/#!msg/android-developers/wFbD5V2DWRc/gv0VF4Y6ICwJ

罠2) BluetoothSocket#connect()は「Service discovery failed」でたまに失敗する

Webを見るとよくある話のようで、ウエイトを入れたり、アダプタにcancelDiscoveryをさせろとか、リフレクションで中のメソッドを呼び出せとか、いろいろある。どうにもすっきりしないし、時間がかかってNGなので、何かのタイムアウトなんだと思う。なので、とりあえずリトライすることにした(リトライの条件はもう少ししっかりしたほうがいいかも)
cf:  http://stackoverflow.com/questions/3397071/service-discovery-failed-exception-using-bluetooth-on-android

コード

ASCIIで1行書き込むと、1行返ってくる感じのサンプル。

本当はすべてUIじゃないスレッドの中でのコードにしたかったのだけど、上述の罠があって、getDefaultAdapter()だけUIスレッドで渡すようにしているので、コピペでは現実的に動かない。

close()漏れとかあるので、ためしに書いてみるてな感じの参考程度に。sendLogMessage()は、適当なTextViewにログを出すためのメソッドなので、Log.dとかでよいです。

final String command = "HELO\n";
final UUID sppUuid = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
BluetoothAdapter.getDefaultAdapter();
if (btAdapter == null) {
sendLogMessage("Bluetooth adapter is not available.");
return;
}
sendLogMessage("Bluetooth adapter is found.");
if (!btAdapter.isEnabled()) {
sendLogMessage("Bluetooth is disabled. Check configuration.");
return;
}
sendLogMessage("Bluetooth is enabled.");
BluetoothDevice btDevice = null;
Set<BluetoothDevice> bondedDevices = btAdapter.getBondedDevices();
for (BluetoothDevice dev : bondedDevices) {
sendLogMessage("Paired device: " + dev.getName() + " (" + dev.getAddress() + ")");
if (dev.getName().equals("MyDeviceName")) {
btDevice = dev;
}
}
if (btDevice == null) {
sendLogMessage("Target Bluetooth device is not found.");
return;
}
sendLogMessage("Target Bluetooth device is found.");
BluetoothSocket btSocket;
try {
btSocket = btDevice.createRfcommSocketToServiceRecord(sppUuid);
} catch (IOException ex) {
sendLogMessage("Failed to create RfComm socket: " + ex.toString());
return;
}
sendLogMessage("Created a bluetooth socket.");
for (int i = 0; ; i++) {
try {
btSocket.connect();
} catch (IOException ex) {
if (i < 5) {
sendLogMessage("Failed to connect. Retrying: " + ex.toString());
continue;
}
sendLogMessage("Failed to connect: " + ex.toString());
return;
}
break;
}
sendLogMessage("Connected to the bluetooth socket.");
try {
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(btSocket.getOutputStream(), "ASCII"));
writer.write(command);
writer.flush();
} catch (IOException ex) {
sendLogMessage("Failed to write a command: " + ex.toString());
return;
}
sendLogMessage("Command is sent: " + command);
String output;
try {
BufferedReader reader = new BufferedReader(new InputStreamReader(btSocket.getInputStream(), "ASCII"));
output = reader.readLine();
} catch (IOException ex) {
sendLogMessage("Failed to write a command: " + ex.toString());
return;
}
sendLogMessage("Result: " + output);
try {
btSocket.close();
} catch (IOException ex) {
sendLogMessage("Failed to close the bluetooth socket.");
return;
}
sendLogMessage("Closed the bluetooth socket.");

0 件のコメント:

コメントを投稿