I using MI BAND 2 BLE. I made demo app for ready characteristics from BLE. My sample code here
package id.aashari.code.miband2.Activities;
public class MainActivity extends Activity {
Boolean isListeningHeartRate = false;
BluetoothAdapter bluetoothAdapter;
BluetoothGatt bluetoothGatt;
BluetoothDevice bluetoothDevice;
Button btnStartConnecting, btnGetBatteryInfo, btnGetHeartRate, btnWalkingInfo, btnStartVibrate, btnStopVibrate;
EditText txtPhysicalAddress;
TextView txtState, txtByte;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initializeObjects();
initilaizeComponents();
initializeEvents();
getBoundedDevice();
}
void getBoundedDevice() {
Set<BluetoothDevice> boundedDevice = bluetoothAdapter.getBondedDevices();
for (BluetoothDevice bd : boundedDevice) {
if (bd.getName().contains("MI Band 2")) {
txtPhysicalAddress.setText(bd.getAddress());
}
}
}
void initializeObjects() {
bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
}
void initilaizeComponents() {
btnStartConnecting = (Button) findViewById(R.id.btnStartConnecting);
btnGetBatteryInfo = (Button) findViewById(R.id.btnGetBatteryInfo);
btnWalkingInfo = (Button) findViewById(R.id.btnWalkingInfo);
btnStartVibrate = (Button) findViewById(R.id.btnStartVibrate);
btnStopVibrate = (Button) findViewById(R.id.btnStopVibrate);
btnGetHeartRate = (Button) findViewById(R.id.btnGetHeartRate);
txtPhysicalAddress = (EditText) findViewById(R.id.txtPhysicalAddress);
txtState = (TextView) findViewById(R.id.txtState);
txtByte = (TextView) findViewById(R.id.txtByte);
}
void initializeEvents() {
btnStartConnecting.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
startConnecting();
}
});
btnGetBatteryInfo.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
getBatteryStatus();
}
});
btnStartVibrate.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
startVibrate();
}
});
btnStopVibrate.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
stopVibrate();
}
});
btnGetHeartRate.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
startScanHeartRate();
}
});
}
void startConnecting() {
String address = txtPhysicalAddress.getText().toString();
bluetoothDevice = bluetoothAdapter.getRemoteDevice(address);
Log.v("test", "Connecting to " + address);
Log.v("test", "Device name " + bluetoothDevice.getName());
bluetoothGatt = bluetoothDevice.connectGatt(this, true, bluetoothGattCallback);
}
void stateConnected() {
bluetoothGatt.discoverServices();
txtState.setText("Connected");
}
void stateDisconnected() {
bluetoothGatt.disconnect();
txtState.setText("Disconnected");
}
void startScanHeartRate() {
txtByte.setText("...");
BluetoothGattCharacteristic bchar = bluetoothGatt.getService(CustomBluetoothProfile.HeartRate.service)
.getCharacteristic(CustomBluetoothProfile.HeartRate.controlCharacteristic);
bchar.setValue(new byte[]{21, 2, 1});
bluetoothGatt.writeCharacteristic(bchar);
}
void listenHeartRate() {
BluetoothGattCharacteristic bchar = bluetoothGatt.getService(CustomBluetoothProfile.HeartRate.service)
.getCharacteristic(CustomBluetoothProfile.HeartRate.measurementCharacteristic);
bluetoothGatt.setCharacteristicNotification(bchar, true);
BluetoothGattDescriptor descriptor = bchar.getDescriptor(CustomBluetoothProfile.HeartRate.descriptor);
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
bluetoothGatt.writeDescriptor(descriptor);
isListeningHeartRate = true;
}
void getBatteryStatus() {
txtByte.setText("...");
BluetoothGattCharacteristic bchar = bluetoothGatt.getService(CustomBluetoothProfile.Basic.service)
.getCharacteristic(CustomBluetoothProfile.Basic.batteryCharacteristic);
if (!bluetoothGatt.readCharacteristic(bchar)) {
Toast.makeText(this, "Failed get battery info", Toast.LENGTH_SHORT).show();
}
else
{
Toast.makeText(getApplicationContext(),"Successfully readed", Toast.LENGTH_LONG).show();
}
}
void startVibrate() {
BluetoothGattCharacteristic bchar = bluetoothGatt.getService(CustomBluetoothProfile.AlertNotification.service)
.getCharacteristic(CustomBluetoothProfile.AlertNotification.alertCharacteristic);
bchar.setValue(new byte[]{2});
if (!bluetoothGatt.writeCharacteristic(bchar)) {
Toast.makeText(this, "Failed start vibrate", Toast.LENGTH_SHORT).show();
}
}
void stopVibrate() {
BluetoothGattCharacteristic bchar = bluetoothGatt.getService(CustomBluetoothProfile.AlertNotification.service)
.getCharacteristic(CustomBluetoothProfile.AlertNotification.alertCharacteristic);
bchar.setValue(new byte[]{0});
if (!bluetoothGatt.writeCharacteristic(bchar)) {
Toast.makeText(this, "Failed stop vibrate", Toast.LENGTH_SHORT).show();
}
}
final BluetoothGattCallback bluetoothGattCallback = new BluetoothGattCallback() {
#Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
super.onConnectionStateChange(gatt, status, newState);
Log.v("test", "onConnectionStateChange");
if (newState == BluetoothProfile.STATE_CONNECTED) {
stateConnected();
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
stateDisconnected();
}
}
#Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
super.onServicesDiscovered(gatt, status);
Log.v("test", "onServicesDiscovered");
listenHeartRate();
}
#Override
public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
super.onCharacteristicRead(gatt, characteristic, status);
Log.v("test", "onCharacteristicRead");
byte[] data = characteristic.getValue();
txtByte.setText(Arrays.toString(data));
}
#Override
public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
super.onCharacteristicWrite(gatt, characteristic, status);
Log.v("test", "onCharacteristicWrite");
}
#Override
public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
super.onCharacteristicChanged(gatt, characteristic);
Log.v("test", "onCharacteristicChanged");
byte[] data = characteristic.getValue();
txtByte.setText(Arrays.toString(data));
}
#Override
public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
super.onDescriptorRead(gatt, descriptor, status);
Log.v("test", "onDescriptorRead");
}
#Override
public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
super.onDescriptorWrite(gatt, descriptor, status);
Log.v("test", "onDescriptorWrite");
}
#Override
public void onReliableWriteCompleted(BluetoothGatt gatt, int status) {
super.onReliableWriteCompleted(gatt, status);
Log.v("test", "onReliableWriteCompleted");
}
#Override
public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) {
super.onReadRemoteRssi(gatt, rssi, status);
Log.v("test", "onReadRemoteRssi");
}
#Override
public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) {
super.onMtuChanged(gatt, mtu, status);
Log.v("test", "onMtuChanged");
}
};
}
this is the output
I can read characteristics but it only show byte value I converted into String but it value is always same.example I read battery level it only showing byte type value i attached output screen
I trying to do same with Mi Band 3.
My code is:
private void connect(){
String macAddress = "E9:FD:0C:CB:67:2C";
bleDevice = rxBleClient.getBleDevice(macAddress);
connectDisposable = bleDevice.establishConnection(true)
.subscribe(
rxBleConnection -> {
// All GATT operations are done through the rxBleConnection.
Log.d(TAG, "Is connected");
rxBleConnection.readCharacteristic(UUID_CHARACTERISTIC_HEART_RATE_MEASUREMENT).subscribe(
bytes -> {
Log.d(TAG, "Data " + bytes);
},
throwable -> {
Log.d(TAG, "Error " + throwable.getMessage());
}
);
},
throwable -> {
// Handle an error here.
Log.d(TAG, "Error " + throwable.getMessage());
}
);
}
Can you provide any explanations how you solved the problem.
Related
Hey I am working on application in which I am using BLE to transfer User ID from mobile to mobile by using my application when they interact with each other , which means in one mobile my app will work as Peripheral Mode and in other it will work as a Central mode. So what I have done is I open my first app as a Peripheral Mode which starts advertising and other as Central which starts a service in background to scan device and make connection to read data. This functionality works fine I send message(User ID) from Peripheral to central it pops on central device. Now I want to send User ID of Central Mode device to Peripheral.
As per my understanding, In BLE we cannot send data from central to peripheral we can only scan from central. But I heard that we can write characteristics from Peripheral to get some response in back So is it possible that i can send my Used ID from Central to Peripheral through response or any other alternate way is possible?
I want to do this
I am just new in BLE so I have no idea how to perform this functionality. I am sharing my code that what I am doing below :
SO Central Mode or You say scanning which I start in background service is:
GATT Service
public class GattService extends Service {
private static int NOTIFICATION_ID = 0;
public static final ParcelUuid UUID = ParcelUuid.fromString("0000FED8-0000-1000-8000-00805F9B34FB");
public static final java.util.UUID SERVICE_UUID = java.util.UUID.fromString("00001111-0000-1000-8000-00805F9B34FB");
public static final java.util.UUID CHAR_UUID = java.util.UUID.fromString("00002222-0000-1000-8000-00805F9B34FB");
private BluetoothAdapter bluetoothAdapter;
private BluetoothGattServer server;
private BluetoothLeAdvertiser bluetoothLeAdvertiser;
private boolean start;
#Override
public void onCreate() {
super.onCreate();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
setupBluetooth();
return Service.START_STICKY;
}
private void setupBluetooth() {
BluetoothManager bluetoothManager = (BluetoothManager) this.getApplicationContext().getSystemService(Context.BLUETOOTH_SERVICE);
server = bluetoothManager.openGattServer(this, serverCallback);
initServer();
bluetoothAdapter = bluetoothManager.getAdapter();
advertise();
}
private void initServer() {
BluetoothGattService service = new BluetoothGattService(SERVICE_UUID, BluetoothGattService.SERVICE_TYPE_PRIMARY);
BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(CHAR_UUID, BluetoothGattCharacteristic.PROPERTY_WRITE | BluetoothGattCharacteristic.PROPERTY_READ, BluetoothGattCharacteristic.PERMISSION_READ | BluetoothGattCharacteristic.PERMISSION_WRITE);
service.addCharacteristic(characteristic);
server.addService(service);
}
private void advertise() {
bluetoothLeAdvertiser = bluetoothAdapter.getBluetoothLeAdvertiser();
AdvertiseData advertisementData = getAdvertisementData();
AdvertiseSettings advertiseSettings = getAdvertiseSettings();
bluetoothLeAdvertiser.startAdvertising(advertiseSettings, advertisementData, advertiseCallback);
start = true;
}
private AdvertiseData getAdvertisementData() {
AdvertiseData.Builder builder = new AdvertiseData.Builder();
builder.setIncludeTxPowerLevel(true);
builder.addServiceUuid(UUID);
bluetoothAdapter.setName("BLE client");
builder.setIncludeDeviceName(true);
return builder.build();
}
private AdvertiseSettings getAdvertiseSettings() {
AdvertiseSettings.Builder builder = new AdvertiseSettings.Builder();
builder.setAdvertiseMode(AdvertiseSettings.ADVERTISE_MODE_BALANCED);
builder.setTxPowerLevel(AdvertiseSettings.ADVERTISE_TX_POWER_HIGH);
builder.setConnectable(true);
return builder.build();
}
private final AdvertiseCallback advertiseCallback = new AdvertiseCallback() {
#SuppressLint("Override")
#Override
public void onStartSuccess(AdvertiseSettings advertiseSettings) {
final String message = "Advertisement successful";
sendNotification(message);
}
#SuppressLint("Override")
#Override
public void onStartFailure(int i) {
final String message = "Advertisement failed error code: " + i;
sendNotification(message);
}
};
private BluetoothGattServerCallback serverCallback = new BluetoothGattServerCallback() {
#Override
public void onConnectionStateChange(BluetoothDevice device, int status, int newState) {
super.onConnectionStateChange(device, status, newState);
if (newState == BluetoothProfile.STATE_CONNECTED) {
sendNotification("Client connected");
}
}
#Override
public void onServiceAdded(int status, BluetoothGattService service) {
super.onServiceAdded(status, service);
}
#Override
public void onCharacteristicReadRequest(BluetoothDevice device, int requestId, int offset, BluetoothGattCharacteristic characteristic) {
super.onCharacteristicReadRequest(device, requestId, offset, characteristic);
server.sendResponse(device, requestId, BluetoothGatt.GATT_FAILURE, 0, null);
}
#Override
public void onCharacteristicWriteRequest(BluetoothDevice device, int requestId, BluetoothGattCharacteristic characteristic, boolean preparedWrite, boolean responseNeeded, int offset, byte[] value) {
super.onCharacteristicWriteRequest(device, requestId, characteristic, preparedWrite, responseNeeded, offset, value);
byte[] bytes = value;
String message = new String(bytes);
sendNotification(message);
if (characteristic.getUuid().equals(CHAR_UUID)) {
server.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, 0, null);
}
int length = value.length;
byte[] reversed = new byte[length];
for (int i = 0; i < length; i++) {
reversed[i] = value[length - (i + 1)];
}
characteristic.setValue(reversed);
server.notifyCharacteristicChanged(device, characteristic, true);
}
#Override
public void onDescriptorReadRequest(BluetoothDevice device, int requestId, int offset, BluetoothGattDescriptor descriptor) {
super.onDescriptorReadRequest(device, requestId, offset, descriptor);
}
#Override
public void onDescriptorWriteRequest(BluetoothDevice device, int requestId, BluetoothGattDescriptor descriptor, boolean preparedWrite, boolean responseNeeded, int offset, byte[] value) {
super.onDescriptorWriteRequest(device, requestId, descriptor, preparedWrite, responseNeeded, offset, value);
}
#Override
public void onExecuteWrite(BluetoothDevice device, int requestId, boolean execute) {
super.onExecuteWrite(device, requestId, execute);
}
#Override
public void onNotificationSent(BluetoothDevice device, int status) {
super.onNotificationSent(device, status);
}
#Override
public void onMtuChanged(BluetoothDevice device, int mtu) {
super.onMtuChanged(device, mtu);
}
};
#Override
public void onDestroy() {
if (start) {
bluetoothLeAdvertiser.stopAdvertising(advertiseCallback);
}
super.onDestroy();
}
private void sendNotification(String message) {
NotificationManager notificationManager = (NotificationManager) this.getSystemService(Context.NOTIFICATION_SERVICE);
int notificationId = 1;
String channelId = "channel-01";
String channelName = "Channel Name";
int importance = NotificationManager.IMPORTANCE_HIGH;
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
NotificationChannel mChannel = new NotificationChannel(
channelId, channelName, importance);
notificationManager.createNotificationChannel(mChannel);
}
NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(this, channelId)
.setSmallIcon(R.mipmap.ic_launcher)
.setContentTitle(getString(R.string.app_name))
.setContentText(message)
.setAutoCancel(true);
Intent intent = new Intent(this, MainActivity.class);
TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);
stackBuilder.addNextIntent(intent);
PendingIntent resultPendingIntent = stackBuilder.getPendingIntent(
0,
PendingIntent.FLAG_UPDATE_CURRENT
);
mBuilder.setContentIntent(resultPendingIntent);
notificationManager.notify(notificationId, mBuilder.build());
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
}
And my Peripheral device which send Characteristic after successful connection is:
public class ServicesList extends AppCompatActivity implements AdapterView.OnItemClickListener {
private ListView servicesList;
private LinearLayout messageContainer;
private BluetoothDevice device;
private List<String> servicesListNames;
private ArrayAdapter<String> servicesAdapter;
private Handler handler;
private List<BluetoothGattService> services;
private BluetoothGatt currentGatt;
private EditText message;
private Button send;
private BluetoothGattCharacteristic characteristic;
private ProgressDialog dialog;
public static final java.util.UUID DES_UUID = java.util.UUID.fromString("00003333-0000-1000-8000-00805F9B34FB");
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.services_list);
handler = new Handler();
dialog = new ProgressDialog(this);
dialog.setCancelable(false);
dialog.setMessage("Loading");
device = getIntent().getExtras().getParcelable("device");
servicesList = (ListView) findViewById(R.id.services_list);
messageContainer = (LinearLayout) findViewById(R.id.message_container);
message = (EditText) findViewById(R.id.message);
send = (Button) findViewById(R.id.send);
currentGatt = device.connectGatt(this, false, gattCallback);
dialog.show();
servicesListNames = new ArrayList<>();
servicesAdapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, servicesListNames);
servicesList.setAdapter(servicesAdapter);
servicesList.setOnItemClickListener(this);
send.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if(!message.getText().toString().trim().isEmpty()) {
characteristic.setValue(message.getText().toString().getBytes());
currentGatt.writeCharacteristic(characteristic);
message.setText("");
}
}
});
}
private BluetoothGattCallback gattCallback = new BluetoothGattCallback() {
#Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
super.onConnectionStateChange(gatt, status, newState);
if(newState == BluetoothProfile.STATE_CONNECTED) {
currentGatt.discoverServices();
}else{
if(dialog.isShowing()){
handler.post(new Runnable() {
#Override
public void run() {
dialog.hide();
}
});
}
}
}
#Override
public void onServicesDiscovered(final BluetoothGatt gatt, int status) {
super.onServicesDiscovered(gatt, status);
services = currentGatt.getServices();
for(BluetoothGattService service : services){
Log.d("Khurram", "Uuid = " + service.getUuid().toString());
servicesListNames.add(Helper.getServiceName(service.getUuid().toString()));
handler.post(new Runnable() {
#Override
public void run() {
servicesAdapter.notifyDataSetChanged();
}
});
}
if (dialog.isShowing()){
handler.post(new Runnable() {
#Override
public void run() {
dialog.hide();
}
});
}
}
#Override
public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
super.onCharacteristicRead(gatt, characteristic, status);
if (status == BluetoothGatt.GATT_SUCCESS) {
// log("Characteristic read successfully");
readCharacteristic(characteristic);
} else {
// logError("Characteristic read unsuccessful, status: " + status);
// Trying to read from the Time Characteristic? It doesnt have the property or
permissions
// set to allow this. Normally this would be an error and you would want to:
// disconnectGattServer();
}
}
#Override
public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic,
int status) {
super.onCharacteristicWrite(gatt, characteristic, status);
gatt.executeReliableWrite();
}
#Override
public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic
characteristic) {
super.onCharacteristicChanged(gatt, characteristic);
readCharacteristic(characteristic);
}
#Override
public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status)
{
super.onDescriptorRead(gatt, descriptor, status);
}
#Override
public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status)
{
super.onDescriptorWrite(gatt, descriptor, status);
}
#Override
public void onReliableWriteCompleted(BluetoothGatt gatt, int status) {
super.onReliableWriteCompleted(gatt, status);
}
#Override
public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) {
super.onReadRemoteRssi(gatt, rssi, status);
}
#Override
public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) {
super.onMtuChanged(gatt, mtu, status);
}
};
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
if(services != null){
BluetoothGattService notificationService = services.get(position);
if(notificationService.getUuid().equals(GattService.SERVICE_UUID)){
characteristic = notificationService.getCharacteristic(GattService.CHAR_UUID);
if(characteristic != null) {
messageContainer.setVisibility(View.VISIBLE);
}
}else{
Toast.makeText(this, "Testing", Toast.LENGTH_SHORT).show();
}
}
}
private void readCharacteristic(BluetoothGattCharacteristic characteristic) {
byte[] messageBytes = characteristic.getValue();
log("Read: " + StringUtils.byteArrayInHexFormat(messageBytes));
String message = StringUtils.stringFromBytes(messageBytes);
if (message == null) {
logError("Unable to convert bytes to string");
Toast.makeText(this, "Unable to convert bytes to string", Toast.LENGTH_SHORT).show();
return;
}
Toast.makeText(this, message, Toast.LENGTH_SHORT).show();
}
}
So what I am doing is I have a EditText in peripheral in which i enter the text like User ID and I am returning it back from service as response after reversing it and show notification in Central Mode.
But I am not able to get the response Can you guys help me please ?
If you guys know my scenario you can also assist me with new way through BLE by which I can exchange data between 2 android phones.
Thanks,
I'm trying to read the data taken from a BLE scale after the measurement.
If I try to read the model name everything works.
Pearing correctly. I receive the confirmation message correctly on the display of the successful date setting. After the measurement, the device exposes the services and I use a simple button to start the connection via address.
buttonConnect.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
String deviceAddress = "B4:99:4C:6E:20:68";
BluetoothDevice device = getBluetoothAdapter(getApplicationContext()).getRemoteDevice(deviceAddress);
connectToDevice(device, false);
}
});
connection via GATT
public void connectToDevice(BluetoothDevice device, boolean pairing) {
if (mGatt == null) {
mGatt = device.connectGatt(this, false, gattCallbackNoPairing);
}
}
callBack method called by connectGatt
private final BluetoothGattCallback gattCallbackNoPairing = new BluetoothGattCallback() {
#Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
Log.i("onConnectionStateChange", "Status: " + status);
switch (newState) {
case BluetoothProfile.STATE_CONNECTED:
Log.i("gattCallback", "STATE_CONNECTED");
gatt.discoverServices();
break;
case BluetoothProfile.STATE_DISCONNECTED:
Log.e("gattCallback", "STATE_DISCONNECTED");
break;
default:
Log.e("gattCallback", "STATE_OTHER");
}
}
#Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
Log.i("Callback", "onServicesDiscovered");
BluetoothGattCharacteristic characteristic = gatt.getService(ADGattUUID.WeightScaleService).getCharacteristic(ADGattUUID.WeightScaleMeasurement);
BluetoothGattDescriptor descriptor = characteristic.getDescriptor(ADGattUUID.ClientCharacteristicConfiguration);
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
boolean result = gatt.writeDescriptor(descriptor);
Log.i("writeDescriptor", String.valueOf(result));
}
when I arrive at this point and execute writeCharacteristic, I always get false
#Override
public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor,
int status) {
Log.i("Callback", "onDescriptorWrite");
BluetoothGattCharacteristic characteristic = gatt.getService(ADGattUUID.WeightScaleService).getCharacteristic(ADGattUUID.WeightScaleMeasurement);
characteristic.setValue(new byte[]{1, 1});
boolean result1 = gatt.writeCharacteristic(characteristic);
Log.i("writeCharacteristic", String.valueOf(result1));
}
#Override
public void onCharacteristicChanged(BluetoothGatt gatt,
BluetoothGattCharacteristic
characteristic) {
Log.i("Callback", "onCharacteristicChanged");
Log.i("onCharacteristicChanged", new String(characteristic.getValue()));
}
};
Indeed in the documentation it is indicated as "Indicate". So how do I recover this data?
value read
Ok, I was able to decode the measurement using the following class
public static Bundle readCharacteristic(BluetoothGattCharacteristic characteristic) {
Bundle bundle = new Bundle();
int flag = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT8, 0);
String flagString = Integer.toBinaryString(flag);
int offset=0;
for(int index = flagString.length(); 0 < index ; index--) {
String key = flagString.substring(index-1 , index);
if(index == flagString.length()) {
double convertValue = 0;
if(key.equals("0")) {
bundle.putString(ADGattService.KEY_UNIT, VALUE_WEIGHT_SCALE_UNITS_KG);
convertValue = 0.005f;
}
else {
bundle.putString(ADGattService.KEY_UNIT, VALUE_WEIGHT_SCALE_UNITS_LBS);
convertValue = 0.01f;
}
// Unit
offset+=1;
// Value
double value = (double)(characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT16, offset)) * convertValue;
bundle.putDouble("weight", value);
offset+=2;
}
else if(index == flagString.length()-1) {
if(key.equals("1")) {
}
else {
Calendar calendar = Calendar.getInstance(Locale.getDefault());
}
}
else if(index == flagString.length()-2) {
if(key.equals("1")) {
offset+=1;
}
}
else if(index == flagString.length()-3) {
if(key.equals("1")) {
// BMI and Height
}
}
}
return bundle;
}
nowadays, I develop App with android BLE.
but I have a problem..
here is code....
// Device connect call back
private final BluetoothGattCallback btleGattCallback = new BluetoothGattCallback() {
#Override
public void onCharacteristicChanged(BluetoothGatt gatt, final BluetoothGattCharacteristic characteristic) {
// this will get called anytime you perform a read or write characteristic operation
peripheralTextView.append("device read or wrote to\n");
broadcastUpdate(ACTION_DATA_AVAILABLE,characteristic);
}
#Override
public void onConnectionStateChange(final BluetoothGatt gatt, final int status, final int newState) {
// this will get called when a device connects or disconnects
System.out.println(newState);
switch (newState) {
case BluetoothProfile.STATE_DISCONNECTED:
MainActivity.this.runOnUiThread(new Runnable() {
public void run() {
peripheralTextView.append("device disconnected\n");
connectToDevice.setVisibility(View.VISIBLE);
disconnectDevice.setVisibility(View.INVISIBLE);
}
});
break;
case BluetoothProfile.STATE_CONNECTED:
MainActivity.this.runOnUiThread(new Runnable() {
public void run() {
peripheralTextView.append("device connected\n");
connectToDevice.setVisibility(View.INVISIBLE);
disconnectDevice.setVisibility(View.VISIBLE);
}
});
// discover services and characteristics for this device
bluetoothGatt.discoverServices();
break;
default:
MainActivity.this.runOnUiThread(new Runnable() {
public void run() {
peripheralTextView.append("we encounterned an unknown state, uh oh\n");
}
});
break;
}
}
#Override
public void onServicesDiscovered(final BluetoothGatt gatt, final int status) {
// this will get called after the client initiates a BluetoothGatt.discoverServices() call
MainActivity.this.runOnUiThread(new Runnable() {
public void run() {
peripheralTextView.append("device services have been discovered\n");
}
});
displayGattServices(bluetoothGatt.getServices());
}
#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);
}
}
};
private void broadcastUpdate(final String action,
final BluetoothGattCharacteristic characteristic) {
// 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));
System.out.println(stringBuilder.toString());
}
}
..........\
public void disconnectDeviceSelected() {
peripheralTextView.append("Disconnecting from device\n");
bluetoothGatt.disconnect();
}
private void displayGattServices(List<BluetoothGattService> gattServices) {
if (gattServices == null) return;
// Loops through available GATT Services.
for (BluetoothGattService gattService : gattServices) {
final String uuid = gattService.getUuid().toString();
System.out.println("Service discovered: " + uuid);
MainActivity.this.runOnUiThread(new Runnable() {
public void run() {
peripheralTextView.append("Service disovered: "+uuid+"\n");
}
});
new ArrayList<HashMap<String, String>>();
List<BluetoothGattCharacteristic> gattCharacteristics =
gattService.getCharacteristics();
// Loops through available Characteristics.
for (BluetoothGattCharacteristic gattCharacteristic :
gattCharacteristics) {
final String charUuid = gattCharacteristic.getUuid().toString();
System.out.println("Characteristic discovered for service: " + charUuid);
MainActivity.this.runOnUiThread(new Runnable() {
public void run() {
peripheralTextView.append("Characteristic discovered for service: "+charUuid+"\n");
}
});
}
}
}
and I run this code ...
scanning and connectting is done ;
but onCharacteristicRead doesn't work ;(
I need your help plz!
call this method in onServicesDiscovered and if this method return true then send command to ble device
public Boolean enableTXNotification() {
/*
if (mBluetoothGatt == null) {
showMessage("mBluetoothGatt null" + mBluetoothGatt);
broadcastUpdate(DEVICE_DOES_NOT_SUPPORT_UART);
return;
}
*/
BluetoothGattService RxService = mBluetoothGatt.getService(Lock.RX_SERVICE_UUID);
if (RxService == null) {
showMessage("Rx service not found!");
broadcastUpdate(Lock.DEVICE_DOES_NOT_SUPPORT_UART);
return false;
}
BluetoothGattCharacteristic TxChar = RxService.getCharacteristic(Lock.TX_CHAR_UUID);
if (TxChar == null) {
showMessage("Tx charateristic not found!");
broadcastUpdate(Lock.DEVICE_DOES_NOT_SUPPORT_UART);
return false;
}
mBluetoothGatt.setCharacteristicNotification(TxChar, true);
BluetoothGattDescriptor descriptor = TxChar.getDescriptor(Lock.CCCD);
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
mBluetoothGatt.writeDescriptor(descriptor);
return true;
}
let me know if you have any doubt
I'm trying to read GATT characteristic values from a Bluetooth LE device (a Heart Rate bracelet). Its specs are:
Services
Characteristics
I have not yet figured out how to "read" the specifications and "translate" them into code.
I need to show on my app the heartbeats detected by the device. What is the way to read the GATT values? A code example would be much appreciated :)
Follow my actual source code.
SETUP THE BLUETOOT CONNECTION
private BluetoothAdapter mBluetoothAdapter;
private BluetoothGatt mBluetoothGatt;
private Handler mHandler;
private static final int REQUEST_ENABLE_BT = 1;
private static final long SCAN_PERIOD = 10000;
// ...
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_bluetooth);
mHandler = new Handler();
// BLE is supported?
if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
Toast.makeText(this, "Bluetooth Low Energy non supportato", Toast.LENGTH_SHORT).show();
finish();
}
final BluetoothManager bluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
mBluetoothAdapter = bluetoothManager.getAdapter();
// Bluetooth is supported?
if (mBluetoothAdapter == null) {
Toast.makeText(this, "Bluetooth non supportato", Toast.LENGTH_SHORT).show();
finish();
}
}
#Override
protected void onResume() {
super.onResume();
// Bluetooth is enabled?
if (!mBluetoothAdapter.isEnabled()) {
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
}
scanLeDevice(true);
}
#Override
protected void onPause() {
super.onPause();
if (mBluetoothAdapter != null && mBluetoothAdapter.isEnabled()) {
scanLeDevice(false);
}
}
DISCOVER BLE DEVICES AND CONNECT WITH HEART RATE MONITOR
// Device scan callback.
private BluetoothAdapter.LeScanCallback mLeScanCallback =
new BluetoothAdapter.LeScanCallback() {
#Override
public void onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord) {
runOnUiThread(new Runnable() {
#Override
public void run() {
Log.i(TAG, "Name: " + device.getName() + " (" + device.getAddress() + ")");
String deviceAddress = device.getAddress();
if (deviceAddress.equals("C0:19:37:54:9F:30")) {
connectToDevice(device);
}
}
});
}
};
public void connectToDevice(BluetoothDevice device) {
if (mBluetoothGatt == null) {
Log.i(TAG, "Attempting to connect to device " + device.getName() + " (" + device.getAddress() + ")");
mBluetoothGatt = device.connectGatt(this, true, gattCallback);
scanLeDevice(false);// will stop after first device detection
}
}
private final BluetoothGattCallback gattCallback = new BluetoothGattCallback() {
#Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
Log.i(TAG, "Status: " + status);
switch (newState) {
case BluetoothProfile.STATE_CONNECTED:
Log.i(TAG, "STATE_CONNECTED");
//BluetoothDevice device = gatt.getDevice(); // Get device
gatt.discoverServices();
break;
case BluetoothProfile.STATE_DISCONNECTED:
Log.e(TAG, "STATE_DISCONNECTED");
break;
default:
Log.e(TAG, "STATE_OTHER");
}
}
#Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
List<BluetoothGattService> services = gatt.getServices();
Log.i(TAG, "Services: " + services.toString());
BluetoothGattCharacteristic bpm = services.get(2).getCharacteristics().get(0);
gatt.readCharacteristic(services.get(0).getCharacteristics().get(0));
}
#Override
public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
// my attempt to read and print characteristics
byte[] charValue = characteristic.getValue();
byte flag = charValue[0];
Log.i(TAG, "Characteristic: " + flag);
//gatt.disconnect();
}
};
try with this inside gattCallback:
#Override
public synchronized void onCharacteristicChanged(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic) {
final byte[] dataInput = characteristic.getValue();
}
EDIT
And I think you are going to receive a byte with the hear rate data, use this function to get the int value:
public int unsignedByteToInt(byte b) {
return b & 0xFF;
}
And call it inside onCharacteristicChanged():
final byte[] dataInput = characteristic.getValue();
int hearRate = unsignedByteToInt(dataInput);
EDIT 2
Create a notification listener for the heart rate:
public void setHeartRateNotification(boolean enable){
String uuidHRCharacteristic = "YOUR CHARACTERISTIC";
BluetoothGattService mBluetoothLeService = null;
BluetoothGattCharacteristic mBluetoothGattCharacteristic = null;
for (BluetoothGattService service : mBluetoothGatt.getServices()) {
if ((service == null) || (service.getUuid() == null)) {
continue;
}
if (uuidAccelService.equalsIgnoreCase(service.getUuid().toString())) {
mBluetoothLeService = service;
}
}
if(mBluetoothLeService!=null) {
mBluetoothGattCharacteristic =
mBluetoothLeService.getCharacteristic(UUID.fromString(uuidHRCharacteristic));
}
else{
Log.i("Test","mBluetoothLeService is null");
}
if(mBluetoothGattCharacteristic!=null) {
setCharacteristicNotification(mBluetoothGattCharacteristic, enable);
Log.i("Test","setCharacteristicNotification:"+true);
}
else{
Log.i("Test","mBluetoothGattCharacteristic is null");
}
}
And set it onServiceDiscover inside gattCallback:
#Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
Log.i("Test", "onServicesDiscovered received: " + status);
setHeartRateNotification(true);
}
I'm developing an Android app than can transmit commands to a 4.0 Bluetooth serial device. I am suppose to get a response from the device. I was able to connect to the device and send the command, but how can I get this response? I have tried with a BluetoothGattServerCallback but it doesn`t work. This is my code:
public class MainActivity extends Activity {
private Handler handler;
private static final long SCAN_PERIOD = 12000;
private BluetoothAdapter btAdapter;
private LinearLayout pairedDevicesList;
private LinearLayout availableDevicesList;
private ProgressBar progressBar;
private BluetoothGatt gatt;
private List<BluetoothGattService> services;
private BluetoothGattServer gattServer;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
pairedDevicesList = (LinearLayout) findViewById(R.id.paired_devices);
availableDevicesList = (LinearLayout) findViewById(R.id.available_devices);
progressBar = (ProgressBar) findViewById(R.id.progress);
}
#Override
public void onResume() {
handler = new Handler();
final BluetoothManager bluetoothManager =
(BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
gattServer = bluetoothManager.openGattServer(this, new BluetoothGattServerCallback() {
#Override
public void onCharacteristicWriteRequest(BluetoothDevice device, int requestId, BluetoothGattCharacteristic characteristic, boolean preparedWrite, boolean responseNeeded, int offset, byte[] value) {
super.onCharacteristicWriteRequest(device, requestId, characteristic, preparedWrite, responseNeeded, offset, value);
Log.i(getClass().getSimpleName(), "onCharacteristicWriteRequest");
}
#Override
public void onNotificationSent(BluetoothDevice device, int status) {
super.onNotificationSent(device, status);
Log.i(getClass().getSimpleName(), "onNotificationSent");
}
});
btAdapter = bluetoothManager.getAdapter();
final BluetoothAdapter btAdapter = BluetoothAdapter.getDefaultAdapter();
pairedDevicesList.removeAllViews();
final Set<BluetoothDevice> pairedDevices = btAdapter.getBondedDevices();
if (pairedDevices != null) {
for (final BluetoothDevice pairedDevice : pairedDevices) {
addListItem(pairedDevicesList, pairedDevice);
}
}
scanLeDevice(true);
super.onResume();
}
private void scanLeDevice(final boolean enable) {
if (enable) {
// Stops scanning after a pre-defined scan period.
handler.postDelayed(new Runnable() {
#Override
public void run() {
btAdapter.stopLeScan(leScanCallback);
progressBar.setVisibility(View.INVISIBLE);
}
}, SCAN_PERIOD);
btAdapter.startLeScan(leScanCallback);
progressBar.setVisibility(View.VISIBLE);
Log.i(getClass().getSimpleName(), "scanning bluetooth LE devices");
} else {
progressBar.setVisibility(View.INVISIBLE);
btAdapter.stopLeScan(leScanCallback);
}
}
// Device scan callback.
private BluetoothAdapter.LeScanCallback leScanCallback =
new BluetoothAdapter.LeScanCallback() {
#Override
public void onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord) {
runOnUiThread(new Runnable() {
#Override
public void run() {
addListItem(availableDevicesList, device);
}
});
}
};
#Override
public void onPause() {
scanLeDevice(false);
super.onPause();
}
private void addListItem(final LinearLayout list, final BluetoothDevice device) {
//only add devices with the right name
if (device == null ||
device.getName() == null ||
//name of prototype: RNDEF8
!device.getName().startsWith("RND")) {
return;
}
Log.i(getClass().getSimpleName(), "device: " + device.getName());
final View item = getLayoutInflater()
.inflate(R.layout.li_bluetooth_device, list, false);
((TextView)item.findViewById(android.R.id.text1)).setText(device.getName());
item.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(final View v) {
gatt = device.connectGatt(getApplicationContext(), false, gattCallback);
}
});
list.addView(item, 0);
}
private final BluetoothGattCallback gattCallback = new BluetoothGattCallback() {
#Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
Log.i("onConnectionStateChange", "Status: " + status);
switch (newState) {
case BluetoothProfile.STATE_CONNECTED:
Log.i("gattCallback", "STATE_CONNECTED");
gatt.discoverServices();
break;
case BluetoothProfile.STATE_DISCONNECTED:
Log.e("gattCallback", "STATE_DISCONNECTED");
break;
default:
Log.e("gattCallback", "STATE_OTHER");
}
}
#Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
services = gatt.getServices();
Log.i("onServicesDiscovered", services.toString());
}
#Override
public void onCharacteristicRead(BluetoothGatt gatt,
BluetoothGattCharacteristic
ch, int status) {
Log.i(getClass().getSimpleName(), ch.getPermissions() + "");
gatt.disconnect();
}
#Override
public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
}
};
private static final UUID Read_UUID = UUID.fromString("00035b03-58e6-07dd-021a-08123a000300");
public void sendCommand(final byte[] value) {
BluetoothGattService service = gatt.getService(Read_UUID);
gattServer.addService(service);
BluetoothGattCharacteristic characteristic = service.getCharacteristic(UUID.fromString("00035b03-58e6-07dd-021a-08123a000301"));
BluetoothGattDescriptor descriptor = characteristic.getDescriptor(UUID.fromString("00002902-0000-1000-8000-00805f9b34fb"));
characteristic.setValue(value);
gatt.setCharacteristicNotification(characteristic, true);
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
gatt.writeDescriptor(descriptor);
Log.i(getClass().getSimpleName(), "" + gatt.writeCharacteristic(characteristic));
}
public void sendSU(View view) {
Log.i(getClass().getSimpleName(), "clicked su");
sendCommand("\r".getBytes());
}
public void sendSL(View view) {
Log.i(getClass().getSimpleName(), "clicked sl");
sendCommand("SL".getBytes());
}
public void sendSP(View view) {
Log.i(getClass().getSimpleName(), "clicked sp");
sendCommand("SP".getBytes());
}
}
Thanks!