質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.44%
Bluetooth

Bluetoothとは短距離の間でデータを交換するための無線通信規格である。固定・モバイル両方のデバイスから、短波の電波送信を行うことで、高いセキュリティをもつパーソナルエリアネットワーク(PAN)を構築する。

Java

Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

Android

Androidは、Google社が開発したスマートフォンやタブレットなど携帯端末向けのプラットフォームです。 カーネル・ミドルウェア・ユーザーインターフェイス・ウェブブラウザ・電話帳などのアプリケーションやソフトウェアをひとつにまとめて構成。 カーネル・ライブラリ・ランタイムはほとんどがC言語/C++、アプリケーションなどはJavaSEのサブセットとAndroid環境で書かれています。

Q&A

解決済

3回答

7363閲覧

Android BLE onCharacteristicChangedが複数回呼び出される

LS_Takao

総合スコア13

Bluetooth

Bluetoothとは短距離の間でデータを交換するための無線通信規格である。固定・モバイル両方のデバイスから、短波の電波送信を行うことで、高いセキュリティをもつパーソナルエリアネットワーク(PAN)を構築する。

Java

Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

Android

Androidは、Google社が開発したスマートフォンやタブレットなど携帯端末向けのプラットフォームです。 カーネル・ミドルウェア・ユーザーインターフェイス・ウェブブラウザ・電話帳などのアプリケーションやソフトウェアをひとつにまとめて構成。 カーネル・ライブラリ・ランタイムはほとんどがC言語/C++、アプリケーションなどはJavaSEのサブセットとAndroid環境で書かれています。

0グッド

0クリップ

投稿2018/08/07 07:31

編集2018/08/10 06:13

Bluetooth Low Energyを用いてプログラムを開発しています。
送信側にて64msec周期でキャラクタリスティックを更新し、更新通知が受信側に届いていることをパケットキャプチャ(nRF51 dongle + Wireshark)で確認しました。
※受信間隔にバラツキはありますが、概ね64msec周期で受信できています。

2501 112.789743 Slave_0x283eb820 Master_0x283eb820 ATT 53 Rcvd Handle Value Notification, Handle: 0x0019 (Unknown) 2503 112.898854 Slave_0x283eb820 Master_0x283eb820 ATT 53 Rcvd Handle Value Notification, Handle: 0x0019 (Unknown) 2505 112.907883 Slave_0x283eb820 Master_0x283eb820 ATT 53 Rcvd Handle Value Notification, Handle: 0x0019 (Unknown) 2507 113.017212 Slave_0x283eb820 Master_0x283eb820 ATT 53 Rcvd Handle Value Notification, Handle: 0x0019 (Unknown) 2511 113.030233 Slave_0x283eb820 Master_0x283eb820 ATT 53 Rcvd Handle Value Notification, Handle: 0x0019 (Unknown) 2513 113.139254 Slave_0x283eb820 Master_0x283eb820 ATT 53 Rcvd Handle Value Notification, Handle: 0x0019 (Unknown) 2515 113.147382 Slave_0x283eb820 Master_0x283eb820 ATT 53 Rcvd Handle Value Notification, Handle: 0x0019 (Unknown) 2519 113.266009 Slave_0x283eb820 Master_0x283eb820 ATT 53 Rcvd Handle Value Notification, Handle: 0x0019 (Unknown) 2521 113.273321 Slave_0x283eb820 Master_0x283eb820 ATT 53 Rcvd Handle Value Notification, Handle: 0x0019 (Unknown) 2523 113.382328 Slave_0x283eb820 Master_0x283eb820 ATT 53 Rcvd Handle Value Notification, Handle: 0x0019 (Unknown)

気がかりな点として、1回の更新通知でonCharacteristicChanged()が複数回呼び出されています。
これは正常な動作(仕様)なのでしょうか?
もしくは、このような事象が発生したという方はいらっしゃいますでしょうか?

事象が発生した際のソースコードは以下の通りです。
※SERVICE_UUID、CHARACTERISTIC_UUIDは伏せ字にしてあります。

Java

1// import 2import android.bluetooth.BluetoothAdapter; 3import android.bluetooth.BluetoothDevice; 4import android.bluetooth.BluetoothGatt; 5import android.bluetooth.BluetoothGattCallback; 6import android.bluetooth.BluetoothGattCharacteristic; 7import android.bluetooth.BluetoothGattDescriptor; 8import android.bluetooth.BluetoothGattService; 9import android.bluetooth.BluetoothManager; 10import android.bluetooth.BluetoothProfile; 11import android.bluetooth.le.BluetoothLeScanner; 12import android.bluetooth.le.ScanCallback; 13import android.bluetooth.le.ScanResult; 14import android.bluetooth.le.ScanFilter; 15import android.bluetooth.le.ScanSettings; 16 17import android.content.Context; 18import android.os.ParcelUuid; 19import android.support.v7.app.AppCompatActivity; 20import android.os.Bundle; 21import android.util.Log; 22 23import java.util.ArrayList; 24import java.util.Arrays; 25import java.util.Locale; 26import java.util.UUID; 27 28import static android.bluetooth.BluetoothGatt.CONNECTION_PRIORITY_HIGH; 29 30 31public class MainActivity extends AppCompatActivity { 32 // グローバル変数定義 33 private ArrayList mScanFilterList; 34 private ScanSettings mScanSettings; 35 private BluetoothLeScanner mBtLeScanner; 36 private BluetoothDevice mBtDevice; 37 private BluetoothGatt mBtGatt; 38 private BluetoothGattCharacteristic mBtGattCharacteristic; 39 private byte[] m_byReceiveData = new byte[20]; 40String strMsg; 41long m_CntNG = 0; 42long m_CntOK = 0; 43 44 // UUID定義 45 // 対象のサービスUUID. 46 private static final String SERVICE_UUID = "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXX1"; 47 // キャラクタリスティックUUID. 48 private static final String CHARACTERISTIC_UUID = "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXX2"; 49 // キャラクタリスティック設定UUID(固定値). 50 private static final String CHARACTERISTIC_UUID_CONFIG = "00002902-0000-1000-8000-00805f9b34fb"; 51 52 // 初期処理 53 @Override 54 protected void onCreate(Bundle savedInstanceState) { 55 super.onCreate(savedInstanceState); 56 57 BluetoothManager btManager; 58 BluetoothAdapter btAdapter; 59 60 setContentView(R.layout.activity_main); 61 62 // 前回受信データの初期化 63 for(int i=0; i<m_byReceiveData.length; i++) { 64 m_byReceiveData[i] = 0x00; 65 } 66 67 btManager = (BluetoothManager)getSystemService(Context.BLUETOOTH_SERVICE); 68 btAdapter = btManager.getAdapter(); 69 70 // フィルタを設定する。 71 ScanFilter scanFilter = new ScanFilter.Builder() 72 .setServiceUuid(ParcelUuid.fromString(SERVICE_UUID)).build(); 73 mScanFilterList = new ArrayList(); 74 mScanFilterList.add(scanFilter); 75 76 mScanSettings = new ScanSettings.Builder() 77 .setScanMode(ScanSettings.SCAN_MODE_BALANCED).build(); 78 79 // スキャンを開始する。 80 mBtLeScanner = btAdapter.getBluetoothLeScanner(); 81 mBtLeScanner.startScan(mScanFilterList, mScanSettings, mScanCallback); 82 } 83 84 // スキャン結果コールバック関数 85 private final ScanCallback mScanCallback = new ScanCallback() { 86 // スキャン結果通知処理 87 @Override 88 public void onScanResult(int callbackType, ScanResult result) { 89 super.onScanResult(callbackType, result); 90 91 // デバイスを発見した 92 if((null != result) && (null != result.getDevice())) { 93 mBtDevice = result.getDevice(); 94 95 // デバイスに接続する。 96 mBtGatt = mBtDevice.connectGatt(getApplicationContext(), false, mGattCallback); 97 } 98 } 99 }; 100 101 // GATTコールバック関数 102 private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback(){ 103 // 接続状態変化検出処理 104 @Override 105 public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState){ 106 // 接続状況が変化したら実行. 107 if(newState == BluetoothProfile.STATE_CONNECTED) { 108 // 接続に成功したらサービスを検索する. 109 gatt.discoverServices(); 110 } else if(newState == BluetoothProfile.STATE_DISCONNECTED) { 111 // 接続が切れたらGATTを空にする. 112 if(mBtGatt != null){ 113 mBtGatt.close(); 114 mBtGatt = null; 115 } 116 117 // スキャンを再開する。 118 mBtLeScanner.startScan(mScanFilterList, mScanSettings, mScanCallback); 119 } 120 } 121 122 // サービス検出処理 123 @Override 124 public void onServicesDiscovered(BluetoothGatt gatt, int status){ 125 BluetoothGattService bleService; 126 boolean bRet; 127 BluetoothGattDescriptor bleDescriptor; 128 129 // Serviceが見つかったら実行. 130 if(status == BluetoothGatt.GATT_SUCCESS) { 131 // UUIDが同じかどうかを確認する. 132 bleService = gatt.getService(UUID.fromString(SERVICE_UUID)); 133 if(bleService != null){ 134 // 指定したUUIDを持つCharacteristicを確認する. 135 mBtGattCharacteristic = bleService.getCharacteristic(UUID.fromString(CHARACTERISTIC_UUID)); 136 if(mBtGattCharacteristic != null) { 137 // Service, CharacteristicのUUIDが同じならBluetoothGattを更新する. 138 mBtGatt = gatt; 139 140 // Notifyを高速で受け取るための要求 141 mBtGatt.requestConnectionPriority(CONNECTION_PRIORITY_HIGH); 142 143 // キャラクタリスティックが見つかったら、Notificationをリクエスト. 144 bRet = mBtGatt.setCharacteristicNotification(mBtGattCharacteristic, true); 145 146 if(true == bRet) { 147 // Characteristic の Notificationを有効化する. 148 bleDescriptor = mBtGattCharacteristic.getDescriptor(UUID.fromString(CHARACTERISTIC_UUID_CONFIG)); 149 if(null != bleDescriptor) { 150 bleDescriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE); 151 mBtGatt.writeDescriptor(bleDescriptor); 152 } 153 154 // scan 終了 155 mBtLeScanner.stopScan(mScanCallback); 156 } 157 } 158 } 159 } 160 } 161 162 // 更新通知受信処理 163 @Override 164 public synchronized void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic){ 165 super.onCharacteristicChanged(gatt, characteristic); 166 167 byte byReceiveData[]; 168 169 // キャラクタリスティックのUUIDをチェック(getUuidの結果が全て小文字で帰ってくるのでUpperCaseに変換) 170 if(CHARACTERISTIC_UUID.equals(characteristic.getUuid().toString().toUpperCase())){ 171 // characteristicを取得する。 172 byReceiveData = characteristic.getValue(); 173 174 // データ長が異なる場合は処理をスキップする。 175 if(byReceiveData.length != m_byReceiveData.length) { 176strMsg = String.format(Locale.US, "***** Length (%d:%d) *****", byReceiveData.length, m_byReceiveData.length); 177Log.e("TAG", strMsg); 178 return; 179 } 180 181 // 前回と同一データの場合は処理スキップする。 182 if(Arrays.equals(byReceiveData, m_byReceiveData)) { 183m_CntNG++; 184strMsg = String.format("xxx Receive NG(%d)", m_CntNG); 185Log.e("TAG", strMsg); 186 return; 187 } 188 else { 189m_CntOK++; 190strMsg = String.format("ooo Receive OK(%d)", m_CntOK); 191Log.e("TAG", strMsg); 192 System.arraycopy(byReceiveData, 0, m_byReceiveData, 0, m_byReceiveData.length); 193 } 194 } 195 } 196 }; 197}

実行結果は以下のように出力されています。
08-10 14:34:38.108 E: ooo Receive OK(1)
08-10 14:34:38.108 E: xxx Receive NG(1)
08-10 14:34:38.109 E: ooo Receive OK(2)
08-10 14:34:38.110 E: xxx Receive NG(2)
08-10 14:34:38.156 E: ooo Receive OK(3)
08-10 14:34:38.157 E: xxx Receive NG(3)
08-10 14:34:38.258 E: ooo Receive OK(4)
08-10 14:34:38.259 E: xxx Receive NG(4)
08-10 14:34:38.316 E: ooo Receive OK(5)
08-10 14:34:38.317 E: xxx Receive NG(5)

送信する更新通知は20byteです。
送信側、受信側共にAndroid(7.0)、Bluetooth V4.2です。
開発環境はAndroidStudio(3.0.1)です。

アドバイス、宜しく御願い致します。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

hillacken

2018/08/09 09:44

パケットキャプチャとは自作ソフトですか?
hillacken

2018/08/09 09:45

伝送路上は1回だけ送信されています。というのは何で確認したのでしょうか?市販のBLEパケットスニファでしょうか?
LS_Takao

2018/08/09 09:52

hillacken 様。御質問ありがとうございます。御推察の通りで、パケットキャプチャは「nRF51 dongle」+「Wireshark」で行いました。
hillacken

2018/08/09 10:23

送信は大丈夫そうですね。受信側のandroidのソースコードを貼らないと、Windさん以上の回答はないと思います。
LS_Takao

2018/08/09 12:00

hillacken 様。アドバイスありがとうございます。掲載用にシンプルなコードに改めたところ、該当の事象が発生しなくなりました。実装ミスと思われます。お騒がせし申し訳ございませんでした。
LS_Takao

2018/08/10 06:20

事象が発生するソースコードと実行結果を追記しました。事象が発生しなくなった環境との差分を調査中です。
LS_Takao

2018/08/16 08:54

「該当の事象が発生しなくなった」というのは誤情報でした。申し訳ございません。
guest

回答3

0

これは正常な動作(仕様)なのでしょうか?

正常な動作ではありません。
送信側が20bytesを1パケットとして送信しているのなら、
受信側は1回しか発生しません。

(追記)BLEの仕様を言うのであれば、ConnectionIntervalは1.25msec単位になるので、
正確に言えば64msec周期はあり得ないです。

投稿2018/08/07 14:43

編集2018/08/07 15:29
Wind

総合スコア442

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

LS_Takao

2018/08/07 23:37

Wind 様 御回答ありがとうございます。 > 送信側が20bytesを1パケットとして送信しているのなら、 > 受信側は1回しか発生しません。 伝送路上は1回だけ送信されています。 送信側は タスク(TimerTask派生クラス)を起動しており、そのタスクで64msec周期毎に以下を行っています。 BluetoothGattCharacteristic.setValue(20byteのデータ) BluetoothGattServer.notifyCharacteristicChanged(前述のCharacteristic) 上記処理にてパケットキャプチャでは送信間隔が不安定ですが、平均64msecでパケットが送信されていることは確認できています。 ※Androidタブレット間では受信側で以下を行わないと100msec周期になる。 BluetoothGatt.requestConnectionPriority(CONNECTION_PRIORITY_HIGH); 受信パケット数が「1」に対して、onCharacteristicChanged()が複数回呼び出されているという状況です。 複数回呼び出された時のonCharacteristicChanged()の引数で渡されたデータは全く同一でした。 ※データはBluetoothGattCharacteristic.getValue()で取得しています。
Wind

2018/08/10 16:47

時間間隔を見ると1msec程度で連続しているので、送信側が同じパケットを多重送信しているってことは無さそうですね。 正常に動作したシンプルなコードも書いて貰えないでしょうか?
LS_Takao

2018/08/16 08:51

Wind 様 御回答ありがとうございます。 「正常に動作した」「該当の事象が発生しなくなった」というのは誤情報でした。 申し訳ございません。 ※「前回と同一データか?」のチェックに用いるバッファ(m_byReceiveData)を複数用意したのですが、その判定に誤りがあり、現状も該当の事象が発生しています。 ソースコードの内容としては最小限の機能になっているのですが、お気づきの点等アドバイスをいただけると助かります。 宜しくお願い致します。
Wind

2018/08/20 02:50

サンプルコードのUUIDを変更して接続してみたところ、 こちらの環境ではmBtLeScannerで二重接続しました。 同様の現象が発生していませんでしょうか? 【Logcatの表示で、下記の通り2回ずつ表示された】 D/BluetoothAdapter: isLeEnabled(): ON D/BluetoothAdapter: isLeEnabled(): ON D/BluetoothGatt: close() D/BluetoothGatt: close()
LS_Takao

2018/08/20 08:08

Wind 様 御回答ありがとうございます。 Logcatに「D/BluetoothAdapter: isLeEnabled(): ON」は出力されていないのですが、onClientConnectionState()やwriteDescriptor()が複数回行ったログが出力されています。 D: connect() - device: XX:XX:XX:XX:XX:XX, auto: false D: registerApp() D: registerApp() - UUID=XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX D: onClientRegistered() - status=0 clientIf=21 D: connect() - device: XX:XX:XX:XX:XX:XX, auto: false D: registerApp() D: registerApp() - UUID=XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX D: onClientRegistered() - status=0 clientIf=22 D: onClientConnectionState() - status=0 clientIf=21 device=XX:XX:XX:XX:XX:XX D: discoverServices() - device: XX:XX:XX:XX:XX:XX D: onClientConnectionState() - status=0 clientIf=22 device=XX:XX:XX:XX:XX:XX D: discoverServices() - device: XX:XX:XX:XX:XX:XX D: onSearchComplete() = Device=XX:XX:XX:XX:XX:XX Status=0 D: requestConnectionPriority() - params: 1 D: setCharacteristicNotification() - uuid: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX enable: true D: onSearchComplete() = Device=XX:XX:XX:XX:XX:XX Status=0 D: requestConnectionPriority() - params: 1 D: setCharacteristicNotification() - uuid: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX enable: true D: writeDescriptor() - uuid: 00002902-0000-1000-8000-00805f9b34fb D: stopScan D: getLeState() returning 12 D: STATE_ON D: writeDescriptor() - uuid: 00002902-0000-1000-8000-00805f9b34fb D: writeDescriptor: mDeviceBusy = true, and return false D: stopScan D: getLeState() returning 12 D: STATE_ON D: could not find callback wrapper D: onDescriptorWrite() - Device=XX:XX:XX:XX:XX:XX handle=26 D: onNotify() - Device=XX:XX:XX:XX:XX:XX handle=25 D: onNotify() - Device=XX:XX:XX:XX:XX:XX handle=25 D: onNotify() - Device=XX:XX:XX:XX:XX:XX handle=25 お気づきの点等ございましたらアドバイスしていただけると助かります。
Wind

2018/08/20 11:36

他に方法があるのかもしれませんが、 BLEはデバイスを見つけてから接続するまでに時間がかかり、 その間に同じデバイスを見つけることがあるので、 見つけたデバイスを内部で保存しておいてから接続した方が良いです。 例えば下記URLの「スキャンしたデバイスのリスト保存」を参考にしてみてください。 https://qiita.com/HideMatsu/items/6a6758eca86500881126
LS_Takao

2018/08/23 03:01

ありがとうございます。 参考にさせていただきます。
guest

0

ベストアンサー

onServicesDiscovered()内の

// Service, CharacteristicのUUIDが同じならBluetoothGattを更新する. mBtGatt = gatt;

これを消すと
どう動きますか?

私が昔書いたコードでは、onServicesDiscovered()内でBluetoothGattへの代入は行っておりませんでした。

投稿2018/08/20 02:23

hillacken

総合スコア359

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

LS_Takao

2018/08/20 07:19

hillacken 様。 アドバイスありがとうございます。 「mBtGatt = gatt;」をNOPにしたところ、該当事象が発生しなくなりました。 上記コードを元に戻すと該当事象が発生するため、アドバイスいただいた処理に起因していると思われます。 障害箇所の特定を行っていただきありがとうございました。 ※補足  複数回呼び出される事象は毎回必ず発生するのではなく、20回中15回程度の頻度で発生していました。  NOPにした後の該当事象の発生回数は20回中0回でした。 mBtDevice.connectGatt()で受け取った「BluetoothGatt」とonServicesDiscovered()で受け取った「BluetoothGatt」は異なると言うことでしょうか? onServicesDiscovered()で受け取った「BluetoothGatt」はサービスやUUIDのチェック用で、要求や指示に用いてはいけないということでしょうか? (要求や指示に用いる「BluetoothGatt」はconnectGatt()で返却された物を用いる?) お気づきの点等ございましたらアドバイスしていただけると助かります。
hillacken

2018/08/20 09:21

異なると言うことでしょうね。 内部実装について詳しくはわかりません。
hillacken

2018/08/20 12:28

>要求や指示に用いる「BluetoothGatt」はconnectGatt()で返却された物を用いる という事で良いと思います。 私はそのようにしていました。
LS_Takao

2018/08/23 03:00

ありがとうございました。 参考にさせていただきます。
guest

0

2018/08/16現在解消に至っておりません。
最新の確認結果は以下の通りです。
・送信パケット数:556
・ooo Receive OK:539
・xxx Receive NG:1078
OKが1回に対して、NGが2回発生しています。
※送信パケット数とOKの数が合わないのは受信側を強制終了したためと思われます。

投稿2018/08/09 12:03

編集2018/08/16 08:36
LS_Takao

総合スコア13

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.44%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問