タンジブルユーザーインターフェースをLinkingで挑戦

こんにちは、やましんです。
以前から気になっていたIoTと相性が抜群のタンジブルユーザーインターフェースを開発してみました。
いろんな応用ができるので、みなさんも大好きなワクワク系テクノロジーだと思います。

#タンジブルユーザーインターフェースって何？
既存のコンピュータの概念を一新し、形のない情報を直接触れることができる（タンジブル）ようにした、より実体感のあるインタフェースだそうです。
　例えば、瓶のふたを開けたら音楽を再生したり、テーブルに置かれた積木を回転するとボリュームを変えることができたり！　おおぉー、なんか、かっこいい！！
[タンジブルユーザーインターフェース](https://ja.wikipedia.org/wiki/%E3%82%BF%E3%83%B3%E3%82%B8%E3%83%96%E3%83%AB%E3%83%A6%E3%83%BC%E3%82%B6%E3%82%A4%E3%83%B3%E3%82%BF%E3%83%95%E3%82%A7%E3%83%BC%E3%82%B9)

#この記事で紹介するタンジブルユーザーインターフェース
今回はタンジブルなボリュームスイッチを開発することにしました。上述の積木を手に取って回転するとボリュームが変化するやつ。いずれは壁にいっぱいボリュームスイッチを貼り付けて、家中の家電のボリュームを制御してやる！わくわく。
　早速、友人に相談したところ[Linking](https://linkingiot.com/)なるデバイスが使えそうなことがわかった。手に入れたデバイスはSizukuシリーズというものらしいが、そのシリーズの中でもBluetoothと加速度センサーを搭載したものを使用する。
　Sizukuの底面には磁石がついているので、ホワイトボードに貼り付けて、これを指でグリグリ回転させることで家電を制御しよう！えーと、家電、家電・・・。何を制御しようかなぁ？
・・・フィジビリも兼ねてとりあえずスマホを制御してみよう。
　ということで、ホワイトボードに貼り付けたボリュームスイッチ（Sizuku）を指で回して、スマホの背景色を変えることができるタンジブルユーザーインターフェースを作ってみる。
![IMG_20170410_153457.png](https://qiita-image-store.s3.amazonaws.com/0/175188/e735ef0c-a73c-dcc4-c049-810ea4ae812d.png) 
図1. 完成予想図（ホワイドボードに貼り付けた、スマホとボリュームスイッチ）

#開発したタンジブルユーザーインターフェースの挙動
開発したタンジブルユーザーインターフェースの動画をアップしておきました。ボリュームスイッチを回転させるとスマホ画像の色が変化する様子がわかると思います。
[![ビデオは削除された可能性があります](http://img.youtube.com/vi/WUA5KMkROGA/0.jpg)](http://www.youtube.com/watch?v=WUA5KMkROGA)
動画1. タンジブルユーザーインターフェースの挙動

#準備したもの
- [Linking Android SDK](https://linkingiot.com/developer/api.html)
- [Linking アプリ（Google Playからダウンロード）](https://play.google.com/store/apps/details?id=com.nttdocomo.android.smartdeviceagent&hl=ja)
- [Sizuku6x](http://www.braveridge.com/PressRelease/Sizuku.pdf)
- ホワイトボード（ダイソーで300円で購入）
- スマホ（SC-01H/Androidバージョン5.1.1）

#Linkingアプリを使ってSizukiの接続確認
まずは、Google PlayからダウンロードしたLinking アプリを使って、Sizukuを接続確認してみた。
接続方法はLinkingのホームページからダウンロードできるドキュメントに記載がある。
　何回か手順どおりにスマホを操作してSizukuとの接続を試みたが、何度やっても「デバイスが見つからない」・・・・と言われる。
　Sizukuにはボタンらしきものは無いので、電源がOFFになっていることは無いはずばなのだが。
もしかすると、ふたを開けたらスイッチがあるのか？
　底蓋を開けてみた。電池が入ってませんでした。orz
　コンビニにボタン電池買いに行くと、同じ径のボタン電池にもいくつか種類があるようで、CR2016は在庫があり、CR2025は在庫切れの状態。選択肢が無いのでCR2016を購入して帰宅したけど、残念ながらハズレでした。
後で調べて分かったんだけど、Sizukuが使用する電池はCR2016でもCR2025でもなく**CR2032**でした。
コンビニで買ったCR2016も3Vだし、少し薄いけど端子が接触すれば動いてくれるはず。今回はCR2016を使用しました。良い子のみなさんは必ずCR2032を使ってくださいね。
　また、ボタン電池のネーミングルールが乗っているサイトがあったのでリンクを貼っておきました。
[ボタン電池 CR2016 CR2025 の違いを理解しておく](https://zibunlog.com/goods_item/lithium/)

![20170410_160229.png](https://qiita-image-store.s3.amazonaws.com/0/175188/b6f75dc4-5eed-82a5-211c-5f6b48117bc2.png)
図2. 底蓋をあけたSizukuとボタン電池。

電池が入ればこっちのもの。手順通りでスマホとSizukuを接続できました。

　よく、取扱説明書のトラブルシューティングの最初に「電源プラグがコンセントにささっていますか？」って書いてあって「バカにするなよ！」って思ってたんだけど。とても大切なアドバイスなんですね。

#回転を目視できるようにSizukuに目印をつける
Sizukuをボリュームスイッチのように回転させるので、回転角度がわかりやすいように蛍光テープで目印を貼りました。この時点では、加速度センサーのX、Y、Z方向が不明だったので、とりあえず、中にある正方形の基盤の一辺と平行になる位置に貼った。（後でわかりましたが、この位置がY軸の＋方向でした。）



![20170409_182224.png](https://qiita-image-store.s3.amazonaws.com/0/175188/0adf81de-d5b7-5a10-c982-5d938782a2e4.png)![20170409_182216.png](https://qiita-image-store.s3.amazonaws.com/0/175188/d7f37a13-3b86-c536-4b3b-e21dec78e951.png)
図3. 右：蛍光テープを貼ったSizuku


#プログラミング
最後になりましたが、スマホアプリのコードを紹介します。
今回開発したスマホアプリは「Sizukuからとらえた加速度を基にボタンの背景を変更する」というアプリ。Linking Android SDKを流用してプログラミングを行った。
Linkingを使用しているからなのか、私の見ていないコードで何かしているのはわかりませんが、Bluetoothを全く意識せずにプログラミングできました。

##デバイスのセンサ情報を取得するためのレシーバー
　どうやらLinkingがBluetoothを隠蔽してくれており、ペアリングしているデバイスのセンサー情報を取得するレシーバーを用意すれば良いようだ。　onSensorData()は加速度センサーの値を受け取るメソッド。X、Yの値を使用して背景色を作成する。

```Android:LightDemoActivity.java
    class SensorReceiver implements ControlSensorData.SensorDataInterface {

        String bd;
        int type;
        float x;
        float y;
        float z;
        byte[] originalData;

        @Override
        public void onStopSensor(final String bd, int type, int reason) {
            Toast.makeText(LightDemoActivity.this, "センサ情報の取得を停止しました", Toast.LENGTH_SHORT).show();
        }

        @Override
        public void onSensorData(String bd, int type, float x, float y, float z,
                                 byte[] originalData, long time) {

            this.bd = bd;
            this.type = type;
            this.x = x;
            this.y = y;
            this.z = z;
            this.originalData = originalData;

            //Sizukuの加速度に応じてボタンの背景を変更する
            sensorButton.setBackgroundColor(Color.rgb((int)(x * 256.0), (int)(y * 256.0), 128));
            
        }


    }
```

##センサー情報の取得をリクエストする処理
初期化時に呼び出し、加速度情報を取得できるようにリクエストする。sensorIdには１が入っている。

```Android:LightDemoActivity.java
    private void getSensorData() {

        GetDeviceInformation getDeviceInformation = new GetDeviceInformation(this);
        List<DeviceInfo> devices = getDeviceInformation.getInformation();

        if (devices.size() == 0) {
            Toast.makeText(this,  "デバイス情報が取得できませんでした", Toast.LENGTH_SHORT).show();
            return;
        }

        DeviceInfo info = devices.get(0);

        /** 0 : ジャイロセンサー **/
        /** 1 : 加速度センサー **/
        /** 2 : 方位センサー **/
        mSensorData.setType(sensorId);                  /** リクエストするセンサID **/
        mSensorData.setBDaddress(info.getBdaddress());  /** リクエストするペアリングデバイスのBDアドレス **/
        
    }


```

##初期化処理
onCreate（）が呼び出される際にmSensorData.start()をコールすることで、加速度センサーからのデータレシーブを開始している。
sensorButtonは大きなトグルボタンで、このボタンでも加速度センサーの受信の開始・終了をコントロールできる。
また、このボタンの背景色をリアルタイムで変更している。


```Android:LightDemoActivity.java
public class LightDemoActivity extends AppCompatActivity {

    Button sensorButton;

    /** ペアリングしているデバイスへセンサ情報取得要求などを設定するクラス **/
    ControlSensorData mSensorData;

    /** デバイスからセンサ情報が取得された場合に、Linkingからデータを受け取るためのReceiver **/
    SensorReceiver receiver;

    int sensorId = -1;

    @Override
    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_pairing_light_demo);
        sensorButton = (Button) findViewById(R.id.sensor_start_button);
        
        /** sensorId取得不可の場合、Activityを終了する **/
        sensorId = getIntent().getIntExtra(PairingConst.INTENT_EXTRA_SENSOR_ID, -1);
        if (sensorId == -1) {
            finish();
        }

        receiver = new SensorReceiver();
        mSensorData = new ControlSensorData(this, receiver);

        /** センサー情報設定 **/
        getSensorData();

        /** 開始ボタンの動作設定 **/
        sensorButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if(!isConnected) {
                    mSensorData.setInterval(Integer.valueOf(100));
                    Integer result = mSensorData.start();
                    Toast.makeText(getApplication(), "センサー開始要求 : " + result, Toast.LENGTH_SHORT).show();
                }else{
                    Integer result = mSensorData.stop();
                    Toast.makeText(getApplication(), "センサー停止要求 : " + result, Toast.LENGTH_SHORT).show();
                }
            }
        });

        mSensorData.setInterval(Integer.valueOf(100));
        Integer result = mSensorData.start();
        Toast.makeText(getApplication(), "センサー開始要求 : " + result, Toast.LENGTH_SHORT).show();

        sensorButton.setBackgroundColor(Color.rgb(128, 128, 128));
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);

        /** センサーデータ取得開始依頼の結果 **/
        int result = mSensorData.onActivityResult(requestCode, resultCode, data);
        if (result != ErrorCode.RESULT_OTHER_ERROR) {
            switch (result) {
                case ErrorCode.RESULT_OK:
                    isConnected = true;
                    Toast.makeText(this, "センサ情報の取得を開始します", Toast.LENGTH_SHORT).show();
                    break;
                default:
                    isConnected = false;
                    Toast.makeText(this, "エラー : " + result, Toast.LENGTH_SHORT).show();
                    break;
            }
        }
    }

}

```



#今後の応用
「職場机に、タンジブルで形状の違う多くのボリュームスイッチを吸盤で貼り付けたら、DJミキサーに早変わり」なんてステキやん。