In my application the following code is executed:
gatt[0] = bluetoothAdapter.getRemoteDevice(macAddress).connectGatt(context, true, new AbstractCallback(subscriber) {
#Override
public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
... // do something with new characteristic value
}
})
Where AbstractCallback is:
private abstract class AbstractScaleCallback extends BluetoothGattCallback {
... // fields and constants
private final byte[] TURN_ON_COMMAND = ...
private static final String COMMAND_CHARACTERISTIC_UUID = ...
private BluetoothGattCharacteristic commandCharacteristic;
private Subscription subscriber;
AbstractScaleCallback(Subscription subscriber) {
this.subscriber = subscriber;
}
#Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
if (newState == BluetoothProfile.STATE_CONNECTED) {
gatt.discoverServices();
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
gatt.close();
}
}
#Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
if (status == BluetoothGatt.GATT_SUCCESS) {
... // find other characteristics
commandCharacteristic = measurementService.getCharacteristic(UUID.fromString(COMMAND_CHARACTERISTIC_UUID));
commandCharacteristic.setValue(TURN_ON_COMMAND);
gatt.writeCharacteristic(commandCharacteristic);
}
}
#Override
public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
if (characteristic.getUuid().equals(UUID.fromString(COMMAND_CHARACTERISTIC_UUID)) && Arrays.equals(characteristic.getValue(), TURN_ON_COMMAND)) {
if (status == BluetoothGatt.GATT_SUCCESS) {
... // set up characteristic notifications
} else {
... // throw error
}
}
}
}
On all my test devices the code yields the same results from the application side - all the callbacks are called in the right order, and gatt.writeCharacteristic(commandCharacteristic) method returns 'true'. But it seems that on some of those devices the characteristic value on the peripheral device remains unchanged. Even though, the characteristic change notification seems to work all right.
From what I found, all of the problematic devices seem to share the same series of SoC - Snapdragon 600.
Does anyone have an idea what could be the problem and how to solve it?
Related
I am trying to receive notifications in a characteristic and then write text in another characteristic of the same BLE device. But when writing to the device most of the time it disconnects me, because something will not go well I imagine. This is the code.
BluetoothGatt gatt = null;
BluetoothGattCallback gattCallback = new BluetoothGattCallback() {
#Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
super.onConnectionStateChange(gatt, status, newState);
if (newState == BluetoothProfile.STATE_CONNECTED) {
gatt.discoverServices();
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
gatt.close();
}
}
#Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
super.onServicesDiscovered(gatt, status);
BluetoothGattCharacteristic characteristic = null;
if (BLOODGLUCOSE_MODEL.equals(GlobalNames.ADF_B27)) {
characteristic = gatt.getService(BLOODGLUCOSE_SERVICEUDID).getCharacteristic(BLOODGLUCOSE_NOTIFY_CHARACTERISTICSUDID);
gatt.setCharacteristicNotification(characteristic, true);
BluetoothGattDescriptor descriptor = characteristic.getDescriptor(CLIENT_CHARACTERISTIC_CONFIG_UUID);
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
gatt.writeDescriptor(descriptor);
}
}
#Override
public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
super.onDescriptorWrite(gatt, descriptor, status);
BluetoothGattCharacteristic characteristic = null;
try {
if (BLOODGLUCOSE_MODEL.equals(GlobalNames.ADF_B27)) {
characteristic =
gatt.getService(BLOODGLUCOSE_SERVICEUDID)
.getCharacteristic(BLOODGLUCOSE_WRITE_CHARACTERISTICSUDID);
byte[] byteValue = new byte[10];
byteValue[0] = (byte)0x5A;
byteValue[1] = (byte)0x0A;
byteValue[2] = (byte)0x00;
byteValue[3] = (byte)0x14;
byteValue[4] = (byte)0x09;
byteValue[5] = (byte)0x14;
byteValue[6] = (byte)0x0A;
byteValue[7] = (byte)0x05;
byteValue[8] = (byte)0x09;
byteValue[9] = (byte)0xAF;
characteristic.setValue(byteValue);
(gatt.writeCharacteristic(characteristic);
}
} catch (NullPointerException e) { }
}
}
I can see that writeDescriptor() and writeCharacteristic always returns true.
Does anyone see any errors?
Thanks.
I'm trying to read the heart rate from a BLE device (It's a smartwatch and the model is CURREN R5 Pro), but when I get the Heart Rate Service, it returns null.
This is the code:
class Constants {
final static UUID HEART_RATE_SERVICE_UUID = convertFromInteger(0x180D);
final static UUID HEART_RATE_MEASUREMENT_UUID = convertFromInteger(0x2A37);
final static UUID HEART_RATE_CONTROL_POINT_UUID = convertFromInteger(0x2A39);
final static UUID CLIENT_CHARACTERISTIC_CONFIG_UUID = convertFromInteger(0x2902);
private static UUID convertFromInteger(int i) {
final long MSB = 0x0000000000001000L;
final long LSB = 0x800000805f9b34fbL;
long value = i & 0xFFFFFFFF;
return new UUID(MSB | (value << 32), LSB);
}
}
private final BluetoothGattCallback bluetoothGattCallback = new BluetoothGattCallback() {
#Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
if (newState == BluetoothProfile.STATE_CONNECTED) {
bluetoothGatt.discoverServices();
Log.d(TAG, "Connected");
}
if (newState == BluetoothProfile.STATE_DISCONNECTED) {
Log.d(TAG, "Disconnected");
}
}
#Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
if (status == BluetoothGatt.GATT_SUCCESS) {
//getCharacteristic(HEART_RATE_MEASUREMENT_UUID) returns null
BluetoothGattCharacteristic characteristic = gatt.getService(HEART_RATE_SERVICE_UUID)
.getCharacteristic(HEART_RATE_MEASUREMENT_UUID);
gatt.setCharacteristicNotification(characteristic, true);
BluetoothGattDescriptor descriptor = characteristic.getDescriptor(CLIENT_CHARACTERISTIC_CONFIG_UUID);
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
gatt.writeDescriptor(descriptor);
}
}
#Override
public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
BluetoothGattCharacteristic characteristic = gatt.getService(HEART_RATE_SERVICE_UUID)
.getCharacteristic(HEART_RATE_CONTROL_POINT_UUID);
characteristic.setValue(DATA_STREAMING_COMMAND);
gatt.writeCharacteristic(characteristic);
}
#Override
public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
if (HEART_RATE_MEASUREMENT_UUID.equals(characteristic.getUuid())) {
Log.d(TAG, "HEART_RATE_MEASUREMENT_UUID");
//PROCESS DATA
}
if (HEART_RATE_CONTROL_POINT_UUID.equals(characteristic.getUuid())) {
Log.d(TAG, "HEART_RATE_CONTROL_POINT_UUID");
//PROCESS DATA
}
}
};
However, the only app that is able to read the heart rate from the device is Wearfit. Other applications are not able to read the heart rate from this device.
The services that I'm able to get are these:
00001800-0000-1000-8000-00805f9b34fb
00001801-0000-1000-8000-00805f9b34fb
6e400001-b5a3-f393-e0a9-e50e24dcca9e
00001530-1212-efde-1523-785feabcd123
0000fee7-0000-1000-8000-00805f9b34fb
Has anyone experienced this situation? Is there anything wrong with my code? Thanks for your help.
I need some response data from Ble. When I am writing something on ble I need to read response data from Ble. I am able to successfully enable and disable my ble device but only missing response data from ble. I also need to convert decimal time into Integer hex format like for 60 min into 0x3c.
private BluetoothGattCallback gattCallback= new BluetoothGattCallback() {
#RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
#Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
super.onServicesDiscovered(gatt,status);
clientGatt =gatt;
if (status == BluetoothGatt.GATT_SUCCESS) {
BluetoothGattService service = gatt.getServices().get(2);
List<BluetoothGattCharacteristic> gattCharacteristics = service.getCharacteristics();
for (BluetoothGattCharacteristic gattCharacteristic : gattCharacteristics) {
if (gattCharacteristic.getUuid().toString().equalsIgnoreCase(AppConstant.RECEIVE_UUID)) {
readCharacteristic=gattCharacteristic;
}
if (gattCharacteristic.getUuid().toString().equalsIgnoreCase(AppConstant.SEND_UUID_STR)) {
writeCharacteristic = gattCharacteristic;
}
}
}
}
#RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
#Override
public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
super.onCharacteristicRead(gatt,characteristic,status);
LogUtils.errorLog("onCharacteristicRead", "##: "+characteristic.getValue()[0]);
}
#RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
#Override
public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
super.onCharacteristicChanged(gatt, characteristic)
}
#RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
#Override
public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
super.onCharacteristicWrite(gatt, characteristic, status);
setCharacteristicNotification(characteristic,true);
}
#TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
#Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
super.onConnectionStateChange(gatt, status, newState);
clientGatt = gatt;
switch (newState) {
case BluetoothProfile.STATE_CONNECTED:
isBLE_Connected=true;
gatt.discoverServices();
break;
case BluetoothProfile.STATE_DISCONNECTED:
isBLE_Connected=false;
if(status==133 || status==22 || status==62){
refreshDeviceCache();
clientGatt.discoverServices();
}else{
clientGatt.disconnect();
clientGatt.close();
}
break;
}
}
};
Before onCharacteristicChanged is called you had to enable notification.
Someting like:
//Enable local notifications
gatt.setCharacteristicNotification(characteristic, true);
//Enabled remote notifications
BluetoothGattDescriptor desc =characteristic.getDescriptor(CONFIG_DESCRIPTOR);
desc.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
gatt.writeDescriptor(desc);
should help.
You when you write something on characteristic with the help of BluetoothGattDescriptor as
Mike's answer
You need to override the following method to listen to the changes has been completed then you can read the characteristics:
#Override
public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
super.onDescriptorWrite(gatt, descriptor, status);
Log.d(TAG, "onDescriptorWrite :" + ((status == BluetoothGatt.GATT_SUCCESS) ? "Sucess" : "false"));
}
I can scan for Bluetooth LE devices through startLeScan or via the new getBluetoothLeScanner and that works fine. However even though it keeps on scanning it never detects the same device twice. That's unfortunate because I would like to receive events when the rssi of a beacon changes. Does Android support that?
Implement a BluetoothGattCallback and override the onReadRemoteRssi method.
private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
#Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
String intentAction;
if (newState == BluetoothProfile.STATE_CONNECTED) {
broadcastUpdate(intentAction);
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
broadcastUpdate(intentAction);
}
}
#Override
public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) {
super.onReadRemoteRssi(gatt, rssi, status);
// use rssi value here
}
#Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
if (status == BluetoothGatt.GATT_SUCCESS) {
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);
}
}
#Override
public void onCharacteristicChanged(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic) {
broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
}
};
Two solutions which I used.
1) Make a connection, call readRemoteRssi(); of gatt and override onReadRemoteRssi, and have your own check to see if the RSSI is changed.
2) StrartLEScan every 20 Sec(20 sec is enough time to receive new advertisement packets) and have a check on onLeScan callback to see if the device is found, if yes then compare RSSI.
RSSI is the value which your device(scanner) derives.
Also if you have code for GATT STACK, you will get onLEScan called many times for single device, it is GattService which truncate duplicate results.
I want to perform read and write operation with bluetooth peripheral.I have write sample application in which after giving particular input it give me output but inside code onCharacteristicRead not get called after onCharacteristicWrite. Please help me out.
This is my code
BluetoothGattCharacteristic WriteCharacteristic =null;
BluetoothGattCharacteristic ReadCharacteristic =null;
private BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
/* State Machine Tracking */
private int mState = 0;
private void reset() { mState = 0; }
private void advance() {
mState++;
}
public void onReliableWriteCompleted(BluetoothGatt gatt, int status) {
Log.e("onReliableWriteCompleted", "Called");
};
#Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
Log.d(TAG, "Connection State Change: "+status+" -> "+connectionState(newState));
if (status == BluetoothGatt.GATT_SUCCESS && newState==BluetoothProfile.STATE_CONNECTED) {
/*
* Once successfully connected, we must next discover all the services on the
* device before we can read and write their characteristics.
*/
gatt.discoverServices();
//String servicename = gatt.getService(BATTERY_DATA_CHAR).toString();
//System.out.println(servicename);
mHandler.sendMessage(Message.obtain(null, MSG_PROGRESS, "Discovering Services..."));
} else if (status == BluetoothGatt.GATT_SUCCESS && newState == BluetoothProfile.STATE_DISCONNECTED) {
/*
* If at any point we disconnect, send a message to clear the weather values
* out of the UI
*/
connectedDevice = null;
mHandler.sendEmptyMessage(MSG_CLEAR);
} else if (status != BluetoothGatt.GATT_SUCCESS) {
/*
* If there is a failure at any stage, simply disconnect
*/
gatt.disconnect();
connectedDevice = null;
}
}
#Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
Log.d(TAG, "Services Discovered: "+status);
mHandler.sendMessage(Message.obtain(null, MSG_PROGRESS, "Enabling Sensors..."));
/*
* With services discovered, we are going to reset our state machine and start
* working through the sensors we need to enable
*/
//reset();
String test="";
List<BluetoothGattService> strname = gatt.getServices();
String servicename =DINSTANCE_SERVICE.toString();
for(int count=0;count<strname.size();count++)
{
if(servicename.equalsIgnoreCase(strname.get(count).getUuid().toString()))
{
ReadCharacteristic = gatt.getService(DINSTANCE_SERVICE).getCharacteristic(DISTAINCE_READ_DATA);
WriteCharacteristic = gatt.getService(DINSTANCE_SERVICE).getCharacteristic(DISTANCE_WRITE_DATA);
test="123";
//characteristic.getStringValue(18);
//byte bbb[] = characteristic.getValue();
// gatt.readCharacteristic(characteristic);
/*String strInput = "012072";
byte passvalues[] = strInput.getBytes();
WriteCharacteristic.setValue(passvalues);
gatt.writeCharacteristic(WriteCharacteristic);*/
WriteCharacteristic.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT);
//tChar.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT);
WriteCharacteristic.setValue(012072, BluetoothGattCharacteristic.FORMAT_SINT32, 0);
gatt.writeCharacteristic(WriteCharacteristic);
break;
}
}
System.out.println(servicename);
}
public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status)
{
Log.e("onDescriptorRead","Read method gets called.");
};
#Override
public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
//For each read, pass the data up to the UI thread to update the display
byte bbb[] = characteristic.getValue();
if (HUMIDITY_DATA_CHAR.equals(characteristic.getUuid())) {
mHandler.sendMessage(Message.obtain(null, MSG_HUMIDITY, characteristic));
}
if (PRESSURE_DATA_CHAR.equals(characteristic.getUuid())) {
mHandler.sendMessage(Message.obtain(null, MSG_PRESSURE, characteristic));
}
if (PRESSURE_CAL_CHAR.equals(characteristic.getUuid())) {
mHandler.sendMessage(Message.obtain(null, MSG_PRESSURE_CAL, characteristic));
}
if(DISTAINCE_READ_DATA.equals(characteristic.getUuid()))
{
mHandler.sendMessage(Message.obtain(null, MSG_BATTERY, characteristic));
}
if(BATTERY_READ_CHAR.equals(characteristic.getUuid()))
{
mHandler.sendMessage(Message.obtain(null, MSG_BATTERY, characteristic));
}
//After reading the initial value, next we enable notifications
//setNotifyNextSensor(gatt);
}
#Override
public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status)
{
//After writing the enable flag, next we read the initial value
// readNextSensor(gatt);
try {
if (status != BluetoothGatt.GATT_SUCCESS)
throw new AssertionError("Error on char write");
super.onCharacteristicWrite(gatt, characteristic, status);
if (characteristic.getUuid().equals(DISTANCE_WRITE_DATA)) {
BluetoothGattService syncService = gatt.getService(DINSTANCE_SERVICE);
BluetoothGattCharacteristic tChar = syncService.getCharacteristic(DISTANCE_WRITE_DATA);
ReadCharacteristic = syncService.getCharacteristic(DISTAINCE_READ_DATA);
if (tChar == null)throw new AssertionError("characteristic null when sync time!");
tChar.setValue(012072,BluetoothGattCharacteristic.FORMAT_SINT32, 0);
gatt.readCharacteristic(ReadCharacteristic);
// gatt.readDescriptor(null);
}
} catch (AssertionError e) {
e.printStackTrace();
}
}
#Override
public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
/*
* After notifications are enabled, all updates from the device on characteristic
* value changes will be posted here. Similar to read, we hand these up to the
* UI thread to update the display.
*/
if (HUMIDITY_DATA_CHAR.equals(characteristic.getUuid())) {
mHandler.sendMessage(Message.obtain(null, MSG_HUMIDITY, characteristic));
}
if (PRESSURE_DATA_CHAR.equals(characteristic.getUuid())) {
mHandler.sendMessage(Message.obtain(null, MSG_PRESSURE, characteristic));
}
if (PRESSURE_CAL_CHAR.equals(characteristic.getUuid())) {
mHandler.sendMessage(Message.obtain(null, MSG_PRESSURE_CAL, characteristic));
}
}
#Override
public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
//Once notifications are enabled, we move to the next sensor and start over with enable
String strInput = "012072";
byte passvalues[] = strInput.getBytes();
descriptor.setValue(passvalues);
advance();
Log.e("onDescriptorWrite", "called");
//enableNextSensor(gatt);
}
#Override
public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status)
{
Log.e("onReadRemoteRssi", "called");
Log.d(TAG, "Remote RSSI: "+rssi);
}
private String connectionState(int status) {
switch (status) {
case BluetoothProfile.STATE_CONNECTED:
return "Connected";
case BluetoothProfile.STATE_DISCONNECTED:
return "Disconnected";
case BluetoothProfile.STATE_CONNECTING:
return "Connecting";
case BluetoothProfile.STATE_DISCONNECTING:
return "Disconnecting";
default:
return String.valueOf(status);
}
}
};
Here you can set up (subscribe) to the charactersitic through either notification or indication by calling:
mBluetoothGatt.setCharacteristicNotification(characteristic, true)
mBluetoothGatt.readCharacteristic(characteristic);
from here