I want to get the Heart Rate through BluetoothLe from my Mi Band 2. I tried to follow the example from Getting Started with Bluetooth Low Energy but I didn't succeed to write the value from Heart Rate. I think I miss something but I cannot realize what. It's the first time I am working with bluetooth and smart band. I wish you could help me. I do not know if for starting the Heart Rate sensor should I use as {0x01} byte.
So following the example I downloaded the BluetoothLeDemo app and I got from there BleWrapper BleDefinedUUIDs and BleNamesResolver. I have 2 buttons Scan and stop. I created a BleWrapper, called mBleWrapper to use it for starting scanning and stop scanning.
mBleWrapper = new BleWrapper(this, new BleWrapperUiCallbacks.Null(){
#Override
public void uiDeviceConnected(BluetoothGatt gatt, BluetoothDevice device) {
super.uiDeviceConnected(gatt, device);
}
#Override
public void uiDeviceDisconnected(BluetoothGatt gatt, BluetoothDevice device) {
super.uiDeviceDisconnected(gatt, device);
}
#Override
public void uiAvailableServices(BluetoothGatt gatt, BluetoothDevice device, List<BluetoothGattService> services) {
super.uiAvailableServices(gatt, device, services);
BluetoothGattCharacteristic c=null;
for(BluetoothGattService service : services) {
String serviceName = BleNamesResolver.resolveUuid(service.getUuid().toString());
Log.i("SERVIDE", serviceName);
}
}
c=gatt.getService(BleDefinedUUIDs.Service.HEART_RATE).getCharacteristic(BleDefinedUUIDs.Characteristic.HEART_RATE_MEASUREMENT);
mBleWrapper.writeDataToCharacteristic(c, new byte[]{0x01});
mState = mSensorState.ACC_ENABLE;
}
#Override
public void uiCharacteristicForService(BluetoothGatt gatt, BluetoothDevice device, BluetoothGattService service, List<BluetoothGattCharacteristic> chars) {
super.uiCharacteristicForService(gatt, device, service, chars);
}
#Override
public void uiCharacteristicsDetails(BluetoothGatt gatt, BluetoothDevice device, BluetoothGattService service, BluetoothGattCharacteristic characteristic) {
super.uiCharacteristicsDetails(gatt, device, service, characteristic);
}
#Override
public void uiNewValueForCharacteristic(BluetoothGatt gatt, BluetoothDevice device, BluetoothGattService service, BluetoothGattCharacteristic ch, String strValue, int intValue, byte[] rawValue, String timestamp) {
super.uiNewValueForCharacteristic(gatt, device, service, ch, strValue, intValue, rawValue, timestamp);
switch (mState) {
case ACC_READ:
Log.i("READ", "heart rate dta");
}
Log.i("Value", "Val" + intValue);
}
#Override
public void uiGotNotification(BluetoothGatt gatt, BluetoothDevice device, BluetoothGattService service, BluetoothGattCharacteristic characteristic) {
super.uiGotNotification(gatt, device, service, characteristic);
String ch = BleNamesResolver.resolveCharacteristicName(characteristic.getUuid().toString());
Log.d("AAA", "uiGotNotification: " + ch);
}
#Override
public void uiSuccessfulWrite(BluetoothGatt gatt, BluetoothDevice device, BluetoothGattService service, BluetoothGattCharacteristic ch, String description) {
super.uiSuccessfulWrite(gatt, device, service, ch, description);
BluetoothGattCharacteristic c;
switch (mState) {
case ACC_ENABLE:
Log.i("ENABLED", "Heart Rate enabled");
c = gatt.getService(BleDefinedUUIDs.Service.HEART_RATE).getCharacteristic(BleDefinedUUIDs.Characteristic.HEART_RATE_MEASUREMENT);
mBleWrapper.requestCharacteristicValue(c);
mState = mSensorState.ACC_READ;
break;
case ACC_READ:
Log.i("WRITE", "SUCCESSfule write");
break;
}
}
#Override
public void uiFailedWrite(BluetoothGatt gatt, BluetoothDevice device, BluetoothGattService service, BluetoothGattCharacteristic ch, String description) {
super.uiFailedWrite(gatt, device, service, ch, description);
switch (mState) {
case ACC_ENABLE:
Log.i("FAILED", "Feailed to enbale Heart Rate");
}
}
#Override
public void uiNewRssiAvailable(BluetoothGatt gatt, BluetoothDevice device, int rssi) {
super.uiNewRssiAvailable(gatt, device, rssi);
}
#Override
public void uiDeviceFound(BluetoothDevice device, int rssi, byte[] record) {
super.uiDeviceFound(device, rssi, record);
String msg = "uiDeviceFound: "+device.getAddress()+","+device.getName();
if(devicesList.contains(device)==false) {
Log.i("Devicce", "deviceFound: " + msg);
devicesList.add(device);
genericListAdapter.notifyDataSetChanged();
}
}
});
I display all devices found with a ListView and onClick I want to connect to that item and write the Heart Rate in the Log.
deviceListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
BluetoothGatt gatt;
BluetoothGattCharacteristic c;
connectDevice((BluetoothDevice) parent.getItemAtPosition(position));
Log.i("Read", "Get Heart Rate");
if(mBleWrapper.isConnected()==false){
Log.i("not connected", "NOT CONNECTE");
return;
}
gatt = mBleWrapper.getGatt();
c = gatt.getService(BleDefinedUUIDs.Service.HEART_RATE).getCharacteristic(BleDefinedUUIDs.Characteristic.HEART_RATE_MEASUREMENT);
mBleWrapper.requestCharacteristicValue(c);
mState=mSensorState.ACC_READ;
}
});
I also added at BleWrapper class onDescpritorWriter
#Override
public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
super.onDescriptorWrite(gatt, descriptor, status);
String deviceName = gatt.getDevice().getName();
String serviceName = BleNamesResolver.resolveServiceName(descriptor.getCharacteristic().getService().getUuid().toString().toLowerCase(Locale.getDefault()));
String charName = BleNamesResolver.resolveCharacteristicName(descriptor.getCharacteristic().getUuid().toString().toLowerCase(Locale.getDefault()));
String description = "Device: " + deviceName + " Service: " + serviceName + " Characteristic: " + charName;
if(status == BluetoothGatt.GATT_SUCCESS) {
mUiCallback.uiSuccessfulWrite(mBluetoothGatt, mBluetoothDevice, mBluetoothSelectedService, descriptor.getCharacteristic(), description);
}
else {
mUiCallback.uiFailedWrite(mBluetoothGatt, mBluetoothDevice, mBluetoothSelectedService, descriptor.getCharacteristic(), description + " STATUS = " + status);
}
}
And for UUIDS for service I used
UUID HEART_RATE = UUID.fromString("0000180d-0000-1000-8000-00805f9b34fb");
and for Charachteristics
UUID HEART_RATE_MEASUREMENT = UUID.fromString("00002a37-0000-1000-8000-00805f9b34fb");
In other examples I saw that they also used the UUIDS for descriptor, but me not. I know that is a long post and question, but please I really need some help.
There are something you can check around.
Obviously, you have successfully discovered devices around. According to this(Bluetooth official document) heart rate service (UUID:180d) need set notify to TRUE first. Something like:
Use UUID:00002902-0000-1000-8000-00805f9b34fb to get the descriptor of the characteristic.
BluetoothGattDescriptor descriptor = gattCharacteristic.getDescriptor(Client_Characteristic_Configuration);
Set the feature (Notify) of descriptor to TRUE:
descriptor.setValue((BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE));
Write the value to the BLE device (using Gatt object from the callback function)
gatt.writeDescriptor(descriptor);
After above, you may get the data in override function:
public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status)
By this way:
BluetoothGattCharacteristic Char = gatt.getService(FORA_SERVICE_UUID).getCharacteristic(The uuid you want to connect);
byte[] data = Char.getValue();
And maybe you can show the example link. It can be more clear what you had used.
Related
I am developing an application that connects through Bluetooth Low Energy to a Bluetooth scale and read weight.
The scale is Hi Weigh CF 150.
I have followed this tutorial
https://punchthrough.com/android-ble-guide/
I am connecting to to the scale and I come to the point where I try to discover the services of the scale.
class GattCallBack extends BluetoothGattCallback {
#Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
super.onConnectionStateChange(gatt, status, newState);
String deviceAddress = gatt.getDevice().getAddress();
if ( status == BluetoothGatt.GATT_SUCCESS) {
if (newState == BluetoothProfile.STATE_CONNECTED) {
Log.w(TAG, "Scale Connected successfully.");
bluetoothGatt = gatt;
Handler handler = new Handler(Looper.getMainLooper());
handler.post(new Runnable() {
#Override
public void run() {
if (bluetoothGatt != null) {
bluetoothGatt.discoverServices();
}
}
});
//#TODO Store a reference to BluetoothGatt
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
Log.i(TAG, "Succesfully disconected from Service $deviceAddress");
gatt.close();
} else {
Log.i(TAG, "Error $status encountered for #deviceAAddress! Disconnecting");
gatt.close();
}
}
}
#Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
super.onServicesDiscovered(gatt, status);
Log.w(TAG, " Devices service " + gatt.getDevice().getName() + ": " + gatt.getServices().size());
Iterator<BluetoothGattService> iter = gatt.getServices().iterator();
while (iter.hasNext()) {
Log.w(TAG, "Found service: " + iter.next().getUuid());
}
}
}
At this point the onServicesDiscovered(BluetoothGatt gatt, int status) callback always has zero discovered services with status 129.
Status 129 is a generic error from what I have understand and I have no other info about it.
By scanning the same scale with Nordic semiconstructor nrfConnect app, the services of the scale are discovered as you can see at the screenshot.
From the Nordic's app I can see that the service is "49535343-fe7d-4ae5-8fa9-9fafd205e455" and the characteristic for the weight is "49535343-1e4d-4bd9-ba61-23c647249616"
Is it possible that I have to use password to read the services (I a have given a password but I don't know how to use it).
Any help is appreciated.
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 working with BLE connection in my app. I have a single class for the bluetooth functionalities and I am passing a command from a different fragment class for writing any value.
So based on the scenario, inside the fragment, on click of a button will send a write command to the bluetooth class. Its working fine in the first time and I am getting the response. But while clicking the button the button for the second time, onCharacteristicChanged() is called twice and the third time click makes it being called thrice and so on. I genuinely can't figure it out. I will post my code below. Please have a look. In case of any queries, please do let me know. Thanks in advance.
I am writing data inside the OnDescriptorWrite() where as recieving data inside
onCharacteristicChanged().
Inside the fragment:
rvw_availableList.addOnItemTouchListener((
new RecyclerItemClickListener(myContext, new RecyclerItemClickListener.OnItemClickListener() {
#Override
public void onItemClick(View view, int position)
{
BluetoothDevice bl_device = al_deviceList.get(position);
bleUtil.writeData(bl_device,"3AxxxxxxD");
}
})
));
Now inside the writeData() of BleUtil Class:
public void writeData(BluetoothDevice bluetoothDevice, final String value)
{
BluetoothGattCallback gattCallback =
new BluetoothGattCallback() {
#Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
gatt.discoverServices();
}
#Override
public void onServicesDiscovered(final BluetoothGatt gatt, final int status) {
activity.runOnUiThread(new Runnable() {
public void run() {
// prgd_progress.HideProgressDialog();
Log.d("onServicesDiscovered", "Service uuid ");
List<BluetoothGattService> gattServices = gatt.getServices();
Log.d("onServicesDiscovered", "Services count: "+gattServices.size());
for (BluetoothGattService gattService: gattServices)
{
Log.d("aniservice",gattService.getUuid().toString());
}
if (status == BluetoothGatt.GATT_SUCCESS) {
ArrayList<String> alst_uuid = new ArrayList<String>();
BluetoothGattCharacteristic characteristic =
gatt.getService(UUID.fromString(SERVICE_ID)).getCharacteristics().get(0);
Log.d("anicheck",characteristic.getUuid().toString());
Log.d("anicheck",characteristic.getDescriptors().get(0).getUuid().toString());
// BluetoothGattCharacteristic characteristic =
// gattServices.get(0).getCharacteristics().get(0);
// Log.d("foundoutchar",gattServices.get(0).getUuid()+" "+gattServices.get(0).getCharacteristics().get(0).getUuid()+"");
gatt.setCharacteristicNotification(characteristic,true);
for (BluetoothGattDescriptor descriptor:characteristic.getDescriptors()){
Log.e("anicheck", "BluetoothGattDescriptor: "+descriptor.getUuid().toString());
}
final BluetoothGattDescriptor descriptor = characteristic.getDescriptor(UUID.fromString("00002902-0000-1000-8000-00805f9b34fb"));
if(descriptor!= null)
{
descriptor.setValue(
BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
gatt.writeDescriptor(descriptor);
}
else
{
Toast.makeText(activity,"nullval", Toast.LENGTH_SHORT).show();
}
// Log.d("foundoutchar", descriptor.getUuid().toString());
}
}
});
}
#Override
public void onCharacteristicWrite(final BluetoothGatt gatt, final BluetoothGattCharacteristic characteristic, final int status) {
super.onCharacteristicWrite(gatt, characteristic, status);
activity.runOnUiThread(new Runnable() {
public void run()
{
}
});
}
#Override
public void onCharacteristicRead(BluetoothGatt gatt, final BluetoothGattCharacteristic characteristic, int status)
{
Log.d("onCharacteristicread",characteristic.getValue().toString());
Log.d("onCharacteristicread","called");
}
#Override
public void onCharacteristicChanged(BluetoothGatt gatt, final BluetoothGattCharacteristic characteristic) {
super.onCharacteristicChanged(gatt, characteristic);
byte[] charValue = characteristic.getValue();
final String str_result = bytesToHex(charValue);
Log.d("onCharacteristicfullres",str_result);
final Intent intent = new Intent("ble_data"); //FILTER is a string to identify this intent
intent.putExtra("val", "getdeviceinfo");
intent.putExtra("data", str_result);
LocalBroadcastManager.getInstance(context).sendBroadcast(intent);
activity.runOnUiThread(new Runnable() {
public void run()
{
// byte[] charValue = characteristic.getValue();
// String str_result = bytesToHex(charValue);
// Log.d("onCharacteristicfullres",str_result);
//Toast.makeText(activity, "On char changed "+str_result, Toast.LENGTH_SHORT).show();
}
});
}
#Override
public void onDescriptorWrite(final BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status)
{
activity.runOnUiThread(new Runnable() {
public void run()
{
Log.d("oncharadesc","abcd");
// prgd_progress.HideProgressDialog();
BluetoothGattService service = gatt.getService(UUID.fromString(SERVICE_ID));
for (BluetoothGattCharacteristic characteristics: service.getCharacteristics())
{
Log.d("getit",characteristics.getUuid().toString());
}
final BluetoothGattCharacteristic characteristic =
gatt.getService(UUID.fromString(SERVICE_ID)).getCharacteristics().get(0);
byte[] byt_arr;
byt_arr = hexStringToByteArray(value);
characteristic.setValue(byt_arr);
gatt.writeCharacteristic(characteristic);
}
});
}
};
BluetoothGatt bluetoothGatt = bluetoothDevice.connectGatt(activity, true, gattCallback);
}
It's because you call connectGatt multiple times. Every time you call connectGatt you create a GATT client object which listens to notifications. So after three presses you will have three GATT clients that all handles each notification.
You should change the code so that you use the previously created GATT client when you write your data.
Firstly as mentioned in other answers you should not create multiple instances of BluetoothGattCallback just implement those in a way that they exists as single entity for every device for example holding those objects in HashMap or things like that. I also would like to add check if you receive buzy state from ble. It happens on some ble's that they notify twice for single write first response represents buzy state and other gives us the data, well depends on device to device. Thus, kindly perform some checks on ble's behaviour too.
I agree with Emil. Try to establish a connection first, and if the connection is successful, try writing something to the characteristic or descriptor. Also note that in the method writeData() the BluetoothGattCallback is constantly created, it needs to be created only once for each connected device and the caching of the result of the onServicesDiscovered() method so as not to cause it constantly.
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
}
}
}