I'm trying to writeCharacteristic() to a BLE device.
I used Google example app to establish the connection, the connection is fine. I can see the BLE Services and the services Characteristics.
The writeCharacteristic() method return true and the onCharacteristicWrite() callback return status BluetoothGatt.GATT_SUCCESS but nothing happening with the device:
I tried all the solutions in stack overflow and nothing helps.
Here is my code:
In the BluetoothLeService class i have sendData() method, which gets a byte[]
I'm sending each command separately because of the maximum payload of 33 bytes.
public void sendData(byte[] data) {
String lService = "71387664-eb78-11e6-b006-92361f002671";
String lCharacteristic = "71387663-eb78-11e6-b006-92361f002671";
BluetoothGattService mBluetoothLeService = null;
BluetoothGattCharacteristic mBluetoothGattCharacteristic = null;
if (mBluetoothGattCharacteristic == null) {
mBluetoothGattCharacteristic = mBluetoothGatt.getService(UUID.fromString(lService)).getCharacteristic(UUID.fromString(lCharacteristic));
}
mBluetoothGattCharacteristic.setValue(data);
boolean write = mBluetoothGatt.writeCharacteristic(mBluetoothGattCharacteristic);
}
In your code you check property of characteristics?
If Write something on device then characteristics must have write property or write no response property or write & notify property.
So Check in your code on which characteristics you are write.
Your code looks OK, if you've got GATT_SUCCESS it means that characteristic was written successfully.
You can also try to read this characteristic and check if value is updated. Also it's possible that characteristic was written successfully but corresponded device's functionality will be triggered only after disconnection from device. And also it's possible that there is some bug on a device side.
I found the issue. Hope my answer will help others.
The issue was that, I was sending byte[] array bigger than it's allow(20 byte). Also, I needed to add "\r" inside this byte[] array, thats way, the BLE Devise knows it's the end of the command.
Another thing, I needed to create a queue and pop from it after I get response in onCharacteristicRead().
class DataQueues {
private ArrayList<byte[]> queueArray;
private int queuSize;
protected DataQueues() {
queueArray = new ArrayList<>();
}
protected void addToQueue(byte[] bytesArr) {
queueArray.add(bytesArr);
}
protected byte[] popQueue() {
if (queueArray.size() >= 0)
return queueArray.remove(0);
else {
return null;
}
}
protected int getArrSize() {
return queueArray.size();
}
}
Hope this will help some one.
Related
Below is the sketch of my Simblee :
#include <SimbleeBLE.h>
void setup() {
Serial.begin(9600);
Serial.println("Waiting for connection...");
SimbleeBLE.deviceName = "Simblee";
SimbleeBLE.advertisementData = "data";
// SimbleeBLE.customUUID = "2220";
SimbleeBLE.advertisementInterval = MILLISECONDS(300);
SimbleeBLE.txPowerLevel = -20; // (-20dbM to +4 dBm)
SimbleeBLE.begin();
}
void loop() {
}
void SimbleeBLE_onConnect()
{
Serial.println("Simblee Connected");
}
void SimbleeBLE_onDisconnect()
{
Serial.println("Simblee Disconnected");
}
void SimbleeBLE_onReceive(byte *data, int len)
{
Serial.println("Data received");
SimbleeBLE.send(1);
printf("%s\n", data);
}
void serialEvent()
{
Serial.println("Serial event");
}
I can easily connect and read data from it, bit writing to it doesn't work from my app. SimbleeBLE_onReceive is never being called.
It actually never is called even when using third party BLE apps like nRF Connect and BLE Terminal.
So I'm suspecting that the issue is with my arduino Sketch and not related to the android code.
The android code is returning GATT_ERROR 133 when trying to write.
Is anything missing from this code ? Strangely I can't find any example online where we're simply sending data to Simblee (without using SimlbeeMobile)
Thanks,
Try changing your onReceive signature to:
void SimbleeBLE_onReceive(char *data, int len)
I am trying to write to a characteristic of a BLE device. According to the documentation, the characteristic is capable of read and write, with different usages. I was already successful with reading from it but i am having some problems with write. Every time i try to write to it the onWriteCharacterstic function arrives with a status code 6, which is supposed to be GATT_REQUEST_NOT_SUPPORTED.
I am not really sure what can cause it. I added all the necessary bluetooth permission to my app, and the documentation states that it is capable of write.
My code looks somewhat like this (simplified):
#Override
public void onServicesDiscovered( BluetoothGatt gatt, int status ){
if( status == BluetoothGatt.GATT_SUCCESS ){
mGatt = gatt;
mService= gatt.getService(UUID_SERVICE);
mChar = mService.getCharacteristic(UUID_CHAR);
byte[] value = {...}
mChar.setValue(value);
boolean retval = mGatt.writeCharacteristic(mChar); //retval is true
}
}
#Override
public void onCharacteristicWrite (BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status)
{
... //status here is 6
}
I also made some check with the functions getProperties() and getPermissions() for the characteristic. The getPermissions() function returns 0, which i couldn't match to anything in the android documentation, and the getProperties() returns 10, which is also weird. With properties it would mean that it supports notifications, which it doesn't.
It seems like it was a problem with the phone. After it had it's bluetooth turned off for a few hours it worked correctly.
I am developing an Android Application that connects to a BLE Device and reads the Gatt Services and Gatt Characteristics. I used the BluetoothLeGatt sample project from the Android Development Site as my reference.
So far, I am able to programmatically connect to a device (I took note of my Device's Address to be able to do this) and filter out the specific Gatt Service I want to read and that Services' specific Characteristics by taking note of the UUID of both the Service and the Characteristics. The sample provided by Google also updates whenever there's a message sent from my BLE Device to my Android Application. Overall, I have no problems at this end.
However, upon reading up further on GATT, I found that it is possible to connect to multiple BLE devices (all slaves OR servers - being the ones that send the data) using a single Android Application (as master OR client - as the one who receives said data). So what I tried to do was to have 2 BLE Devices (different Address), took note of their Address, and then my application tries to connect to them once the application sees that those 2 addresses are up and running.
In code, I call this function when I see my 2 BLE Devices:
private void connectToDevice(){
mDeviceName = deviceList.get(currentIndex).getName();
mDeviceAddress = deviceList.get(currentIndex).getAddress();
Log.e(TAG, "connecting to device name = " + mDeviceName);
mBluetoothLeService.connect(mDeviceAddress);
}
Where currentIndex is initially set to zero. Then once I get a successful connection, I do:
private final BroadcastReceiver mGattUpdateReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
if (BluetoothLeService.ACTION_GATT_CONNECTED.equals(action)) {
Log.e(TAG, "connected");
mConnected = true;
if(currentIndex < deviceList.size()-1) currentIndex ++;
connectToDevice();
}
}
};
Where I check if I still have devices to connect to in my deviceList, if so, increment my counter and then connect until I exhaust everything in my list.
However, I seem to have no success at all using this method.
Kindly note that switching connection (round robin) between my devices isn't an option. This will be an issue when I have a lot of devices and it's important to get their messages real time without delays. This said, I have to have a live connection to my devices.
Has anyone tried to connect to multiple BLE Devices in Android? I'm not sure on how to proceed on this.
Indeed it is possible to connect to more than one peripheral from your Android device. However, it will make your code much more complex since you will need to manage each connection and responses.
For each connection you would have to implement a BluetoothGatt with it's callbacks. I tested it many months ago with a dummy test and as I said, it worked well and I was able to connect to different peripherals. However, if you chain many commands there seem to be some overlapping issues described in this thread.
As asked here is the relevant code : (Here the ArrayList contains the founded peripheral devices)
for(int i=0;i< Utility.selectedDeviceList.size();i++) {
Log.d(Utility.TAG,"state"+ Utility.selectedDeviceList.get(i).getmConnectionState());
if (Utility.selectedDeviceList.get(i).getmConnectionState() != Utility.CONNECTED) {
Log.d(Utility.TAG,"Connecting LeSerive::" + Utility.selectedDeviceList.get(i).getAddress());
Utility.mBluetoothLeService.connect(i, Utility.selectedDeviceList.get(i).getAddress());
}
}
This for loop is a part of runnable interface which is called inside a handler having a looper.
public void run() {
Looper.prepare();
Looper mLooper = Looper.myLooper();
Log.d(Utility.TAG,"BLE Thread Started::");
mHandler = new Handler(mLooper) {
#Override
public void handleMessage(Message msg) {
switch (msg.what) {
case Utility.BLE_SYNC:
Log.d(Utility.TAG,"BLE Sync Connecting::");
mHandler.post(SynState);
break;
}
};
Looper.loop();
}
I used this approach because their is lot of communication between peripherals to send and receive the data from them.
This is the connect method which inside a Service :
public boolean connect(int tag,final String address) {
if (mBluetoothAdapter == null || address == null) {
Log.w(Utility.TAG, "BluetoothAdapter not initialized or unspecified address.");
return false;
}
Utility.selectedDeviceList.get(tag).setmConnectionState(Utility.CONNECTING);
if( Utility.selectedDeviceList.get(tag).getmBluetoothGatt()==null){
Log.w(Utility.TAG, "new connect :: "+ Utility.selectedDeviceList.get(tag).getAddress());
BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address);
if (device == null) {
Log.w(Utility.TAG, "Device not found. Unable to connect.");
return false;
}
try {
Utility.selectedDeviceList.get(tag).setmBluetoothGatt(device.connectGatt(this, false, mGattCallback));
}
catch (Exception e)
{
e.printStackTrace();
Log.d(Utility.TAG,"ConnectGatt exception caught");
}
}
return true;
}
This is the mGattCallBack :
private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
#Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
}
#Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
Log.d(Utility.TAG, "onServicesDiscovered");
}
#Override
public void onCharacteristicRead(BluetoothGatt gatt,BluetoothGattCharacteristic characteristic,int status) {
}
#Override
public void onCharacteristicWrite(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic, int status) {
super.onCharacteristicWrite(gatt, characteristic, status);
Log.d(Utility.TAG,">>onCharacteristicWrite");
}
#Override
public void onCharacteristicChanged(BluetoothGatt gatt,BluetoothGattCharacteristic characteristic) {
}
};
Hope it clears few things for you
It is possible to connect to multiple devices at a time. in my experience it works pretty stable and the number of devices you can connect to (stable) depends on your hardware. I found out that best practise (for me) was to create one separate service for the scanning stuff and one service for each Bluetoothconnection. it's important not to use bound services because the termination of a connection is not stible when binding to it.
With this pattern you can control your connection easily. To transport data out of your service you can use a broadcastreceiver, for example if you want to display the data in your main activity. Termination of the connection is pretty important so stop the service and in onDestroy call
mConnectedGatt.disconnect();
ble_device=null;
For the Scanning part I've used a List of Strings where I saved all the mac Adresses I want to find. When i found one device I deleted it from the list and if the list is empty it stopped the scanner service. To transmit my found device I used a broadcastreceiver and sent it to my main Activity. There I transmitted it to the right service.
Hope this helps
I'v made a Ble(Bluetooth 4.0 LE) App.
This App send byte data to BT device.
I had test when I made this function, but as soon as send(write) device was disconnected.
why disconnect?
Especially, LG SMART Phone.
Plz Help me..
//////////////
public static void Send_Data(byte[] data) {
if (mByteCharacteristic != null ) {
mByteCharacteristic.setValue(data);
mByteCharacteristic setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE);
if (bluetoothGatt != null) {
bluetoothGatt.writeCharacteristic(mByteCharacteristic);
}
}
}
///////////////
Make sure that:
mByteCharacteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE) != 0x0
Otherwise try omitting the setWriteType() call. By default the characteristic should be using the correct write type.
How to pair a Bluetooth Low Energy(BLE) device with Android to read encrypted data.
Using the information in the Android BLE page, I am able to discover the device, connect to it, discover services and read un-encrypted characteristics.
When I try to read an encrypted characteristic (one that will cause iOS to show a popup asking to pair and then complete the read) I am getting an error code 5, which corresponds to Insufficient Authentication.
I am not sure how to get the device paired or how to provide the authentication information for the read to complete.
I toyed with BluetoothGattCharacteristics by trying to add descriptors, but that did not work either.
Any help is appreciated!
When you get the GATT_INSUFFICIENT_AUTHENTICATION error, the system starts the bonding process for you. In the example below I'm trying to enable notifications and indications on glucose monitor. First I'm enabling the notifications on Glucose Measurement characteristic which can cause the error to appear.
#Override
public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
if (status == BluetoothGatt.GATT_SUCCESS) {
if (GM_CHARACTERISTIC.equals(descriptor.getCharacteristic().getUuid())) {
mCallbacks.onGlucoseMeasurementNotificationEnabled();
if (mGlucoseMeasurementContextCharacteristic != null) {
enableGlucoseMeasurementContextNotification(gatt);
} else {
enableRecordAccessControlPointIndication(gatt);
}
}
if (GM_CONTEXT_CHARACTERISTIC.equals(descriptor.getCharacteristic().getUuid())) {
mCallbacks.onGlucoseMeasurementContextNotificationEnabled();
enableRecordAccessControlPointIndication(gatt);
}
if (RACP_CHARACTERISTIC.equals(descriptor.getCharacteristic().getUuid())) {
mCallbacks.onRecordAccessControlPointIndicationsEnabled();
}
} else if (status == BluetoothGatt.GATT_INSUFFICIENT_AUTHENTICATION) {
// this is where the tricky part comes
if (gatt.getDevice().getBondState() == BluetoothDevice.BOND_NONE) {
mCallbacks.onBondingRequired();
// I'm starting the Broadcast Receiver that will listen for bonding process changes
final IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
mContext.registerReceiver(mBondingBroadcastReceiver, filter);
} else {
// this situation happens when you try to connect for the second time to already bonded device
// it should never happen, in my opinion
Logger.e(TAG, "The phone is trying to read from paired device without encryption. Android Bug?");
// I don't know what to do here
// This error was found on Nexus 7 with KRT16S build of Andorid 4.4. It does not appear on Samsung S4 with Andorid 4.3.
}
} else {
mCallbacks.onError(ERROR_WRITE_DESCRIPTOR, status);
}
};
Where the mBondingBroadcastReceiver is:
private BroadcastReceiver mBondingBroadcastReceiver = new BroadcastReceiver() {
#Override
public void onReceive(final Context context, final Intent intent) {
final BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
final int bondState = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, -1);
final int previousBondState = intent.getIntExtra(BluetoothDevice.EXTRA_PREVIOUS_BOND_STATE, -1);
Logger.d(TAG, "Bond state changed for: " + device.getAddress() + " new state: " + bondState + " previous: " + previousBondState);
// skip other devices
if (!device.getAddress().equals(mBluetoothGatt.getDevice().getAddress()))
return;
if (bondState == BluetoothDevice.BOND_BONDED) {
// Continue to do what you've started before
enableGlucoseMeasurementNotification(mBluetoothGatt);
mContext.unregisterReceiver(this);
mCallbacks.onBonded();
}
}
};
Remember to unregister the broadcast receiver when exiting the activity. It may have not been unregistered by the receicver itself.
You might need to check the Kernel smp.c file, which method of paring it invoke for paring. 1) passkey 2)Just work or etc . i guess if it will be able to invoke MIMT and passkey level of security , there will not be any authentication issue. Make sure all flags is set to invoke the SMP passkey methods. track by putting some print in smp.c file.
A solution which works in ICS : with btmgmt tool in android and hooking it in encryption APIs. with passkey or any other methods. it works. You might need to add the passkey APIs in btmgmt from latest bluez code.
i think new android 4.4 provide pairing method. same problem already i am facing so wait for update and hope over problem solved createBond() method .
http://developer.android.com/reference/android/bluetooth/BluetoothDevice.html#setPairingConfirmation%28boolean%29