初めに
去年micro:bitとweather:bit(スイッチサイエンスに終売のお知らせがあったのが残念)でとりあえず温度測れるようにしたんですけど、単体での計測だったのでBLEのお勉強も兼て温度を定期的に送信することを目的に遊んでみました。
micro:bitとweather:bitは大して苦労はしなかったのですが、AndroidでBLEを扱うプログラムをちゃんと組んだのが初めてで知らないことだらけで苦労したので、その辺を載せておこうと思います。
micro:bitとAndroidの通信
UARTを使って通信します。
基本的に、micro:bitからAndroidへの通信です。
なので、TX Characteristicを利用します。
この時に、BluetoothGatt#setCharacteristicNotification()メソッドで、Notifyを有効にします。
そのあとに、Descriptorに値を設定するのですが、割とNotify関連を調べると、BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUEを設定する記事が多いです。
(さらにmicro:bitのことを調べると、Web Bluetooth APIの記事が多くて・・・)
micro:bitのTX Characteristicの仕様を確認すると、Indicateが必須となっています。(無知とは罪ですね・・・)
なので、micro:bitでTX Characteristicを使う場合、BluetoothGattDescriptor.ENABLE_INDICATION_VALUEを設定します。
私はこんな感じのメソッドを作って対応しました。
kotlinにまだ慣れていないので書き方として良くないところがあるかもしれませんが、見逃してくださいw
private fun setNotify(ch : BluetoothGattCharacteristic) : BluetoothGattDescriptor?{
var isNotify : Boolean = ch.properties.and(BluetoothGattCharacteristic.PROPERTY_NOTIFY) == BluetoothGattCharacteristic.PROPERTY_NOTIFY
var isIndicate : Boolean = ch.properties.and(BluetoothGattCharacteristic.PROPERTY_INDICATE) == BluetoothGattCharacteristic.PROPERTY_INDICATE
return null
}
if(false == bleGatt?.setCharacteristicNotification(ch, true)){
return null
}
var descriptor : BluetoothGattDescriptor? = null
ch?.descriptors.forEach{ desc ->
if(NOTIFY_DESCRIPTOR == desc.uuid) {
if(isNotify){
desc.value = BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE
}else{
desc.value = BluetoothGattDescriptor.ENABLE_INDICATION_VALUE
}
if (true == bleGatt?.writeDescriptor(desc)) {
descriptor = desc
}
}
}
return descriptor
}
BluetoothGattCharacteristicのプロパティを調べることで、BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUEを設定すべきか、BluetoothGattDescriptor.ENABLE_INDICATION_VALUEをすべきかがわかるようです。
なので、プロパティを見て設定するようにしています。
(無駄に汎用的にしたかったんです)
こんな感じにすることで、無事にmicro:bitから送信された文字列を受信することができました。
なぜかうまくいかないdiscoverServices()メソッド
connectGatt()メソッドを呼び出すと、BluetoothGattCallbackのonConnectionStateChange()メソッドが呼び出されます。その中でBluetoothGatt#discoverServices()を呼び出しているんですが、なぜか、onServicesDiscovered()メソッドが呼び出されません。
3回くらい接続しなおすと、onServicesDiscovered()メソッドが呼び出されます。
コチラは現在調査中・・・
解決したら更新します。
もし何かアドバイスがあるようでしたらコメントに書いていただけると助かります。
ちなみに、以下のようにしたら、少し良くなったような気がするが、まだおかしい。
方向性が間違ってる(ログ見て遅延させたらよくなるんじゃね?とお気軽に試しただけ)のと、なにか使い方が良くないと思われる。
private val gattCallback = object : BluetoothGattCallback(){
// ・・・中略・・・
bleGatt = gatt
handler2.postDelayed({
if(true == bleGatt?.discoverServices()){
Log.d(LOG_TAG,"discoverServices success")
}else{
Log.d(LOG_TAG,"discoverServices failed")
}
}, 200)
// ・・・中略・・・
}