BLE2 Library additional functions

wes58

Active Member
Licensed User
Longtime User
I have started recently playing with a BLE devices. When I wrote B4A app to connect to them, I found out that I couldn't connect to the device. I have wasted 2 days, thinking that the problem is the device. I have modified firmware of the device, flashed to the device but not luck. Then I realised, that since I can connect with other application like "nRF Connect" and "BLE Scanner", the problem must be my B4A application. I have described the problem and a solution in the thread here: https://www.b4x.com/android/forum/threads/solved-ble-connection-problems.133142/.

So to fix the problem I had to add an additional function that used BTtransport parameter. This is a java function:
Java:
    /**
     * Connects to a device with the given id. You can only connect to previously discovered devices.
     * Note that the Disconnected event will be raised if the connection has failed.
     * AutoConnect - disable/enable auto connect
     * BtTransport -  AUTO 0, BR_EDR 1, LE 2
     */
    public void Connect3(String DeviceId, boolean AutoConnect, int BtTransport) {
        charsToReadQueue.clear();
        BluetoothDevice bd = this.devices.get(DeviceId);
        int Transport = BtTransport;
        if(Transport > 2){
            Transport = 0;
        }
        if (bd == null)
            throw new RuntimeException("MacAddress not found. Make sure to call Scan before trying to connect.");
        bd.connectGatt(BA.applicationContext, AutoConnect, new GattCallback(), Transport);
    }

This fixed the problem.
Next problem (maybe not a problem, but something I didn't like), was, that to connect to BLE device, you had to scan to find the device first, then connect to the found device. This takes time - depending on the scan process.
So I wanted to connect to a specific device (with a known MAC address) without scanning.

So here is another java function that allows you to connect to the paired device with a MAC Address. Note, the device has to be paired with the phone!!!
Java:
    /**
     * Connects to a device with a paired device with the given id.
     * AutoConnect - disable/enable auto connect
     * BtTransport -  AUTO 0, BR_EDR 1, LE 2
     */
    public void ConnectToAddress(String DeviceId, boolean AutoConnect, int BtTransport){
        BluetoothDevice device = blueAdapter.getRemoteDevice(DeviceId);
        BluetoothDevice bd = this.devices.get(DeviceId);
        int Transport = BtTransport;
        if(Transport > 2){
            Transport = 0;
        }
        int bs = device.getBondState();
        if(bs == BluetoothDevice.BOND_BONDED){
            device.connectGatt(BA.applicationContext, AutoConnect, new GattCallback(), Transport);
        }
        else{
            String sMac = device.getAddress();
            Toast.makeText(BA.applicationContext, "Device " + sMac + " not Bonded", Toast.LENGTH_SHORT).show();
         }
     }

I have posted those 2 functions in the "Wish forum", but it doesn't look like anything will happen, so I decided to post the guide how to upgrade the BLE2 library.

1. Thanks to Erel, you can download the library source code (of all internal libraries) here: https://github.com/AnywhereSoftware/B4A
2. Then, on your PC, go to the Libs_BLE2 folder and navigate through subfolders, until you find BleManager2.java
3. Open the file with any text editor and add the 2 functions above.
4. Add to the import section in the file:
Java:
import android.widget.Toast;
5. You can modify the library version number - line 54 in this file:
Java:
@Version(1.39f)
6. Download SCL library compiler here: https://www.b4x.com/android/forum/t...uild-libraries-without-eclipse.29918/#content
7. Run LibraryCompiler.exe.
8. In "Project Folder" - click Browse, and select Libs_BLE2 folder
9. Library Name - give the name for the library that you want to have. Note: BLE2 library is an internal library, so you want to give modified library a different name.
10. Click Compile - and the compiled library will be put in your external libraries folder. This will also create an XML file for the library.

In your B4A app you can call those functions, for example like this:
B4X:
Sub ConnectDevAddress(Name As String, Id As String)
    Log("name " & Name & ", Id " & Id)
    ConnectedName = Name
    manager.ConnectToAddress(Id, True, 2) 'btTransport parameter Auto = 0
End Sub

Sub ConnectDevice(Name As String, Id As String)
    Log("name " & Name & ", Id " & Id)
    ConnectedName = Name
    manager.StopScan
    Sleep(1000)
    manager.Connect3(Id, True, 2) 'btTransport parameter Auto = 0
End Sub
 

rabbitBUSH

Well-Known Member
Licensed User
So I wanted to connect to a specific device (with a known MAC address) without scanning.
I noticed somewhere mention of "bonding" or "binding" which seemed attached to MAC address. Is this in the quote the same thing? Because I think in my device there was a way to AT=configure it with a binding (though I am not sure which direction that is - ie. the App looks for the device MAC or the other way around (latter would not seem to make much sense though).
EDIT " Sorry I just read the code itself and it answers the question above

Will you make a new library as @Erel suggests?
 
Last edited:

MbedAndroid

Well-Known Member
Licensed User
Longtime User
here the compiled lib. I named it Ble3. Thanks @wes8 for his work.

The function for paired devices with already know Mac isnt always usable, as i discovered for example Polar HRM belt will change it's mac address every time you change the battery, or even when the battery is low (2.7v). Then you still need to reseach the belt.
I'm now saving Polars indent , which is a string with some hexadecimal number, (number is different for the random Mac)
 

Attachments

  • ble3.jar
    11.3 KB · Views: 855
  • ble3.xml
    11 KB · Views: 796

rabbitBUSH

Well-Known Member
Licensed User
change it's mac address every time you change the battery,
Oi - that's unfriendly. Fortunately this thing is keeping the same MAC.

which is a string with some hexadecimal number
what do you think that "setting" is called? not sure if that is the correct term - but in the Nordic nRF Connect app there are various many lines of settings. maybe you can find it in there and then you will know what or why its used to connect and discrover services.
 

rabbitBUSH

Well-Known Member
Licensed User
I saw this in the extended log :
onClientConnectionState() - status=133 clientIf=12 device=50:65:83:6F:92:15

that -133 seems / might relate to GATT as in GATT error 133.

found a long [heated] discussion about this - is been an ongoing thing for a long time apparently - GATT error code -133 - whole bunch of suggestions most of which i don't have the skills to tackle.

EDIT -> as I thought in an earlier posting -133 relates to (among other things :

When a connection times out, you will receive a connection update with status code 133. This is not the official error code for a connection timeout though .
from in this thread over there - - -
 
Last edited:

MbedAndroid

Well-Known Member
Licensed User
Longtime User
what do you think that "setting" is called? not sure if that is the correct term - but in the Nordic nRF Connect app there are various many lines of settings. maybe you can find it in there and then you will know what or why its used to connect and discrover services.
not sure what you mean by this. I was interested in this modification as it could speed up the connection by using the mac. But then it turns out that for Polar this is useless. Comparing with other app's they also dont use the Mac, but i guess only the ident (for example: Polar H9 73D4062F).
Previous i got a list with many Polar Devices saved, all with different Mac's, still the same HRM belt. Now it works with 1 saved device in the list of saved devices.
But still i need to start the BLE manages with a search, which gives some delay. Other app's find the HRM belt much faster, so there should be another way to speed up the process..
 

wes58

Active Member
Licensed User
Longtime User
not sure what you mean by this. I was interested in this modification as it could speed up the connection by using the mac. But then it turns out that for Polar this is useless. Comparing with other app's they also dont use the Mac, but i guess only the ident (for example: Polar H9 73D4062F).
Previous i got a list with many Polar Devices saved, all with different Mac's, still the same HRM belt. Now it works with 1 saved device in the list of saved devices.
But still i need to start the BLE manages with a search, which gives some delay. Other app's find the HRM belt much faster, so there should be another way to speed up the process..
Is you Polar device paired with the phone? Connected with Mac Address only works for paired devices - i.e. you can see them on the phone bluetooth paired devices list. Otherwise you have to do a scan.
 

rabbitBUSH

Well-Known Member
Licensed User
Is you Polar device paired with the phone?
Ja - its not a Polar device - and it is paired with the phone. The MAC is found an reported - no connection after that.
 

wes58

Active Member
Licensed User
Longtime User
To check, if you need to use the function that I used in post #1 to connect to the BLE device using additional parameter "Transport" equal to 2 (LE device only) you can
add this Java function to the BLE2 library source code:
Java:
    /** Return Device Type
     * 0 - Bluetooth device type, Unknown
     * 1 - Bluetooth device type, Classic - BR/EDR devices
     * 2 - Bluetooth device type, Low Energy - LE-only
     * 3 - Bluetooth device type, Dual Mode - BR/EDR/LE
     */
    public int getDeviceType(String DeviceId){
        BluetoothDevice device = blueAdapter.getRemoteDevice(DeviceId);
        int devType = device.getType();
        return devType;
    }
You can call it in B4A:
B4X:
Public Sub GetDevType(DeviceId As String) As Int
    Dim devType As Int = manager.getDeviceType(DeviceId)
    Return devType
End Sub

If you get device type = 3, meaning that device is Dual Mode, you should probably use the connectGatt() function with the Transport parameter = 2 (selected to LE device only) - functions in post #1
That's why I couldn't connect to my device (which is a dual mode device) using the connect function that was in original BLE2 library.

2. Another problem I had, was writing to BLE device, which I described there https://www.b4x.com/android/forum/t...untimeexception-error-writing-data-to.134118/
Application crashed when there was an error with writing to the device.
It happened to me in the following situation:
1. Device was paired with the phone using pincode - everything works fine.
2. After disabling security (pincode required for pairing) on the device. Device was still paired with the phone (which wasn't required!). I could connect to the device, but app crashed after 5 retries, when trying to write to the device.

What I found out was, that the app crashed because the function WriteData in BLE2 library, throws RuntimeException when it fails to write to the device.
Here is the function:
Java:
    /**
     * Writes the data to the specified characteristic.
     */
    public void WriteData(String Service, String Characteristic, byte[] Data) throws InterruptedException {
        BluetoothGattCharacteristic chr = getChar(getService(Service), Characteristic);
        chr.setValue(Data);
        int retries = 5;
        while (true) {
            if (!gatt.writeCharacteristic(chr)) {
                if (--retries <= 0) {
                    throw new RuntimeException("Error writing data to: " + Characteristic);
                }
            }
            else
                break;
            BA.Log("retries: " + retries);
            Thread.sleep(150 * (5 - retries));
        }
    }

There is also a second (callback) function that raises a "_writecomplete" event when the write is complete:
Java:
        @Override
        public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
            if (characteristic == null || characteristic.getUuid() == null) {
                return;
            }
            ba.raiseEventFromDifferentThread(BleManager2.this, null, 0, eventName + "_writecomplete", false,
                    new Object[] {characteristic.getUuid().toString(), status});
        }

I don't think that the application should crash when this happens. It should notify the user of failure to write.
So I have decided to modify the WriteData function (removing the RuntimeException) as follows:
Java:
    /**
     * Writes the data to the specified characteristic.
     */
    public void WriteData(String Service, String Characteristic, byte[] Data) throws InterruptedException {
        BluetoothGattCharacteristic chr = getChar(getService(Service), Characteristic);
        chr.setValue(Data);
        int retries = 5;
        while (true) {
            if (!gatt.writeCharacteristic(chr)) {
                if (--retries <= 0) {
                    ba.raiseEventFromDifferentThread(BleManager2.this, null, 0, eventName + "_writecomplete", false,
                            new Object[]{chr.getUuid().toString(), -1});        //status = -1 error
                    return;
                }
            }
            else
                break;
            BA.Log("retries: " + retries);
            Thread.sleep(150 * (5 - retries));
        }
    }
I added the same event as for write complete, but the returned status is set to -1 - indicating writing error.

In B4A you can check the status as follows:
B4X:
Sub Manager_writecomplete(Characteristic As String, status As Int)    'status = 0 GATT_SUCCESS
    Log("write " & Characteristic & " ,status = " & status)   'status = -1 error, status = 0 success
End Sub
If you think that you may need those functions, you can add them to the BLE2 library source code and re-compile the library as i described in post #1
 

Star-Dust

Expert
Licensed User
Longtime User
I have started recently playing with a BLE devices. When I wrote B4A app to connect to them, I found out that I couldn't connect to the device. I have wasted 2 days, thinking that the problem is the device. I have modified firmware of the device, flashed to the device but not luck. Then I realised, that since I can connect with other application like "nRF Connect" and "BLE Scanner", the problem must be my B4A application. I have described the problem and a solution in the thread here: https://www.b4x.com/android/forum/threads/solved-ble-connection-problems.133142/.

So to fix the problem I had to add an additional function that used BTtransport parameter. This is a java function:
Java:
    /**
     * Connects to a device with the given id. You can only connect to previously discovered devices.
     * Note that the Disconnected event will be raised if the connection has failed.
     * AutoConnect - disable/enable auto connect
     * BtTransport -  AUTO 0, BR_EDR 1, LE 2
     */
    public void Connect3(String DeviceId, boolean AutoConnect, int BtTransport) {
        charsToReadQueue.clear();
        BluetoothDevice bd = this.devices.get(DeviceId);
        int Transport = BtTransport;
        if(Transport > 2){
            Transport = 0;
        }
        if (bd == null)
            throw new RuntimeException("MacAddress not found. Make sure to call Scan before trying to connect.");
        bd.connectGatt(BA.applicationContext, AutoConnect, new GattCallback(), Transport);
    }

This fixed the problem.
Next problem (maybe not a problem, but something I didn't like), was, that to connect to BLE device, you had to scan to find the device first, then connect to the found device. This takes time - depending on the scan process.
So I wanted to connect to a specific device (with a known MAC address) without scanning.

So here is another java function that allows you to connect to the paired device with a MAC Address. Note, the device has to be paired with the phone!!!
Java:
    /**
     * Connects to a device with a paired device with the given id.
     * AutoConnect - disable/enable auto connect
     * BtTransport -  AUTO 0, BR_EDR 1, LE 2
     */
    public void ConnectToAddress(String DeviceId, boolean AutoConnect, int BtTransport){
        BluetoothDevice device = blueAdapter.getRemoteDevice(DeviceId);
        BluetoothDevice bd = this.devices.get(DeviceId);
        int Transport = BtTransport;
        if(Transport > 2){
            Transport = 0;
        }
        int bs = device.getBondState();
        if(bs == BluetoothDevice.BOND_BONDED){
            device.connectGatt(BA.applicationContext, AutoConnect, new GattCallback(), Transport);
        }
        else{
            String sMac = device.getAddress();
            Toast.makeText(BA.applicationContext, "Device " + sMac + " not Bonded", Toast.LENGTH_SHORT).show();
         }
     }

I have posted those 2 functions in the "Wish forum", but it doesn't look like anything will happen, so I decided to post the guide how to upgrade the BLE2 library.

1. Thanks to Erel, you can download the library source code (of all internal libraries) here: https://github.com/AnywhereSoftware/B4A
2. Then, on your PC, go to the Libs_BLE2 folder and navigate through subfolders, until you find BleManager2.java
3. Open the file with any text editor and add the 2 functions above.
4. Add to the import section in the file:
Java:
import android.widget.Toast;
5. You can modify the library version number - line 54 in this file:
Java:
@Version(1.39f)
6. Download SCL library compiler here: https://www.b4x.com/android/forum/t...uild-libraries-without-eclipse.29918/#content
7. Run LibraryCompiler.exe.
8. In "Project Folder" - click Browse, and select Libs_BLE2 folder
9. Library Name - give the name for the library that you want to have. Note: BLE2 library is an internal library, so you want to give modified library a different name.
10. Click Compile - and the compiled library will be put in your external libraries folder. This will also create an XML file for the library.

In your B4A app you can call those functions, for example like this:
B4X:
Sub ConnectDevAddress(Name As String, Id As String)
    Log("name " & Name & ", Id " & Id)
    ConnectedName = Name
    manager.ConnectToAddress(Id, True, 2) 'btTransport parameter Auto = 0
End Sub

Sub ConnectDevice(Name As String, Id As String)
    Log("name " & Name & ", Id " & Id)
    ConnectedName = Name
    manager.StopScan
    Sleep(1000)
    manager.Connect3(Id, True, 2) 'btTransport parameter Auto = 0
End Sub
here the compiled lib. I named it Ble3. Thanks @wes8 for his work.

The function for paired devices with already know Mac isnt always usable, as i discovered for example Polar HRM belt will change it's mac address every time you change the battery, or even when the battery is low (2.7v). Then you still need to reseach the belt.
I'm now saving Polars indent , which is a string with some hexadecimal number, (number is different for the random Mac)
I also had the same problem. Thanks for the proposed solution.

I suggest to insert it in the libraries section in the forum with the necessary documentation. I think it can be useful
 
Top