I have a problem with CharacteristicNotification, I follow docs from android and search in many stackoverflow post.
All my code works until I try to get data from my device, when I subscribe to notification I get this log : BluetoothGatt: setCharacteristicNotification()
But my methods was never call
My UUID are good and my service send data I already check with other application.
I enable location for my app.
I have this permission in Manifest :
uses-permission android:name="android.permission.BLUETOOTH" android:required="true"
uses-permission android:name="android.permission.BLUETOOTH_ADMIN"
uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"
I have try with 3 mobiles all are samsung but different Android version
I really don't understand what happen
Here my callback :
private final BluetoothGattCallback gattCallback = new BluetoothGattCallback() {
#Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
switch (newState) {
case BluetoothProfile.STATE_CONNECTED:
Log.i("gattCallback", "STATE_CONNECTED");
gatt.discoverServices();
break;
case BluetoothProfile.STATE_DISCONNECTED:
Log.e("gattCallback", "STATE_DISCONNECTED");
break;
default:
Log.e("gattCallback", "STATE_OTHER");
}
}
#Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
Log.i("onServicesDiscovered", "connection to service");
BluetoothGattCharacteristic characteristic =
gatt.getService(UUID.fromString("a389d578-6285-4b3d-a9dc-b83a2ba4c095"))
.getCharacteristic(UUID.fromString("9ecbe31b-f77e-4886-a0a8-c416906387e7"));
gatt.readCharacteristic(characteristic);
BluetoothGattDescriptor descriptor = characteristic.getDescriptor(UUID.fromString("00002902-0000-1000-8000-00805F9B34FB"));
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
descriptor.setValue(BluetoothGattDescriptor.ENABLE_INDICATION_VALUE);
gatt.writeDescriptor(descriptor);
gatt.setCharacteristicNotification(characteristic, true);
}
#Override
public void onCharacteristicRead(BluetoothGatt gatt,
BluetoothGattCharacteristic
characteristic, int status) {
Log.i("onCharacteristicRead", "read");
}
#Override
public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
Log.i("onCharacteristicChanged", "changement");
}
};
Two errors:
1.
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
descriptor.setValue(BluetoothGattDescriptor.ENABLE_INDICATION_VALUE);
The second call overwrites the first value.
You must wait for the read callback before you start the descriptor write operation. Generally, you must always wait for the GATT callback before you can start the next operation.
Related
I've been developing an app for Android who communicates with a physical piece of hardware via bluetooth. Until now we've been using normal bluetooth, but this has now changed to BLE. I am not an android developer, why this is a bit difficult for me.
At the moment I am just running a proof of concept. Whenever the physical device receives something, it transmits it back, and I can confirm this using proper BLE terminal apps. I've identified the characteristics I need to use, and I can connect to the device and send data to it. Though I cannot receive data, I am never receiving anything back.
The code shown below shows what I'm currently doing, trying to send (successfully) to the device and not so successfully receiving data. Am I totally misunderstand this concept?
Hope someone can give me a hand.
#Override
public void onConnectionStateChange(BluetoothGatt
gatt, int status, int newState) {
super.onConnectionStateChange(gatt, status, newState);
switch (newState) {
case BluetoothProfile.STATE_CONNECTED:
Log.i("GattCallback", "connected");
gatt.discoverServices();
break;
case BluetoothProfile.STATE_DISCONNECTED:
Log.i("GattCallback", "Disconnected");
break;
default:
System.out.println(newState);
}
}
#Override
public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
super.onCharacteristicChanged(gatt, characteristic);
byte[] newValue = characteristic.getValue();
System.out.println("RECEIVED: " + Arrays.toString(newValue));
}
#Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
if (status == BluetoothGatt.GATT_SUCCESS) {
for (BluetoothGattService gattService : gatt.getServices()) {
Log.i("derp", "onServicesDiscovered: service=" + gattService.getUuid());
for (BluetoothGattCharacteristic characteristic : gattService.getCharacteristics()) {
Log.i("derp", "onServicesDiscovered: characteristic=" + characteristic.getUuid());
if(characteristic.getUuid().toString().contains("9616")){
System.out.println("We have found the read characteristic.");
gatt.setCharacteristicNotification(characteristic, true);
BluetoothGattDescriptor desc = characteristic.getDescriptor(UUID.fromString("49535343-1e4d-4bd9-ba61-23c647249616"));
if(desc == null){
System.out.println("NULL!");
return;
}
desc.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
gatt.writeDescriptor(desc);
System.out.println("Should be subscribed.");
}
else if(characteristic.getUuid().toString().contains("9bb3")){
System.out.println("Send characteristic found");
characteristic.setValue("Test\n\r");
boolean res = gatt.writeCharacteristic(characteristic);
if(res)
System.out.println("Success");
break;
}
}
}
}
}
I am trying to subscribe to multiple characteristics within the service and obtain notifications for both of the characteristics.
Here is my version with 1 char:
#Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
super.onServicesDiscovered(gatt, status);
List<BluetoothGattCharacteristic> characteristics = gatt.getServices().get(2).getCharacteristics();
BluetoothGattCharacteristic characteristic = characteristics.get(0);
gatt.setCharacteristicNotification(characteristic, true);
characteristic.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT);
BluetoothGattDescriptor descriptor = characteristic.getDescriptors().get(0);
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
gatt.writeDescriptor(descriptor);
}
#Override
public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
super.onCharacteristicChanged(gatt, characteristic);
if (characteristic.getUuid().equals(characteristicIDs[0])) {
Log.i(TAG,"Char for Beacons");
int threshValue = characteristic.getValue()[0];
sendBeaconToCloud(threshValue);
} else if (characteristic.getUuid().equals(characteristicIDs[1])) {
Log.d(TAG,"Char for Sensors");
convertSensorValues(characteristic.getValue());
} else {
Toast.makeText(BluetoothConnActivity.this,"Unknown char", Toast.LENGTH_SHORT).show();
}
}
So then I decided to copy the block of code within onServicesDiscovered and also
characterics.get(1)
This didn't work, so I followed the method specified in here:
How to subscribe to multiple BluetoothLE Characteristics with Android
So this is the produced code:
public List<BluetoothGattCharacteristic> chars;
public BluetoothGatt myGatt;
#Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
super.onServicesDiscovered(gatt, status);
chars = gatt.getServices().get(2).getCharacteristics();
this.myGatt = gatt;
subscribeToMultiple();
}
private void subscribeToMultiple() {
if (chars.size() == 0) return;
BluetoothGattCharacteristic characteristic = chars.get(0);
myGatt.setCharacteristicNotification(characteristic, true);
characteristic.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT);
BluetoothGattDescriptor descriptor = characteristic.getDescriptors().get(0);
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
myGatt.writeDescriptor(descriptor);
}
public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
super.onDescriptorWrite(gatt,descriptor,status);
chars.remove(0);
subscribeToMultiple();
}
However, I just get output:
D/BluetoothGatt: setCharacteristicNotification() - uuid: 0000a001-0000-1000-8000-00805f9b34fb enable: true
D/BluetoothGatt: setCharacteristicNotification() - uuid: 0000a002-0000-1000-8000-00805f9b34fb enable: true
but onCharacteristicChanged is never called.
I would really appreciate any help.
First you can set for 0.When you receive onCharacteristicChanged for characteristics 0 then unsubscribe from it and enable for the characteristic 1.when you receive for 1 unsubscribe for 1 and re enable 0. It worked for me once.
After connect connectGatt and readCharacteristic BluetoothGatt disconnects and onConnectionStateChange calls on ASUS device. On sony xperia z2 disconnect doesn't happen. I spent a lot of time ... Does anyone have any ideas ?
connect
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
mBluetoothGatt = mDevice.connectGatt(ActivityExamination.this, true, mCallback2 , BluetoothDevice.TRANSPORT_LE);
} else {
mBluetoothGatt = mDevice.connectGatt(ActivityExamination.this, true, mCallback2 );
}
and listener
BluetoothGattCallback mCallback2 = new BluetoothGattCallback() {
#Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
super.onConnectionStateChange(gatt, status, newState);
Log.e("onConnectionStateChange", "STATUS: " + status + " STATE: " + newState);
switch (newState) {
case BluetoothProfile.STATE_CONNECTED:
Log.i("gattCallback", "STATE_CONNECTED");
gatt.discoverServices();
break;
case BluetoothProfile.STATE_DISCONNECTED:
//status is 8
Log.e("gattCallback", "STATE_DISCONNECTED");
Log.i("gattCallback", "reconnecting...");
gatt.connect();
break;
default:
Log.e("gattCallback", "STATE_OTHER");
}
}
#Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
super.onServicesDiscovered(gatt, status);
Log.e("onServicesDiscovered", "STATUS: " + status);
BluetoothGattService mainService = mBluetoothGatt.getService(UUID_SERVICE_MAIN);
BluetoothGattService deviceInfoService = mBluetoothGatt.getService(UUID_SERVICE_DEVICE_INFO);
BluetoothGattCharacteristic mainCharacteristic = mainService.getCharacteristic(UUID_MAIN_DATA);
BluetoothGattCharacteristic modelCharacteristic = deviceInfoService.getCharacteristic(UUID_DEVICE_MODEL);
mBluetoothGatt.readCharacteristic(modelCharacteristic);
}
#Override
public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
super.onCharacteristicRead(gatt, characteristic, status);
Log.e("onCharacteristicRead", "STATUS: " + status + " CHAR: " + characteristic.toString());
}
#Override
public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
super.onCharacteristicChanged(gatt, characteristic);
Log.e("onCharacteristicChanged", "DESCRIPTOR: ");
}
#Override
public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
super.onDescriptorWrite(gatt, descriptor, status);
Log.e("onDescriptorWrite", "DESCRIPTOR: " + descriptor.toString());
}
};
I've run into similar issues in the past, and most of the time it was related to doing to much work in the actual callback method, which is generally called on a Binder thread if I'm remembering correctly.
Have you tried following a pattern similar to the Android tutorials? When receiving events from the bluetooth stack, they send broadcast events to notify of updates. This changes the thread context to the main thread, which would negate any potential of blocking the binder thread -- which may solve your issue.
Generally I set up a Service to handle my Bluetooth LE communication, and create 2 Handlers, one of the main thread and one a background thread. The background thread handler is used for parsing events, and the other for initiating communication with the remote BLE device i.e. connection calls, write, read, etc. This helps take the guess work out of which thread you're calling what on.
Hopefully this helps.
I'm writing printing data to the BluetoothGattCharacteristic of a Zebra ZD410 printer. I do this by chunking the data into 20 byte chunks and writing a chunk at a time with the following code:
mCharacteristic.setValue(bytes);
boolean status = mGatt.writeCharacteristic(mCharacteristic);
and then waiting until I receive BluetoothGattCallback.onCharacteristicWrite() before initiating the writing of the next chunk. This works fine.
If I disconnect() and close() the BluetoothGatt and later connect to the same device again with BluetoothDevice.connectGatt() and then try to write to the Characteristic after onServicesDiscovered() has been called is done and I have my Characteristic again, writing will fail. What I mean by this is that when I write to the Characteristic now, onCharacteristicWrite() will be called with a Characteristic who's getValue() returns the value of the last write on the old Gatt.
After trying to solve this for two days and reading tons of SO posts I haven't found a solution.
How can I fix this?
EDIT
Here is the code for the BluetoothGattCallback
private final BluetoothGattCallback gattCallback = new BluetoothGattCallback()
{
#Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState)
{
FALog.i(TAG, "onConnectionStateChange Status: " + status);
switch (newState)
{
case BluetoothProfile.STATE_CONNECTED:
FALog.i(TAG, "gattCallback STATE_CONNECTED");
gatt.discoverServices();
break;
case BluetoothProfile.STATE_DISCONNECTED:
disconnectAndCloseGatt();
mCharacteristic = null;
connectionFailed();
FALog.e(TAG, "gattCallback STATE_DISCONNECTED");
break;
default:
FALog.e(TAG, "gattCallback STATE_OTHER");
}
}
#Override
public void onServicesDiscovered(BluetoothGatt gatt, int status)
{
BluetoothGattService service = gatt.getService(PRINTER_SERVICE_UUID);
if (service != null)
{
BluetoothGattCharacteristic characteristic = service.getCharacteristic
(PRINTER_SERVICE_CHARACTERISTIC_UUID);
if (characteristic != null)
{
mCharacteristic = characteristic;
mInternalState = STATE_CONNECTED;
mState = State.CONNECTED;
notifyStateChanged();
print("~JA");
FALog.d(TAG, "Printer connected");
mBluetoothActivity.runOnUiThread(new Runnable()
{
#Override
public void run()
{
mListener.onPrinterConnected();
}
});
}
}
}
#Override
public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status)
{
FALog.d(TAG, "received onCharacteristicWrite " + new String(characteristic.getValue()) + "; success: " +
(status == BluetoothGatt.GATT_SUCCESS));
if (status == BluetoothGatt.GATT_SUCCESS)
{
handler.removeCallbacks(writeRunnable);
popQueueAndReleaseLock();
}
}
};
Try writeCharacteristic after the onDescriptorWrite() callback instead of onServicesDiscovered() callback. writeDescriptor holds the mDeviceBusy.
Im am trying to discover the services of my ble device. When I use a LG l3 my code works fine ( I see the uuid I'm looking for). When I try a Samsung s4 I get different service uuids. Why am i getting different uuids ?
private final BluetoothGattCallback gattCallback = new BluetoothGattCallback() {
#Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
switch (newState) {
case BluetoothProfile.STATE_CONNECTED:
if (gatt != null) {
mGatt.discoverServices();
}
break;
case BluetoothProfile.STATE_DISCONNECTED:
break;
default:
}
}
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
List<BluetoothGattService> services = gatt.getServices();
for (BluetoothGattService service : services) {
Log.d("DEBUG", "Found service uuid:" + service.getUuid());
/// not getting the UUID im looking for
}
}
}