| 278行目: | 278行目: | ||
==== スマートフォン(Android)のソフトウェア ==== | ==== スマートフォン(Android)のソフトウェア ==== | ||
クライアントからサーバに接続する時、RFCOMMチャネルを作成してBluetoothSocketを作成する必要があるが、<br> | クライアントからサーバに接続する時、RFCOMMチャネルを作成してBluetoothSocketを作成する必要があるが、<br> | ||
この時に指定するUUIDは、SPP(シリアルポートプロファイル) | この時に指定するUUIDは、SPP(シリアルポートプロファイル)のUUIDである00001101-0000-1000-8000-00805f9b34fbを指定する。<br> | ||
<br> | |||
また、デバイス名はMYBTとしているため、BondedリストからBluetoothDeviceを探す際の名前も変更する。<br> | また、デバイス名はMYBTとしているため、BondedリストからBluetoothDeviceを探す際の名前も変更する。<br> | ||
<br> | |||
Bluetooth通信を使用するパーミッションを設定する。<br> | |||
ディスカバリは行わないため、LOCATIONのパーミッションは設定しない。<br> | |||
<syntaxhighlight lang="xml"> | |||
<!-- AndroidManifest.xml --> | |||
<?xml version="1.0" encoding="utf-8"?> | |||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.keicode.android.testapps.bttest1c"> | |||
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/> | |||
<uses-permission android:name="android.permission.BLUETOOTH"/> | |||
<application | |||
android:allowBackup="true" | |||
android:icon="@mipmap/ic_launcher" | |||
android:label="@string/app_name" | |||
android:supportsRtl="true" | |||
android:theme="@style/AppTheme" | |||
> | |||
<activity android:name=".MainActivity" android:configChanges="orientation"> | |||
<intent-filter> | |||
<action android:name="android.intent.action.MAIN" /> | |||
<category android:name="android.intent.category.LAUNCHER" /> | |||
</intent-filter> | |||
</activity> | |||
</application> | |||
</manifest> | |||
</syntaxhighlight> | |||
<br> | |||
状態を表示するためのTextViewと、サーバから送信される文字を表示するためのTextViewの計2つのTextViewを並べている。<br> | |||
<syntaxhighlight lang="xml"> | |||
<!-- res/layout/activity_main.xml --> | |||
<?xml version="1.0" encoding="utf-8"?> | |||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" | |||
xmlns:tools="http://schemas.android.com/tools" | |||
android:id="@+id/activity_main" | |||
android:layout_width="match_parent" | |||
android:layout_height="match_parent" | |||
android:paddingBottom="@dimen/activity_vertical_margin" | |||
android:paddingLeft="@dimen/activity_horizontal_margin" | |||
android:paddingRight="@dimen/activity_horizontal_margin" | |||
android:paddingTop="@dimen/activity_vertical_margin" | |||
tools:context="com.keicode.android.testapps.bttest1c.MainActivity"> | |||
<TextView | |||
android:id="@+id/btStatusTextView" | |||
android:layout_width="wrap_content" | |||
android:layout_height="wrap_content" | |||
android:text="Not connected" /> | |||
<TextView | |||
android:id="@+id/tempTextView" | |||
android:layout_width="wrap_content" | |||
android:layout_height="wrap_content" | |||
android:text="(unknown)" | |||
android:layout_below="@id/btStatusTextView" | |||
android:textSize="24sp"/> | |||
</RelativeLayout> | |||
</syntaxhighlight> | |||
<br> | |||
以下のサンプルコードでは、ワーカースレッド用に1つのクラスを定義しているだけである。<br> | |||
例外が発生した場合、その状況をHandlerを介してUI側にフィードバックしている。<br> | |||
<syntaxhighlight lang="java"> | <syntaxhighlight lang="java"> | ||
// MainActivity.java | |||
package com.keicode.android.testapps.bttest1c; | |||
import android.bluetooth.BluetoothAdapter; | |||
import android.bluetooth.BluetoothDevice; | |||
import android.bluetooth.BluetoothSocket; | |||
import android.os.Handler; | |||
import android.os.Message; | |||
import android.support.v7.app.AppCompatActivity; | |||
import android.os.Bundle; | |||
import android.util.Log; | |||
import android.widget.TextView; | |||
import java.io.IOException; | |||
import java.io.InputStream; | |||
import java.io.OutputStream; | |||
import java.nio.charset.StandardCharsets; | |||
import java.util.Set; | |||
public class MainActivity extends AppCompatActivity | |||
{ | |||
static final String TAG = "BTTEST1"; | |||
BluetoothAdapter bluetoothAdapter; | |||
TextView btStatusTextView; | |||
TextView tempTextView; | |||
BTClientThread btClientThread; | |||
final Handler handler = new Handler() | |||
{ | |||
@Override | |||
public void handleMessage(Message msg) | |||
{ | |||
String s; | |||
switch(msg.what) | |||
{ | |||
case Constants.MESSAGE_BT: | |||
s = (String) msg.obj; | |||
if(s != null) | |||
{ | |||
btStatusTextView.setText(s); | |||
} | |||
break; | |||
case Constants.MESSAGE_TEMP: | |||
s = (String) msg.obj; | |||
if(s != null) | |||
{ | |||
tempTextView.setText(s); | |||
} | |||
break; | |||
} | |||
} | |||
}; | |||
@Override | |||
protected void onCreate(Bundle savedInstanceState) | |||
{ | |||
Log.d(TAG, "onCreate"); | |||
super.onCreate(savedInstanceState); | |||
setContentView(R.layout.activity_main); | |||
// Find Views | |||
btStatusTextView = (TextView) findViewById(R.id.btStatusTextView); | |||
tempTextView = (TextView) findViewById(R.id.tempTextView); | |||
if(savedInstanceState != null) | |||
{ | |||
String temp = savedInstanceState.getString(Constants.STATE_TEMP); | |||
tempTextView.setText(temp); | |||
} | |||
// Initialize Bluetooth | |||
bluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); | |||
if( bluetoothAdapter == null ) | |||
{ | |||
Log.d(TAG, "This device doesn't support Bluetooth."); | |||
} | |||
} | |||
@Override | |||
protected void onResume() | |||
{ | |||
super.onResume(); | |||
btClientThread = new BTClientThread(); | |||
btClientThread.start(); | |||
} | |||
@Override | |||
protected void onPause() | |||
{ | |||
super.onPause(); | |||
if(btClientThread != null) | |||
{ | |||
btClientThread.interrupt(); | |||
btClientThread = null; | |||
} | |||
} | |||
@Override | |||
protected void onSaveInstanceState(Bundle outState) | |||
{ | |||
super.onSaveInstanceState(outState); | |||
outState.putString(Constants.STATE_TEMP, tempTextView.getText().toString()); | |||
} | |||
public class BTClientThread extends Thread | |||
{ | |||
InputStream inputStream; | |||
OutputStream outputStrem; | |||
BluetoothSocket bluetoothSocket; | |||
public void run() | |||
{ | |||
byte[] incomingBuff = new byte[64]; | |||
BluetoothDevice bluetoothDevice = null; | |||
Set<BluetoothDevice> devices = bluetoothAdapter.getBondedDevices(); | |||
for(BluetoothDevice device : devices) | |||
{ | |||
if(device.getName().equals(Constants.BT_DEVICE)) | |||
{ | |||
bluetoothDevice = device; | |||
break; | |||
} | |||
} | |||
if(bluetoothDevice == null) | |||
{ | |||
Log.d(TAG, "No device found."); | |||
return; | |||
} | |||
try | |||
{ | |||
bluetoothSocket = bluetoothDevice.createRfcommSocketToServiceRecord(Constants.BT_UUID); | |||
while(true) | |||
{ | |||
if(Thread.interrupted()) | |||
{ | |||
break; | |||
} | |||
try | |||
{ | |||
bluetoothSocket.connect(); | |||
handler.obtainMessage(Constants.MESSAGE_BT, "CONNECTED " + bluetoothDevice.getName()).sendToTarget(); | |||
inputStream = bluetoothSocket.getInputStream(); | |||
outputStrem = bluetoothSocket.getOutputStream(); | |||
while(true) | |||
{ | |||
if(Thread.interrupted()) | |||
{ | |||
break; | |||
} | |||
// Send Command | |||
String command = "GET:TEMP"; | |||
outputStrem.write(command.getBytes()); | |||
// Read Response | |||
int incomingBytes = inputStream.read(incomingBuff); | |||
byte[] buff = new byte[incomingBytes]; | |||
System.arraycopy(incomingBuff, 0, buff, 0, incomingBytes); | |||
String s = new String(buff, StandardCharsets.UTF_8); | |||
// Show Result to UI | |||
handler.obtainMessage(Constants.MESSAGE_TEMP, s).sendToTarget(); | |||
// Update again in a few seconds | |||
Thread.sleep(3000); | |||
} | |||
} | |||
catch(IOException e) | |||
{ | |||
// connect will throw IOException immediately | |||
// when it's disconnected. | |||
Log.d(TAG, e.getMessage()); | |||
} | |||
handler.obtainMessage(Constants.MESSAGE_BT, "DISCONNECTED").sendToTarget(); | |||
// Re-try after 3 sec | |||
Thread.sleep(3 * 1000); | |||
} | |||
} | |||
catch(InterruptedException e) | |||
{ | |||
e.printStackTrace(); | |||
} | |||
catch(IOException e) | |||
{ | |||
e.printStackTrace(); | |||
} | |||
if(bluetoothSocket != null) | |||
{ | |||
try | |||
{ | |||
bluetoothSocket.close(); | |||
} | |||
catch(IOException e) | |||
{ | |||
} | |||
bluetoothSocket = null; | |||
} | |||
handler.obtainMessage(Constants.MESSAGE_BT, "DISCONNECTED - Exit BTClientThread").sendToTarget(); | |||
} | |||
} | |||
} | |||
</syntaxhighlight> | |||
<br> | |||
<syntaxhighlight lang="java"> | |||
// Constants.java | |||
package com.keicode.android.testapps.bttest1c; | package com.keicode.android.testapps.bttest1c; | ||