1

I try to communicate with an µ-controller through an android device via BLE. I am able to write my custom characteristic (toggle an LED on my Dev-Kit) but can't read the value of the LED status ('0x00' for off, '0x01' for on).

I want to read it when i click on the ExpandableListView when i click on an item. For the moment i implemented it within the onChildClickListener. If a characteristic-permission "PROPERTY_WRITE" > 0 then it should write the value.

private final ExpandableListView.OnChildClickListener servicesListClickListner =
            new ExpandableListView.OnChildClickListener() {
                @Override
                public boolean onChildClick(ExpandableListView parent, View v, int groupPosition,
                                            int childPosition, long id) {
                    if (mGattCharacteristics != null) {
                        final BluetoothGattCharacteristic characteristic =
                                mGattCharacteristics.get(groupPosition).get(childPosition);
                        final int charaProp = characteristic.getProperties();
                        if ((charaProp | BluetoothGattCharacteristic.PROPERTY_READ) > 0) {
                            // If there is an active notification on a characteristic, clear
                            // it first so it doesn't update the data field on the user interface.
                            if (mNotifyCharacteristic != null) {
                                mBluetoothLeService.setCharacteristicNotification(
                                        mNotifyCharacteristic, false);
                                mNotifyCharacteristic = null;
                            }
                            mBluetoothLeService.readCharacteristic(characteristic);
                        }
                        if ((charaProp | BluetoothGattCharacteristic.PROPERTY_NOTIFY) > 0) {
                            mNotifyCharacteristic = characteristic;
                            mBluetoothLeService.setCharacteristicNotification(
                                    characteristic, true);
                        }

                        if ((charaProp | BluetoothGattCharacteristic.PROPERTY_WRITE) > 0) {         

                            mBluetoothLeService.readCustomCharacteristic();
                            mBluetoothLeService.writeCustomCharacteristic(0x01);


                        }
                        return true;
                    }
                    return false;
                } 

But it always fails to read the characteristic. The LED value is written and the LED switches to 'ON' but it doesn't read the value of the LED. I want to read the value for switching the LED on/off with clicking on the characteristic in my list.

I can't figure out what i'm doing wrong. It should be reading the characteristic, write it in a textfield. On my BLE-Device i have enabled reading. I can read the value with my ubuntu-terminal using gatttool -> char-read-hnd [hnd] [val].

This is my implementation of the readCustomCharacteristic() in mBluetoothLeService

public void readCustomCharacteristic() {
        if (mBluetoothAdapter == null || mBluetoothGatt == null) {
            Log.w(TAG, "BluetoothAdapter not initialized");
            return;
        }
        /*check if the service is available on the device*/
        BluetoothGattService mCustomService = mBluetoothGatt.getService(UUID.fromString("edfec62e-9910-0bac-5241-d8bda6932a2f"));
        if(mCustomService == null){
            Log.w(TAG, "Custom BLE Service not found");
            return;
        }
        /*get the read characteristic from the service*/
        BluetoothGattCharacteristic mReadCharacteristic = mCustomService.getCharacteristic(UUID.fromString("18192021-2223-2425-2627-282930313233"));
        mBluetoothGatt.readCharacteristic(mReadCharacteristic);

        if(mBluetoothGatt.readCharacteristic(mReadCharacteristic) == false){
            Log.w(TAG, "Failed to read characteristic");
        }
        return;
    }
Ian
  • 147
  • 1
  • 13
  • Why do you execute mBluetoothGatt.readCharacteristic(mReadCharacteristic) twice? – Emil Aug 30 '16 at 19:03
  • Where do i execute it twice? I execute it for the execution and i need the confirmation if it was successfull for printing "Failed to read characteristic" in logcat. Is there a way to save the state in a variable? But it shoudln't be wrong if i read it twice.. It should only do it twice and update my data via callback and the broadcastUpdate method. – Ian Aug 30 '16 at 19:07
  • mBluetoothGatt.readCharacteristic(mReadCharacteristic); if(mBluetoothGatt.readCharacteristic(mReadCharacteristic) == false){ Log.w(TAG, "Failed to read characteristic"); } – Emil Aug 30 '16 at 21:48
  • You first have it on one line and then in an if-expression. The if-expression will probably always fail since there can only be one outstanding GATT operation at a time. – Emil Aug 30 '16 at 21:49
  • What do you mean with "fail" ? It prints to the log so in my understanding the if-expression is working fine. so "false == false" .. how can i put the state in a variable? – Ian Aug 31 '16 at 10:24

1 Answers1

0

It looks like what you're probably missing is setting the descriptor to Notify/indicate. It's a pain that we have to do this manually (iOS doesn't), but I guess these are the tools we're given. Here's a little snippet that might help:

BluetoothGattDescriptor descriptor = characteristic.getDescriptor(DESCRIPTOR_CONFIG_UUID);
bleGatt.setCharacteristicNotification(characteristic, true);
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
bleGatt.writeDescriptor(descriptor);

Depending on your peripheral, you might have to set the desciptor value to ENABLE_INDICATION_VALUE instead. If you don't know the descriptor UUID, you can do

characteristic.getDescriptors()

to get a list of the available descriptors.

After writing the descriptor, you should get a call to onDescriptorWrite in your gatt callback. If everything works correctly, and future characteristic changes should call the onCharacteristicChanged method in your gatt callback.

Hope this helps!

spurrkins
  • 126
  • 2
  • 11
  • Your statement is wrong. You have to turn on notification or indication on BLE device in order to use it. iOS does not turn it on automatically, although device can have it turned on by firmware design. What is ```toBeNotified``` in the source code sample that you have provided? – Darko Djuric Aug 30 '16 at 15:39
  • Thanks for your answer! In the code for my peripheral i have set an individual UUID for my service and an UUID for my characteristic (128bit). I don't have set an notification descriptor or something like that. I used an example and followed the tutorial provided by the manufacturer of my device and ther is no characteristic configuration descriptor. But i even can't read out the predifined "custom services and characteristics" with an descriptor. The permission for this charac is read/write/write_no_response. The standard example code from android developers doesn't work either. – Ian Aug 30 '16 at 16:01
  • I only know about iOS from what a coworker told me, sorry if I was mistaken. It's true that it does need to be enabled on the firmware level. Unfortunately Android doesn't seem to care that it's already been enabled at the hardware level. I was converting my code to a more generalized snippet and I missed that variable. I have since fixed it. – spurrkins Aug 30 '16 at 16:13
  • @Smokehead, that's interesting. When I ran into this issue, I was working with an in-house piece of hardware where I couldn't find a descriptor UUID. So, I did the characteristic.getDescriptors piece of code. If I were you, in your second snippet of code I would insert `mReadCharacteristic.getDescriptors()` and print out the resulting list to see what descriptors are available. – spurrkins Aug 30 '16 at 16:17
  • How can i print them..? It's a `List` how can i print them out or get them visible in logcat? – Ian Aug 30 '16 at 16:49
  • Use a for each loop. Do `for(BluetoothGattDescriptor desc : mReadCharacteristic.getDescriptors()){ Log.d("Descriptor", desc.getUUID().toString()) }` This should print the UUIDs. There will (probably) only be one. Then you can change your code to get that descriptor and set it to notify/indicate. – spurrkins Aug 30 '16 at 17:16
  • Upon further inspection of your original question (which I should have done in the first place), it seems your original goal is a little different from what I have explained how to do.You may have some problems because it takes time for your peripheral to receive your command, process it, and send a response, so you're probably reading the old value when you tell it to read. I would still suggest continuing with what I explained. Then, in your gatt callback, you will receive a notification when the peripheral responds. Then, make the callback do whatever changes you want on the android side. – spurrkins Aug 30 '16 at 17:28
  • At first, thanks for your help! But it is always displayed "failed to read characteristic" in logcat so i think it can't even read the old value. For the `desc.getUUID().toString())` part i can't build my project because it says: "cannot resolve method "getUUID()" " Also i don't understand why the characteristics can't be read by the code published by developers.android.com . Shoulnd't this read method work? It displays the services and characteristics but it doesn't read or write anything. [link](https://developer.android.com/guide/topics/connectivity/bluetooth-le.html) – Ian Aug 30 '16 at 17:49
  • The method is actually `getUuid()`. Are you not using Android Studio? It should be suggesting methods as you type. – spurrkins Aug 30 '16 at 17:54
  • I'm using Android Studio.. I saw it now.. It doesn't work either. I got 1 UUID for a descriptor for every characteristic i click on. even characteristic descriptors from other services' characteristics are still the same one. but it still does not read anything out. I think i try something else to do it. I can't see why it doesn't start working. I don't know why it does only read the official standard services like "Heart Rate Measurement" or "battery level" etc. Shouldn't the standard code from android-developers work for custom services too? – Ian Aug 30 '16 at 18:05
  • I'm not familiar enough with the firmware involved with BLE to say for sure. I've only approached the firmware from a relatively high level. In the firmware, when it receives an updated characteristic, what does it do? Does it write a different characteristic? Does it write it with a Notify or an Indicate property? – spurrkins Aug 30 '16 at 18:10
  • My Android app does write the value of the characteristic which i insert the UUID of. I didn't implement a write method for every characteristic which is writable. It can be written with and without response. I also use my terminal to conenct to the device and see if i can find read/write characteristics. In my terminal i can write and read the characteristic and my logic from the device works too (LED switches to on if i write 0x01 and switches to off when i write 0x00). It even works with other commercial BLE-Scanner apps. But i want to write my own BLE app for reading out sensor data. – Ian Aug 30 '16 at 18:59
  • I'm still trying to understand what's happening here. You write one characteristic to the peripheral. Does the peripheral then update a different characteristic on its own? Or are you trying to read the characteristic you just wrote? – spurrkins Aug 30 '16 at 19:33
  • I want to write the characteristic to turn on an LED on my device. After that i want to read the same charachteristic value. I want to check if it is 0x00 or 0x01 for "LED on" or "LED off" to write either "0x01" or "0x00". By now i can only switch it on becaus i can't check if the value in this specific characteristic is 0x01 or 0x00. My first attempt writes 0x01. But i don't have implemented `if (char_value == 0x00)` or `if (char_value == 0x01)` . Befor i do that i wanted to know if i can read the value and how i can store it into the variable `char_value` . – Ian Aug 30 '16 at 19:56
  • After you get the characteristic from the service, try doing `characteristic.getValue()` – spurrkins Aug 30 '16 at 20:05
  • Tried that but .getValue() returns the type byte[] and i couldn't transform it to my hex- or string-value. When i printed that to logcat it gave me an 8 char long string `toString(mReadCharacteristic.getValue())`. `getStringValue`crashed the whole app also `getIntValue` did the same. – Ian Aug 30 '16 at 20:08
  • Why couldn't you transform it? It's supposed to be a byte array. When you write a characteristic, you're making a byte array and setting a value to work as a command for the peripheral right? If you're reading the same characteristic, you should get the value and read the same byte you set in the command. – spurrkins Aug 30 '16 at 20:13
  • I have written 0x01 as value. As far as i understand these are two times 16 bit -> 32 bit -> 4 byte. But with `getValue()` i can print an 8 char long string in logcat. How can this be my written value? Is it my written value in another format? Is it my value at all or did i get another value or even another random value from memory without connection to any characteristics or services? – Ian Aug 30 '16 at 20:21
  • 0x01 is a 1 byte command. Each hex character is 4 bits. Your characteristic should have a width associated with it that is the number of bytes in the value. You can still write a single byte to this and just have it take up the least significant byte. – spurrkins Aug 30 '16 at 20:36
  • My fault.. for sure 4 bits in one hex-character. So you are trying to say that i only have to look for the last bit in the bytearray i get from `getValue()` ? – Ian Aug 30 '16 at 20:41
  • Either the first or the last. If I were you, I'd look at each byte in the byte array to see what's in there. You can iterate through the byte array and do `String.format("%02x ", b)` where `b` is a specific byte in the array. This will convert it to hex. – spurrkins Aug 30 '16 at 20:45
  • None of the characters in my string have the value 0x01 or 0x00 .. This can't be the right value.. – Ian Aug 31 '16 at 07:58
  • In your code, are you reading this value immediately after writing it? Because it takes time for the characteristic to update. You'll know that a reliable write is in progress when the onCharacteristicWrite callback is called. – spurrkins Aug 31 '16 at 15:15
  • No at first i'm reading it then i write. For testing writing isn't even included. I tried writing with Ubuntu Terminal with HCITool. I just want android to read the value at the moment. But i found out, i can't read out the descriptor. I found it, but can't read it. For now my reading works a bit better. I don't know what happened but i can read values from custom services with the standard function. But now the writing process fails. I have to check what has changed to find out what happens. But thanks to you, i thought about descriptors a bit more and found out some interesting things.. – Ian Sep 01 '16 at 07:08
  • Emil's comment above is also correct. You should do `boolean status = gatt.readCharacteristic`, then deal with that. readCharacteristic is also an asynchronous operation, I'm not sure if that could also be tripping you up. When you call readCharacteristic, it will call your callback onCharacteristicRead callback as soon as it's done. – spurrkins Sep 01 '16 at 16:33