2017-04-14 237 views
0

我做了一个应用程序,通过BLE与设备进行通信。我写下了沟通所需的一切。设备通过characteristic.setValue()接收发送的值,但不会按照设想的那样在方法onCharacteristicChanged中返回反馈,尽管该设置已将描述符设置为通知。例如,我发送"GTN",应该得到"GTN deviceId"(就像在iOS上),但它只是调用onCharacteristicWrite()。还检查了该特性支持的属性。它支持PROPERTY_NOTIFY & PROPERTY_WRITE_NO_RESPONSE。我没有想到为什么它不想获得回调消息。有人可以检查,并尝试告诉我,如果我发送/订阅时犯了一些大错误?Android BLE onCharacteristicChanged()使用notify not triggered

MainActivity.java

public class MainActivity extends AppCompatActivity { 

    private static final int REQUEST_LOCATION_PERMISSION = 1; 
    private static final int REQUEST_ENABLE_BLUETOOTH = 2; 
    private static final int REQUEST_ENABLE_LOCATION = 3; 
    private static final int BLUETOOTH_ENABLED = -1; 
    private static final int LOCATION_ENABLED = -1; 
    private static final int MANUFACTURER_ID = xxxxx; 
    private static final UUID SERVICE =xxxxxxxxxxxxx; 
    private static final UUID CHARACTERISTIC =xxxxxxxxxxxxxxxxx; 
    private static final UUID DESCRIPTOR = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb"); 

    private final static String TAG = "BLE"; 
    private byte[] mfrData; 
    private BluetoothManager bluetoothManager; 
    private BluetoothAdapter bluetoothAdapter; 
    private BluetoothGatt bluetoothGatt; 
    private BluetoothLeScanner bluetoothLeScanner; 
    private BluetoothGattService customService; 
    private BluetoothGattCallback bluetoothGattCallback = new BluetoothGattCallback() { 

     @Override 
     public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) { 
      super.onConnectionStateChange(gatt, status, newState); 
      if (newState == BluetoothProfile.STATE_CONNECTED) { 
       gatt.discoverServices(); 
      } 
     } 

     @Override 
     public void onServicesDiscovered(BluetoothGatt gatt, int status) { 
      super.onServicesDiscovered(gatt, status); 
      BluetoothGattService service = gatt.getService(SERVICE); 
      BluetoothGattCharacteristic characteristic = service.getCharacteristic(CHARACTERISTIC); 
      gatt.setCharacteristicNotification(characteristic, true); 
      enableDataNotifications(gatt, characteristic); 
     } 

     @Override 
     public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { 
      super.onCharacteristicRead(gatt, characteristic, status); 
      Log.d(TAG, "onCharacteristicRead: "); 
     } 

     @Override 
     public void onCharacteristicWrite(final BluetoothGatt gatt, final BluetoothGattCharacteristic characteristic, int status) { 
      super.onCharacteristicWrite(gatt, characteristic, status); 
      Log.d(TAG, "onCharacteristicWrite: "); 

     } 

     @Override 
     public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { 
      super.onCharacteristicChanged(gatt, characteristic); 
      Log.d(TAG, "onCharacteristicChanged: "); 
     } 

     @Override 
     public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) { 
      super.onDescriptorRead(gatt, descriptor, status); 
      Log.d(TAG, "onDescriptorRead: "); 
     } 

     @Override 
     public void onDescriptorWrite(final BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) { 
      super.onDescriptorWrite(gatt, descriptor, status); 
      Log.d(TAG, "onDescriptorWrite: "); 
      checkCharacteristicProperties(gatt.getService(SERVICE).getCharacteristic(CHARACTERISTIC)); 
      new Thread(new Runnable() { 
       @Override 
       public void run() { 
        writeDataToCharCommand("GTN", gatt); 
       } 
      }).start(); 
     } 

     @Override 
     public void onReliableWriteCompleted(BluetoothGatt gatt, int status) { 
      super.onReliableWriteCompleted(gatt, status); 
      Log.d(TAG, "onReliableWriteCompleted: "); 
     } 

     @Override 
     public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) { 
      super.onReadRemoteRssi(gatt, rssi, status); 
      Log.d(TAG, "onReadRemoteRssi: "); 
     } 

     @Override 
     public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) { 
      super.onMtuChanged(gatt, mtu, status); 
      Log.d(TAG, "onMtuChanged: "); 
     } 

    }; 
    private ScanCallback scanCallback = new ScanCallback() { 
     @Override 
     public void onScanResult(int callbackType, ScanResult result) { 
      super.onScanResult(callbackType, result); 
      if (result.getScanRecord() != null && result.getScanRecord().getManufacturerSpecificData(MANUFACTURER_ID) != null) { 
       Log.d(TAG, "onScanResult: found device"); 
       bluetoothLeScanner.stopScan(scanCallback); 
       boolean isBonded = result.getDevice().createBond(); 
       if (isBonded) { 
        bluetoothGatt = result.getDevice().connectGatt(MainActivity.this, false, bluetoothGattCallback, BluetoothDevice.TRANSPORT_LE); 
       } 
      } 
     } 

     @Override 
     public void onBatchScanResults(List<ScanResult> results) { 
      super.onBatchScanResults(results); 
      Log.d(TAG, "onBatchScanResults: "); 
     } 

     @Override 
     public void onScanFailed(int errorCode) { 
      super.onScanFailed(errorCode); 
      Log.d(TAG, "onScanFailed: "); 
     } 
    }; 

    @Override 
    protected void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 
     setContentView(R.layout.activity_main); 

     setupBluetoothData(); 
     checkIfSupportedBluetooth(); 
     checkLocationPermission(); 
    } 

    private void checkCharacteristicProperties(BluetoothGattCharacteristic pChar) { 
     Log.d(TAG, "checkCharacteristicProperties: PROPERTY_READ " + ((pChar.getProperties() & BluetoothGattCharacteristic.PROPERTY_READ) != 0)); 
     Log.d(TAG, "checkCharacteristicProperties: PROPERTY_NOTIFY " + ((pChar.getProperties() & BluetoothGattCharacteristic.PROPERTY_NOTIFY) != 0)); 
     Log.d(TAG, "checkCharacteristicProperties: PROPERTY_BROADCAST " + ((pChar.getProperties() & BluetoothGattCharacteristic.PROPERTY_BROADCAST) != 0)); 
     Log.d(TAG, "checkCharacteristicProperties: PROPERTY_EXTENDED_PROPS " + ((pChar.getProperties() & BluetoothGattCharacteristic.PROPERTY_EXTENDED_PROPS) != 0)); 
     Log.d(TAG, "checkCharacteristicProperties: PROPERTY_INDICATE " + ((pChar.getProperties() & BluetoothGattCharacteristic.PROPERTY_INDICATE) != 0)); 
     Log.d(TAG, "checkCharacteristicProperties: PROPERTY_SIGNED_WRITE " + ((pChar.getProperties() & BluetoothGattCharacteristic.PROPERTY_SIGNED_WRITE) != 0)); 
     Log.d(TAG, "checkCharacteristicProperties: PROPERTY_WRITE " + ((pChar.getProperties() & BluetoothGattCharacteristic.PROPERTY_WRITE) != 0)); 
     Log.d(TAG, "checkCharacteristicProperties: PROPERTY_WRITE_NO_RESPONSE " + ((pChar.getProperties() & BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE) != 0)); 
    } 

    private void setupBluetoothData() { 
     bluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE); 
     bluetoothAdapter = bluetoothManager.getAdapter(); 
    } 

    private void checkIfSupportedBluetooth() { 
     if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) { 
      Toast.makeText(this, R.string.ble_not_supported, Toast.LENGTH_SHORT).show(); 
      finish(); 
     } 

     if (bluetoothAdapter == null) { 
      Toast.makeText(this, R.string.error_bluetooth_not_supported, Toast.LENGTH_SHORT).show(); 
      finish(); 
     } 
    } 

    private void checkLocationPermission() { 
     if (!hasGrantedLocationPermission()) { 
      requestLocationPermission(); 
     } else { 
      checkIfBluetoothEnabled(); 
     } 
    } 

    private boolean hasGrantedLocationPermission() { 
     int result = ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION); 
     return result == PackageManager.PERMISSION_GRANTED; 
    } 

    private void requestLocationPermission() { 
     ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, REQUEST_LOCATION_PERMISSION); 
    } 

    private void checkIfBluetoothEnabled() { 
     if (!bluetoothAdapter.isEnabled()) { 
      Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); 
      startActivityForResult(enableBtIntent, REQUEST_ENABLE_BLUETOOTH); 
     } else { 
      displayLocationSettingsRequest(); 
     } 
    } 

    private void displayLocationSettingsRequest() { 
     GoogleApiClient googleApiClient = new GoogleApiClient.Builder(this) 
       .addApi(LocationServices.API).build(); 
     googleApiClient.connect(); 

     LocationRequest locationRequest = LocationRequest.create(); 
     locationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY); 
     locationRequest.setInterval(10000); 
     locationRequest.setFastestInterval(10000/2); 

     LocationSettingsRequest.Builder builder = new LocationSettingsRequest.Builder().addLocationRequest(locationRequest); 
     builder.setAlwaysShow(true); 

     PendingResult<LocationSettingsResult> result = LocationServices.SettingsApi.checkLocationSettings(googleApiClient, builder.build()); 
     result.setResultCallback(new ResultCallback<LocationSettingsResult>() { 
      @Override 
      public void onResult(@NonNull LocationSettingsResult locationSettingsResult) { 

       final Status status = locationSettingsResult.getStatus(); 
       switch (status.getStatusCode()) { 
        case LocationSettingsStatusCodes.SUCCESS: { 
         scanForDevices(); 
         break; 
        } 
        case LocationSettingsStatusCodes.RESOLUTION_REQUIRED: { 
         try { 
          status.startResolutionForResult(MainActivity.this, REQUEST_ENABLE_LOCATION); 
         } catch (IntentSender.SendIntentException e) { 
          e.printStackTrace(); 
         } 
         break; 
        } 
        case LocationSettingsStatusCodes.SETTINGS_CHANGE_UNAVAILABLE: { 
         break; 
        } 
       } 
      } 
     }); 
    } 

    @Override 
    protected void onActivityResult(int requestCode, int resultCode, Intent data) { 
     super.onActivityResult(requestCode, resultCode, data); 
     switch (requestCode) { 
      case REQUEST_ENABLE_BLUETOOTH: { 
       if (resultCode == BLUETOOTH_ENABLED) { 
        displayLocationSettingsRequest(); 
       } else { 
        Toast.makeText(this, "Restart app", Toast.LENGTH_SHORT).show(); 
       } 
       break; 
      } 
      case REQUEST_ENABLE_LOCATION: { 
       if (resultCode == LOCATION_ENABLED) { 
        scanForDevices(); 
       } else { 
        Toast.makeText(this, "Restart app", Toast.LENGTH_SHORT).show(); 
       } 
       break; 
      } 
      default: { 
       break; 
      } 
     } 
    } 

    private void scanForDevices() { 
     Log.d(TAG, "scanForDevices: "); 
     Toast.makeText(this, "Scanning", Toast.LENGTH_SHORT).show(); 
     ScanSettings scanSettings = new ScanSettings.Builder() 
       .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY) 
       .build(); 

     ScanFilter scanFilter = new ScanFilter.Builder() 
       .setManufacturerData(0x0361, mfrData) 
       .build(); 

     bluetoothLeScanner = bluetoothAdapter.getBluetoothLeScanner(); 
     bluetoothLeScanner.startScan(Collections.singletonList(scanFilter), 
       scanSettings, scanCallback); 

    } 

    private String translate(byte[] value) throws UnsupportedEncodingException { 
     return new String(value, StandardCharsets.UTF_8); 
    } 

    @Override 
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { 
     super.onRequestPermissionsResult(requestCode, permissions, grantResults); 
     switch (requestCode) { 
      case REQUEST_LOCATION_PERMISSION: { 
       if (grantResults.length > 0 
         && grantResults[0] == PackageManager.PERMISSION_GRANTED) { 
        checkIfBluetoothEnabled(); 
       } else { 
        Toast.makeText(this, "Restart app", Toast.LENGTH_SHORT).show(); 
       } 
       break; 
      } 
      default: { 
       break; 
      } 
     } 
    } 

    private void writeDataToCharCommand(String data, BluetoothGatt gatt) { 
     Log.d(TAG, "writeDataToCharCommand: "); 
     gatt.getService(SERVICE).getCharacteristic(CHARACTERISTIC).setValue(data); 
     gatt.writeCharacteristic(gatt.getService(SERVICE).getCharacteristic(CHARACTERISTIC)); 
    } 

    private void enableDataNotifications(BluetoothGatt gatt, BluetoothGattCharacteristic gattCharacteristic) { 
     Log.d(TAG, "enableDataNotifications: "); 
     boolean enabled = gatt.setCharacteristicNotification(gattCharacteristic, true); 
     if (enabled) { 
      BluetoothGattDescriptor descriptor = gattCharacteristic.getDescriptor(DESCRIPTOR); 
      descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE); 
      gatt.writeDescriptor(descriptor); 
     } 
    } 
} 
+0

我看到您在服务发现中以及在enableDataNotifications中调用了setCharacteristicNotification两次。这真的是必需的。为蓝牙文件调用添加排队方法。也可以尝试在发现服务之前添加600 ms的延迟。 –

回答

0

我有BLE完美通知工作。

我创建了此方法来标记启用的特征通知。

public void markCharForNotification(BluetoothGattCharacteristic readableChar) { 

     mBluetoothGatt.setCharacteristicNotification(readableChar, true); 

     List<BluetoothGattDescriptor> listDescr = readableChar.getDescriptors(); 

     BluetoothGattDescriptor descriptor = listDescr.get(0); 
     descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE); 
     mBluetoothGatt.writeDescriptor(descriptor); 

    } 

希望它有助于

+0

'enableDataNotifications()'做同样的东西thx试试 – vasile

0

你可能也想运行的通知特性checkCharacteristicProperties。

总之,正确的方法,使通知是先检查属性并启用通知或指示,基于这些属性:

BluetoothGattDescriptor descriptor = characteristic.getDescriptor(CLIENT_CHARACTERISTIC_CONFIG_DESCRIPTOR_UUID); 
    int properties = characteristic.getProperties(); 
    if (descriptor != null) { 
     if ((properties & BluetoothGattCharacteristic.PROPERTY_NOTIFY) > 0) { 
      descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE); 
     } else if ((properties & BluetoothGattCharacteristic.PROPERTY_INDICATE) > 0) { 
      descriptor.setValue(BluetoothGattDescriptor.ENABLE_INDICATION_VALUE); 
     } 
     gatt.writeDescriptor(descriptor); 

希望这有助于。