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 特征通知问题的主要内容,如果未能解决你的问题,请参考以下文章

如何在 iOS 应用程序中从蓝牙 LE 设备获取通知

低功耗蓝牙监听多个特征通知

低功耗蓝牙通知间隔

如何从 iPhone 向蓝牙 LE 设备发送电子邮件、短信通知?

在 Swift 中结合纬度/经度特征(蓝牙 LE)用于 Mapkit 注释

iOS 蓝牙 LE 无法以编程方式获取通知,但可以在其他应用程序中