Android BLE 无法从设备接收 Gatt 特性通知

Posted

技术标签:

【中文标题】Android BLE 无法从设备接收 Gatt 特性通知【英文标题】:Android BLE can't receive Gatt Characteristic notification from device 【发布时间】:2014-04-29 14:34:51 【问题描述】:

当我在特性上写入值时,我试图从设备接收通知,但我没有收到任何东西。我启用了关于特性的通知,然后我写了值。我已经看到设备中的特性已更改其值,但我无法收到通知。这是我的代码:

设备活动:

public class DevicesActivity extends Activity 

private BLEService mBluetoothLeService;
private String mDeviceAddress;
private boolean mConnected = false;
private BluetoothGattCharacteristic mNotifyCharacteristic;

private final ServiceConnection mServiceConnection = new ServiceConnection() 

    @Override
    public void onServiceConnected(ComponentName componentName, IBinder service) 
        mBluetoothLeService = ((BLEService.LocalBinder) service).getService();
        if (!mBluetoothLeService.initialize()) 
            finish();
        
        mBluetoothLeService.context = DevicesActivity.this;
        mBluetoothLeService.connect(mDeviceAddress);
    

    @Override
    public void onServiceDisconnected(ComponentName componentName) 
        mBluetoothLeService = null;
    
;

private final BroadcastReceiver mGattUpdateReceiver = new BroadcastReceiver() 
    @Override
    public void onReceive(Context context, Intent intent) 
        final String action = intent.getAction();
        if (BLEService.ACTION_GATT_CONNECTED.equals(action)) 
            mConnected = true;
            invalidateOptionsMenu();
         else if (BLEService.ACTION_GATT_DISCONNECTED.equals(action)) 
            mConnected = false;
            invalidateOptionsMenu();
         else if (BLEService.ACTION_GATT_SERVICES_DISCOVERED.equals(action)) 
            List<BluetoothGattService> servicesList;
            servicesList = mBluetoothLeService.getSupportedGattServices();
            Iterator<BluetoothGattService> iter = servicesList.iterator();
            while (iter.hasNext()) 
                BluetoothGattService bService = (BluetoothGattService) iter.next();
                if (bService.getUuid().toString().equals(BLEUUID.SERVICE))
                    mService = bService;
                
            
         else if (BLEService.ACTION_DATA_AVAILABLE.equals(action)) 
            displayData(intent.getStringExtra(BLEService.EXTRA_DATA));
        
    
;

private static final String TAG = "BLEDevice";
public static final String EXTRA_BLUETOOTH_DEVICE = "BT_DEVICE";
private BluetoothAdapter mBTAdapter;
private BluetoothDevice mDevice;
private BluetoothGatt mConnGatt;
private int mStatus;
BluetoothGattService mService;

private EditText pinTxt;
private Button cancelBtn;
private Button unlockBtn;
private Button changePinBtn;

@Override
protected void onCreate(Bundle savedInstanceState) 
    super.onCreate(savedInstanceState);
    requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
    setContentView(R.layout.activity_devices);

    pinTxt = (EditText) findViewById(R.id.pin_txt);
    cancelBtn = (Button) findViewById(R.id.cancel_btn);
    unlockBtn = (Button) findViewById(R.id.unlock_btn);
    changePinBtn = (Button) findViewById(R.id.change_pin_btn);

    unlockBtn.setOnClickListener(new View.OnClickListener() 
        @Override
        public void onClick(View view) 
            String aux = pinTxt.getText().toString();
            mBluetoothLeService.sendCharacteristic(aux, getBTDeviceExtra());
        
    );

    mDevice = getBTDeviceExtra();
    mDeviceAddress = mDevice.getAddress();
    if (mServiceConnection == null)
        Log.v("NULL", "mServiceConnection NULL");
    

    Intent gattServiceIntent = new Intent(this, BLEService.class);
    if (gattServiceIntent==null)
        Log.v("NULL", "mServiceConnection NULL");
    
    bindService(gattServiceIntent, mServiceConnection, BIND_AUTO_CREATE);
    mStatus = BluetoothProfile.STATE_DISCONNECTED;


private void displayData(String data) 
    if (data != null) 
        Toast.makeText(DevicesActivity.this, data, Toast.LENGTH_LONG);
    

@Override
protected void onDestroy() 
    super.onDestroy();
    unbindService(mServiceConnection);
    mBluetoothLeService = null;


@Override
protected void onPause() 
    super.onPause();
    unregisterReceiver(mGattUpdateReceiver);


private static IntentFilter makeGattUpdateIntentFilter() 
    final IntentFilter intentFilter = new IntentFilter();
    intentFilter.addAction(BLEService.ACTION_GATT_CONNECTED);
    intentFilter.addAction(BLEService.ACTION_GATT_DISCONNECTED);
    intentFilter.addAction(BLEService.ACTION_GATT_SERVICES_DISCOVERED);
    intentFilter.addAction(BLEService.ACTION_DATA_AVAILABLE);
    return intentFilter;


@Override
protected void onResume() 
    super.onResume();
    registerReceiver(mGattUpdateReceiver, makeGattUpdateIntentFilter());
    if (mBluetoothLeService != null) 
        final boolean result = mBluetoothLeService.connect(mDeviceAddress);
    



private BluetoothDevice getBTDeviceExtra() 
    Intent intent = getIntent();
    if (intent
        == null)             return null;
    

    Bundle extras = intent.getExtras();
    if (extras == null) 
        return null;
    

    BluetoothDevice aux = extras.getParcelable(EXTRA_BLUETOOTH_DEVICE);
    return aux;

BLE服务:

 private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() 

    @Override
    public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) 
        String intentAction;
        if (newState == BluetoothProfile.STATE_CONNECTED) 
            intentAction = ACTION_GATT_CONNECTED;
            mConnectionState = STATE_CONNECTED;
            broadcastUpdate(intentAction);
            Log.i(TAG, "Connected to GATT server.");
            // Attempts to discover services after successful connection.
            Log.i(TAG, "Attempting to start service discovery:" +
                    mBluetoothGatt.discoverServices());

         else if (newState == BluetoothProfile.STATE_DISCONNECTED) 
            intentAction = ACTION_GATT_DISCONNECTED;
            mConnectionState = STATE_DISCONNECTED;
            Log.i(TAG, "Disconnected from GATT server.");
            broadcastUpdate(intentAction);
        
    

    @Override
    public void onServicesDiscovered(BluetoothGatt gatt, int status) 
        if (status == BluetoothGatt.GATT_SUCCESS) 
            for (BluetoothGattService service : gatt.getServices()) 
                if ((service == null) || (service.getUuid() == null)) 
                    continue;
                
                if (BLEUUID.SERVICE.equalsIgnoreCase(service.getUuid().toString())) 
                    mService = service;
                
            
            broadcastUpdate(ACTION_GATT_SERVICES_DISCOVERED);
         else 
            Log.w(TAG, "onServicesDiscovered received: " + status);
        
    

    @Override
    public void onCharacteristicRead(BluetoothGatt gatt,BluetoothGattCharacteristic characteristic,int status) 
        if (status == BluetoothGatt.GATT_SUCCESS) 
            broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
            broadcastUpdate(EXTRA_DATA, characteristic);
            Log.i("CARAC","CARACTERISTICA LEIDA onCharacteristicRead()");
        
    

    @Override
    public void onCharacteristicChanged(BluetoothGatt gatt,BluetoothGattCharacteristic characteristic) 
        readCharacteristic(characteristic);
        broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
        Log.i("CARAC","CAMBIO EN CARACTERISTICA");
    
;

private void broadcastUpdate(final String action) 
    final Intent intent = new Intent(action);
    sendBroadcast(intent);


@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
private void broadcastUpdate(final String action,final BluetoothGattCharacteristic characteristic) 
    final Intent intent = new Intent(action);

    if (PIN_CHARACTERISTIC.equals(characteristic.getUuid())) 

        final String pin = characteristic.getStringValue(0);
        intent.putExtra(EXTRA_DATA, String.valueOf(pin));
     else 
        // For all other profiles, writes the data formatted in HEX.
        final byte[] data = characteristic.getValue();
        if (data != null && data.length > 0) 
            final StringBuilder stringBuilder = new StringBuilder(data.length);
            for(byte byteChar : data)
                stringBuilder.append(String.format("%02X ", byteChar));
            Log.i("RECIBIDO", "RECIBIDOS DATOS");
            intent.putExtra(EXTRA_DATA, new String(data) + "\n" + stringBuilder.toString());
        
    
    sendBroadcast(intent);


public class LocalBinder extends Binder 
    BLEService getService() 
        return BLEService.this;
    


@Override
public IBinder onBind(Intent intent) 
    return mBinder;


@Override
public boolean onUnbind(Intent intent) 
    // After using a given device, you should make sure that BluetoothGatt.close() is called
    // such that resources are cleaned up properly.  In this particular example, close() is
    // invoked when the UI is disconnected from the Service.
    close();
    return super.onUnbind(intent);


private final IBinder mBinder = new LocalBinder();

public boolean initialize() 
    if (mBluetoothManager == null) 
        mBluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
        if (mBluetoothManager == null) 
            return false;
        
    
    mBluetoothAdapter = mBluetoothManager.getAdapter();
    if (mBluetoothAdapter == null) 
        return false;
    
    return true;


public boolean connect(final String address) 
    if (mBluetoothAdapter == null || address == null) 
        return false;
    

    // Previously connected device.  Try to reconnect.
    if (mBluetoothDeviceAddress != null && address.equals(mBluetoothDeviceAddress)
            && mBluetoothGatt != null) 
        if (mBluetoothGatt.connect()) 
            mConnectionState = STATE_CONNECTING;
            return true;
         else 
            return false;
        
    

    final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address);
    if (device == null) 
        return false;
    

    mBluetoothGatt = device.connectGatt(this, false, mGattCallback);
    mBluetoothDeviceAddress = address;
    mConnectionState = STATE_CONNECTING;
    return true;


public void disconnect() 
    if (mBluetoothAdapter == null || mBluetoothGatt == null) 
        return;
    
    mBluetoothGatt.disconnect();


public void close() 
    if (mBluetoothGatt == null) 
        return;
    
    mBluetoothGatt.close();
    mBluetoothGatt = null;
    Log.e("CIERRE", "CONEXION CERRADA");


public void readCharacteristic(BluetoothGattCharacteristic characteristic) 
    if (mBluetoothAdapter == null || mBluetoothGatt == null) 
        return;
    
    mBluetoothGatt.readCharacteristic(characteristic);
    Log.i("READ", "CARACTERISTICA LEIDA");


public void setCharacteristicNotification(BluetoothGattCharacteristic characteristic, boolean enabled) 
    if (mBluetoothAdapter == null || mBluetoothGatt == null) 
        return;
    
    mBluetoothGatt.setCharacteristicNotification(characteristic, enabled);
    if (PIN_CHARACTERISTIC.equals(characteristic.getUuid()))
        BluetoothGattDescriptor descriptor =
                new BluetoothGattDescriptor(UUID.nameUUIDFromBytes(BLEUUID.PIN_CHARACTERISTIC_CONFIG_DESCRIPTOR.getBytes()),
                        BluetoothGattDescriptor.PERMISSION_WRITE_SIGNED);

        descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
        mBluetoothGatt.writeDescriptor(descriptor);
    


public List<BluetoothGattService> getSupportedGattServices() 
    if (mBluetoothGatt == null) return null;
    return mBluetoothGatt.getServices();


public void sendCharacteristic(String pin, BluetoothDevice device)

    byte[] pinByte = pin.getBytes();
    int pinInt = Integer.valueOf(pin);

    BluetoothGattCharacteristic ch = (BluetoothGattCharacteristic) mService.getCharacteristic(UUID
            .fromString(BLEUUID.PIN_CHARACTERISTIC_UUID));

    ch.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT);

    ch.setValue(pin);

    Toast.makeText(context, "CARACTERISTICA ASIGNADA", Toast.LENGTH_SHORT).show();
    connect(device.getAddress());
    mBluetoothGatt = device.connectGatt(this, false, mGattCallback);
    setCharacteristicNotification(ch, true);

    if (mBluetoothGatt.writeCharacteristic(ch)) 
        Toast.makeText(context, "CARACTERISTICA ESCRITA", Toast.LENGTH_SHORT).show();
    



BLEUUID

public class BLEUUID 

// CARACTERISTICA PIN
public  static final String SERVICE ="0000fff0-0000-1000-8000-00805f9b34fb";
public static final String PIN_CHARACTERISTIC_UUID="0000fff9-0000-1000-8000-00805f9b34fb";
public static final String PIN_CHARACTERISTIC_CONFIG_DESCRIPTOR="0x2902";

private static HashMap<String, String> attributes = new HashMap();
static 
    attributes.put(SERVICE, "Service");
    attributes.put(PIN_CHARACTERISTIC_UUID, "Pin");

当我调试时,onCharacteristicChange() 永远不会执行。

有人知道问题出在哪里

【问题讨论】:

我不确定自己编写特性是否算作应该被通知的事情,这不是正常的用例。特性本身的变化通常是您收到的通知。例如。它代表温度或其他变化的测量值,以便您获得更新。 我发送 PIN 码,设备必须向我发送“OKAY”或“FAIL”。在 nRF Master Control 应用程序中,我看到发送 PIN 时的值变成“OKAY”,但我试图捕捉这个值但没有成功。 你确定特征的属性是notify吗?可能是indicate。这几乎是同一件事,但事实并非如此。为此您需要设置:descriptor.setValue(BluetoothGattDescriptor.ENABLE_INDICATION_VALUE); 检查您的设备规格 是的,该属性已通知,尽管我也尝试使用 ENABLE_INDICATION_VALUE;它也没有工作。 我注意到android.bluetooth.BluetoothGattCharacteristic.getStringValue() 可能会崩溃。我正在开发的应用程序不会崩溃,但getStringValue() 后面的代码永远不会执行。我通过在它周围包裹 trycatch(Exception e) 来修复它。 BluetoothGatt﹕ Unhandled exception in callback java.lang.NullPointerException at android.bluetooth.BluetoothGattCharacteristic.getStringValue(BluetoothGattCharacteristic.java:506) 【参考方案1】:

以下是在 Android 上使用 BLE 的一般模式:

    您尝试连接 您会收到一个回调,表明它已连接 您发现服务 你被告知服务被发现 你得到了特征 对于每个特征,您都会获得描述符 对于描述符,您将其设置为使用 BluetoothGattDescriptor.setValue() 启用通知/指示 您使用 BluetoothGatt.writeDescriptor() 编写描述符 您可以使用 BluetoothGatt.setCharacteristicNotification() 在本地启用特征通知。没有这个,您将不会被回调。 您会收到描述符已写入的通知 现在您可以将数据写入特征。在将任何内容写入任何特征之前,所有特征和描述符配置都已完成。

【讨论】:

我必须写入设备然后设备会广播一些数据。为此,我写了一个服务的特征。我通过接收器获得了一些数据。但这不是我需要的数据。设备将执行一些任务,然后它将广播数据。为此,我认为该服务的任何特征都将广播所需的数据。我正走在正确的道路上。实际上在通知集上我没有得到任何数据。请建议 @Ajit,我不确定你在问什么,但听起来你没有正确设置特征通知。我添加了新的第 9 步,并更新了第 7 步和第 8 步,使其更加清晰。 我明白了,但特点是有写属性,没有通知属性。 请查看***.com/questions/26800606/… 谢谢,你拯救了我的夜晚。

以上是关于Android BLE 无法从设备接收 Gatt 特性通知的主要内容,如果未能解决你的问题,请参考以下文章

samsung ble api 无法从多个 GATT 特征中获取通知

蓝牙 GATT 向 BLE 设备发送数据

如何在 android 中将特征写入 BLE GATT 服务器?

iOS 蓝牙BLE开发

Android BLE GATT 断开与设备断开

Android BLE - 连接到多个设备似乎失败并且两个连接的 GATT 响应相同?