At work we have a device that updates a custom characteristic in a custom service every few seconds and uses BLE. All I want to do is monitor those updates. My objective here is to write the bare minimum of code in order to connect to that device, then to the service and then monitor its characteristic. However I can't connect to the device.
Every time a bluetooth device is found by scanning this function is called:
void checkDevice(BluetoothDevice device){
if (targetFound) return;
if (device.getAddress().equals(DEVICE_ADDRESS)){
logger.append("-> Target device found with name: " + device.getName() + "\n");
targetFound = true;
targetDevice = device;
bleAdapter.stopLeScan(new BluetoothAdapter.LeScanCallback() {
#Override
public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord) {
logger.append("-> Scan has stopped\n");
}
});
logger.append("-> Attempting to connect to target device\n");
gattConnection = targetDevice.connectGatt(this,false,gattCallBackFuntion);
}
}
This is my implementation of the gattCallBackFunction:
private BluetoothGattCallback gattCallBackFuntion = new BluetoothGattCallback() {
#Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
super.onConnectionStateChange(gatt, status, newState);
if (newState == BluetoothProfile.STATE_CONNECTED) {
Boolean gattRes = gattConnection.discoverServices();
logger.append("-> Connected to GATT Server. Starting service discovery. Result: " + gattRes.toString() + "\n");
}
else{
logger.append("-> Connection state has changed but is " + newState);
}
}
#Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
super.onServicesDiscovered(gatt, status);
List<BluetoothGattService> services = gatt.getServices();
String list = "";
String tab = " ";
for (int i = 0; i < services.size(); i++){
BluetoothGattService service = services.get(i);
list = list + tab + service.toString() + "\n";
}
logger.append("-> Services Discovered: \n");
logger.append(list);
}
};
Both of these functions are implemented in the same class which is the only activity.
As I understand it at some point the function onConnectionStateChange must be invoked by Android indicating a connection to the Device. However this never happens. The app prints "Attempting to connect to target device" and nothign else happens
Any ideas?
Most of the Bluetooth gatt callbacks run inside a background thread hence you should not perform any UI operations inside the callback. You need to run all the operations on UI thread as per the Android UI guidelines
Related
I'm working on a mobile app that implements Bluetooth LE and communicates with a HC-08 device.
While everything is ok on iOS with CoreBluetooth, i encounter some problems on my Android version.
I start by getting a Bluetooth Device
BluetoothDevice bluetoothDevice;
After that, i connect to it:
bluetoothDevice.connectGatt(context, false, callback);
Which is calling a callback function inside which i wait for STATE_CONNECTED and discover services, then write to characteristic:
#Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
super.onConnectionStateChange(gatt, status, newState);
if(status == BluetoothGatt.GATT_SUCCESS){
Log.e("BLE_LOG", "onConnectionStateChange with success");
if(newState == BluetoothProfile.STATE_CONNECTED){
Log.e("BLE_LOG", "sucessfully connected to device: " + bluetoothDevice.getName() + "/" + bluetoothDevice.getAddress());
bluetoothGatt = gatt;
gatt.discoverServices();
}
else if(newState == BluetoothProfile.STATE_DISCONNECTED){
Log.e("BLE_LOG", "succesfully Disconnected from device: " + bluetoothDevice.getName() + "/" + bluetoothDevice.getAddress());
gatt.close();
}
}
else{
Log.e("BLE_LOG", "onConnectionStateChange with status: " + status + " and newState: " + newState);
}
}
#Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
super.onServicesDiscovered(gatt, status);
if(status == BluetoothGatt.GATT_SUCCESS){
Log.e("BLE_LOG", "sucessfully discovered services for device: " + bluetoothDevice.getName() + "/" + bluetoothDevice.getAddress());
service = bluetoothGatt.getService(UUID.fromString(BLEFunctions.PROPRIETARY_SERVICE));
BluetoothGattCharacteristic characteristic = service.getCharacteristic(UUID.fromString(BLEFunctions.PROPRIETARY_CHARACTERISTIC));
//Souscription notifications
if(bluetoothGatt.setCharacteristicNotification(characteristic, true)){
Log.e("BLE_LOG", "subscribed to notifications from characteristic");
}
characteristic.setValue(SET_ON);
characteristic.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT);
bluetoothGatt.writeCharacteristic(characteristic);
}
else{
Log.e("BLE_LOG", "Error discovering services for device: " + bluetoothDevice.getName() + "/" + bluetoothDevice.getAddress() + " with status: " + status);
}
}
When services are discovered, i'm writting to characteristic, which actually work because my bluetooth device is acting as expected (it switches on a relay), and onCharacteristicWrite is called:
#Override
public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
super.onCharacteristicWrite(gatt, characteristic, status);
if(status == BluetoothGatt.GATT_SUCCESS){
Log.e("BLE_LOG", "sucessfully written characteristic for device: " + bluetoothDevice.getName() + "/" + bluetoothDevice.getAddress());
}
else{
Log.e("BLE_LOG", "Error writing characteristic for device: " + bluetoothDevice.getName() + "/" + bluetoothDevice.getAddress() + " with status: " + status);
}
}
The problem is that, as soon as the characteristic is written, the device disconnect and i can't continue my process.
At a point in the program, i've got connectionStateChange with status 8 (which seems to be : GATT_INSUFFICIENT_AUTHORIZATION)
I investigated several potential causes, as :
-Maybe the characteristic needs bonding before writing? I know it is handled automatically on iOS for example.
-Maybe the device i use has some incompatibilities with Android (but it works well with software like 'LightBlue', 'BLE Scanner', etc...
Can't figure out what is wrong.
Please help me solve this problem
Adding the log to help:
2022-05-05 09:58:25.600 22204-22204/com.******.******E/BLE_LOG: clicked on device: Hall/B0:B1:13:76:0B:1E
2022-05-05 09:58:26.065 22204-22467/com.******.******E/BLE_LOG: onConnectionStateChange with success
2022-05-05 09:58:26.068 22204-22467/com.******.******E/BLE_LOG: sucessfully connected to device: AAA-000000000001001/B0:B1:13:76:0B:1E
2022-05-05 09:58:26.727 22204-22467/com.******.******E/BLE_LOG: sucessfully discovered services for device: AAA-000000000001001/B0:B1:13:76:0B:1E
2022-05-05 09:58:26.732 22204-22467/com.******.******E/BLE_LOG: subscribed to notifications from characteristic
2022-05-05 09:58:26.754 22204-22467/com.******.******E/BLE_LOG: sucessfully written characteristic for device: AAA-000000000001001/B0:B1:13:76:0B:1E
2022-05-05 09:58:31.854 22204-22467/com.******.******E/BLE_LOG: onConnectionStateChange with status: 8 and newState: 0
Using my device with software like nRF Connect or LightBlue works well. (no disconnection)
I made progress and managed to point out a reason why the problem occures.
Actually, i'm discovering services via
gatt.discoverServices();
and as soon as i get the callback method, i get the service and characteristic i need and write to it.
#Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
super.onServicesDiscovered(gatt, status);
if(status == BluetoothGatt.GATT_SUCCESS){
Log.e("BLE_LOG", "successfully discovered services");
}
BluetoothGattService service = gatt.getService(UUID.fromString("0000ffe0-0000-1000-8000-00805f9b34fb"));
Log.e("BLE_LOG", "got serivce with uuid: " + service.getUuid().toString());
bluetoothGatt = gatt;
characteristic = service.getCharacteristic(UUID.fromString("0000ffe1-0000-1000-8000-00805f9b34fb"));
Log.e("BLE_LOG", "got characteristic with uuid: " + characteristic.getUuid().toString());
byte[] data = {
0x24, 0x6F, 0x6E, 0x25
};
characteristic.setValue(data);
characteristic.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE);
bluetoothGatt.writeCharacteristic(characteristic);
}
When i deport the write part in another function, like a button function, it works well and doesn't disconnect.
public void clicked_open(View v){
byte[] data = {
0x24, 0x6F, 0x6E, 0x25
};
characteristic.setValue(data);
characteristic.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE);
bluetoothGatt.writeCharacteristic(characteristic);
}
It's as if something wasn't totally finished when i call my write as soon as i get the service discovered callback.
Maybe there is a way to ensure everything is ok before writing...
As you realized in your last edit, you are writing to the characteristic too soon. onServicesDiscovered is called multiple times, once for each found characteristic. You are already checking for the finished discovery and just need to move your writing operation there.
#Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
super.onServicesDiscovered(gatt, status);
BluetoothGattService service = gatt.getService(UUID.fromString("0000ffe0-0000-1000-8000-00805f9b34fb"));
Log.e("BLE_LOG", "got serivce with uuid: " + service.getUuid().toString());
if(status == BluetoothGatt.GATT_SUCCESS){
Log.e("BLE_LOG", "successfully discovered services");
bluetoothGatt = gatt;
characteristic = service.getCharacteristic(UUID.fromString("0000ffe1-0000-1000-8000-00805f9b34fb"));
Log.e("BLE_LOG", "got characteristic with uuid: " + characteristic.getUuid().toString());
byte[] data = {
0x24, 0x6F, 0x6E, 0x25
};
characteristic.setValue(data);
characteristic.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE);
bluetoothGatt.writeCharacteristic(characteristic);
}
}
The parameter status will only be GATT_SUCCESS after the discovery finished, see the documentation for more information.
A BLE Tracker Device tries to connect to my BLE Android app. I would like to know about the event when the device tries to connect to me (means before it actually gets connected to me) and in case the device fails to connect to me i would like to know the reason why device could not connect to me.
private BluetoothGattServerCallback mGattServerCallback = new BluetoothGattServerCallback() {
#Override
public void onConnectionStateChange(BluetoothDevice device, int status, int newState) {
Log.d(TAG, "onConnectionStateChange Status: " + status);
// logs += "\n\nonConnectionStateChange Status: " + status;
// updateHomeUI();
if (newState == BluetoothProfile.STATE_CONNECTED) {
connectedDeviceAddress = device.getAddress().toString();
Log.i(TAG, "BluetoothDevice CONNECTED:\n" + device);
FragmentManager fragmentManager = getSupportFragmentManager();
try {
logs += "\n\nBluetoothDevice CONNECTED: \n" + device;
updateHomeUI();
} catch (Exception e) {
e.printStackTrace();
}
mRegisteredDevices.add(device);
mHandler.post(new Runnable() {
#Override
public void run() {
Toast.makeText(MainActivity.this, "Device CONNECTED: " + connectedDeviceAddress, Toast.LENGTH_SHORT).show();
}
});
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
Log.i(TAG, "BluetoothDevice DISCONNECTED: " + device);
//Remove device from any active subscriptions
logs += "\n\nBluetooth Device DISCONNECTED:\n" + device;
updateHomeUI();
mRegisteredDevices.remove(device);
//restarting everything -- test
// bluetoothAdapter.disable();
// bluetoothAdapter.enable();
// startAdvertising();
// startServer();
mHandler.post(new Runnable() {
#Override
public void run() {
Toast.makeText(MainActivity.this, "Device DISCONNECTED", Toast.LENGTH_SHORT).show();
}
});
}
if (newState == BluetoothProfile.STATE_CONNECTING) {
Log.d(TAG, "Connecting...Status: " + status);
logs += "\n\nConnecting...Status: " + status;
updateHomeUI();
}
}
};
Above is the piece of code that i am using. You can see i have implemented the callback method onConnectionStateChange. I am also checking for the following values:
STATE_CONNECTED
STATE_DISCONNECTED
STATE_CONNECTING
Even though i am checking for all these, i realised that my code never gets inside STATE_CONNECTING check, thus i am not able to know when the device tries to connect to me and i cant find a way to know the error if in case the device fails to connect to my app.
Any help would be apprecicated.
For a BLE peripheral's point of view, there is no such state as the remote central is "connecting". There is just disconnected and suddenly connected. On the link layer, there is just one packet the central sends to the peripheral: "hey you're now connected", in response to an advertisement sent by the peripheral.
If you read Android's documentation at https://developer.android.com/reference/android/bluetooth/BluetoothGattServerCallback#onConnectionStateChange(android.bluetooth.BluetoothDevice,%20int,%20int), you will see that CONNECTING is not one of the possible states for this callback.
I'm trying to write an Android app that mimics functionality already present in an iOS app I wrote. I am interfacing with 2 different BLE devices:
Blood Pressure Cuff
Weight Scale
On iOS, I have both devices working well and reporting data. On Android, I can't get it to work. After hours of research and testing, I think the basic issue I'm trying to solve is this:
On iOS, I call the following code to enable the BLE device to notify my iOS device when it has data to report:
#pragma mark - CBPeripheralDelegate Protocol methods
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error {
for (CBCharacteristic *characteristic in [service characteristics]) {
[peripheral setNotifyValue:YES forCharacteristic:characteristic];
}
}
That's it. The notes for this method in iOS say the following:
If the specified characteristic is configured to allow both notifications and indications, calling this method enables notifications only.
Based on that (and the fact that it works in iOS), I'm figuring that the configuration descriptor for the characteristic for which I want notifications should be configured like this:
descriptor.setValue(BluetoothGattDescriptor.ENABLE_INDICATION_VALUE);
gatt.writeDescriptor(descriptor);
With that in mind, my BLEDevice class looks like this:
public abstract class BLEDevice {
protected BluetoothAdapter.LeScanCallback mLeScanCallback;
protected BluetoothGattCallback mBluetoothGattCallback;
protected byte[] mBytes;
protected Context mContext;
protected GotReadingCallback mGotReadingCallback;
protected String mDeviceName;
public final static UUID UUID_WEIGHT_SCALE_SERVICE
= UUID.fromString(GattAttributes.WEIGHT_SCALE_SERVICE);
public final static UUID UUID_WEIGHT_SCALE_READING_CHARACTERISTIC
= UUID.fromString(GattAttributes.WEIGHT_SCALE_READING_CHARACTERISTIC);
public final static UUID UUID_WEIGHT_SCALE_CONFIGURATION_CHARACTERISTIC
= UUID.fromString(GattAttributes.WEIGHT_SCALE_CONFIGURATION_CHARACTERISTIC);
public final static UUID UUID_WEIGHT_SCALE_CONFIGURATION_DESCRIPTOR
= UUID.fromString(GattAttributes.WEIGHT_SCALE_CONFIGURATION_DESCRIPTOR);
abstract void processReading();
interface GotReadingCallback {
void gotReading(Object reading);
}
public BLEDevice(Context context, String deviceName, GotReadingCallback gotReadingCallback) {
mContext = context;
BluetoothManager btManager = (BluetoothManager)mContext.getSystemService(Context.BLUETOOTH_SERVICE);
final BluetoothAdapter btAdapter = btManager.getAdapter();
if (btAdapter != null && !btAdapter.isEnabled()) {
Intent enableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
mContext.startActivity(enableIntent);
}
mDeviceName = deviceName;
mBluetoothGattCallback = new BluetoothGattCallback() {
#Override
public void onCharacteristicChanged(BluetoothGatt gatt, final BluetoothGattCharacteristic characteristic) {
byte[] data = characteristic.getValue();
mBytes = data;
Log.d("BluetoothGattCallback.onCharacteristicChanged", "data: " + data.toString());
}
#Override
public void onConnectionStateChange(final BluetoothGatt gatt, final int status, final int newState) {
// this will get called when a device connects or disconnects
if (newState == BluetoothProfile.STATE_CONNECTED) {
gatt.discoverServices();
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
if (mBytes != null) {
processReading();
}
}
}
#Override
public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
super.onDescriptorWrite(gatt, descriptor, status);
Log.d("onDescriptorWrite", "descriptor: " + descriptor.getUuid() + ". characteristic: " + descriptor.getCharacteristic().getUuid() + ". status: " + status);
}
#Override
public void onServicesDiscovered(final BluetoothGatt gatt, final int status) {
// this will get called after the client initiates a BluetoothGatt.discoverServices() call
BluetoothGattService service = gatt.getService(UUID_WEIGHT_SCALE_SERVICE);
if (service != null) {
BluetoothGattCharacteristic characteristic;
characteristic = service.getCharacteristic(UUID_WEIGHT_SCALE_READING_CHARACTERISTIC);
if (characteristic != null) {
gatt.setCharacteristicNotification(characteristic, true);
}
characteristic = service.getCharacteristic(UUID_WEIGHT_SCALE_CONFIGURATION_CHARACTERISTIC);
if (characteristic != null) {
BluetoothGattDescriptor descriptor = characteristic.getDescriptor(UUID_WEIGHT_SCALE_CONFIGURATION_DESCRIPTOR);
if (descriptor != null) {
descriptor.setValue(BluetoothGattDescriptor.ENABLE_INDICATION_VALUE);
gatt.writeDescriptor(descriptor);
}
}
}
}
};
mLeScanCallback = new BluetoothAdapter.LeScanCallback() {
#Override
public void onLeScan(final BluetoothDevice device, final int rssi, final byte[] scanRecord) {
Log.d("LeScanCallback", device.toString());
if (device.getName().contains("{Device Name}")) {
BluetoothGatt bluetoothGatt = device.connectGatt(mContext, false, mBluetoothGattCallback);
btAdapter.stopLeScan(mLeScanCallback);
}
}
};
btAdapter.startLeScan(mLeScanCallback);
}
}
NOTE: It might be important to know that these 2 devices function in the following way:
The BLE device is turned on an a measurement is initiated on the device.
Once the measurement has been taken, the BLE device attempts to initiate a BLE connection.
Once the BLE connection is made, the device pretty much immediately sends the data, sometimes sending a couple of data packets. (If previous data measurements haven't been successfully sent over BLE, it keeps them in memory and sends all of them, so I only really care about the final data packet.)
Once the final data packet is sent, the BLE device disconnects rapidly.
If the BLE device fails to send data (as is currently happening on the Android app), the BLE device disconnects pretty rapidly.
In my LogCat, I see a lot of output that's exactly like I'd expect.
I see a list of services like I expect, including the data service I want.
I see a list of characteristics like I expect, including the data characteristic I want.
I see a list of descriptors like I expect, including the "configuration" (0x2902) descriptor.
The most recent failure I'm experiencing is a status of "128" being reported in onCharacteristicWrite. The comments to question #3 (below) seem to indicate this is a resource issue.
I've looked at the following questions:
Android BLE onCharacteristicChanged not called
Android BLE, read and write characteristics
Android 4.3 onDescriptorWrite returns status 128
Here's why they don't give me what I need:
This question's answer was not to read the descriptor's value. I'm not doing that, so that can't be what's getting in the way.
This is basically an overview of the various methods that are available, which I think I now understand. The big key in this question/answer is not to write multiple times to different descriptors, but I'm also not doing that. I only care about the one characteristic.
This question/answer seems to be related to BLE resource limitations, but I don't think this applies. I'm only connecting this one device and I'm trying to do a very, very simple data transfer. I don't think I'm hitting resource ceilings.
I've tried a bunch of examples and tutorials, including Google's Android sample code. None of them seem to enable the BLE device to notify my Android device of data updates. It's obviously not the device, since the iOS version works. So, what is the iOS code doing in the background to get the notifications to work and what code on the Android side will mimic that functionality?
EDIT/UPDATE
Based on #yonran's comments, I updated my code by changing the onServicesDiscovered implementation to this:
#Override
public void onServicesDiscovered(final BluetoothGatt gatt, final int status) {
// this will get called after the client initiates a BluetoothGatt.discoverServices() call
BluetoothGattService service = gatt.getService(UUID_WEIGHT_SCALE_SERVICE);
if (service != null) {
BluetoothGattCharacteristic characteristic = service.getCharacteristic(UUID_WEIGHT_SCALE_READING_CHARACTERISTIC);
if (characteristic != null) {
if (gatt.setCharacteristicNotification(characteristic, true) == true) {
Log.d("gatt.setCharacteristicNotification", "SUCCESS!");
} else {
Log.d("gatt.setCharacteristicNotification", "FAILURE!");
}
BluetoothGattDescriptor descriptor = characteristic.getDescriptors().get(0);
if (0 != (characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_INDICATE)) {
// It's an indicate characteristic
Log.d("onServicesDiscovered", "Characteristic (" + characteristic.getUuid() + ") is INDICATE");
if (descriptor != null) {
descriptor.setValue(BluetoothGattDescriptor.ENABLE_INDICATION_VALUE);
gatt.writeDescriptor(descriptor);
}
} else {
// It's a notify characteristic
Log.d("onServicesDiscovered", "Characteristic (" + characteristic.getUuid() + ") is NOTIFY");
if (descriptor != null) {
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
gatt.writeDescriptor(descriptor);
}
}
}
}
}
That does seem to have changed some things a little bit. Here's the current Logcat, following that code change:
D/BluetoothGatt﹕ setCharacteristicNotification() - uuid: <UUID> enable: true
D/gatt.setCharacteristicNotification﹕ SUCCESS!
D/onServicesDiscovered﹕ Characteristic (<UUID>) is INDICATE
D/BluetoothGatt﹕ writeDescriptor() - uuid: 00002902-0000-1000-8000-00805f9b34fb
D/BluetoothGatt﹕ onDescriptorWrite() - Device=D0:5F:B8:01:6C:9E UUID=<UUID>
D/onDescriptorWrite﹕ descriptor: 00002902-0000-1000-8000-00805f9b34fb. characteristic: <UUID>. status: 0
D/BluetoothGatt﹕ onClientConnectionState() - status=0 clientIf=6 device=D0:5F:B8:01:6C:9E
So, it would appear that I'm now setting everything up properly (since setCharacteristicNotification returns true and the onDescriptorWrite status is 0). However, onCharacteristicChanged still never fires.
I've been able to successfully catch onCharacteristicChanged() with multiple services and characteristics by:
Writing descriptor values in the broadcastReceiver() in the main loop after service discovery is finished.
private final BroadcastReceiver UARTStatusChangeReceiver = new BroadcastReceiver() {
//more code...
if (action.equals(uartservice.ACTION_GATT_SERVICES_DISCOVERED)) {
mService.enableTXNotification();
}
and
By adding a delay between descriptor value settings
public void enableTXNotification(){
/*
if (mBluetoothGatt == null) {
showMessage("mBluetoothGatt null" + mBluetoothGatt);
broadcastUpdate(DEVICE_DOES_NOT_SUPPORT_UART);
return;
}
*/
/**
* Enable Notifications for the IO service and characteristic
*
*/
BluetoothGattService IOService = mBluetoothGatt.getService(IO_SERVICE_UUID);
if (IOService == null) {
showMessage("IO service not found!");
broadcastUpdate(DEVICE_DOES_NOT_SUPPORT_IO);
return;
}
BluetoothGattCharacteristic IOChar = IOService.getCharacteristic(IO_CHAR_UUID);
if (IOChar == null) {
showMessage("IO charateristic not found!");
broadcastUpdate(DEVICE_DOES_NOT_SUPPORT_IO);
return;
}
mBluetoothGatt.setCharacteristicNotification(IOChar,true);
BluetoothGattDescriptor descriptorIO = IOChar.getDescriptor(CCCD);
descriptorIO.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
mBluetoothGatt.writeDescriptor(descriptorIO);
/**
* For some reason android (or the device) can't handle
* writing one descriptor after another properly. Without
* the delay only the first characteristic can be caught in
* onCharacteristicChanged() method.
*/
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
/**
* Enable Indications for the RXTX service and characteristic
*/
BluetoothGattService RxService = mBluetoothGatt.getService(RXTX_SERVICE_UUID);
if (RxService == null) {
showMessage("Rx service not found!");
broadcastUpdate(DEVICE_DOES_NOT_SUPPORT_UART);
return;
}
BluetoothGattCharacteristic RxChar = RxService.getCharacteristic(RXTX_CHAR_UUID);
if (RxChar == null) {
showMessage("Tx charateristic not found!");
broadcastUpdate(DEVICE_DOES_NOT_SUPPORT_UART);
return;
}
mBluetoothGatt.setCharacteristicNotification(RxChar,true);
BluetoothGattDescriptor descriptor = RxChar.getDescriptor(CCCD);
descriptor.setValue(BluetoothGattDescriptor.ENABLE_INDICATION_VALUE );
mBluetoothGatt.writeDescriptor(descriptor);
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
/**
* Enable Notifications for the Battery service and Characteristic?
*/
BluetoothGattService batteryService = mBluetoothGatt.getService(BATTERY_SERVICE_UUID);
if (batteryService == null) {
showMessage("Battery service not found!");
broadcastUpdate(DEVICE_DOES_NOT_SUPPORT_BATTERY);
return;
}
BluetoothGattCharacteristic batteryChar = batteryService.getCharacteristic(BATTERY_CHAR_UUID);
if (batteryChar == null) {
showMessage("Battery charateristic not found!");
broadcastUpdate(DEVICE_DOES_NOT_SUPPORT_BATTERY);
return;
}
}
I was facing the same problem.
that's because when the device is sending the indicate value, your application is charged in another process and that's why you never get the indicate value which make the onCharacteristicChanged never fires.
to resolve your problem try to put all traitement in a service. and just call functions from your activity.
has anyone tried using HM-10 Bluetooth module?
I'm able to pair with it using an Android device and passing the pre-defined PIN. Based on the UART return, the pairing is successful (module returns OK+CONN - means a connection was established)
However, after a few seconds (2-3), the UART receives OK+LOST; means the connection was lost. Also, the LED starts blinking (normally, when a connection is active, it stays lit)
Is this normal behaviour for bluetooth in general or the HM-10 module.
This is the product's website: http://www.jnhuamao.cn/bluetooth.asp?ID=1
I'm not sure, but HM -10 don't support rfcom. It's mean that you must use GATT functionality for communication. Entity of BLE is usage of minimum data package as it possible, so BLE don't hold the connection all times and use something like statuses [attributes].
So, few code lines for example, how work with BLE:
1.
BluetoothAdapter mBluetoothAdapter = mBluetoothManager.getAdapter();
BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(DEVICE_ADDR);
That's device initiation, the same like with simple bluetooth, where DEVICE_ADDR is the MAC of your BLE(how to find this address you can find in google or stack overflow, its trivial)
2.
BluetoothGattService mBluetoothGattService;
BluetoothGatt mBluetoothGatt = device.connectGatt(this, false, mGattCallback);
BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
#Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
if (newState == BluetoothProfile.STATE_CONNECTED) {
mBluetoothGatt.discoverServices();
}
}
#Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
if (status == BluetoothGatt.GATT_SUCCESS) {
List<BluetoothGattService> gattServices = mBluetoothGatt.getServices();
for(BluetoothGattService gattService : gattServices) {
if("0000ffe0-0000-1000-8000-00805f9b34fb".equals(gattService.getUuid().toString()))
{
mBluetoothGattService = gattService;
}
}
} else {
Log.d(TAG, "onServicesDiscovered received: " + status);
}
}
};
So, what this code mean: if u can see from this part of code, i describe how GATT service find. This service needed for "attribute" communication. gattService.getUuid() has few uuids for communication(4 in my module), some of them used for RX, some for TX etc. "0000ffe0-0000-1000-8000-00805f9b34fb" that is one of uuid that use for communication thats why i check it.
The final part of code is message sending:
BluetoothGattCharacteristic gattCharacteristic = mBluetoothGattService.getCharacteristic(UUID.fromString("0000ffe1-0000-1000-8000-00805f9b34fb"));
String msg = "HELLO BLE =)";
byte b = 0x00;
byte[] temp = msg.getBytes();
byte[] tx = new byte[temp.length + 1];
tx[0] = b;
for(int i = 0; i < temp.length; i++)
tx[i+1] = temp[i];
gattCharacteristic.setValue(tx);
mBluetoothGatt.writeCharacteristic(gattCharacteristic);
After sending message contain hold on and you can send another message or can close connection.
More info, you can find on https://developer.android.com/guide/topics/connectivity/bluetooth-le.html.
PS: MAC address of your module can find with ble scanner code or with AT cmd:
on my firmware AT+ADDR or AT+LADDR
About UUIDs usage: not sure, but in my case, i find it with next AT+UUID [Get/Set system SERVER_UUID] -> Response +UUID=0xFFE0, AT+CHAR [Get/Set system CHAR_UUID] - Response +CHAR=0xFFE1. Thats why i make conclusion that UUID which i must use fe "0000[ffe0/is 0xFFE0 from AT response]-0000-1000-8000-00805f9b34fb"
I am able to discover, connect to bluetooth.
Source Code---
Connect via bluetooth to Remote Device:
//Get the device by its serial number
bdDevice = mBluetoothAdapter.getRemoteDevice(blackBox);
//for ble connection
bdDevice.connectGatt(getApplicationContext(), true, mGattCallback);
Gatt CallBack for Status:
private BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
#Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
//Connection established
if (status == BluetoothGatt.GATT_SUCCESS
&& newState == BluetoothProfile.STATE_CONNECTED) {
//Discover services
gatt.discoverServices();
} else if (status == BluetoothGatt.GATT_SUCCESS
&& newState == BluetoothProfile.STATE_DISCONNECTED) {
//Handle a disconnect event
}
}
#Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
//Now we can start reading/writing characteristics
}
};
Now I want to send commands to Remote BLE device but don't know how to do that.
Once the command is sent to the BLE device, the BLE device will respond by broadcasting
data which my application can receive.
You need to break this process into a few steps, when you connect to a BLE device and discover Services:
Display available gattServices in onServicesDiscovered for your callback
To check whether you can write a characteristic or not
check for BluetoothGattCharacteristic PROPERTIES -I didn't realize that need to enable the PROPERTY_WRITE on the BLE hardware and that wasted a lot of time.
When you write a characteristic, does the hardware perform any action to explicitly indicate the operation (in my case i was lighting an led)
Suppose mWriteCharacteristic is a BluetoothGattCharacteristic
The part where to check the PROPERTY should be like:
if (((characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_WRITE) |
(charaProp & BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE)) > 0) {
// writing characteristic functions
mWriteCharacteristic = characteristic;
}
And, to write your characteristic:
// "str" is the string or character you want to write
byte[] strBytes = str.getBytes();
byte[] bytes = activity.mWriteCharacteristic.getValue();
YourActivity.this.mWriteCharacteristic.setValue(bytes);
YourActivity.this.writeCharacteristic(YourActivity.this.mWriteCharacteristic);
Those are the useful parts of the code that you need to implement precisely.
Refer this github project for an implementation with just a basic demo.
A noob-friendly guide to make Android interact with a LED-lamp.
Step 1.
Get an tool to scan your BLE device. I used "Bluetooth LE Lab" for Win10, but this one will do it as well: https://play.google.com/store/apps/details?id=com.macdom.ble.blescanner
Step 2.
Analyse the behavior of the BLE device by entering data, I recommend to enter hex values.
Step 3.
Get the sample of the Android docs. https://github.com/googlesamples/android-BluetoothLeGatt
Step 4.
Modify the UUIDs you find in SampleGattAttributes
My config:
public static String CUSTOM_SERVICE = "0000ffe5-0000-1000-8000-00805f9b34fb";
public static String CLIENT_CHARACTERISTIC_CONFIG = "0000ffe9-0000-1000-8000-00805f9b34fb";
private static HashMap<String, String> attributes = new HashMap();
static {
attributes.put(CUSTOM_SERVICE, CLIENT_CHARACTERISTIC_CONFIG);
attributes.put(CLIENT_CHARACTERISTIC_CONFIG, "LED");
}
Step 5.
In BluetoothService.java modify onServicesDiscovered:
#Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
if (status == BluetoothGatt.GATT_SUCCESS) {
for (BluetoothGattService gattService : gatt.getServices()) {
Log.i(TAG, "onServicesDiscovered: ---------------------");
Log.i(TAG, "onServicesDiscovered: service=" + gattService.getUuid());
for (BluetoothGattCharacteristic characteristic : gattService.getCharacteristics()) {
Log.i(TAG, "onServicesDiscovered: characteristic=" + characteristic.getUuid());
if (characteristic.getUuid().toString().equals("0000ffe9-0000-1000-8000-00805f9b34fb")) {
Log.w(TAG, "onServicesDiscovered: found LED");
String originalString = "560D0F0600F0AA";
byte[] b = hexStringToByteArray(originalString);
characteristic.setValue(b); // call this BEFORE(!) you 'write' any stuff to the server
mBluetoothGatt.writeCharacteristic(characteristic);
Log.i(TAG, "onServicesDiscovered: , write bytes?! " + Utils.byteToHexStr(b));
}
}
}
broadcastUpdate(ACTION_GATT_SERVICES_DISCOVERED);
} else {
Log.w(TAG, "onServicesDiscovered received: " + status);
}
}
Convert the byte-String using this function:
public static byte[] hexStringToByteArray(String s) {
int len = s.length();
byte[] data = new byte[len / 2];
for (int i = 0; i < len; i += 2) {
data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
+ Character.digit(s.charAt(i + 1), 16));
}
return data;
}
PS: The above code is far away from production, but I hope it helps those, who are new to BLE.