Vuzix Bladeで開発してみた（Bluetooth of Server 編 ）

#はじめに
お世話になります。Apv(@apv23973)です。
色々やりたかったのに途中で変な病気にかかって少々中断してしまいました…
ともあれ復活したので開発再開です。

今回はVuzix bladeとスマホを使ったBluetooth通信にチャレンジです。

#Bluetooth通信
TCP/IP のソケット通信と同様に考えることができます。
つまり処理を依頼するクライアント側（今回はVuzix blade）
処理を行うサーバ側（今回はXperia SOV34：Android 8.0.0)
と分けることが出来ます。
※Bluetoothに関する詳細な内容は参考URLの内容を見て頂ければと思います。

#実装

```ruby:activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <TextView
        android:id="@+id/txt_hellow"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World!"/>

</LinearLayout>
```

画面ファイルです。
ディフォルトに画面に表示している
Hellow World!!をクライアント側に返却するようにします。


```ruby:Constants.java
import java.util.UUID;

public interface Constants {

    public static final String BT_NAME = "Blade";
    public static final UUID BT_UUID = UUID.fromString(
            "41eb5f39-6c3a-4067-8bb9-bad64e6e0908");
}

```

Bluetooth接続用の名前とUUIDを定数として設定するインタフェースを用意します。
UUID については自前で作成した UUID を使えばOKです。
※今回は参考URLで指定されてた値をそのまま使用しています。 

```ruby:MainActivity.java
import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothServerSocket;
import android.bluetooth.BluetoothSocket;
import android.os.Handler;
import android.os.Message;
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;

public class MainActivity extends Activity {

    // タグ
    static final String TAG = "BTAppServerTAG";

    BluetoothAdapter bluetoothAdapter = null;
    BTServerThread btServerThread;

    final Handler handler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            switch(msg.what){
            }
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    @Override
    protected void onResume(){
        super.onResume();

        // 画面表示で通信処理を再開
        if(bluetoothAdapter == null) {
            bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
            if (bluetoothAdapter == null) {
                Log.d(TAG, "This device doesn't support Bluetooth.");
            }
        }

        btServerThread = new BTServerThread();
        btServerThread.start();
    }

    @Override
    protected void onPause(){
        super.onPause();

        // 画面表示で通信処理を停止
        if(btServerThread != null){
            btServerThread.cancel();
            btServerThread = null;
        }
    }

    /*
     Bluetooth通信処理
     */
    public class BTServerThread extends Thread {

        BluetoothServerSocket bluetoothServerSocket;
        BluetoothSocket bluetoothSocket;
        InputStream inputStream;
        OutputStream outputStream;

        public void run() {

            byte[] incomingBuff = new byte[64];

            try {
                while (true) {

                    if (Thread.interrupted()) {
                        break;
                    }

                    try {

                        // ソケット情報を取得
                        bluetoothServerSocket
                                = bluetoothAdapter.listenUsingInsecureRfcommWithServiceRecord(
                                Constants.BT_NAME,
                                Constants.BT_UUID);


                        bluetoothSocket = bluetoothServerSocket.accept();
                        bluetoothServerSocket.close();
                        bluetoothServerSocket = null;

                        inputStream = bluetoothSocket.getInputStream();
                        outputStream = bluetoothSocket.getOutputStream();

                        while (true) {

                            if (Thread.interrupted()) {
                                break;
                            }

                            int incomingBytes = inputStream.read(incomingBuff);
                            byte[] buff = new byte[incomingBytes];
                            System.arraycopy(incomingBuff, 0, buff, 0, incomingBytes);
                            String cmd = new String(buff, StandardCharsets.UTF_8);

                            String resp = processCommand(cmd);
                            outputStream.write(resp.getBytes());
                        }

                    } catch (IOException e) {
                        e.printStackTrace();
                    }

                    if (bluetoothSocket != null) {
                        try {
                            bluetoothSocket.close();
                            bluetoothSocket = null;
                        } catch (IOException e) {}
                    }

                    // Bluetooth connection broke. Start Over in a few seconds.
                    Thread.sleep(3 * 1000);
                }
            }
            catch(InterruptedException e){
                Log.d(TAG, "Cancelled ServerThread");
            }

            Log.d(TAG, "ServerThread exit");
        }

        public void cancel() {
            if (bluetoothServerSocket != null) {
                try {
                    bluetoothServerSocket.close();
                    bluetoothServerSocket = null;
                    super.interrupt();
                } catch (IOException e) {}
            }
        }

        protected String processCommand(String cmd){

            Log.d(TAG, "processCommand " + cmd);
            String resp = "処理中...";

            try {

                //送信元に結果返却
                if (cmd.equals("REQUEST")) {
                    TextView txtResult = (TextView) findViewById(R.id.txt_hellow);
                    String strResult = txtResult.getText().toString();
                    resp = (strResult == null) ? "n/a" : strResult;
                } else {
                    Log.d(TAG, "Unknown Command");
                }

            } catch (Exception e){
                Log.d(TAG, "Exception - processCommand " + e.getMessage());
            }

            return resp;
        }
    }
}
```

メインスレッドでBluetoothの処理を行っています。
ソケット情報を取得した後に接続待ち状態でLOOPします。

```
int incomingBytes = inputStream.read(incomingBuff);
byte[] buff = new byte[incomingBytes];
System.arraycopy(incomingBuff, 0, buff, 0, incomingBytes);
String cmd = new String(buff, StandardCharsets.UTF_8);

String resp = processCommand(cmd);
outputStream.write(resp.getBytes());
```

この部分でクライアント側からリクエストを受け取り
String resp = processCommand(cmd);
でサーバー側の処理を行った後に
outputStreamでクライアント側に応答を返却しています。
今回は"REQUEST"という文字を受け取った時に
Hellow World!!を返すようにしています。


#実行
![Screenshot_20190414-184528.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/379857/7df817d2-f159-d5c2-1235-029b0258eecb.png)


スマホ側の画面です。
そりゃサーバ側ですからHellow World!!だけですね（笑
クライアント側の処理が実装出来たら少しは見ごたえがあるかと思います。

#さいごに
今回はBluetoothのServer側(スマホ側)の処理を書いていきました。
次回はClient側(Vuzix blade側)を記述していきます。

一応、分かりやすさ追求のため、
必要最低限の事しか書かないつもりです。

#参考
＜Android 間での RFCOMM チャネルの Bluetooth 通信 1/2＞
https://android.keicode.com/basics/bluetooth-rfcomm-socket.php

＜Android 間での RFCOMM チャネルの Bluetooth 通信 2/2＞
https://android.keicode.com/basics/bluetooth-rfcomm-socket-2.php