前の記事では、音楽再生サービスを拡張して、定期的に歌唱パターンを通知するようにしました。
通知を受信して、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 の個数分の番号を送ります。
また、色テーブルは、調整のしやすさも考えてスマートフォン側からも書き換えられるようにしています。
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 数の指定と、色テーブルの色指定に関するコードは割愛しています)
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 に送信します。
目次のページにリンクしたデモは、LED が 16個なので、この方法で作っています。
ここで、ファームやアプリなどソフト側の話は一度終わって、回路や箱を作っていく話にしたいと思います。