Unable to Read from BLE Device, Write is working (Android) - java

I am trying to read values from BLE device. Steps I followed:
I am able to discover the BLE device and connect to it.
I am able to find the required characteristic by parsing through the
services and get the GattCharacteristic.
I am able to write a value to the BLE device characteristic and that's
verified.
The properties for that characteristic that I am trying to read the
value from are: READ, WRITE and INDICATE/NOTIFY.
The functions I am using for read and write are as follows:
a) Read function:
public void readCharacteristic(BluetoothGattCharacteristic characteristic)
{
if (mBluetoothAdapter == null || mBluetoothGatt == null) {
Log.w(TAG, "BluetoothAdapter not initialized");
return;
}
boolean status;
Log.w(TAG, "SCBABLe: Writing BLuetooth");
status=mBluetoothGatt.readCharacteristic(characteristic);
if(status) {
Log.w(TAG, "Read Successfully");
}
else
{
Log.w(TAG, "Read Unsuccessfully");
}
}
b) Write function
public void writeCharacteristic(BluetoothGattCharacteristic characteristic)
{
if (mBluetoothAdapter == null || mBluetoothGatt == null)
{
Log.w(TAG, "BluetoothAdapter not initialized");
return;
}
boolean status;
Log.w(TAG, "SCBABLe: Writing BLuetooth");
status=mBluetoothGatt.writeCharacteristic(characteristic);
if(status) {
Log.w(TAG, "Write Successfully");
}
else
{
Log.w(TAG, "Write Unuccessfully");
}
}
I am calling the above two from a different activity, after I get the required characteristic(by matching the UUIDs).
//Current Activity Name: DeviceControlActivity
//BluetoothLeService is the other activity where Read and write are defined
private static final DeviceControlActivity holder1 = new DeviceControlActivity();
public BluetoothGattCharacteristic reqChar;
public BluetoothLeService reqService;
private BluetoothLeService mBluetoothLeService;
private void displayGattServices(List<BluetoothGattService> gattServices)
{
if (gattServices == null) return;
String uuid = null;
for (BluetoothGattService gattService : gattServices)
{
uuid = gattService.getUuid().toString();
// Loops through available Characteristics.
for (BluetoothGattCharacteristic gattCharacteristic : gattCharacteristics)
{
uuid = gattCharacteristic.getUuid().toString();
//SampleGattAttributes.UNKNOWN_CHARACTERISTIC is the hardcoded uuid against which i am checking
if((uuid.equals((SampleGattAttributes.UNKNOWN_CHARACTERISTIC))))
{
holder1.reqChar = gattCharacteristic;
holder1.reqService = mBluetoothLeService;
//Call for write
byte [] byte1= {0x01, 0x10};
holder1.reqChar.setValue(byte1);
holder1.reqService.writeCharacteristic(holder1.reqChar);
//Call for read
holder1.reqService.readCharacteristic(holder1.reqChar);
Result: Read function is returning false and write function is returning true so the value is getting written successfully for the required characteristic.(verified it)
Please, could anyone help and tell why the read is not getting executed? Why is it still returning false when it has Read as property and proper value defined?

The problem is in these few lines.
holder1.reqService.writeCharacteristic(holder1.reqChar);
//Call for read
holder1.reqService.readCharacteristic(holder1.reqChar);
After calling writeCharacteristic, if you do any extra read/write/etc. , the call will not be executed and return false. You have to wait for BluetoothGattCallback.onCharacteristicWrite before doing further operation.

In the Android BLE implementation, the gatt operation calls need to be queued so that only one operation (read, write, etc.) is in effect at a time. So for example, after gatt.readCharacteristic(characteristicX) is called, you need to wait for the gatt callbackBluetoothGattCallback.onCharacteristicRead() to indicate the read is finished. If you initiate a second gatt.readCharacteristic() operation before the previous one completes, the second one will fail (by returning false) This goes for all of the gatt.XXX() operations.
Its a little work, but I think the best solution is to create a command queue for all the gatt operations and run them one at a time. You can use the command pattern to accomplish this.

Related

(java, bluetooth-lowenergy, ble) how to get data from android device via BLE

i'm beginner of BLE(Bluetooth-LowEnergy). I wanted to get data from android device via BLE.
I've already read about characteristic in Google Document.
and i've already searched on google too.
my device didn't respond to my request byte code.
i think it's because i set wrong characteristics.
cus i think i didn't understand about characteristics perfectly.
does anybody help me to set right characteristics please ?
Here's custom Uuid(it's better to see added Images on the top.)
CUSTOM SERVICE
0783b03e-8535-b5a0-7140-a304d2495cb7
NOTIFY Uuid : 0783B03E-8535-B5A0-7140-A304D2495CB8
Read Uuid : 00002a19-0000-1000-8000-00805f9b34fb
Write Uuid : 0783b03e-8535-b5a0-7140-a304d2495cba
and Here's Uuid I set
public final UUID serviceUuid = UUID.fromString("0783b03e-8535-b5a0-7140-a304d2495cb7");
public final UUID notifyUuid = UUID.fromString("0783b03e-8535-b5a0-7140-a304d2495cb8");
public final UUID readUuid = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb");
public final UUID writeUuid = UUID.fromString("0783b03e-8535-b5a0-7140-a304d2495cba");
and Here's my code
BluetoothHandler.java
targetGattCharacteristic = targetGattService.getCharacteristic(Define.GetInstance().notifyUuid);
BluetoothGattCharacteristic readGattCharacteristic = targetGattService.getCharacteristic(Define.GetInstance().notifyUuid);
if (readGattCharacteristic != null) {
mBleService.setCharacteristicNotification(readGattCharacteristic, true);
} else {
callInterface();
return;
}
(BluetoothService.java)
public void setCharacteristicNotification(BluetoothGattCharacteristic characteristic,
boolean enabled) {
if (mBluetoothAdapter == null || mBluetoothGatt == null) {
Log.w(TAG, "BluetoothAdapter not initialized");
return;
}
mBluetoothGatt.setCharacteristicNotification(characteristic, enabled);
BluetoothGattDescriptor gD = new BluetoothGattDescriptor(UUID.fromString(Define.GetInstance().readUuid.toString()), BluetoothGattDescriptor.PERMISSION_WRITE | BluetoothGattDescriptor.PERMISSION_READ);
characteristic.addDescriptor(gD);
if (Define.GetInstance().notifyUuid.equals(characteristic.getUuid())) {
BluetoothGattDescriptor descriptor = characteristic.getDescriptor(
UUID.fromString(Define.GetInstance().readUuid.toString()));
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
mBluetoothGatt.writeDescriptor(descriptor);
}
}
i solved it by referring to this site.
https://medium.com/#avigezerit/bluetooth-low-energy-on-android-22bc7310387a

Not able to read characteristic value sent from one BLE Device to Other

I am working on Android Application that sends a String to suppose "Hello World" through BLE. When I scan from other BLE Scanner I got the Service and Characteristic name on the Scanner app but not able to read the actual value of Characteristic that is "Hello World".
Here are snippets of code:-
UUID
public static final String UUID_SENSOR_SERVICE = "0000180f-0000-1000-8000-00805f9b34fb";
public static final String UUID_SENSORS_LEVEL = "00002a19-0000-1000-8000-00805f9b34fb";
Creating BluetoothGattService
private BluetoothGattService createService() {
Log.d(TAG, "createService: ");
BluetoothGattService service = new BluetoothGattService(UUID.fromString(SimpleGattAtributes.UUID_SENSOR_SERVICE), SERVICE_TYPE_PRIMARY);
// Counter characteristic (read-only, supports subscriptions)
BluetoothGattCharacteristic data = new BluetoothGattCharacteristic(UUID.fromString(SimpleGattAtributes.UUID_SENSORS_LEVEL), PROPERTY_READ , PERMISSION_READ);
service.addCharacteristic(data);
return service;
}
Starting GattServer
private void startGattServer(Context mContext){
Log.d(TAG, "startGattServer: ");
BluetoothGattServerCallback mGattServerCallback = new BluetoothGattServerCallback() {
#Override
public void onCharacteristicReadRequest(BluetoothDevice device, int requestId, int offset, BluetoothGattCharacteristic characteristic) {
if (SimpleGattAtributes.UUID_SENSORS_LEVEL.equals(characteristic.getUuid())) {
Log.d(TAG, "onCharacteristicReadRequest: I am here");
byte[] value = "HelloWorld".getBytes(Charset.forName("UTF-8"));
mGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, 0, value);
}
}
};
mGattServer = bluetoothManager.openGattServer(mContext, mGattServerCallback);
mGattServer.addService(createService());
}
After running the above code, when I open Scanner app, I got my application broadcasting BLE beacons. But When I try to read my characteristic value that is "Hellow World". I did not get it.
Testing on Android Phones
Trying to read the characteristic value
Can Anyone help me in that? I use logs and what I see is that BluetoothGattServerCallback is not triggering when I click on Read button. It must be triggered.
Thanks in advance.
I found the solution. Thanks to #davidgyoung
Using breakpoints I found that If condition in BluetoothGattServerCallback is evaluating to false always.
What I am doing wrong in if condition is I am comparing UUID with a String, that always evaluates to false.
I have to compare a UUID with a UUID.
Corrected if condition
if(UUID.fromString(SimpleGattAtributes.UUID_SENSORS_LEVEL).equals(characteristic.getUuid())){
// read logic here
}
I have checked and it works perfectly.

Waiting for a callback method to be called in Android

I am writing an android application which interacts with a sensor using bluetooth and obtains temperature values. I am doing this by calling connectGatt() which is asynchronous and calls a callback once the connection is established. The problem I am facing is that my code has to wait until the connection gets established.
This is the implementation of the method which is being used in the code below.
public boolean connect(final String address)
{
Log.v(LOG_TAG,"IN CONNECT METHOD"+Thread.currentThread().getName());
if(btadapter == null || address == null)
{
Log.v(LOG_TAG,"Unable to get Bluetooth Adapter or Address is not valid");
return false;
}
if(address != null && address.equals(btaddress) && btgatt != null)
{
Log.v(LOG_TAG,"Trying to connect to a bt gatt profile directly");
boolean result = btgatt.connect();
if(result)
return true;
return false;
}
btdevice = btadapter.getRemoteDevice(address);
if(btdevice == null)
{
Log.v(LOG_TAG,"Could not find device.");
return false;
}
btgatt = btdevice.connectGatt(this,false,btgattcallback);
Log.v(LOG_TAG,btgatt.toString());
btaddress = address;
Log.v(LOG_TAG,"Connecting to the device");
btConnectionState = STATE_CONNECTING;
return true;
}
Currently I could solve this problem by writing the following code in a handler thread as I don't want to block the UI thread while waiting for the callback. But I am not convinced with the approach.
if(mLocation != null)
{
connect(btaddress); // Method encapsulates calls to connectGatt method.
while (btConnectionState != STATE_CONNECTED) {
continue;
}
while (!services_discovered) {
continue;
}
//Other Code
I feel that there could be better ways of solving this but couldn't find any on the web. I saw a couple of answers using CountDownLatch and Semaphores but I didn't understand them clearly.
Can anyone help me with understanding how to handle situations like these?
Thank you.

Android Wait until Text to Speech OnInit is called

I had an issue where Text to Speech would not speak anything. I realised this was due to the fact that I was attempting to call 'Speak()' before TTS had initialised.
I need to wait until TTS has initialised, so that I can call 'Speak()' successfully. I thought doing something along the lines of this would work:
#Override
public void onInit(int status) {
if (status == TextToSpeech.SUCCESS) {
mTTSInitialised = true;
} else {
Log.e("TTS", "Initialisation Failed!");
}
}
...
while(!mTTSInitialised){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
But this fails to initialise at all. Is there a way to do this effectively?
The initialisation of the Text to Speech engine is asynchronous, which is why you realised you have to 'wait' for it to complete, before requesting that it processes an utterance.
Even when it eventually initialises successfully, it can be subsequently killed by the system, or it can of course fail to initialise, so you always need to be ready to handle a request to speak, where the engine isn't prepared.
Add the following helper class
public class PendingTTS {
private String pendingUtterance;
private int pendingQueueType;
public String getPendingUtterance() {
return this.pendingUtterance;
}
public void setPendingUtterance(#NonNull final String pendingUtterance) {
this.pendingUtterance = pendingUtterance;
}
public int getPendingQueueType() {
return this.pendingQueueType;
}
public void setPendingQueueType(final int pendingQueueType) {
this.pendingQueueType = pendingQueueType;
}
}
Assuming you're using an Activity, you need to declare the following variables:
private volatile PendingTTS pendingTTS;
private static final int MAX_INIT_ATTEMPTS = 4;
private volatile int initCount;
and initialise the Text to Speech object in onCreate()
tts = new TextToSpeech(YOURActivity.this, YOURonInitListener);
In your onInitListener you would check if there is any pending speech:
#Override
public void onInit(final int status) {
switch (status) {
case TextToSpeech.SUCCESS:
initCount = 0;
// Set up tts stuff
tts.setOnUtteranceProgressListener(YOURprogressListener);
if (pendingTTS != null) {
// We have pending speech, process it and check the result
int speechResult = tts.speak(pendingTTS.getPendingUtterance(),pendingTTS.getPendingQueueType(),
// remaining tts variables here)
switch (speechResult){
case TextToSpeech.SUCCESS:
// Result was successful
pendingTTS = null;
break;
case TextToSpeech.ERROR:
// Speech failed
// Check if it has repeatedly failed up to the max attempts
if(initCount < MAX_INIT_ATTEMPTS){
initCount ++;
tts = new TextToSpeech(YOURActivity.this, YOURonInitListener);
} else {
// Totally broken - let the user know it's not working
}
break;
}
} else {
// there was nothing to process
}
break;
case TextToSpeech.ERROR:
// Check if it has repeatedly failed up to the max attempts
if(initCount < MAX_INIT_ATTEMPTS){
initCount ++;
tts = new TextToSpeech(YOURActivity.this, YOURonInitListener);
} else {
// Totally broken - let the user know it's not working
}
break;
}
I've glued the above together from my code - where the speech and initialisation methods are all separated, but I tried to give you an overview above of everything you need to handle.
Elsewhere in your code, when you make a tts.speak(//stuff here) request, you need to check the result as demonstrated above, to make sure it was successful. Again, in my code, this is separated into one single method. If it does fail, you need to set the PendingTTS parameters prior to attempting to initialise again:
pendingTTS = new PendingTTS();
pendingTTS.setPendingQueueType(// your queue type);
pendingTTS.setPendingUtterance(// your utterance);
It is is successful, make sure pendingTTS is set to null.
The overall design is that if the initialisation failed, it will attempt to initialise again, up to the maximum allowed attempts. If the speech fails, it will attempt to initialise the engine again, firstly setting the PendingTTS parameters.
Hope you managed to follow that.
Hmm..
Not a very good idea.
You can try to add the text to the TTS queue and let it do it's work. This snippet can be inside button click, etc as:
tts.speak(toSpeak, TextToSpeech.QUEUE_ADD, null);
Small tutorial that would help.

readCharacteristic not returning data for custom attributes

When my code didn't work, I started with the project I found here and ran it against our custom bluetooth device on my moto-x. Against the general attributes with profiles, I get data back from the following code:
public void readCharacteristic(BluetoothGattCharacteristic characteristic) {
if (mBluetoothAdapter == null || mBluetoothGatt == null) {
Log.w(TAG, "BluetoothAdapter not initialized");
return;
}
mBluetoothGatt.readCharacteristic(characteristic);
}
which returns data asynchronously in:
public void onCharacteristicRead(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic,
int status) {
if (status == BluetoothGatt.GATT_SUCCESS) {
broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
}
}
This also works if i run this code against a heart rate monitor.
If I run it against one of our custom properties without a default profile, the data never comes back. Ever.
A google search included this: Cannot read characteristic. Android BLE but setting up notifications did not solve my problem.
Any advice?
I think the PROPERTY_READ, PROPERTY_WRITE isn't set to enabled in your custom properties.
Check for BluetoothGattCharacteristic PROPERTIES -I didn't realize that need to enable the PROPERTY_WRITE, PROPERTY_READ on the BLE hardware and that wasted a lot of time.
The part where to check the PROPERTY should be something like:
if ((characteristic.getProperties() &&
BluetoothGattCharacteristic.PROPERTY_READ)> 0) {
// toast or log here
}
These enable/disable the app or for that matter any app to connect to the bluetooth low energy device
Refer servicesListClickListner on this page

Categories

Resources