我制作了一个通过BLE与设备通信的应用。我写了一些他们沟通所需要的东西。设备通过characteristic.setValue()
接收发送的值,但不会按照预期的方式返回方法onCharacteristicChanged
中的反馈,尽管它具有为通知设置的描述符。例如,我发送"GTN"
并且应该"GTN deviceId"
(就像在iOS上一样),但它只调用onCharacteristicWrite()
。还检查了特征支持的属性。它支持PROPERTY_NOTIFY
& PROPERTY_WRITE_NO_RESPONSE
。我没有想法为什么它不想得到那个回调消息。有人检查并尝试告诉我,如果我在发送/订阅时犯了一些重大错误吗?
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 :(得分: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);
}
希望有所帮助
答案 1 :(得分:0)
您可能还想在通知特性上运行checkCharacteristicProperties。
无论如何,启用通知的正确方法是首先检查属性并根据这些属性启用notify或指示:
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);
希望这有帮助。