I'm trying to connect programmatically my device to for example on my Headsets... I had KitKat version and all worked perfect (Bluetooth always was connecting without problems autommatically) but since I've updated to Lolipop it doesn't. I'd like to know if there is any way to connect any paired device of my Android phone to Bluetooth when it turns on.
Since now I've this code (gets the Device name and Device Address) because I thought with it I could connect doing something like device.connect(MAC-Address); but it didn't work...
BluetoothAdapter bluetoothAdapter
= BluetoothAdapter.getDefaultAdapter();
Set < BluetoothDevice > pairedDevices = bluetoothAdapter.getBondedDevices();
if (pairedDevices.size() > 0) {
for (BluetoothDevice device: pairedDevices) {
mDeviceName.add(device.getName());
mDeviceMAC.add(device.getAddress());
}
}
bluetoothClass.setDeviceName(mDeviceName);
bluetoothClass.setDeviceMac(mDeviceMAC);
Question
On my MotoG (KitKat) if I turn my Bluetooth it connects autommatically to device (if it's near and paired ofc...) but on my LG G3 I must go to Configuration/Bluetooth/Paired devices/ and there tap the device to connect... and I want to avoid this... should be possible?
I would like to know if there is any possibility to connect to specific Bluetooth just adding the Device name or Device MAC... More or less like Android does when I click on my device to connect it connects autommatically... I just want to get that CLICK event.
I know that Android should connect autommatically to a paired device but there's any exceptions that doesn not ... the only way to pair it it's doing the click... that's why I'm wondering if it's there a way to do it...
I've read and tested kcoppock answer but it still don't work ..
Any suggestion?
EDIT
The main thing that I wanted to do is to connect my Bluetooth autommatically but since I've read on Hey you answer... I figured it out and I know it's an Android bug, so the thing that I would like to do is select the paired devices and then click on the device that I want to connect (Without doing any Intent) and connect it, instead to go Configuration/Bluetooth/....
Btw I've read any answers on StackOverflow and I found something with Sockets are they used to connect Bluetooth?Could be it a solution?
Edit to answer latest question
You can avoid using an intent to search for paired devices. When connecting to a device that is not paired, a notification will pop up asking to pair the devices. Once paired this message should not show again for these devices, the connection should be automatic (according to how you have written your program).
I use an intent to enable bluetooth, and to make my device discoverable, I then set up my code to connect, and press a button to connect. In your case, you will need to ensure your accessories are discoverable also. In my case I use a unique UUID, and both devices must recognise this to connect. This can only be used if you are programming both devices, whether both are android or one android and one other device type.
Try this, and see if it solves your problem.
This answer is to the original question before it was edited to be another question.
I've edited my answer for clarity as I can see from the comments it is misleading. Your question has two parts.
On my MotoG (KitKat) if I turn my Bluetooth it connects autommatically
to device (if it's near and paired ofc...) but on my LG G3 I must go
to Configuration/Bluetooth/Paired devices/ and there tap the device to
connect... and I want to avoid this... should be possible?
This is less of a programming issue and more of a platform issue.
There is a well documented bug in Android 5.0 with Bluetooth not automatically connecting and many other BT issues. These issues continue with all the updates on 5.0. versions and is not fixed until the 5.1. upgrade.
http://www.digitaltrends.com/mobile/android-lollipop-problems/11/
http://forums.androidcentral.com/lg-g3/473064-bluetooth-streaming-choppy-lg-3-lollipop.html
First port of call is to update to 5.1
These issues have been addressed in the Lollipop update 5.1
http://www.reddit.com/r/Android/comments/306m3y/lollipop_51_bluetooth/
Edit:
I don't believe this is going to fix your problem of the automatic pairing, you wanted to know how to use BTGatt.
I've seen if I type device. to check what can I do it let me
connectGatt() means /.../
But I can't figure it out how to do this...
To use BluetoothGatt
https://developer.android.com/reference/android/bluetooth/BluetoothGatt.html
This class provides Bluetooth GATT functionality to enable
communication with Bluetooth Smart or Smart Ready devices.
/.../
GATT capable devices can be discovered using the Bluetooth device
discovery or BLE scan process.
https://developer.android.com/reference/android/bluetooth/BluetoothGattCallback.html
Here is a great example of how to use BluetoothGatt (it uses hear rate):
https://github.com/googlesamples/android-BluetoothLeGatt/blob/master/Application/src/main/java/com/example/android/bluetoothlegatt/BluetoothLeService.java
I have reproduced some of the code here, in case the link dies.
It basically follows similar lines to a regular bluetooth connection. You need to discover and find supported devices.
Monitor state, etc.
These are the two most pertinent features to gatt.
The callback:
// Implements callback methods for GATT events that the app cares about. For example,
// connection change and services discovered.
private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
#Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
String intentAction;
if (newState == BluetoothProfile.STATE_CONNECTED) {
intentAction = ACTION_GATT_CONNECTED;
mConnectionState = STATE_CONNECTED;
broadcastUpdate(intentAction);
Log.i(TAG, "Connected to GATT server.");
// Attempts to discover services after successful connection.
Log.i(TAG, "Attempting to start service discovery:" +
mBluetoothGatt.discoverServices());
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
intentAction = ACTION_GATT_DISCONNECTED;
mConnectionState = STATE_DISCONNECTED;
Log.i(TAG, "Disconnected from GATT server.");
broadcastUpdate(intentAction);
}
}
#Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
if (status == BluetoothGatt.GATT_SUCCESS) {
broadcastUpdate(ACTION_GATT_SERVICES_DISCOVERED);
} else {
Log.w(TAG, "onServicesDiscovered received: " + status);
}
}
#Override
public void onCharacteristicRead(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic,
int status) {
if (status == BluetoothGatt.GATT_SUCCESS) {
broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
}
}
#Override
public void onCharacteristicChanged(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic) {
broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
}
};
The broadcast:
private void broadcastUpdate(final String action,
final BluetoothGattCharacteristic characteristic) {
final Intent intent = new Intent(action);
// This is special handling for the Heart Rate Measurement profile. Data parsing is
// carried out as per profile specifications:
// http://developer.bluetooth.org/gatt/characteristics/Pages/CharacteristicViewer.aspx?u=org.bluetooth.characteristic.heart_rate_measurement.xml
if (UUID_HEART_RATE_MEASUREMENT.equals(characteristic.getUuid())) {
int flag = characteristic.getProperties();
int format = -1;
if ((flag & 0x01) != 0) {
format = BluetoothGattCharacteristic.FORMAT_UINT16;
Log.d(TAG, "Heart rate format UINT16.");
} else {
format = BluetoothGattCharacteristic.FORMAT_UINT8;
Log.d(TAG, "Heart rate format UINT8.");
}
final int heartRate = characteristic.getIntValue(format, 1);
Log.d(TAG, String.format("Received heart rate: %d", heartRate));
intent.putExtra(EXTRA_DATA, String.valueOf(heartRate));
} else {
// For all other profiles, writes the data formatted in HEX.
final byte[] data = characteristic.getValue();
if (data != null && data.length > 0) {
final StringBuilder stringBuilder = new StringBuilder(data.length);
for(byte byteChar : data)
stringBuilder.append(String.format("%02X ", byteChar));
intent.putExtra(EXTRA_DATA, new String(data) + "\n" + stringBuilder.toString());
}
}
sendBroadcast(intent);
}
This question also has some relevant code that may help cut it down when learning:
BLuetooth Gatt Callback not working with new API for Lollipop
Now here's the rub. Are your devices bluetooth smart or smart ready?
This link gives a great list of smart devices. You will also find out when you implement your program.
http://www.bluetooth.com/Pages/Bluetooth-Smart-Devices-List.aspx
This is how i made this work using Java Reflection and BluetoothProfile:
Attributes:
private boolean mIsConnect = true;
private BluetoothDevice mDevice;
private BluetoothA2dp mBluetoothA2DP;
private BluetoothHeadset mBluetoothHeadset;
private BluetoothHealth mBluetoothHealth;
Call:
mBluetoothAdapter.getProfileProxy(getApplicationContext() , mProfileListener, BluetoothProfile.A2DP);
mBluetoothAdapter.getProfileProxy(getApplicationContext() , mProfileListener, BluetoothProfile.HEADSET);
mBluetoothAdapter.getProfileProxy(getApplicationContext() , mProfileListener, BluetoothProfile.HEALTH);
Listener:
private BluetoothProfile.ServiceListener mProfileListener = new BluetoothProfile.ServiceListener() {
public void onServiceConnected(int profile, BluetoothProfile proxy) {
if (profile == BluetoothProfile.A2DP) {
mBluetoothA2DP = (BluetoothA2dp) proxy;
try {
if (mIsConnect) {
Method connect = BluetoothA2dp.class.getDeclaredMethod("connect", BluetoothDevice.class);
connect.setAccessible(true);
connect.invoke(mBluetoothA2DP, mDevice);
} else {
Method disconnect = BluetoothA2dp.class.getDeclaredMethod("disconnect", BluetoothDevice.class);
disconnect.setAccessible(true);
disconnect.invoke(mBluetoothA2DP, mDevice);
}
}catch (Exception e){
} finally {
}
} else if (profile == BluetoothProfile.HEADSET) {
mBluetoothHeadset = (BluetoothHeadset) proxy;
try {
if (mIsConnect) {
Method connect = BluetoothHeadset.class.getDeclaredMethod("connect", BluetoothDevice.class);
connect.setAccessible(true);
connect.invoke(mBluetoothHeadset, mDevice);
} else {
Method disconnect = BluetoothHeadset.class.getDeclaredMethod("disconnect", BluetoothDevice.class);
disconnect.setAccessible(true);
disconnect.invoke(mBluetoothHeadset, mDevice);
}
}catch (Exception e){
} finally {
}
} else if (profile == BluetoothProfile.HEALTH) {
mBluetoothHealth = (BluetoothHealth) proxy;
try {
if (mIsConnect) {
Method connect = BluetoothHealth.class.getDeclaredMethod("connect", BluetoothDevice.class);
connect.setAccessible(true);
connect.invoke(mBluetoothHealth, mDevice);
} else {
Method disconnect = BluetoothHealth.class.getDeclaredMethod("disconnect", BluetoothDevice.class);
disconnect.setAccessible(true);
disconnect.invoke(mBluetoothHealth, mDevice);
}
}catch (Exception e){
} finally {
}
}
}
public void onServiceDisconnected(int profile) {
}
};
I hope this helps anyone trying to connect to Bluetooth Audio devices and headsets.
Related
I'm developing a project that consists of a BLE GATT server ran on Android phone (using BluetoothGattServer Java class) and a BLE client on an IoT board. The concept of using the phone as the server is to somehow protect the IoT board from attacking clients. When my Android application wants to talk to the external device it starts advertising a special set of data and if the external device recognizes the advertisment it connects to the Android BLE GATT server. Then the external device reads the presented services and chareacteristics and registers for notifications on some of the chars.
By far it all happens well.
After that the external device tries to write authentication data to one of the chars. If everything was clean and perfect, the process goes well. But if for a reason the last connection broke in the middle of some operation and was not properly closed, the external device cannot read/write the characteristic. If I restart the phone or clear Bluetooth cache (Settings > Apps > Bluetooth > Clear Data) all the operations proceed fine, but I cannot force users to do this in normal operation and I haven't found how and if I could clear this cache programmatically from inside the app.
I read over the web for GATT client cache undocumented "Refresh" method, but in GATT server there is no such.
I read about and tried the "Service changed" characteristic (0x2A05) but it doesn't help me much.
Having doubts about which of the devices is causing the problem I have tried with another phone as a client. I ran "BLE Scanner" App on it and tried to connect to the server phone - the problem persists. I can connect, all the characteristics are discovered, but when I try to read/write some char the connection brakes after a 30 sec timeout - exactly the same behavior like in the original situation. The conclusion is I have a problem with Android BluetoothGattServer.
During the development the problem occurs mostly when connection is broken by some error in communication. In real life usage after I have all errors fixed that will not happen, but having in mind tha it is wireless radio connection, it can be disconnected by literally everything and I shall have a reliable mechanism to reconnect.
I open the server with this code:
private void startServer() {
BluetoothAdapter bluetoothAdapter = mBluetoothManager.getAdapter();
mBluetoothLeAdvertiser = bluetoothAdapter.getBluetoothLeAdvertiser();
if (mBluetoothLeAdvertiser == null) {
Log.w("BLE", "Failed to create advertiser");
return;
}
AdvertiseSettings settings = new AdvertiseSettings.Builder()
.setAdvertiseMode(AdvertiseSettings.ADVERTISE_MODE_LOW_LATENCY)
.setConnectable(true)
.setTimeout(0)
.setTxPowerLevel(AdvertiseSettings.ADVERTISE_TX_POWER_HIGH)
.build();
byte bData[] = new byte[24];
bData = ...... // some proprietary advertising data
AdvertiseData data = new AdvertiseData.Builder()
.setIncludeDeviceName(false)
.setIncludeTxPowerLevel(false)
.addManufacturerData(iManufID, bData)
.build();
mBluetoothLeAdvertiser
.startAdvertising(settings, data, mAdvertiseCallback);
mBluetoothGattServer = mBluetoothManager.openGattServer(ctxActivity, mGattServerCallback);
mBluetoothGattServer.clearServices();
mBluetoothGattServer.addService(BLEProfile.createBLEService());
/* Static method, which builds Service->Chars->Descriptors.
I assign the Client Config descriptor (0x2902) to each characteristic. */
}
For stopping the sertver I use this code:
private void stopServer() {
if (mBluetoothGattServer == null) return;
mBluetoothGattServer.clearServices();
mBluetoothGattServer.close();
if (mBluetoothLeAdvertiser == null) return;
mBluetoothLeAdvertiser.stopAdvertising(mAdvertiseCallback);
}
I stop and start the server each time a connection was broken.
Does anyone have an idea what am I doing wrong?
Also it is important to mention that in real life IoT devices will be many in a room, phones may be many in a room. One phone should be able to be connected by any of the IoT devices it requests sequentially and one IoT device should be able to connect to more than one phone sequentially. The advertising data of the phone's GATT Server defines which of the IoT devices is requested to connect and it will change each time the phone requests connection with a different device.
Update:
Here is the code for the Server Callback:
private BluetoothGattServerCallback mGattServerCallback = new BluetoothGattServerCallback() {
#Override
public void onMtuChanged(BluetoothDevice device, int mtu) {
super.onMtuChanged(device, mtu);
Log.i("BLE", "MTU changed: "+mtu);
}
#Override
public void onCharacteristicWriteRequest(BluetoothDevice device, int requestId, BluetoothGattCharacteristic characteristic, boolean preparedWrite, boolean responseNeeded, int offset, byte[] value) {
if(BLEProfile.XXXXXX.equals(characteristic.getUuid()))
{
// ... some data checks ...
mBluetoothGattServer.sendResponse(device,
requestId,
iValid,
0,
bEncrypted);
characteristic.setValue(bEncrypted);
mBluetoothGattServer.notifyCharacteristicChanged(device, characteristic, true);
} else if (...) /* similar operations for all other characteristics */
{
...
}
else
{
Log.i("BLE", "Write not mine characteristic");
mBluetoothGattServer.sendResponse(device,
requestId,
BluetoothGatt.GATT_FAILURE,
0,
null);
}
}
#Override
public void onConnectionStateChange(BluetoothDevice device, int status, int newState) {
if (newState == BluetoothProfile.STATE_CONNECTED) {
Log.i("BLE", "BluetoothDevice ... CONNECTED: " + device);
if(device != null)
{
BluetoothGattService mServ = mBluetoothGattServer.getService(BLEProfile.GATT_SERVICE);
if(mServ != null)
{
BluetoothGattCharacteristic mChar = mServ.getCharacteristic(BLEProfile.SERVICE_CHANGED);
if(mChar != null)
mBluetoothGattServer.notifyCharacteristicChanged(device, mChar, false);
}
}
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
mRegisteredDevices.remove(device);
stopAdvertising();
startAdvertising();
}
}
#Override
public void onCharacteristicReadRequest(BluetoothDevice device, int requestId, int offset,
BluetoothGattCharacteristic characteristic) {
/* Not used currently. Just some testing code below. */
if(BLEProfile.XXXXXX.equals(characteristic.getUuid()))
{
mBluetoothGattServer.sendResponse(device,
requestId,
BluetoothGatt.GATT_SUCCESS,
0,
new byte[] {0x01, 0x02, 0x03, 0x04, 0x05});
} else if(...) /* similar operations for all other characteristics */
{
...
}
else
{
Log.i("BLE", "Read not mine characteristic");
mBluetoothGattServer.sendResponse(device,
requestId,
BluetoothGatt.GATT_FAILURE,
0,
null);
}
}
#Override
public void onDescriptorReadRequest(BluetoothDevice device, int requestId, int offset,
BluetoothGattDescriptor descriptor) {
if (BLEProfile.CLIENT_CONFIG.equals(descriptor.getUuid())) {
Log.d("BLE", "Config descriptor read");
byte[] returnValue;
if (mRegisteredDevices.contains(device)) {
returnValue = BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE;
} else {
returnValue = BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE;
}
/* Not sure why I am responding with GATT_FAILURE here insted of GAT_SUCCESS !?!? May be some copy/paste mistake. */
mBluetoothGattServer.sendResponse(device,
requestId,
BluetoothGatt.GATT_FAILURE,
0,
returnValue);
} else {
Log.w("BLE", "Unknown descriptor read request");
mBluetoothGattServer.sendResponse(device,
requestId,
BluetoothGatt.GATT_FAILURE,
0,
null);
}
}
#Override
public void onDescriptorWriteRequest(BluetoothDevice device, int requestId,
BluetoothGattDescriptor descriptor,
boolean preparedWrite, boolean responseNeeded,
int offset, byte[] value) {
if (BLEProfile.CLIENT_CONFIG.equals(descriptor.getUuid())) {
if (Arrays.equals(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE, value)) {
Log.i("BLE", "Subscribe device to notifications: " + device);
mRegisteredDevices.add(device);
} else if (Arrays.equals(BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE, value)) {
Log.i("BLE", "Unsubscribe device from notifications: " + device);
mRegisteredDevices.remove(device);
}
if (responseNeeded) {
mBluetoothGattServer.sendResponse(device,
requestId,
BluetoothGatt.GATT_SUCCESS,
0,
null);
}
} else {
Log.w("BLE", "Unknown descriptor write request");
if (responseNeeded) {
mBluetoothGattServer.sendResponse(device,
requestId,
BluetoothGatt.GATT_FAILURE,
0,
null);
}
}
}
};
we are using symbol ls4278 bluetooth scanner to integrate in android application. It is connected as keyboard and types scanned barcode in any edittext field...
After scanner OnKeyUp event is called.
public override bool OnKeyUp(Keycode keyCode, KeyEvent e)
{
..
}
I was searching documentation and android sdk, but I can't found such one. But for LI4278 they have android sdk here : https://www.zebra.com/us/en/support-downloads/scanners/general-purpose-scanners/li4278.html
here is also documentation for sdk but LS4278 is not in supported device list.
Does anyone implemented LS4278 scanner in android devices?
The LS4278 product page is here: https://www.zebra.com/us/en/support-downloads/scanners/general-purpose-scanners/ls4278.html and lists support for the "Windows Scanner SDK" ONLY. The LS4278 was discontinued on September 24th 2012 so I am not surprised it does not have Android support. As you say, its successor, the LI4278 does have Android support. As the other answer states, if you want more control over how you receive data then I suggest trying SPP if the scanner supports it.
If it works as a bluetooth keyboard, then no support is needed. Just capture the key events, and react to the data when enter is pressed. Its just a mediocre experience and can mess with on screen keyboards and stop them from using an actual bluetooth keyboard. If the scanner supports SPP, you can pretty trivially parse the scan data out of it via bluetooth serial (I did this about 2 weeks ago).
BluetoothAdapter bta = BluetoothAdapter.getDefaultAdapter();
if(bta != null) {
Set<BluetoothDevice> devices = bta.getBondedDevices();
for (final BluetoothDevice device : devices) {
BluetoothClass btClass = device.getBluetoothClass();
if (btClass.getMajorDeviceClass() == 0x1f00) {
//Only look at devices which are considered uncategorized, so we don't screw up any bt headset, leyboard, mouse, etc
new DeviceThread(device).start();
}
}
IntentFilter filter = new IntentFilter();
filter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
registerReceiver(new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
int state = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.BOND_NONE);
if (state == BluetoothDevice.BOND_BONDED) {
new DeviceThread(device).start();
} else if (state == BluetoothDevice.BOND_NONE) {
DeviceThread thread = threadMap.get(device.getAddress());
if (thread != null) {
thread.interrupt();
}
}
}
}, filter);
}
private class DeviceThread extends Thread {
private BluetoothDevice device;
public DeviceThread(BluetoothDevice device) {
this.device = device;
threadMap.put(device.getAddress(), this);
}
#Override
public void run() {
try {
BluetoothSocket socket = device.createInsecureRfcommSocketToServiceRecord(UUID.fromString("00001101-0000-1000-8000-00805F9B34FB"));
socket.connect();
InputStream inputStream = socket.getInputStream();
while (!Thread.interrupted() && socket.isConnected()) {
inputStream.skip(5);
String data = "";
do {
int code = inputStream.read();
char character = (char) code;
data = data + character;
} while (inputStream.available() > 0);
data = data.substring(0, data.length() - 2);
if (scannerEventListener != null) {
scannerEventListener.onScan(data);
}
}
} catch (IOException ex) {
ex.printStackTrace();
}
Log.d("GABE", "Exiting thread");
}
}
This code will register for bluetooth devices being paired, then check and see if they're unknown device types (scanners don't have a device class). If so, it will start a thread to listen for that device. When its unbonded, it will interrupt that thread. On the thread it opens up a SPP connection to the device and waits for input. When it gets it, it parses the input and sends the result to a listener.
For this to work, the scanner needs to be in SPP mode. Some scanners support it, some don't, and how to set it into that mode varies (the one on my desk has a control barcode I need to scan to set the mode). Generally I would code for it to accept either type of input- hardware keyboard mode or SPP.
My device information
Nordic board :
MTU size : 247.
This board send notification multiple packets in one connection interval (Just counter value like 1,2,3,4...) at "Heart Rate Measurement".
Android device :
Version 5.0 and 6.0. (Using two device).
communicate Bluetooth Low Energy(BLE) with board.
board connect with my Android app MTU is setting 244.
Application source is google sample project BluetoothLeGatt
App send notification value at "Heart Rate Service" characteristic.
Receive notification value at Gattcallback "onCharacteristicChanged()"
Problem
My Android app lost some packet.
I read this post. Maximizing BLE Throughput on iOS and Android. So I send E-mail this post author and I search another information for Android.
I found some similarly question. but that question answer was not work. Then I found one question what I want exactly. but this question have no answers. Android receiving multiple BLE packets per connection interval. Unfortunately I don't have any reply E-mail answer.
My question is how do I set Android BLE notification. (Not Nordic board setting)
(My question is same Android receiving multiple BLE packets per connection interval)
Under line is my sample code. at notification.
#Connect
public boolean connect(final String address) {
if (mBluetoothAdapter == null || address == null) {
Log.w(TAG, "BluetoothAdapter not initialized or unspecified address.");
return false;
}
// Previously connected device. Try to reconnect.
if (mBluetoothDeviceAddress != null && address.equals(mBluetoothDeviceAddress) && mBluetoothGatt != null) {
Log.d(TAG, "Trying to use an existing mBluetoothGatt for connection.");
if (mBluetoothGatt.connect()) {
mConnectionState = STATE_CONNECTING;
return true;
} else {
return false;
}
}
final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address);
if (device == null) {
Log.w(TAG, "Device not found. Unable to connect.");
return false;
}
// We want to directly connect to the device, so we are setting the autoConnect
// parameter to false.
mBluetoothGatt = device.connectGatt(this, false, mGattCallback);
Log.d(TAG, "Trying to create a new connection.");
mBluetoothDeviceAddress = address;
mConnectionState = STATE_CONNECTING;
return true;
}
#GattCallback
/*broadcastUpdate method is display value*/
#Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
if (newState == BluetoothProfile.STATE_CONNECTED) {
mConnectionState = STATE_CONNECTED;
// Attempts to discover services after successful connection.
Log.i(TAG, "Attempting to start service discovery:");
mBluetoothGatt.discoverServices();
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
Log.i(TAG, "Disconnected from GATT server.");
mConnectionState = STATE_DISCONNECTED;
broadcastUpdate(ACTION_GATT_DISCONNECTED);
}
}
#Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
if (status == BluetoothGatt.GATT_SUCCESS) {
Log.d(TAG, "onServicesDiscovered success: (status)" + status);
//findServiceOther(gatt);
broadcastUpdate(ACTION_GATT_SERVICES_DISCOVERED);
Log.d(TAG, "Request MTU");
gatt.requestMtu(SET_MTU);
} else {
Log.w(TAG, "onServicesDiscovered failed: (status)" + status);
}
}
#Override
public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
if (status == BluetoothGatt.GATT_SUCCESS) {
Log.d(TAG, "onCharRead Success");
broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
} else {
Log.d(TAG, "OnCharRead Error: " + status);
}
}
#Override
public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
if (characteristic.getUuid().equals(SampleGattAttributes.UUID_HEART_RATE_MEASUREMENT))
broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic, FLAG_HEART_RATE);
else
broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
}
#Override
public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) {
super.onMtuChanged(gatt, mtu, status);
boolean priority = gatt.requestConnectionPriority(BluetoothGatt.CONNECTION_PRIORITY_HIGH);
Log.d(TAG, "MTU changed (mtu/status) / Priority : (" + mtu + "/" + status + ") / " + priority);
changed_MTU_Size = mtu;
broadcastUpdate(ACTION_DATA_AVAILABLE, changed_MTU_Size, FLAG_MTU);
broadcastUpdate(ACTION_GATT_CONNECTED);
}
#set notification
public void setCharacteristicNotification(BluetoothGattCharacteristic characteristic, boolean enabled) {
if (mBluetoothAdapter == null || mBluetoothGatt == null) {
Log.w(TAG, "BluetoothAdapter not initialized");
return;
}
mBluetoothGatt.setCharacteristicNotification(characteristic, enabled);
if (SampleGattAttributes.UUID_HEART_RATE_MEASUREMENT.equals(characteristic.getUuid())) {
// This is specific to Heart Rate Measurement.
BluetoothGattDescriptor descriptor = characteristic.getDescriptor(SampleGattAttributes.UUID_CLIENT_CHARACTERISTIC_CONFIG);
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
mBluetoothGatt.writeDescriptor(descriptor);
}
}
Edit_1
I test Nordic offical app in Google play store.
nRF Connect for Mobile
but this app miss packet too.
Edit_2
I found some problem too.
< Nordic board constant setting>
HEART_RATE_MEAS_INTERVAL : 10
MIN_CONN_INTERVAL : MSEC_TO_UNITS(40, UNIT_1_25_MS)
MAX_CONN_INTERVAL : MSEC_TO_UNITS(40, UNIT_1_25_MS)
SLAVE_LATENCY 0
CONN_SUP_TIMEOUT MSEC_TO_UNITS(4000, UNIT_10_MS)
< Run in my Android app >
skip requestMTU : receive 20byte packet successful. (Data rate average is 2200byte/second in my app)
requestMTU : I try so many MTU size (ex: 23(can small size), 40, 100, 255(target) ...) but lost some packet (Data rate 8500 ~ 9500 byte/second in my app)
I wonder requestMTU and notification receive interrelation.
i have implemented a file transfer over LE on a nrf51422 and SD310 by using the Nordic UART Service. The android App is written in CPP by using Qt and the QtBluetooth library. Therefore, this answer my not really help you. But, i have taken some afford to achive a usable data rate. 1st, set the connection parameters to 7.5ms (min) and 15ms (max). 2nd, on the peripheral side, i send up to 7 packets (til the buffer is full). So the peripheral send up to 7 packets per connection event.
On the Android side, the charachteritic changed event arrive me frequently with a data size of 20 bytes (due to the maximum MTU size of 23, 3 bytes used by nordic uart service) and i read the data instantly.
Perhaps your mistake is the MTU size of 244. The default BLE MTU size is 23 (Core spec 4.1).
Regards
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"