Android 蓝牙 le gatt 特征通知问题
Posted
技术标签:
【中文标题】Android 蓝牙 le gatt 特征通知问题【英文标题】:Android Bluetooth le gatt characteristic notification issue 【发布时间】:2015-06-27 11:01:25 【问题描述】:我有一个带电池的硬件,健康温度计服务 (HTS)。电池是读取特性,健康温度计是特性通知。首先,我针对 HTS 特性通知。我写了下面的代码。服务发现之后,就是 enableNextSensor。它进入 NOTIFY 部分,但根本不调用 onCharacteristicChanged 方法。我在这里做错了什么?
public class MainActivity extends ActionBarActivity implements BluetoothAdapter.LeScanCallback
private static final String TAG="MainActivity";
private BluetoothAdapter mBluetoothAdapter;
private BluetoothGatt mConnectedGatt;
BluetoothDevice device;
Handler mHandler;
TextView hello;
/* Client Configuration Descriptor */
private static final UUID CONFIG_DESCRIPTOR = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb");
/** Health Thermometer service UUID */
public final static UUID HT_SERVICE_UUID = UUID.fromString("00001809-0000-1000-8000-00805f9b34fb");
/** Health Thermometer Measurement characteristic UUID */
private static final UUID HT_MEASUREMENT_CHARACTERISTIC_UUID = UUID.fromString("00002A1C-0000-1000-8000-00805f9b34fb");
@Override
protected void onCreate(Bundle savedInstanceState)
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mHandler = new Handler();
hello = (TextView) findViewById(R.id.Temperature);
/*
* Bluetooth in android 4.3 is accessed via the BluetoothManager, rather than
* the old static BluetoothAdapter.getInstance()
*/
BluetoothManager manager = (BluetoothManager) getSystemService(BLUETOOTH_SERVICE);
mBluetoothAdapter = manager.getAdapter();
startScan();
@Override
protected void onResume()
super.onResume();
/*
* We need to enforce that Bluetooth is first enabled, and take the
* user to settings to enable it if they have not done so.
*/
if (mBluetoothAdapter == null || !mBluetoothAdapter.isEnabled())
//Bluetooth is disabled
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivity(enableBtIntent);
finish();
return;
/*
* Check for Bluetooth LE Support. In production, our manifest entry will keep this
* from installing on these devices, but this will allow test devices or other
* sideloads to report whether or not the feature exists.
*/
if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE))
Toast.makeText(this, "No LE Support.", Toast.LENGTH_SHORT).show();
finish();
return;
//Begin scanning for LE devices
startScan();
private Runnable mStopRunnable = new Runnable()
@Override
public void run()
stopScan();
;
private Runnable mStartRunnable = new Runnable()
@Override
public void run()
startScan();
;
private void startScan()
//Scan for devices advertising the thermometer service
mBluetoothAdapter.startLeScan(new UUID[] HT_SERVICE_UUID, this);
mHandler.postDelayed(mStopRunnable, 5000);
private void stopScan()
mBluetoothAdapter.stopLeScan(this);
mHandler.postDelayed(mStartRunnable, 2500);
@Override
protected void onPause()
super.onPause();
//Make sure dialog is hidden
//mProgress.dismiss();
//Cancel any scans in progress
mHandler.removeCallbacks(mStopRunnable);
mHandler.removeCallbacks(mStartRunnable);
mBluetoothAdapter.stopLeScan(this);
@Override
protected void onStop()
super.onStop();
//Disconnect from any active tag connection
if (mConnectedGatt != null)
mConnectedGatt.close();
mConnectedGatt.disconnect();
mConnectedGatt = null;
private BluetoothGattCallback mGattCallback = new BluetoothGattCallback()
/* State Machine Tracking */
private int mState = 0;
private void reset() mState = 0;
private void advance() mState++;
private String connectionState(int status)
switch (status)
case BluetoothProfile.STATE_CONNECTED:
return "Connected";
case BluetoothProfile.STATE_DISCONNECTED:
return "Disconnected";
case BluetoothProfile.STATE_CONNECTING:
return "Connecting";
case BluetoothProfile.STATE_DISCONNECTING:
return "Disconnecting";
default:
return String.valueOf(status);
/*
* Send an enable command to each sensor by writing a configuration
* characteristic. This is specific to the SensorTag to keep power
* low by disabling sensors you aren't using.
*/
private void enableNextSensor(BluetoothGatt gatt)
Log.i(TAG,"******************************************************************enableNextSensor");
BluetoothGattCharacteristic characteristic;
characteristic = gatt.getService(HT_SERVICE_UUID)
.getCharacteristic(HT_MEASUREMENT_CHARACTERISTIC_UUID);
// Check characteristic property
final int properties = characteristic.getProperties();
if ((properties | BluetoothGattCharacteristic.PROPERTY_READ) > 0)
Log.i(TAG,"**************************READ");
// If there is an active notification on a characteristic, clear
// it first so it doesn't update the data field on the user interface.
/*if (mNotifyCharacteristic != null)
mBluetoothLeService.setCharacteristicNotification(
mNotifyCharacteristic, false);
mNotifyCharacteristic = null;
mBluetoothLeService.readCharacteristic(characteristic);*/
if ((properties | BluetoothGattCharacteristic.PROPERTY_NOTIFY) > 0)
Log.i(TAG,"**************************NOTIFY");
//mNotifyCharacteristic = characteristic;
gatt.setCharacteristicNotification(
characteristic, true);
BluetoothGattDescriptor descriptor = characteristic.getDescriptor(
CONFIG_DESCRIPTOR);
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
gatt.writeDescriptor(descriptor);
@Override
public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status)
Log.i(TAG,"*********************************************************onCharacteristicWrite****************************");
//After writing the enable flag, next we read the initial value
readNextSensor(gatt);
/*
* Read the data characteristic's value for each sensor explicitly
*/
private void readNextSensor(BluetoothGatt gatt)
BluetoothGattCharacteristic characteristic;
characteristic = gatt.getService(HT_SERVICE_UUID)
.getCharacteristic(HT_MEASUREMENT_CHARACTERISTIC_UUID);
gatt.readCharacteristic(characteristic);
@Override
public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status)
//For each read, pass the data up to the UI thread to update the display
if (HT_MEASUREMENT_CHARACTERISTIC_UUID.equals(characteristic.getUuid()))
//mHandler.sendMessage(Message.obtain(null, MSG_HUMIDITY, characteristic));
// updateTemperatureValue(characteristic);
Log.i(TAG,"*********************************************************onCharacteristicRead****************************");
//After reading the initial value, next we enable notifications
setNotifyNextSensor(gatt);
@Override
public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status)
//Once notifications are enabled, we move to the next sensor and start over with enable
advance();
enableNextSensor(gatt);
private void setNotifyNextSensor(BluetoothGatt gatt)
BluetoothGattCharacteristic characteristic;
characteristic = gatt.getService(HT_SERVICE_UUID)
.getCharacteristic(HT_MEASUREMENT_CHARACTERISTIC_UUID);
Log.i(TAG,"******************setNotify");
//Enable local notifications
gatt.setCharacteristicNotification(characteristic, true);
//Enabled remote notifications
BluetoothGattDescriptor desc = characteristic.getDescriptor(CONFIG_DESCRIPTOR);
desc.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
gatt.writeDescriptor(desc);
@Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState)
Log.d(TAG, "******************************************************************Connetion State change =>" + status + "<= " + connectionState(newState));
Log.d(TAG, "******************************************************************Gatt success =>" + BluetoothGatt.GATT_SUCCESS + "<= ");
Log.d(TAG, "******************************************************************Connetion State connect =>" + BluetoothProfile.STATE_CONNECTED + "<= ");
if (status == BluetoothGatt.GATT_SUCCESS && newState == BluetoothProfile.STATE_CONNECTED)
//hello.setText("Device Connected");
Log.d(TAG,"***********************GATT_SUCCESS");
/*
* Once successfully connected, we must next discover all the services on the
* device before we can read and write their characteristics.
*/
gatt.discoverServices();
else if (status != BluetoothGatt.GATT_SUCCESS)
//hello.setText("Gatt Disconnected");
/*
* If there is a failure at any stage, simply disconnect
*/
gatt.close();
gatt.disconnect();
@Override
public void onServicesDiscovered(BluetoothGatt gatt, int status)
Log.d(TAG, "Services Discovered: "+status);
//hello.setText("Services Discovered");
//if(status == BluetoothGatt.GATT_SUCCESS)
//mHandler.sendMessage(Message.obtain(null, MSG_PROGRESS, "Enabling Sensors..."));
/*
* With services discovered, we are going to reset our state machine and start
* working through the sensors we need to enable
*/
reset();
enableNextSensor(gatt);
@Override
public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic)
Log.i(TAG,"*********************************************************onCharacteristicChanged**** I am here************************");
/*
* After notifications are enabled, all updates from the device on characteristic
* value changes will be posted here. Similar to read, we hand these up to the
* UI thread to update the display.
*/
if (HT_MEASUREMENT_CHARACTERISTIC_UUID.equals(characteristic.getUuid()))
// mHandler.sendMessage(Message.obtain(null, MSG_HUMIDITY, characteristic));
Log.i(TAG,"*********************************************************onCharacteristicChanged****************************");
;
/* BluetoothAdapter.LeScanCallback */
@Override
public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord)
Log.i(TAG, "New LE Device: " + device.getName() + " @ " + rssi);
if(device.getName() != null && device.getName().equals("TC-Geetha"))
Log.i(TAG,"*******Inside connectGatt");
/*
* Make a connection with the device using the special LE-specific
* connectGatt() method, passing in a callback for GATT events
*/
mConnectedGatt = device.connectGatt(this, false, mGattCallback);
/*
* We need to parse out of the AD structures from the scan record
*/
/* List<AdRecord> records = AdRecord.parseScanRecord(scanRecord);
if (records.size() == 0)
Log.i(TAG, "Scan Record Empty");
else
Log.i(TAG, "Scan Record: "
+ TextUtils.join(",", records));
*/
/*
* Create a new beacon from the list of obtains AD structures
* and pass it up to the main thread
*/
//TemperatureBeacon beacon = new TemperatureBeacon(records, device.getAddress(), rssi);
//mHandler.sendMessage(Message.obtain(null, 0, beacon));
@Override
public boolean onCreateOptionsMenu(Menu menu)
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
@Override
public boolean onOptionsItemSelected(MenuItem item)
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings)
return true;
return super.onOptionsItemSelected(item);
来自上述代码的日志:
35 testbluetooth I/MainActivity﹕ New LE Device: TC@ -54
345 testbluetooth I/MainActivity﹕ *******Inside connectGatt
345 testbluetooth D/BluetoothGatt﹕ connect() - device: E9:E3:17:4F:F5:2B, auto: false
345 testbluetooth D/BluetoothGatt﹕ registerApp()
345 testbluetooth D/BluetoothGatt﹕ registerApp() - UUID=ad176678-32ad-4212-9822-fd34b2005bcd
345 testbluetooth D/BluetoothGatt﹕ onClientRegistered() - status=0 clientIf=5
415 testbluetooth D/BluetoothGatt﹕ onClientConnectionState() - status=0 clientIf=5 device=E9:E3:17:4F:F5:2B
415 testbluetooth D/MainActivity﹕ ******************************************************************Connetion State change =>0<= Connected
415 testbluetooth D/MainActivity﹕ ******************************************************************Gatt success =>0<=
415 testbluetooth D/MainActivity﹕ ******************************************************************Connetion State connect =>2<=
415 testbluetooth D/MainActivity﹕ ***********************GATT_SUCCESS
415 testbluetooth D/BluetoothGatt﹕ discoverServices() - device: E9:E3:17:4F:F5:2B
646 testbluetooth D/BluetoothAdapter﹕ stopLeScan()
766 testbluetooth D/BluetoothGatt﹕ onGetService() - Device=E9:E3:17:4F:F5:2B UUID=00001800-0000-1000-8000-00805f9b34fb
766 testbluetooth D/BluetoothGatt﹕ onGetService() - Device=E9:E3:17:4F:F5:2B UUID=00001801-0000-1000-8000-00805f9b34fb
776 testbluetooth D/BluetoothGatt﹕ onGetService() - Device=E9:E3:17:4F:F5:2B UUID=00001809-0000-1000-8000-00805f9b34fb
776 testbluetooth D/BluetoothGatt﹕ onGetService() - Device=E9:E3:17:4F:F5:2B UUID=00001809-0000-1000-8000-00805f9b34fb
776 testbluetooth D/BluetoothGatt﹕ onGetService() - Device=E9:E3:17:4F:F5:2B UUID=0000180f-0000-1000-8000-00805f9b34fb
776 testbluetooth D/BluetoothGatt﹕ onGetService() - Device=E9:E3:17:4F:F5:2B UUID=0000180a-0000-1000-8000-00805f9b34fb
776 testbluetooth D/BluetoothGatt﹕ onGetCharacteristic() - Device=E9:E3:17:4F:F5:2B UUID=00002a00-0000-1000-8000-00805f9b34fb
776 testbluetooth D/BluetoothGatt﹕ onGetCharacteristic() - Device=E9:E3:17:4F:F5:2B UUID=00002a01-0000-1000-8000-00805f9b34fb
776 testbluetooth D/BluetoothGatt﹕ onGetCharacteristic() - Device=E9:E3:17:4F:F5:2B UUID=00002a04-0000-1000-8000-00805f9b34fb
786 testbluetooth D/BluetoothGatt﹕ onGetCharacteristic() - Device=E9:E3:17:4F:F5:2B UUID=00002a1c-0000-1000-8000-00805f9b34fb
786 testbluetooth D/BluetoothGatt﹕ onGetCharacteristic() - Device=E9:E3:17:4F:F5:2B UUID=00002a1c-0000-1000-8000-00805f9b34fb
796 testbluetooth D/BluetoothGatt﹕ onGetCharacteristic() - Device=E9:E3:17:4F:F5:2B UUID=00002a19-0000-1000-8000-00805f9b34fb
796 testbluetooth D/BluetoothGatt﹕ onGetCharacteristic() - Device=E9:E3:17:4F:F5:2B UUID=00002a29-0000-1000-8000-00805f9b34fb
796 testbluetooth D/BluetoothGatt﹕ onGetCharacteristic() - Device=E9:E3:17:4F:F5:2B UUID=00002a24-0000-1000-8000-00805f9b34fb
796 testbluetooth D/BluetoothGatt﹕ onGetCharacteristic() - Device=E9:E3:17:4F:F5:2B UUID=00002a23-0000-1000-8000-00805f9b34fb
806 testbluetooth D/BluetoothGatt﹕ onGetDescriptor() - Device=E9:E3:17:4F:F5:2B UUID=00002902-0000-1000-8000-00805f9b34fb
806 testbluetooth D/BluetoothGatt﹕ onGetDescriptor() - Device=E9:E3:17:4F:F5:2B UUID=00002902-0000-1000-8000-00805f9b34fb
806 testbluetooth D/BluetoothGatt﹕ onGetDescriptor() - Device=E9:E3:17:4F:F5:2B UUID=00002902-0000-1000-8000-00805f9b34fb
806 testbluetooth D/BluetoothGatt﹕ onSearchComplete() = Device=E9:E3:17:4F:F5:2B Status=0
806 testbluetooth D/MainActivity﹕ Services Discovered: 0
806 testbluetooth I/MainActivity﹕ ******************************************************************enableNextSensor
806 testbluetooth I/MainActivity﹕ **************************READ
806 testbluetooth I/MainActivity﹕ **************************NOTIFY
806 testbluetooth D/BluetoothGatt﹕ setCharacteristicNotification() - uuid: 00002a1c-0000-1000-8000-00805f9b34fb enable: true
806 testbluetooth D/BluetoothGatt﹕ writeDescriptor() - uuid: 00002902-0000-1000-8000-00805f9b34fb
826 testbluetooth D/BluetoothGatt﹕ onDescriptorWrite() - Device=E9:E3:17:4F:F5:2B UUID=00002a1c-0000-1000-8000-00805f9b34fb
826 testbluetooth I/MainActivity﹕ ******************************************************************enableNextSensor
826 testbluetooth I/MainActivity﹕ **************************READ
826 testbluetooth I/MainActivity﹕ **************************NOTIFY
826 testbluetooth D/BluetoothGatt﹕ setCharacteristicNotification() - uuid: 00002a1c-0000-1000-8000-00805f9b34fb enable: true
826 testbluetooth D/BluetoothGatt﹕ writeDescriptor() - uuid: 00002902-0000-1000-8000-00805f9b34fb
37.178 testbluetooth D/BluetoothAdapter﹕ startLeScan(): [Ljava.util.UUID;@426bb628
37.188 testbluetooth D/BluetoothAdapter﹕ onClientRegistered() - status=0 clientIf=4
38.840 testbluetooth D/BluetoothAdapter﹕ stopLeScan()
41.353 testbluetooth D/BluetoothAdapter﹕ startLeScan(): [Ljava.util.UUID;@426a4288
41.363 testbluetooth D/BluetoothAdapter﹕ onClientRegistered() - status=0 clientIf=4
42.194 testbluetooth D/BluetoothAdapter﹕ stopLeScan()
44.706 testbluetooth D/BluetoothAdapter﹕ startLeScan(): [Ljava.util.UUID;@42699148
44.726 testbluetooth D/BluetoothAdapter﹕ onClientRegistered() - status=0 clientIf=4
45.307 testbluetooth D/BluetoothAdapter﹕ stopLeScan()
45.327 testbluetooth D/BluetoothGatt﹕ close()
【问题讨论】:
您应该首先尝试通过从enableNextSensor
中删除写入gatt 描述符的部分来运行您的代码。 writeDescriptor
更新本地缓存,setValue
描述符使用标志更新它的本地值,这是没有意义的。这不是启用通知所必需的,调用setCharacteristicNotification
就足够了。
@istirbu 我的经历与你的完全相反。 1. Gatt.writeDescriptor 实际上正在更新远程设备中的描述符值。 2. 此功能是在Android中启用通知的必备功能。 3. 只调用 setCharacteristicNotification 是不够的。
第 1 点已在文档中明确说明。虽然第 2 点和第 3 点在官方文档中不是很清楚,但您可以通过谷歌搜索轻松获得关于这些事实的大量讨论。
【参考方案1】:
是的。我解决了这个问题。它不通知它的指示。
我将属性检查的条件修改为以下。
if ((properties & BluetoothGattCharacteristic.PROPERTY_INDICATE) != 0)
setCharacteristicIndication(BluetoothGattCharacteristic characteristic, true);
public void setCharacteristicIndication(
BluetoothGattCharacteristic characteristic,boolean enabled)
mBluetoothGatt.setCharacteristicNotification(characteristic, enabled);
BluetoothGattDescriptor descriptor = characteristic.getDescriptor(
UUID.fromString(SampleGattAttributes.CLIENT_CHARACTERISTIC_CONFIG));
Log.d(TAG, "**********************************************************************************************is Descriptor null=>" + descriptor);
if(descriptor != null)
descriptor.setValue(BluetoothGattDescriptor.ENABLE_INDICATION_VALUE);
Log.v(TAG, "Enabling indications for " + characteristic.getUuid());
Log.d(TAG, "gatt.writeDescriptor(" + SampleGattAttributes.CLIENT_CHARACTERISTIC_CONFIG + ", value=0x02-00)");
mBluetoothGatt.writeDescriptor(descriptor);
else
Log.d(TAG,"****************************Descriptor Null :"+descriptor);
Log.v(TAG, "Could not enable indications for " + characteristic.getUuid());
【讨论】:
你好。你把那个函数setCharacteristicIndication放在哪里。谢谢你以上是关于Android 蓝牙 le gatt 特征通知问题的主要内容,如果未能解决你的问题,请参考以下文章
如何从 iPhone 向蓝牙 LE 设备发送电子邮件、短信通知?