Reputation: 1616
I am currently working on a project which exchanges small values from android to android over bluetooth LE. I am using nexus9 for android peripheral, Moto G as central. central scan peripheral perfectly and peripheral's services are being added, now i need to write characteristics on peripheral and then peripheral will acknowledge it to central but what happening is peripheral sends certain bytes to central and not reading central characteristics, even this central working fine for ios peripheral. here is my code for android central
private void startScanning() {
if ((btAdapter != null) && (!isScanning)) {
if (scanner == null) {
Log.d(TAG, "scanner was null");
scanner = btAdapter.getBluetoothLeScanner();
}
if (scanCallback == null)
setScanCallback(null);
scanner.startScan(createScanFilters(), createScanSettings(), scanCallback);
isScanning = true;
Log.d(TAG, "in startScanning");
//Toast.makeText(context, context.getString(R.string.scan_started), Toast.LENGTH_SHORT).show();
}
}
public void setScanCallback(final ScanCallback callback) {
scanCallback = new ScanCallback() {
@Override
public void onScanResult(int callbackType, ScanResult scanResult) {
super.onScanResult(callbackType, scanResult);
Log.d(TAG, scanResult.getDevice().getName() + " ");
deviceNames.add(scanResult.getDevice().getName());
deviceRSSIs.add(String.valueOf(scanResult.getRssi()));
adapter.notifyDataSetChanged();
scanResult.getDevice().connectGatt(context, false, new BluetoothGattCallback() {
@Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
super.onConnectionStateChange(gatt, status, newState);
Set<BluetoothGattCharacteristic> characteristicSet;
switch (newState) {
case BluetoothProfile.STATE_DISCONNECTING:
break;
case BluetoothProfile.STATE_DISCONNECTED:
Log.d(TAG, gatt.getDevice().getName() + " is disconnected");
//gatt.close();
break;
case BluetoothProfile.STATE_CONNECTED:
Log.d(TAG, gatt.getDevice().getName() + " is connected");
gatt.discoverServices();
break;
}
}
@Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
if (status == BluetoothGatt.GATT_SUCCESS) {
Log.d(TAG, "Discovered services central");
// TODO: Keep this here to examine characteristics
// eventually we should get rid of the discoverServices step
boolean foundService = false;
try {
List<BluetoothGattService> serviceList = gatt.getServices();
for (BluetoothGattService service : serviceList) {
if (service.getUuid().equals(serviceUUID)) {
foundService = true;
HashSet<BluetoothGattCharacteristic> characteristicSet = new HashSet<>();
characteristicSet.addAll(service.getCharacteristics());
discoveredCharacteristics.put(gatt.getDevice().getAddress(), characteristicSet);
for (BluetoothGattCharacteristic characteristic : characteristicSet) {
if (characteristic.getUuid().equals(characteristicUUID)) {
characteristic.setValue(dataToSend.getBytes());
boolean chr = gatt.writeCharacteristic(characteristic);
characteristic = gatt.getService(serviceUUID).getCharacteristic(characteristicUUID);
gatt.setCharacteristicNotification(characteristic, true);
BluetoothGattDescriptor descriptor = characteristic.getDescriptor(CLIENT_CHARACTERISTIC_CONFIG);
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
boolean desc = gatt.writeDescriptor(descriptor);
// setIndictaionSubscription(gatt, characteristic, true);
}
}
}
}
} catch (Exception e) {
Log.d(TAG, "Exception analyzing discovered services " + e.getLocalizedMessage());
e.printStackTrace();
}
if (!foundService)
Log.d(TAG, "Could not discover chat service!");
} else
Log.d(TAG, "Discovered services appears unsuccessful with code " + status);
super.onServicesDiscovered(gatt, status);
}
@Override
public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
Log.d(TAG, "onDescriptorWrite");
}
@Override
public void onCharacteristicChanged(final BluetoothGatt gatt, final BluetoothGattCharacteristic characteristic) {
Log.d(TAG, "onCharacteristicChanged");
if (!characteristic.getValue().toString().isEmpty()) {
MainActivity.this.runOnUiThread(new Runnable() {
public void run() {
Toast.makeText(getBaseContext(), new String(characteristic.getValue()), Toast.LENGTH_LONG).show();
gatt.readRemoteRssi();
}
});
}
super.onCharacteristicChanged(gatt, characteristic);
}
@Override
public void onCharacteristicWrite(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic, int status) {
Log.d(TAG, "onCharacteristicWrite");
Exception exception = null;
if (status != BluetoothGatt.GATT_SUCCESS) {
String msg = "Write was not successful with code " + status;
Log.d(TAG, msg);
exception = new UnknownServiceException(msg);
}
}
@Override
public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) {
Log.i("RSSIValue", gatt.getDevice().getName() + " " + String.valueOf(rssi) + " " + String.valueOf(status));
super.onReadRemoteRssi(gatt, rssi, status);
}
});
}
};
}
here is my code for peripheral.
private AdvertiseCallback mAdvCallback = new AdvertiseCallback() {
@Override
public void onStartSuccess(AdvertiseSettings settingsInEffect) {
if (settingsInEffect != null) {
Log.d(TAG, "Advertise success TxPowerLv="
+ settingsInEffect.getTxPowerLevel()
+ " mode=" + settingsInEffect.getMode());
} else {
Log.d(TAG, "Advertise success");
}
}
@Override
public void onStartFailure(int errorCode) {
Log.d(TAG, "Advertising failed with code " + errorCode);
}
};
private AdvertiseData createAdvData() {
AdvertiseData.Builder dataBuilder = new AdvertiseData.Builder();
dataBuilder.addServiceUuid(new ParcelUuid(serviceUUID));
dataBuilder.setIncludeTxPowerLevel(false);
dataBuilder.setIncludeDeviceName(true);
// builder.setManufacturerData(0x1234578, manufacturerData);
return dataBuilder.build();
}
private AdvertiseSettings createAdvSettings() {
AdvertiseSettings.Builder builder = new AdvertiseSettings.Builder();
builder.setTxPowerLevel(AdvertiseSettings.ADVERTISE_TX_POWER_HIGH);
builder.setAdvertiseMode(AdvertiseSettings.ADVERTISE_MODE_BALANCED);
builder.setConnectable(false);
return builder.build();
}
private void stopAdvertising() {
if (isAdvertising) {
gattServer.close();
advertiser.stopAdvertising(mAdvCallback);
isAdvertising = false;
}
}
public void createMessage() {
// Chop Message! You can only advertise 20 bytes at a time through ble
byte[] message = ("my text need to send").getBytes();
mDataToSend = new ArrayList<>();
dataToSendIndex = 0;
for(int i=0;i<Math.ceil((float)message.length/(float)20);i++) {
byte[] slice = new byte[20];
for(int ii=0;ii<slice.length && i*20 + ii < message.length;ii++) {
slice[ii] = message[i*20 + ii];
}
mDataToSend.add(slice);
}
// Start sending the message
sendMessage();
}
public void sendMessage() {
if(dataToSendIndex >= mDataToSend.size()) { // If we have incremented through the message, the message is sent
Log.d("auto", "PeripheralActivity > sendMessage() > Message Sent");
// Done Sending!
return;
}
// Set the value of the characteristic to the next data to send
mCharacteristic.setValue(mDataToSend.get(dataToSendIndex));
// Notify the subscriber you have changed the value
gattServer.notifyCharacteristicChanged(btClient, mCharacteristic, true);
// Increment index
dataToSendIndex ++;
}
BluetoothGattServerCallback gattCallback = new BluetoothGattServerCallback() {
@Override
public void onConnectionStateChange(BluetoothDevice device, int status, int newState) {
super.onConnectionStateChange(device, status, newState);
}
// @Override
// public void onServiceAdded(int status, BluetoothGattService service) {
// super.onServiceAdded(status, service);
// }
@Override
public void onCharacteristicReadRequest(BluetoothDevice device, int requestId, int offset, BluetoothGattCharacteristic characteristic) {
super.onCharacteristicReadRequest(device, requestId, offset, characteristic);
Log.d(TAG, characteristic.toString());
}
@Override
public void onCharacteristicWriteRequest(BluetoothDevice device, int requestId, BluetoothGattCharacteristic characteristic, boolean preparedWrite, boolean responseNeeded, int offset, byte[] value) {
super.onCharacteristicWriteRequest(device, requestId, characteristic, preparedWrite, responseNeeded, offset, value);
btClient = device;
gattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, offset, value);
createMessage();
}
@Override
public void onDescriptorReadRequest(BluetoothDevice device, int requestId, int offset, BluetoothGattDescriptor descriptor) {
super.onDescriptorReadRequest(device, requestId, offset, descriptor);
}
@Override
public void onDescriptorWriteRequest(BluetoothDevice device, int requestId, BluetoothGattDescriptor descriptor, boolean preparedWrite, boolean responseNeeded, int offset, byte[] value) {
super.onDescriptorWriteRequest(device, requestId, descriptor, preparedWrite, responseNeeded, offset, value);
btClient = device;
gattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, 0, BluetoothGattDescriptor.ENABLE_INDICATION_VALUE);
createMessage();
}
// @Override
// public void onExecuteWrite(BluetoothDevice device, int requestId, boolean execute) {
// super.onExecuteWrite(device, requestId, execute);
// }
@Override
public void onNotificationSent(BluetoothDevice device, int status) {
super.onNotificationSent(device, status);
sendMessage();
}
// @Override
// public void onMtuChanged(BluetoothDevice device, int mtu) {
// super.onMtuChanged(device, mtu);
// }
};
private void startGattServer() {
// Create GATT Service
service = new BluetoothGattService(serviceUUID , BluetoothGattService.SERVICE_TYPE_PRIMARY);
// Create Config Descriptor
BluetoothGattDescriptor dataDescriptor = new BluetoothGattDescriptor(CLIENT_CHARACTERISTIC_CONFIG, BluetoothGattDescriptor.PERMISSION_WRITE | BluetoothGattDescriptor.PERMISSION_READ);
// Add Descriptor
mCharacteristic.addDescriptor(dataDescriptor);
BluetoothGattDescriptor.PERMISSION_WRITE | BluetoothGattCharacteristic.PERMISSION_READ);
// Add Characteristic
service.addCharacteristic(mCharacteristic);
gattServer = manager.openGattServer(this, gattCallback);
gattServer.addService(service);
}
P.S: there may be a basic mistake which i couldn't able to detect, any help would be very appreciable :)
Upvotes: 1
Views: 1162
Reputation: 1888
I know this is a pretty "old" question, but maybe this answer helps others:
When you're communicating with a GATT server, try not to stack your queries, but always read/write once and wait for the answer or else the server might ignore everything you've send to it.
So in your case it would be
boolean chr = gatt.writeCharacteristic(characteristic);
--> wait for onCharacteristicWrite()
then do
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
boolean desc = gatt.writeDescriptor(descriptor);
gatt.setCharacteristicNotification(characteristic, true);`
--> wait for onDescriptorWrite()
It's a bit cumbersome to do, but it will work far more reliable that sending multiple queries. Hope that helps.
Upvotes: 1