I am working with a BLE compatible health device (that can store Measurements) to read data using android app. I am able to scan services and setup indications on a particular characteristic that stores the readings. While I am able to listen for indications and obtain the values, the indications start from first reading saved on the device to the latest reading on the device, it takes a lot of time to get the latest value (especially as the device stores large number of values).
Is there a way I can get the latest reading first so I can stop reading further older values which are unnecessary for me.
Here is piece of the code relevant to enabling Indications for the desired characteristic.
public static final UUID CCCD = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb");
Boolean gattsetchar = gatt.setCharacteristicNotification(characteristic, true);
if (!gattsetchar){
Log.e(TAG, "set characteristic=" + gattsetchar);
}
BluetoothGattDescriptor descriptor = characteristic.getDescriptor(CCCD);
if(descriptor != null){
if ((characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_INDICATE) != 0) {
Log.d(TAG, "Enabling Indication");
descriptor.setValue(BluetoothGattDescriptor.ENABLE_INDICATION_VALUE);
}
}
Boolean writeDes = mBluetoothGatt.writeDescriptor(descriptor);
Log.d(TAG, "Write Descriptor " + writeDes + " " + descriptor.getCharacteristic().getUuid());
Related
first post here!
I'm trying to use an Arduino Nano RP2040 Connect as a Bluetooth Low Energy peripheral, currently having 1 service with 3 characteristic exposing:
a Int representing the value off of a potentiometer;
a Int representing the RSSI (did it as for visual debug, there's a LED representing it);
a Bool representing the status of a LED;
Service and characteristics UUIDs are randomly generated.
The code is this:
#include <ArduinoBLE.h>
#define LED_RSSI 2
#define LED_STATUS 4
int oldSensorValue = 0;
int oldRssiValue = 0;
bool ledState = false;
long previousMillis = 0;
BLEService sensorService("18902a9a-1f4a-44fe-936f-14c8eea41800");
BLEIntCharacteristic sensorChar("18902a9a-1f4a-44fe-936f-14c8eea41801", BLERead | BLENotify);
BLEIntCharacteristic rssiChar("18902a9a-1f4a-44fe-936f-14c8eea41802", BLERead | BLENotify);
BLEBoolCharacteristic ledChar("18902a9a-1f4a-44fe-936f-14c8eea41803", BLERead | BLEWrite | BLENotify);
void setup() {
Serial.begin(9600);
while (!Serial) {
;
}
pinMode(LED_BUILTIN, OUTPUT);
pinMode(LED_RSSI, OUTPUT);
pinMode(LED_STATUS, OUTPUT);
digitalWrite(LED_STATUS, ledState);
if (!BLE.begin()) {
Serial.println("starting BLE failed!");
while (1)
;
}
BLE.setEventHandler(BLEConnected, blePeripheralConnectHandler);
BLE.setEventHandler(BLEDisconnected, blePeripheralDisconnectHandler);
BLE.setLocalName("Sensor Monitor");
//BLE.setDeviceName(name); //defaults “Arduino”
BLE.setAppearance(0x015); //set appearance as "Sensor" 0x015
sensorService.addCharacteristic(sensorChar);
sensorService.addCharacteristic(rssiChar);
sensorService.addCharacteristic(ledChar);
BLE.addService(sensorService);
BLE.setAdvertisedService(sensorService); // add the service UUID
sensorChar.writeValue(oldSensorValue);
rssiChar.writeValue(oldRssiValue);
ledChar.writeValue(ledState);
ledChar.setEventHandler(BLEWritten, onUpdateLed);
sensorChar.setEventHandler(BLESubscribed, onSubscribedChar);
rssiChar.setEventHandler(BLESubscribed, onSubscribedChar);
ledChar.setEventHandler(BLESubscribed, onSubscribedChar);
sensorChar.setEventHandler(BLEUnsubscribed, onUnsubscribedChar);
rssiChar.setEventHandler(BLEUnsubscribed, onUnsubscribedChar);
ledChar.setEventHandler(BLEUnsubscribed, onUnsubscribedChar);
startAdvertise();
}
void loop() {
BLEDevice central = BLE.central();
if (central) {
while (central.connected()) {
long currentMillis = millis();
long timer2 = millis();
if (currentMillis - previousMillis >= 200) {
previousMillis = currentMillis;
updateSensorValue();
updateRSSI();
}
}
}
}
void startAdvertise() {
if (!BLE.advertise()) {
Serial.println("Bluetooth® device failed to advertise.");
while (1) {
;
}
} else {
Serial.println("Bluetooth® device active, waiting for connections...");
}
}
void updateSensorValue() {
int sensorValue = analogRead(A0);
if (sensorValue != oldSensorValue) {
// Serial.print("sensor Level % is now: ");
// Serial.println(sensorValue);
sensorChar.writeValue(sensorValue);
oldSensorValue = sensorValue;
}
}
void updateRSSI() {
int newRssiValue = BLE.rssi();
if (newRssiValue != 127)
analogWrite(LED_RSSI, map(abs(newRssiValue), 0, 128, 255, 0));
else
analogWrite(LED_RSSI, 0);
if (newRssiValue != oldRssiValue) {
// Serial.print("RSSI is now: ");
// Serial.println(newRssiValue);
rssiChar.writeValue(newRssiValue);
oldRssiValue = newRssiValue;
}
}
void onUpdateLed(BLEDevice central, BLECharacteristic characteristic) {
Serial.println("Central '" + central.address() + "' wrote '" + ledChar.value() + "' to characteristic '" + characteristic.uuid() + "'");
ledState = ledChar.value();
digitalWrite(LED_STATUS, ledState);
}
void onSubscribedChar(BLEDevice central, BLECharacteristic characteristic) {
Serial.println("Central '" + central.address() + "' subscribed to characteristic '" + characteristic.uuid() + "'");
}
void onUnsubscribedChar(BLEDevice central, BLECharacteristic characteristic) {
Serial.println("Central '" + central.address() + "' unsubscribed to characteristic '" + characteristic.uuid() + "'");
}
void blePeripheralConnectHandler(BLEDevice central) {
// central connected event handler
Serial.println("Central '" + central.address() + "' connected");
digitalWrite(LED_BUILTIN, HIGH);
BLE.stopAdvertise();
}
void blePeripheralDisconnectHandler(BLEDevice central) {
// central disconnected event handler
Serial.println("Central '" + central.address() + "' disconnected");
digitalWrite(LED_BUILTIN, LOW);
analogWrite(LED_RSSI, 0);
startAdvertise();
}
I have tried its functionality with nRF Connect and LightBlue on Android, trying to read, subscribe and write characteristics, everything seems to work as intended.
The problem I'm facing is that whatever I do with the UUIDs on the Arduino side I keep seeing the same ones on the apps mentioned above.
On nRF Connect there's the possibility to "Refresh services" and that does fixes temporarely the issue but if I disconnect from the peripheral and reconnect the issue appears again.
Tried cleaning data and cache of both apps, of bluetooth system app, without success.
Its not only those 2 apps that do this behaviour:I'm trying to interface the Arduino with a Unity app ran on Android and it fails to subscribe to the characteristics with the "new" UUIDs but somehow succeed if I set the Arduino with the UUIDs my phone is stuck seeing.
A completely different phone sees updates UUIDs, everytime, but I need to use my phone for developing.
Is there something wrong with my phone behaviour? It's a Motorola Moto G52.
How can I fix this? What am I doing wrong? Any help appreciated
Apparently the client uses GATT caching. This way it saves some round trips for service discovery for every connection start.
A client can cache the database structure (only) in one of the following cases:
The devices are bonded. The server uses the Service Changed characteristic to inform the client when something changes with the db structure, by sending an indication containing the range of the handles that have changed.
Both the client and server support the relatively new Database Hash characteristic, which contains a hash of the db structure which the server stores in a characteristic which the client reads on every reconnection. If it has changed since the last time, the client must rediscover.
The server never changes the db structure during its lifetime. This is indicated to the client by not having the Service Changed characteristic.
If none of the above cases apply, then the client is buggy and behaves incorrectly.
I am implementing BLE in Android Studio. I have connected with the peripheral device ok. In my onServicesDiscovered method I want to analyze the services (and characteristics) and I get something like the following when I print out:
android.bluetooth.BluetoothGattService#41b6dd18
There is 4 services in the list and they all look similar except for the numbers at the end. How can I convert this to useful information. I have seen no reference to this format.
Thanks.
Try to read the uuid from the BluetoothGattService object.
You can find uuid of standard services on Bluetooth SIG website. If the uuid is not there (i.e. custom services), you should read the manual of the peripheral or reach out the peripheral maker.
That depends on what you consider "useful" information.
BLE works mostly like dictionary where you look up long numbers (characteristics) and get binary data, so without prior information about the device you're working on, there is not much you can see when you discover services.
That said, in the BLE docs, there is a method displayGattServices() which puts the discovered services info in an ExpandableListView, and here I changed it to print the UUIDs of services and characteristics to logcat instead.
Besides the UUIDs, you can use getProperties() to find out other characteristic properties such as the format of the characteristic data, or getPermissions() to see whether you can read or write the characteristic.
// Demonstrates how to iterate through the supported GATT
// Services/Characteristics.
private void displayGattServices(List<BluetoothGattService> gattServices) {
final String TAG = "BleServiceInfo";
if (gattServices == null) return;
String uuid;
String unknownServiceString = "Unknown service"
String unknownCharaString = "Unknown characteristic"
// Loops through available GATT Services.
for (BluetoothGattService gattService : gattServices) {
Log.d(TAG, "Service: " + gattService.getUuid().toString());
// Loops through available Characteristics.
for (BluetoothGattCharacteristic gattCharacteristic : gattCharacteristics) {
Log.d(TAG, "\tCharacteristic: " + gattCharacteristic.getUuid().toString());
}
}
}
Call this method from onServicesDisccovered() like this:
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
....
displayGattServices(gatt.getServices());
}
I'm trying to use BLE in Android with a deivice that has fields : battery (RO), status (RO), intensity (R/W).
I followed some tutorials about setting up the Gatt with the device.
I Use the following code :
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
Log.w(LOGGER + mOwner.get().getName(), "onServicesDiscovered received: " + status);
if (status == BluetoothGatt.GATT_SUCCESS) {
// Save all services
for (BluetoothGattService gattService : mBluetoothGatt.getServices()) {
for (BluetoothGattCharacteristic charac : gattService
.getCharacteristics()) {
if (isContained(charac) {
mCharacteristics.set(mCharacteristicsUuid.indexOf(charac.getUuid()
.toString()), charac);
mBluetoothGatt.setCharacteristicNotification(charac, true);
// UUID for notification
BluetoothGattDescriptor descriptor = charac
.getDescriptor(UUID.fromString("00002902-0000-1000-" +
"8000-00805f9b34fb"));
if (descriptor != null) {
descriptor.setValue(BluetoothGattDescriptor
.ENABLE_NOTIFICATION_VALUE);
mBluetoothGatt.writeDescriptor(descriptor);
}
}
}
}
So i try to discover services, if done iterate over them, and their characteristics. If their UUIDs are correct, i add them to mCharacteristics.
If this a value readable (here, everyone of them), I enable notification.
When it's done i try to do a first read :
Log.e(LOGGER + mOwner.get().getName(), "Reading first charac(index=0) : " + mBluetoothGatt
.readCharacteristic(mCharacteristics.get(0)));
}
I have to precise that all of this is inside a dedicated thread, kept up in an Android Service.
I'm sure that the device is connected, and characteristics too.
For each one i verified the value of (Property & REEADABLE), that always > 0...
But for the Read only characteristics i ALWAYS get false on read...
And for the intensity (read/write), read returns true, but onCharacteristicRead() is never called, and the getValue() method returns null.
I'm pretty newer using BLE in Android.
Someone has an idea about the problem ?
Thanks in advance !
EDIT : I finally found the solution but not as I expected...
In fact, if I set the notification enabled, ii can't read the characteristic after...
Can someone tell me how i can perform this way :
> 1) on discover
> a) get all characts + set in list characs I want
> b) enable notification for ALL of these charac (if possible, I supposed, because of the descriptor that can be null ? )
> c) first read to know starting values
You have the answer here: BLE Android, can't enable more than 1 notify on read characteristics.
You have to wait for a GATT operation to complete before you can do another one.
I want to get the value of the HRM of an "A&D UA-651BLE" device.
this is what's written in the datasheet of this device to get the HRM value:
Set the application to pairing mode to start scanning.
Start pairing of A&D BLE device following each instruction manual.
At pairing mode, the application should set time and date and any other device settings
to A&D BLE device. After successful pairing, A&D BLE device shows “End” on the screen.
Take a measurement and finish the measurement, then A&D BLE device start BLE
connection with advertising. The application starts scanning with suitable interval so that
the application catches the advertising of A&D BLE device as soon as it can.
At initial connection or pairing, the Application set “2” to CCCD (Client Characteristic
Configuration Descriptor) so that A&D BLE device sends a measurement data with
Indication.
After A&D device recognizes to be set “2” to CCCD and to be synchronized time and date
within 5 seconds after connected, send the data with Indication.
If the timeout set CCCD and time and date is expired, A&D BLE device will not send data
and store the data in memory. The stored data in A&D BLE device can send next
successful connection.
this is my service code:
public void setCharacteristicNotification(BluetoothGattCharacteristic characteristic,
boolean enabled) {
if (mBluetoothAdapter == null || mBluetoothGatt == null) {
Log.w(TAG, "BluetoothAdapter not initialized");
return;
}
mBluetoothGatt.setCharacteristicNotification(characteristic, enabled);
// This is specific to Heart Rate Measurement.
if (UUID_HEART_RATE_MEASUREMENT.equals(characteristic.getUuid())) {
BluetoothGattDescriptor descriptor = characteristic.getDescriptor(
UUID.fromString(SampleGattAttributes.CLIENT_CHARACTERISTIC_CONFIG));
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
mBluetoothGatt.writeDescriptor(descriptor);
}
}
and this is the method that read data:
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));
Log.e("HRM value",stringBuilder.toString());
dataComposition.put(characteristic.getUuid().toString(),stringBuilder.toString());
intent.putExtra(EXTRA_DATA,dataComposition);
}
the problem is that this code doesn't return any data !!
There's an Android Open Source Project example that does precisely this, easiest option would be to clone the android-BluetoothLeGatt code, build and compare it to your own. If you can't spot the difference / issue simply deploy both app's and step through both sets of code. Having some known working code will also help to rule out the possibility that the HRM is not functioning properly.
Do you have and example , i try this with equal device and i cant obtain the information y try with
public String response() {
if (mConnected) {
mBluetoothLeService.readCharacteristic(characteristica);
byte response[] = characteristica.getValue();
String respuesta = ReadBytes(response);
mBluetoothLeService.disconnect();
return respuesta;
} else {
return null;
}
}
I am developing an Android app that needs to read the Keyfob's Accelerometer data. Until now I've followed this tutorial: https://thenewcircle.com/s/post/1553/bluetooth_smart_le_android_tutorial
With it I was able to connect with the Keyfob, search for services and read some characteristics. The problem is when I try to enable the keyfob's Accelerometer, the bluetooth connection simply drops.
This is the code I use to try to enable the accelerometer:
private void enableAccelerometer(BluetoothGatt gatt){
BluetoothGattCharacteristic characteristic;
BluetoothGattService service;
Log.d(TAG, "ligando acelerometro");
service = gatt.getService(ACCELEROMETER_SERVICE);
if(service == null){
Log.d(TAG, "Not able to find the service");
}
else{
Log.d(TAG, "Service found");
characteristic = service.getCharacteristic(ENABLE_ACCELEROMETER);
if(characteristic == null){
Log.d(TAG, "Characteristic not found");
}
else{
characteristic.setValue(new byte[] {0x01});
if (!gatt.writeCharacteristic(characteristic)){
Log.d(TAG, "writing failed ");
}
else {
Log.d(TAG, "writing successful: ");
}
}
}
This method is called in the "onServicesDiscovered" callback function.
The Texas Instrument CC2540/41 Mini Development Kit User’s Guide states that to enable the accelerometer it is necessary to write "01" in the enable acceleromenter characteristic in the accelerometer serivce, that's what I am doing with this code.
The connection between the phone (LG G2 mini running Android 4.4.2) drops when I write:
characteristic.setValue(new byte[] {0x01});
I am sure it is this line that is making the connection drop, if I comment it out or simply try to write a string instead of a byte, the connection doesn't drop.
Does anyone have any idea what am I doing wrong?
Turns out that after a week I found a way to turn the accelerometer on. I still don't know why it is working only that way, but I just changed:
characteristic.setValue(new byte[] {0x01});
to
characteristic.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
I don't know what value is exactly inside the "BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE" constant, but it worked.