Android の音楽再生とLED表示の連携

[前の記事](http://qiita.com/maki02/items/a4ef82b682e7190357c4)では、音楽再生サービスを拡張して、定期的に歌唱パターンを通知するようにしました。
通知を受信して、LED を点灯させる部分を作っていきます。

受信する通知は、メンバーごとの歌唱パート(0は歌っていない)という情報になります。
これをもとに、LED を光らせていきます。

受信した情報を元に、適切な位置の LED を適切な色で点灯させるために、BLE Nano 側のプログラムも拡張していきます。
そして、Android 側の BLE GATT 通信アプリで、拡張した BLE Nano 側の仕様に合わせて、データを送信していきます。

# BLE Nano の拡張

表示したい色(メンバー・カラー)は決まっているので、BLE Nano 側で色テーブルを持たせ、通信量を減らします。

|番号| 色|
|---|---|
|0|消灯|
|1|白|
|2|赤|
|:|:|
|7|緑|

そして、Android と通信するバイト列データにいくつかのパターンを作って、ON/OFF 以外の制御をできるようにしていきます。
制御する内容としては、次の3つを用意していきます。

- 制御する LED の個数を指定
- 各 LED の色番号 (Byte列)
- 表示する色の指定

簡単なデータは送らないので、こんな感じの簡単なパターンにしました。

|先頭4文字|内容|
|---|---|
|ledn|LEDの個数|
|ledd|色番号のバイト列|
|leds|色番号と色(R,G,B値)|

本当なら、LED の個数は、表示機器側(BLE Nano 側)が持ち、表示機器側からスマートフォンに通知するのかなと思います。
ただ、BLE Nano はそこそこ高価なので、 LED の構成が違ういろんな表示機器に対して使い回したいので、今は、スマートフォンから BLE Nano に対して個数を指定する形にしています。

また、LED の点灯色の指定するには、色番号のバイト列を送ります。
1番目のバイトが1番目の LED の色番号、2番目のバイトが2番目の LED の色番号となり、LED の個数分の番号を送ります。

また、色テーブルは、調整のしやすさも考えてスマートフォン側からも書き換えられるようにしています。

```mainc.cpp
void writeCharCallback(const GattCharacteristicWriteCBParams *params)
{
  if(params->charHandle == writeChar.getValueHandle()) {
    if (params->len >= 4){
      if ((params->data[0] == 'l') && (params->data[1] == 'e') && (params->data[2] == 'd')){
        switch(params->data[3]){
        case 'd':
          for (i = 4; i < params->len; i++) {
            if (i - 4 >= m_strip.num_leds) break;
            int idx = params->data[i];
            if (idx > COLOR_MAX) continue;
            neopixel_set_color(&m_strip, i-4, color_table[idx][0], color_table[idx][1], color_table[idx][2]);
          }
          break;
        case 'n': 
          leds_per_strip = params->data[4];
          m_strip.num_leds = leds_per_strip;
          for (i = 0; i < leds_per_strip; i++) neopixel_set_color(&m_strip, i, 0, 0, 0);
          break;
        case 's':
          uint8_t idx = params->data[4];
          if ((idx < 0) || (idx > COLOR_MAX)) break;
          color_table[idx][0] = params->data[5];
          color_table[idx][1] = params->data[6];
          color_table[idx][2] = params->data[7];
          break;
        }
      }
    }
  }
}
```

BLE Nano 側のコードはこんな感じです。
受信したデータを解析して、LED に色をセットしたり、個数を変更したり、色テーブルを更新したりしています。

# GATT 通信アプリに LED 制御信号送信を追加

音楽再生サービスから受信した intent をもとに、LED 点灯の制御信号を送信します。
(LED 数の指定と、色テーブルの色指定に関するコードは割愛しています)

```DeviceActivity.java
  protected void onCreate(Bundle savedInstanceState) {
   :
    IntentFilter filter = new IntentFilter("com.example.android.remotecontrol.ACTION_STATE_CHANGED");
    receiver = new BroadcastReceiver() {
      public void onReceive(Context context, Intent intent) {
        lightLED(intent);
      }
    };
    registerReceiver(receiver, filter);
   :
  }

  private void lightLED(Intent intent) {
    if (intent.getBooleanExtra("Update", false)) {
      if ( isConnected() ) {
        WriteBytes = new byte[4+mLEDNUM];
        WriteBytes[0]  = 'l';
        WriteBytes[1]  = 'e';
        WriteBytes[2]  = 'd';
        WriteBytes[3]  = 'd';
        WriteBytes[4]  = (intent.getIntExtra("v0", 0) > 0) ? (byte) 1 : 0;
        WriteBytes[5]  = (intent.getIntExtra("v1", 0) > 0) ? (byte) 2 : 0;
        WriteBytes[6]  = (intent.getIntExtra("v2", 0) > 0) ? (byte) 3 : 0;
        WriteBytes[7]  = (intent.getIntExtra("v3", 0) > 0) ? (byte) 4 : 0;
        WriteBytes[8]  = (intent.getIntExtra("v4", 0) > 0) ? (byte) 5 : 0;
        WriteBytes[9]  = (intent.getIntExtra("v5", 0) > 0) ? (byte) 6 : 0;
        WriteBytes[10] = (intent.getIntExtra("v6", 0) > 0) ? (byte) 7 : 0;
        sendData(WriteBytes);
      }
    }
  } 
```

onCreate の中で、intent を受信すると制御信号を送信する lightLED() を呼び出すコードを追加します。

そして、lightLED() では、受信した intent から getXXXExtras メソッドでデータを取り出し、制御信号を作っていきます。
"Update" が true の場合に、点灯パターンを変更するために、色番号を BLE Nano に送信します。
そこで、送信するバイト列 WriteBytes に、送信データを作成していきます。
まず、前半 4 バイトに 'ledd' を格納し、その後ろに各 LED の色番号を格納していきます。
intent で受信した "v0" → 1番目、"v1" → 2番目というように LED を割り当てろこととし、色番号もそれぞれ 1～7 を割り当てることとします。
"v0" ～ "v6" に対して、getIntExtra メソッドで取得した値が 0 以上 (つまり、どこかの高さを歌っている) 場合には色番号を、0 の場合に 0 を WriteBytes[] に格納していきます。
そして、sendData() で、作成したバイト列を送信します。

(isConnected() は BLE で接続中かどうかを返すメソッド、sendData() はキャラクタリスティックを書き込むメソッドです。
詳細は割愛させてください)

これで、音楽再生に連動して LED を点灯することができるようなりました。

今のところ LED の位置に対して色が固定で、うまく使いきれてません。
どう表現したら面白いのか? は、まだまだこれから模索したいと思います。

今回は、1人1人のデータを別々に持たせていますが、まとめる方法もあります。
各タイミングごとの全 LED の色番号のバイト列をそのまま JSON データとして用意します。
音楽再生サービスから GATT 通信アプリに intent 経由でバイト列を渡します。
そして、受信したデータ列に 'ledd' という文字列を追加して BLE Nano に送信します。

[目次のページ](http://qiita.com/maki02/items/d4376df5eaecaf6e5724)にリンクしたデモは、LED が 16個なので、この方法で作っています。

ここで、ファームやアプリなどソフト側の話は一度終わって、回路や箱を作っていく話にしたいと思います。