I'm trying to connect to a BLE device that has one characteristic to write requests for data and another that sends notifications with said data. I'm able to write to the CCCD with no problems, but when I write to the write characteristic, 9 times out of 10 I get a gatt status 133 error. I've checked the properties, set the write type, and added a 600ms wait before each command, but nothing seems to be helping.
public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
super.onDescriptorWrite(gatt, descriptor, status);
Log.d(MY_TAG, "Wrote " + descriptor.getUuid() + " with status " + status);
if (status == BluetoothGatt.GATT_SUCCESS) {
Log.d(MY_TAG, "Write Successful");
enableAllNotifications();
try {
writeData(new byte[] {4, 0, 36});
} catch (InterruptedException e) {
e.printStackTrace();
}
} else {
Log.d(MY_TAG, "Write failed");
}
}
};
enableAllNotifications() just sets notifications on my end, since onCharacteristicChanged() only seems to be called when both have notifications set.
private void writeData(byte[] message) throws InterruptedException {
BluetoothGattCharacteristic characteristic = mainActivity.deviceServices.get(0)
.getCharacteristic(ActiCharacteristicUuids.WriteUUID);
characteristic.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT);
characteristic.setValue(message);
TimeUnit.MILLISECONDS.sleep(600);
bluetoothGatt.writeCharacteristic(characteristic);
}
public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
super.onCharacteristicWrite(gatt, characteristic, status);
Log.d(MY_TAG, "Inside onCharactersiticWrite with status: " + status);
if (status != BluetoothGatt.GATT_SUCCESS) {
Log.d(MY_TAG, "Write failed");
retryWrite(characteristic);
} else {
Log.d(MY_TAG, "Write Successful");
gatt.readCharacteristic(characteristic);
}
}
I don't know if this is helpful, but I've been testing with a Samsung Galaxy S10+ and a Samsung Galaxy S8 Active, and both seem to have these issues. I don't have another device to test if this is just a Samsung issue.
I don't know why this worked, but I had called writeDescriptor inside the main activity. For some reason, changing that call to readDescriptor, and then writing to the CCC inside readDescriptor solved the problem. I guess it was a threading issue? So, if anyone else is experiencing this problem, try to have all of your writes (including writeDescriptor and writeCharacteristic) on the same thread.
Related
For a project I want to send acceleration data via BLE 4.2 (with DLE). An acceleration packet consists of 81 values (24 bits each, total 243 bytes). My goal is to send 18 packets (1458 acceleration packets or 4374 bytes). I use a Samsung Galaxy A3 2017 Edition as a central and a microcontroller (nRF52832dk) as a peripheral. After all packets have been sent, the connection to the smartphone is terminated and new 1458 acceleration packets are recorded. After that, an RTC activates sleep mode, on the nRF52832dk, for 1 minute. After 1 minute, the nRF52832dk is going to advertising for a new connection with the smartphone. The following figure illustrates the problem:
https://ibb.co/Syb4GDg
The software on the nRF52832dk works perfectly fine. I am able to record data, go into sleep mode, wake-up after 1 minute and advertise for new connection. My problem is the software application. I use these tutorials:
• https://github.com/googlesamples/android-BluetoothLeGatt
• https://developer.android.com/guide/topics/connectivity/bluetooth-le
in BluetoothLeGatt I have changed:
mBluetoothGatt = device.connectGatt(this, true, mGattCallback);
with the second parameter on true its possible for the smartphone to autoconnect. This works fine (for the first connection with the nRF)
Originally it was:
mBluetoothGatt = device.connectGatt(this, false, mGattCallback);
Once a connection has been established, the callback routine is called by the GATT handler.
// 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
// New services discovered
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
// Result of a characteristic read operation
public void onCharacteristicRead(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic,
int status) {
if (status == BluetoothGatt.GATT_SUCCESS) {
broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
}
}
#Override
// Change in the characteristic
public void onCharacteristicChanged(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic) {
broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
}
}; </i>
after the connection is established and the first packet is received, i get into the broadcastUpdate
<i>private void broadcastUpdate(final String action,
final BluetoothGattCharacteristic characteristic) {
final Intent intent = new Intent(action);
// This is special handling for the nRF52 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_NRF.equals(characteristic.getUuid())) {
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());
}
} 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);
}
Now I got two problems (I had the same two problems before did the change in the code).
First Problem:
My first problem is, that I just receive only one of the 18 BLE package (the first one).
Second Problem:
My first Problem is also causing my second Problem. The Application does not recognize that the connection has been terminated from the nRF52832dk.
and I have another question. what exactly does
endBroadcast(intent);
?
This post is very complicated, I think you mean you cannot receive nrf52 data.
You need check nrf52 support how many MTU, BLE protect max is '247' and valid data length is '244' valid length is (DEVICE_MTU - OPCODE_LENGTH(1) - HANDLE_LENGTH(2));
So as you say you want receive 2474 bytes, it's at least 17 times.
current process at android is onCharacteristicChanged - > onCharacteristicRead
if you want trigger onCharacteristicChanged you need open remote device notification like this:
private void EnableNotification(BluetoothGattDescriptor d, BluetoothGatt gatt)
{
gatt.SetCharacteristicNotification(d.Characteristic, true);
d.SetValue(new byte[] { 1, 1 });
gatt.WriteDescriptor(d);
}
So, I am developing a Java BLE Android Module using Eclipse (to code the module and Appcelerator (to make the Android App). I am trying to receive data from a BLE Device and show it on an Android Phone. I can scan the device and connect to it.
But I really, really can't receive any data from it.
I have tried at least 10 different stuff but...
The main problem is that I don't know the BLE API very well, and I am a little noobie in Java. Can anyone please help a poor soul to actually read the data from the device?
The main problem is setting the Bluetooth Characteristic UUID (which I have). I just don't know how to do it...
Bellow are the codes for the Module...
public class AndroidbleModule extends KrollModule {
public static final String LCAT = "BLE";
private BluetoothManager btManager;
private BluetoothAdapter btAdapter;
private BluetoothDevice btDevice;
private TiApplication appContext;
private Activity activity;
private KrollFunction onFound;
private KrollFunction onConnections;
private BluetoothLeScanner btScanner;
private UUID uuid;
BluetoothGatt bluetoothGatt;
BluetoothGattCharacteristic btChar;
BluetoothGattCallbackHandler btData;
KrollDict kd;
Boolean isConnected = false;
BluetoothGatt connectedGatt;
private ScanCallback scanCallback = new ScanCallback() {
#Override
public void onScanResult(int callbackType, ScanResult result) {
BluetoothDevice device = result.getDevice();
if (device != null) {
BluetoothDeviceProxy btDeviceProxy = new
BluetoothDeviceProxy(device);
if (device.getName() != null) {
Log.d(LCAT, "Found: " + device.getName() + " " +
device.getAddress());
ArrayList<String> ids = new ArrayList<String>();
if (device.getUuids() != null) {
for (ParcelUuid id1 : device.getUuids()) {
ids.add(id1.toString());
}
}
btDevice = device;
kd = new KrollDict();
kd.put("name", btDevice.getName());
kd.put("macaddress", btDevice.getAddress());
fireEvent("nb_DevicesFound", kd);
btScanner.stopScan(scanCallback);
}
}
}
};
#Kroll.method
public boolean connect()
{
try {
bluetoothGatt = btDevice.connectGatt(appContext, true,
new BluetoothGattCallbackHandler(AndroidbleModule.this));
if (bluetoothGatt != null) {
System.out.println("***** ***** Connected to: =====>>>>> " + btDevice.getAddress() + " " + btDevice.getName());
this.fireEvent("nb_onConnect",null);
isConnected = true;
bluetoothGatt = connectedGatt;
}
} catch (Exception e) {
isConnected = false;
this.fireEvent("nb_NoConnection", null);
}
return true;
};
#Kroll.method
public void readData()
{
System.out.println("WHAT THE HELL DO I DO????");
}
}
public final class BluetoothGattCallbackHandler extends
BluetoothGattCallback {
private static final String LCAT = AndroidbleModule.LCAT;
private KrollProxy proxy;
private static final String UUID_SERVICE_TS002004 = "6E400001-B5A3-F393-E0A9-E50E24DCCA9E";
private static final String UUID_CHARACTERISTIC_WRITE_TS002004 = "6E400002-B5A3-F393-E0A9-E50E24DCCA9E";
private static final String UUID_CHARACTERISTIC_NOTIFY_TS002004 = "6E400003-B5A3-F393-E0A9-E50E24DCCA9E";
BluetoothGattCharacteristic btCharacteristic;
public BluetoothGattCallbackHandler(KrollProxy proxy) {
super();
this.proxy = proxy;
}
#Override
public void onConnectionStateChange(final BluetoothGatt gatt,
final int status, final int newState) {
KrollDict kd = new KrollDict();
kd.put("newState", newState);
kd.put("status", status);
if (proxy.hasListeners("didConnectionStateChange")) {
proxy.fireEvent("didConnectionStateChange", kd);
}
gatt.discoverServices();
}
#Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
super.onServicesDiscovered(gatt, status);
Log.i(LCAT,"onServicesDiscovered");
if (status != BluetoothGatt.GATT_SUCCESS) return;
btCharacteristic =
gatt.getService(UUID.fromString(UUID_SERVICE_TS002004)).getCharacteristic(UUID.fromString(UUID_CHARACTERISTIC_NOTIFY_TS002004));
gatt.setCharacteristicNotification(btCharacteristic,true);
BluetoothGattDescriptor descriptor = btCharacteristic.getDescriptor(UUID.fromString(UUID_CHARACTERISTIC_WRITE_TS002004));
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
gatt.writeDescriptor(descriptor);
}
#Override
public void onCharacteristicChanged(BluetoothGatt gatt,
final BluetoothGattCharacteristic characteristic) {
byte[] data = characteristic.getValue();
Log.i(LCAT, "Char changed " + data.toString());
for (BluetoothGattDescriptor descriptor :
characteristic.getDescriptors()) {
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE;
}
}
#Override
public void onCharacteristicRead(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic, int status) {
super.onCharacteristicRead(gatt, characteristic, status);
Log.i(LCAT,"onCharacteristicRead");
}
#Override
public void onDescriptorRead(BluetoothGatt gatt,
BluetoothGattDescriptor descriptor, int status) {
super.onDescriptorRead(gatt, descriptor, status);
Log.i(LCAT,"onDescriptorRead");
}
}
I expect to some good soul that will go to Heaven to have mercy on me and help me get those sweet bytes of data.
One of the things that is missing in your code, is to Set Notifications, so the Central can listen to the Peripheral's response.
Try something like this:
public void setNotifications() {
BluetoothGattService service = bluetoothGatt.getService(SERVICE_UUID);
BluetoothGattCharacteristic characteristic = service.getCharacteristic(CHARACTERISTIC_UUID);
BluetoothGattDescriptor descriptor = characteristic.getDescriptor(DESCRIPTOR_UUID);
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
characteristic.addDescriptor(descriptor);
characteristic.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE);
bluetoothGatt.writeDescriptor(descriptor);
bluetoothGatt.setCharacteristicNotification(characteristic, true);
}
After that, you can send commands to the device and hear it's return.
public void returnData(String data) {
BluetoothGattService service = bluetoothGatt.getService(SERVICE_UUID);
BluetoothGattCharacteristic characteristic = service.getCharacteristic(CHARACTERISTIC_UUID);
String dataString = data;
dataString.getBytes();
writeCharacteristic.setValue(dataString);
writeCharacteristic.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE);
bluetoothGatt.writeCharacteristic(writeCharacteristic);
}
First of all, read the bluetooth overview. Check if bluetooth permissions have been added to the project.
One thing that is wrong here is that isConnected=true is set too early, because you can consider that you are connected after ble services has been discovered and (status == BluetoothGatt.GATT_SUCCESS). Otherwise, you can't read and write characteristics.
A good starting point can be this repo from google. It is old project and I'm not sure if you have to update some dependency to make it compile, but nothing important.
Establish a connection and start reading bytes is easy, but if you want establish a reliable connection with ble device, due to android fragmentation and manufacturerers that don't follow the bluetooth specs, it can be super difficult, almost imposible to make a bluetooth low energy that works perfect for all devices.
Once you have started to read some bytes, I suggest this video to learn some important tricks about. If you want go deeper, read carefully this fantastic resource about ble.
Once you start to get desperate with ble, probably it will be a good moment to read this list of known issues
Finally, you will discover that the best thing that you can do with ble low energy in android is use open source libraries like the Nordic semiconductor ble library or RxAndroid Ble.
But before use a ble library, it is a good practice understand what is doing the library and understand why you need it.
EDIT: I have never used appcelerator, but here you have a bluetooth module for appcelerator titanium.
You have the wrong uuid for the Client Characteristic Configuration Descriptor. It should be 00002902-0000-1000-8000-00805f9b34fb but you have written 6E400002-B5A3-F393-E0A9-E50E24DCCA9E.
I am trying to read values from BLE device. Steps I followed:
I am able to discover the BLE device and connect to it.
I am able to find the required characteristic by parsing through the
services and get the GattCharacteristic.
I am able to write a value to the BLE device characteristic and that's
verified.
The properties for that characteristic that I am trying to read the
value from are: READ, WRITE and INDICATE/NOTIFY.
The functions I am using for read and write are as follows:
a) Read function:
public void readCharacteristic(BluetoothGattCharacteristic characteristic)
{
if (mBluetoothAdapter == null || mBluetoothGatt == null) {
Log.w(TAG, "BluetoothAdapter not initialized");
return;
}
boolean status;
Log.w(TAG, "SCBABLe: Writing BLuetooth");
status=mBluetoothGatt.readCharacteristic(characteristic);
if(status) {
Log.w(TAG, "Read Successfully");
}
else
{
Log.w(TAG, "Read Unsuccessfully");
}
}
b) Write function
public void writeCharacteristic(BluetoothGattCharacteristic characteristic)
{
if (mBluetoothAdapter == null || mBluetoothGatt == null)
{
Log.w(TAG, "BluetoothAdapter not initialized");
return;
}
boolean status;
Log.w(TAG, "SCBABLe: Writing BLuetooth");
status=mBluetoothGatt.writeCharacteristic(characteristic);
if(status) {
Log.w(TAG, "Write Successfully");
}
else
{
Log.w(TAG, "Write Unuccessfully");
}
}
I am calling the above two from a different activity, after I get the required characteristic(by matching the UUIDs).
//Current Activity Name: DeviceControlActivity
//BluetoothLeService is the other activity where Read and write are defined
private static final DeviceControlActivity holder1 = new DeviceControlActivity();
public BluetoothGattCharacteristic reqChar;
public BluetoothLeService reqService;
private BluetoothLeService mBluetoothLeService;
private void displayGattServices(List<BluetoothGattService> gattServices)
{
if (gattServices == null) return;
String uuid = null;
for (BluetoothGattService gattService : gattServices)
{
uuid = gattService.getUuid().toString();
// Loops through available Characteristics.
for (BluetoothGattCharacteristic gattCharacteristic : gattCharacteristics)
{
uuid = gattCharacteristic.getUuid().toString();
//SampleGattAttributes.UNKNOWN_CHARACTERISTIC is the hardcoded uuid against which i am checking
if((uuid.equals((SampleGattAttributes.UNKNOWN_CHARACTERISTIC))))
{
holder1.reqChar = gattCharacteristic;
holder1.reqService = mBluetoothLeService;
//Call for write
byte [] byte1= {0x01, 0x10};
holder1.reqChar.setValue(byte1);
holder1.reqService.writeCharacteristic(holder1.reqChar);
//Call for read
holder1.reqService.readCharacteristic(holder1.reqChar);
Result: Read function is returning false and write function is returning true so the value is getting written successfully for the required characteristic.(verified it)
Please, could anyone help and tell why the read is not getting executed? Why is it still returning false when it has Read as property and proper value defined?
The problem is in these few lines.
holder1.reqService.writeCharacteristic(holder1.reqChar);
//Call for read
holder1.reqService.readCharacteristic(holder1.reqChar);
After calling writeCharacteristic, if you do any extra read/write/etc. , the call will not be executed and return false. You have to wait for BluetoothGattCallback.onCharacteristicWrite before doing further operation.
In the Android BLE implementation, the gatt operation calls need to be queued so that only one operation (read, write, etc.) is in effect at a time. So for example, after gatt.readCharacteristic(characteristicX) is called, you need to wait for the gatt callbackBluetoothGattCallback.onCharacteristicRead() to indicate the read is finished. If you initiate a second gatt.readCharacteristic() operation before the previous one completes, the second one will fail (by returning false) This goes for all of the gatt.XXX() operations.
Its a little work, but I think the best solution is to create a command queue for all the gatt operations and run them one at a time. You can use the command pattern to accomplish this.
I am working with a BLE device in ANDROID.
Here is where I set up the endpoint 0000fff4-0000-1000-8000-00805f9b34fb to receive notifications (CLIENT_CHARACTERISTIC_CONFIG is 00002902-0000-1000-8000-00805f9b34fb
public void k2DigitalNotification(BluetoothGattCharacteristic characteristic,
boolean enabled)
{
Boolean myStatus;
if (MY_BLUETOOTH_SERVICE.equals(characteristic.getUuid()))
{
Log.v(TAG, "Characteristic: " + characteristic.getUuid());
BluetoothGattDescriptor descriptor = characteristic.getDescriptor(
UUID.fromString(SampleGattAttributes.CLIENT_CHARACTERISTIC_CONFIG));
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
myStatus = mBluetoothGatt.writeDescriptor(descriptor);
Log.v(TAG,"Write Status::"+myStatus);
bluetoothGatt.setCharacteristicNotification(characteristic, true);
}
}
Here is the code where I write out to the device. THIS WORKS! 100%, the data shows up at the embedded BLE device. (endpoint is 0000fff1-0000-1000-8000-00805f9b34fb )
public void k2digitalWriteToCharacteristic(BluetoothGattCharacteristic characteristic) {
if (BLE_ENDPOINT.equals(characteristic.getUuid()))
{
Log.v(TAG,"0Xfff1");
byte[] data3Send = new byte[4];
data3Send[0] = 0x31;
data3Send[1] = 0x01;
data3Send[2] = 0x5a;
data3Send[2] = 0x0d;;
characteristic.setValue(data3Send);
boolean status = mBluetoothGatt.writeCharacteristic(characteristic);
Log.v(TAG, "Status is:" + String.valueOf(status));
}
}
The BLE device is sending the data out.....I've verified this in apps like nRFMaster from Nordic Semi or BLEScanner from Bluepixel.
But I NEVER EVER see the callback.
public void onCharacteristicChanged(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic) {
Log.v(TAG,"A Characteristic Change?!?!?!");
byte[] data = characteristic.getValue();
Log.v(TAG,"Here is the data: "+data[0]);
broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
}
Any help? I've been banging my head against the wall all week.
My issue was regarding the ble device was busy. I was attempting to read and subscribe without much gap in time and the ble device was busy. you can check if the enable notification was successful with the following code:
Boolean status = bluetoothGatt.writeDescriptor(descriptor);
This will return true if the writeDescriptor was successful and false otherwise - hence helping you debug if the ble device is busy.
When my code didn't work, I started with the project I found here and ran it against our custom bluetooth device on my moto-x. Against the general attributes with profiles, I get data back from the following code:
public void readCharacteristic(BluetoothGattCharacteristic characteristic) {
if (mBluetoothAdapter == null || mBluetoothGatt == null) {
Log.w(TAG, "BluetoothAdapter not initialized");
return;
}
mBluetoothGatt.readCharacteristic(characteristic);
}
which returns data asynchronously in:
public void onCharacteristicRead(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic,
int status) {
if (status == BluetoothGatt.GATT_SUCCESS) {
broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
}
}
This also works if i run this code against a heart rate monitor.
If I run it against one of our custom properties without a default profile, the data never comes back. Ever.
A google search included this: Cannot read characteristic. Android BLE but setting up notifications did not solve my problem.
Any advice?
I think the PROPERTY_READ, PROPERTY_WRITE isn't set to enabled in your custom properties.
Check for BluetoothGattCharacteristic PROPERTIES -I didn't realize that need to enable the PROPERTY_WRITE, PROPERTY_READ on the BLE hardware and that wasted a lot of time.
The part where to check the PROPERTY should be something like:
if ((characteristic.getProperties() &&
BluetoothGattCharacteristic.PROPERTY_READ)> 0) {
// toast or log here
}
These enable/disable the app or for that matter any app to connect to the bluetooth low energy device
Refer servicesListClickListner on this page