I'm currently trying to connect to this device Micro:bit. This device has multiple services and characteristics. The problem is that once connected, the device only returns 1 service and 0 characteristics. What I'm missing?
public class MainActivity extends Activity {
private BluetoothAdapter bleAdapter;
private BluetoothGattCharacteristic UART;
private BluetoothGattDescriptor descriptor;
private TextView connectionState;
private final UUID SERVICE = UUID.fromString("6E400001-B5A3-F393-E0A9-E50E24DCCA9E");
private final UUID CHARACTERISTIC = UUID.fromString("6E400003-B5A3-F393-E0A9-E50E24DCCA9E");
private final UUID CONFIG_DESCRIPTOR = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb");
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Check if the system supports Bluetooth Low Energy
if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
Toast.makeText(this, "BLE Not Supported", Toast.LENGTH_SHORT).show();
finish();
}
// Take the system BLE adapter
bleAdapter = ((BluetoothManager) getSystemService(BLUETOOTH_SERVICE)).getAdapter();
// Enable Bluetooth in case it's off.
if (bleAdapter == null || !bleAdapter.isEnabled()) {
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent, 1);
}
// Get paired devices.
Set<BluetoothDevice> pairedDevices = bleAdapter.getBondedDevices();
for (BluetoothDevice device : pairedDevices) {
// Connect to Micro:Bit (the only one paired device)
device.connectGatt(this, true, new BluetoothGattCallback() {
// Check if it connects or disconnects from the Micro:Bit
#Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
super.onConnectionStateChange(gatt, status, newState);
switch (newState) {
case BluetoothProfile.STATE_CONNECTED:
gatt.discoverServices();
runOnUiThread(new Runnable() {
public void run() {
connectionState = findViewById(R.id.state);
connectionState.setText("Connected = True");
connectionState.setTextColor(Color.GREEN);
}
});
break;
case BluetoothProfile.STATE_DISCONNECTED:
gatt.disconnect();
runOnUiThread(new Runnable() {
public void run() {
connectionState = findViewById(R.id.state);
connectionState.setText("Connected = False");
connectionState.setTextColor(Color.RED);
}
});
}
}
#Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
super.onServicesDiscovered(gatt, status);
List<BluetoothGattService> list = new ArrayList<BluetoothGattService>();
list = gatt.getServices();
for (BluetoothGattService elem : list){
Log.i("Service", elem.getUuid().toString());
List<BluetoothGattCharacteristic> list2 = new ArrayList<BluetoothGattCharacteristic>();
list2 = elem.getCharacteristics();
for (BluetoothGattCharacteristic elem2: list2) {
Log.i("Characteristic", elem2.getUuid().toString());
}
}
// Try to set notifications
UART = gatt.getService(SERVICE).getCharacteristic(CHARACTERISTIC);
gatt.setCharacteristicNotification(UART,true);
descriptor = UART.getDescriptor(CONFIG_DESCRIPTOR);
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
gatt.writeDescriptor(descriptor);
}
// Activate every time UART Characteristic is updated
#Override
public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
super.onCharacteristicChanged(gatt, characteristic);
runOnUiThread(new Runnable() {
public void run() {
TextView x = (TextView) findViewById(R.id.x_axis);
TextView y = (TextView) findViewById(R.id.y_axis);
TextView z = (TextView) findViewById(R.id.z_axis);
x.setText("HELLO");
y.setText("IT ");
z.setText("WORKS!");
}
});
}
});
}
}
}
The UUID from the variables SERVICE and CHARACTERISTIC are from here. I try to connect to that UART Service and get notifications when this device updates the TX Characteristic. But I'm just obtaining 1 service on the loop from onServicesDiscovered() and I get null pointer if I try to gatt.getService(SERVICE).getCharacteristic(CHARACTERISTIC). What I'm doing wrong?
Related
I'm trying to programmatically pair with Ble iBeacon using BluetoothGatt API in android.I can able to pair with Ble iBeacon up to lollipop.But i cannot pair in Marshmallow(My testing device is oneplus 3).I'm using altbeacon library for beacon scanning.
Also, i gave ACCESS_COARSE_LOCATION,ACCESS_FINE_LOCATION permission in the manifest file and turned on GPS location.
private BluetoothGatt mGatt;
BluetoothAdapter baBluetoothAdapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
baBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
intentFilter = new IntentFilter();
intentFilter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
registerReceiver(myReceiver, intentFilter);
}
Here i'm sending beacon mac id
public void PairToDevice(String sMacId) {
BluetoothDevice device = baBluetoothAdapter.getRemoteDevice(sMacId);
if (mGatt == null) {
mGatt = device.connectGatt(this, false, gattCallback);
}
}
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.e("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 onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
super.onCharacteristicChanged(gatt, characteristic);
}
#Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
}
}
Here i'm checking bonding state.Bonding state is always none in Marshmallow.But lessthan marshmallow it is working fine.
private BroadcastReceiver myReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (BluetoothDevice.ACTION_BOND_STATE_CHANGED.equals(action)) {
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
int nState = device.getBondState();
if (nState == BluetoothDevice.BOND_BONDED) {
Log.e("Bond Information", "device bonded");
} else if (nState == BluetoothDevice.BOND_BONDING) {
Log.e("Bond Information", "device bonding");
} else if (nState == BluetoothDevice.BOND_NONE) {
Log.e("Bond Information", "bond none");
}
}
}
};
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);
}
Although similar questions have been asked but it's slightly different. I know how to pass data to a connected BLE device but I think I'm doing something wrong for which I need help.
The code below contains all the methods from my class that is extending BroadcastReceiver.
I scan and connect to a device specified by `PEN_ADDRESS`.
In `onServicesDiscovered` method I look for a service whose `UUID` contains `abcd`.
Then I loop through the characteristics of this services and look for three characteristics with specific strings in their `UUID`.
The third characteristic is a writable characteristic in which I'm trying to write data by calling the method `writeCharac(mGatt,writeChar1,123);`
The data `123` passed above is just a dummy data.
I debugged my code while trying writing to this characteristic but on putting breakpoints inside the writeCharac method, I found that the status value is false, indicating that the write was not successful.
Am I missing something here? Please do help!
public class BackgroundReceiverFire extends BroadcastReceiver {
Context context;
private BluetoothAdapter mBluetoothAdapter;
private BluetoothGatt mGatt;
private BluetoothLeService mBluetoothLeService;
private boolean mScanning;
private final String TAG = "READING: ";
private BluetoothDevice mDevice;
private Handler mHandler;
private static final int REQUEST_ENABLE_BT = 1;
private final String PEN_ADDRESS = "FB:23:AF:42:5C:56";
// Stops scanning after 10 seconds.
private static final long SCAN_PERIOD = 10000;
public void onReceive(Context context, Intent intent) {
this.context = context;
Toast.makeText(context, "Started Scanning", LENGTH_SHORT).show();
initializeBluetooth();
startScan();
}
private void initializeBluetooth() {
mHandler = new Handler();
// Use this check to determine whether BLE is supported on the device. Then you can
// selectively disable BLE-related features.
// Initializes a Bluetooth adapter. For API level 18 and above, get a reference to
// BluetoothAdapter through BluetoothManager.
final BluetoothManager bluetoothManager =
(BluetoothManager) context.getSystemService(Context.BLUETOOTH_SERVICE);
mBluetoothAdapter = bluetoothManager.getAdapter();
// Checks if Bluetooth is supported on the device.
if (mBluetoothAdapter == null) {
Toast.makeText(this.context, "No Bluetooth", LENGTH_SHORT).show();
return;
}
}
private void startScan() {
scanLeDevice(true);
}
private void stopScan() {
scanLeDevice(false);
}
private void scanLeDevice(final boolean enable) {
if (enable) {
// Stops scanning after a pre-defined scan period.
mHandler.postDelayed(new Runnable() {
#Override
public void run() {
mScanning = false;
mBluetoothAdapter.stopLeScan(mLeScanCallback);
}
}, SCAN_PERIOD);
mScanning = true;
//Scanning for the device
mBluetoothAdapter.startLeScan(mLeScanCallback);
} else {
mScanning = false;
mBluetoothAdapter.stopLeScan(mLeScanCallback);
}
}
private BluetoothAdapter.LeScanCallback mLeScanCallback =
new BluetoothAdapter.LeScanCallback() {
#Override
public void onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord) {
if (device.getAddress().matches(PEN_ADDRESS)) {
connectBluetooth(device);
Toast.makeText(context, "Device Found: " + device.getAddress(), Toast.LENGTH_LONG).show();
}
}
};
private void connectBluetooth(BluetoothDevice insulinPen) {
if (mGatt == null) {
Log.d("connectToDevice", "connecting to device: " + insulinPen.toString());
mDevice = insulinPen;
mGatt = insulinPen.connectGatt(context, true, gattCallback);
scanLeDevice(false);// will stop after first device detection
}
}
private void enableBluetooth() {
if (!mBluetoothAdapter.isEnabled()) {
mBluetoothAdapter.enable();
}
scanLeDevice(true);
}
private void disableBluetooth() {
if (mBluetoothAdapter.isEnabled()) {
mBluetoothAdapter.disable();
}
}
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:
gatt.discoverServices();
break;
case BluetoothProfile.STATE_DISCONNECTED:
Log.e("gattCallback", "STATE_DISCONNECTED");
Log.i("gattCallback", "reconnecting...");
BluetoothDevice mDevice = gatt.getDevice();
mGatt = null;
connectBluetooth(mDevice);
break;
default:
Log.e("gattCallback", "STATE_OTHER");
break;
}
}
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
mGatt = gatt;
List<BluetoothGattService> services = mGatt.getServices();
Log.i("onServicesDiscovered", services.toString());
Iterator<BluetoothGattService> serviceIterator = services.iterator();
while(serviceIterator.hasNext()){
BluetoothGattService bleService = serviceIterator.next();
if(bleService.getUuid().toString().contains("abcd")){
//Toast.makeText(context,"Got the service",Toast.LENGTH_SHORT);
BluetoothGattCharacteristic readChar1 = bleService.getCharacteristics().get(0);
for (BluetoothGattDescriptor descriptor : readChar1.getDescriptors()) {
descriptor.setValue(BluetoothGattDescriptor.ENABLE_INDICATION_VALUE);
mGatt.writeDescriptor(descriptor);
mGatt.setCharacteristicNotification(readChar1, true);
}
//mGatt.readCharacteristic(readChar1);
BluetoothGattCharacteristic readChar2 = bleService.getCharacteristics().get(1);
for (BluetoothGattDescriptor descriptor : readChar2.getDescriptors()) {
descriptor.setValue(BluetoothGattDescriptor.ENABLE_INDICATION_VALUE);
mGatt.writeDescriptor(descriptor);
mGatt.setCharacteristicNotification(readChar2, true);
}
//mGatt.readCharacteristic(readChar2);
BluetoothGattCharacteristic writeChar1 = bleService.getCharacteristics().get(2);
for (BluetoothGattDescriptor descriptor : writeChar1.getDescriptors()) {
descriptor.setValue( BluetoothGattDescriptor.ENABLE_INDICATION_VALUE);
mGatt.writeDescriptor(descriptor);
mGatt.setCharacteristicNotification(writeChar1, true);
}
writeCharac(mGatt,writeChar1,123);
}
}
//gatt.readCharacteristic(therm_char);
}
public void writeCharac(BluetoothGatt gatt, BluetoothGattCharacteristic charac, int value ){
if (mBluetoothAdapter == null || gatt == null) {
Log.w(TAG, "BluetoothAdapter not initialized");
return;
}
/*
BluetoothGattCharacteristic charac = gattService
.getCharacteristic(uuid);
*/
if (charac == null) {
Log.e(TAG, "char not found!");
}
int unixTime = value;
String unixTimeString = Integer.toHexString(unixTime);
byte[] byteArray = hexStringToByteArray(unixTimeString);
charac.setValue(byteArray);
boolean status = mGatt.writeCharacteristic(charac);
if(status){
Toast.makeText(context,"Written Successfully",Toast.LENGTH_SHORT).show();
}else{
Toast.makeText(context,"Error writing characteristic",Toast.LENGTH_SHORT).show();
}
}
public byte[] hexStringToByteArray(String s) {
int len = s.length();
byte[] data = new byte[len/2];
for(int i = 0; i < len; i+=2){
data[i/2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) + Character.digit(s.charAt(i+1), 16));
}
return data;
}
public void onCharacteristicRead(BluetoothGatt gatt,
BluetoothGattCharacteristic
characteristic, int status) {
Log.i("onCharacteristicRead", characteristic.toString());
String characteristicValue = characteristic.getValue().toString();
Log.d("CHARACTERISTIC VALUE: ", characteristicValue);
gatt.disconnect();
}
public void onCharacteristicChanged(BluetoothGatt gatt,
BluetoothGattCharacteristic
characteristic) {
String value = characteristic.getValue().toString();
Log.d(TAG,value);
}
};
Although the BLE API is asynchronous in nature, the actual signal transmission is inevitably synchronous. You have to wait for the previous connect/write/read call to callback before starting any connecting/writing/reading operation.
In your code onServicesDiscovered(BluetoothGatt gatt, int status) function, you called mGatt.writeDescriptor(descriptor) twice before trying to write the characteristic. The API will refuse to start your write request as it is being busy writing the descriptor, and return false for your mGatt.writeCharacteristic(charac) call.
So just wait for the writeDescriptor to callback before calling writeCharacteristic. This nature is not well documented but you can find some source here and here.
Thanks #reTs and #pooja for suggestions. The issue was due to these two lines
mGatt = nullconnectBluetooth(mDevice);
in STATE_DISCONNECTED. I had figured it out but don't remember the exact reason. Posting it just in case it is helpful.
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!
I am working on a Bluetooth Low Energy (BLE) app. I have a BLE device (scale) which measures weight. I am able to connect with this device. But I am not getting how to read data (weight value) from it.
I want to know if my app is connected to any BLE device, so what are the steps to get notified by device in order to get updated data.
Okay, following is the for my Activity which I am using..
public class BlogBLEActivity extends Activity implements OnItemClickListener
{
private final static String TAG = BlogBLEActivity.class.getSimpleName();
private BluetoothAdapter bluetoothAdapter;
BluetoothManager bluetoothManager;
boolean hasBleFeature = false;
TextView tvMessage;
int messageId = R.string.doesnt_support_ble;
int colorId = android.R.color.holo_red_light;
private boolean mScanning;
private Handler handler = new Handler();
private static final long SCAN_PERIOD = 10000;
private static final int REQUEST_ENABLE_BT = 1209;
ListView listView;
ArrayList<BluetoothDevice> listDevices;
BleDeviceAdapter bleDeviceAdapter;
TextView tvHumidity;
TextView tvTemperature;
TextView tvPressure;
boolean isConnected = false;
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.blog_ble);
initParameters();
initViews();
scanLeDevice(true);
}
#SuppressLint("NewApi")
void initParameters()
{
hasBleFeature = getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE);
Log.i(TAG, "hasBleFeature : " + hasBleFeature);
if (hasBleFeature)
{
messageId = R.string.supports_ble;
colorId = android.R.color.holo_blue_light;
} else
{
messageId = R.string.doesnt_support_ble;
colorId = android.R.color.holo_red_light;
}
bluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
bluetoothAdapter = bluetoothManager.getAdapter();// BluetoothAdapter.getDefaultAdapter();
if (bluetoothAdapter == null || !bluetoothAdapter.isEnabled())
{
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
}
listDevices = new ArrayList<BluetoothDevice>();
bleDeviceAdapter = new BleDeviceAdapter(this, listDevices);
}
void initViews()
{
tvHumidity = (TextView) findViewById(R.id.blog_ble_tv_humidity);
tvTemperature = (TextView) findViewById(R.id.blog_ble_tv_temprature);
tvPressure = (TextView) findViewById(R.id.blog_ble_tv_pressure);
tvMessage = (TextView) findViewById(R.id.blog_ble_tv_message);
tvMessage.setText(getResources().getString(messageId));
tvMessage.setTextColor(getResources().getColor(colorId));
listView = (ListView) findViewById(R.id.blog_ble_list_view);
listView.setAdapter(bleDeviceAdapter);
listView.setOnItemClickListener(this);
}
#SuppressLint("NewApi")
void scanLeDevice(final boolean enable)
{
if (enable)
{
handler.postDelayed(new Runnable()
{
#SuppressLint("NewApi")
#Override
public void run()
{
mScanning = false;
bluetoothAdapter.stopLeScan(leScanCallback);
}
}, SCAN_PERIOD);
mScanning = false;
bluetoothAdapter.startLeScan(leScanCallback);
} else
{
mScanning = false;
bluetoothAdapter.stopLeScan(leScanCallback);
}
}
#SuppressLint("NewApi")
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()
{
if (device != null)
{
bleDeviceAdapter.add(device);
bleDeviceAdapter.notifyDataSetChanged();
}
}
});
}
};
class BleDeviceAdapter extends ArrayAdapter<BluetoothDevice>
{
public BleDeviceAdapter(Context context, List<BluetoothDevice> objects)
{
super(context, R.layout.row_ble_device, R.id.row_ble_device_tv_name, objects);
}
#SuppressLint("NewApi")
#Override
public View getView(int position, View convertView, ViewGroup parent)
{
View row = super.getView(position, convertView, parent);
ViewHolder holder = (ViewHolder) row.getTag();
if (holder == null)
{
holder = new ViewHolder(row);
row.setTag(holder);
}
BluetoothDevice device = getDevice(position);
holder.tvName.setText("" + device.getName());
Log.i(TAG, "" + device.getName());
return row;
}
}
BluetoothDevice getDevice(int position)
{
return (BluetoothDevice) listView.getAdapter().getItem(position);
}
#SuppressLint("NewApi")
#Override
public void onItemClick(AdapterView<?> arg0, View arg1, int position, long arg3)
{
BluetoothDevice device = getDevice(position);
Toast.makeText(this, "" + device.getName(), Toast.LENGTH_SHORT).show();
BluetoothGatt connectGatt = device.connectGatt(this, false, mGattCallback);
}
/* Client Configuration Descriptor */
private static final UUID CONFIG_DESCRIPTOR = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb");
private static final UUID KITCHEN_SCALE_SERVICE = UUID.fromString("0000780a-0000-1000-8000-00805f9b34fb");
private static final UUID KITCHEN_SCALE_FEATURE_CHAR = UUID.fromString("00008aa0-0000-1000-8000-00805f9b34fb");
private static final UUID KITCHEN_SCALE_MEASUREMENT_CHAR = UUID.fromString("00008aa1-0000-1000-8000-00805f9b34fb");
private static final UUID KITCHEN_SCALE_INTERMEDIATE_CHAR = UUID.fromString("00008aa2-0000-1000-8000-00805f9b34fb");
/*
* In this callback, we've created a bit of a state machine to enforce that
* only one characteristic be read or written at a time until all of our
* sensors are enabled and we are registered to get notifications.
*/
#SuppressLint("NewApi")
private BluetoothGattCallback mGattCallback = new BluetoothGattCallback()
{
/* State Machine Tracking */
private int mState = 0;
private void reset()
{
mState = 0;
}
private void advance()
{
mState++;
}
/*
* Send an enable command to each sensor by writing a configuration
* characteristic. This is specific to the SensorTag to keep power low
* by disabling sensors you aren't using.
*/
private void enableNextSensor(BluetoothGatt gatt)
{
BluetoothGattCharacteristic characteristic;
switch (mState)
{
case 0:
Log.i(TAG, "Enabling weight scale");
characteristic = gatt.getService(KITCHEN_SCALE_SERVICE).getCharacteristic(KITCHEN_SCALE_FEATURE_CHAR);
Log.i(TAG, "Feature Properties : "+characteristic.getProperties());
characteristic.setValue(new byte[]
{ 0x09 });
break;
default:
mHandler.sendEmptyMessage(MSG_DISMISS);
Log.i(TAG, "All Sensors Enabled");
return;
}
gatt.writeCharacteristic(characteristic);
}
/*
* Read the data characteristic's value for each sensor explicitly
*/
private void readNextSensor(BluetoothGatt gatt)
{
BluetoothGattCharacteristic characteristic;
switch (mState)
{
case 0:
Log.i(TAG, "Reading weight cal");
characteristic = gatt.getService(KITCHEN_SCALE_SERVICE).getCharacteristic(KITCHEN_SCALE_MEASUREMENT_CHAR);
break;
default:
mHandler.sendEmptyMessage(MSG_DISMISS);
Log.i(TAG, "All Sensors Enabled");
return;
}
gatt.readCharacteristic(characteristic);
}
/*
* Enable notification of changes on the data characteristic for each
* sensor by writing the ENABLE_NOTIFICATION_VALUE flag to that
* characteristic's configuration descriptor.
*/
private void setNotifyNextSensor(BluetoothGatt gatt)
{
BluetoothGattCharacteristic characteristic;
switch (mState)
{
case 0:
Log.i(TAG, "Set notify weight ");
characteristic = gatt.getService(KITCHEN_SCALE_SERVICE).getCharacteristic(KITCHEN_SCALE_MEASUREMENT_CHAR);
break;
default:
mHandler.sendEmptyMessage(MSG_DISMISS);
Log.i(TAG, "All Sensors Enabled");
return;
}
// Enable local notifications
gatt.setCharacteristicNotification(characteristic, true);
// Enabled remote notifications
BluetoothGattDescriptor desc = characteristic.getDescriptor(CONFIG_DESCRIPTOR);
desc.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
gatt.writeDescriptor(desc);
}
#Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState)
{
Log.i(TAG, "Connection State Change: " + status + " -> " + connectionState(newState));
if (status == BluetoothGatt.GATT_SUCCESS && newState == BluetoothProfile.STATE_CONNECTED)
{
/*
* Once successfully connected, we must next discover all the
* services on the device before we can read and write their
* characteristics.
*/
gatt.discoverServices();
mHandler.sendMessage(Message.obtain(null, MSG_PROGRESS, "Discovering Services..."));
} else if (status == BluetoothGatt.GATT_SUCCESS && newState == BluetoothProfile.STATE_DISCONNECTED)
{
/*
* If at any point we disconnect, send a message to clear the
* weather values out of the UI
*/
mHandler.sendEmptyMessage(MSG_CLEAR);
} else if (status != BluetoothGatt.GATT_SUCCESS)
{
/*
* If there is a failure at any stage, simply disconnect
*/
gatt.disconnect();
}
}
#Override
public void onServicesDiscovered(BluetoothGatt gatt, int status)
{
Log.i(TAG, "Services Discovered: " + status);
if (status == BluetoothGatt.GATT_SUCCESS)
{
Log.i(TAG, "No of services discovered: " + gatt.getServices().size());
mHandler.sendMessage(Message.obtain(null, MSG_PROGRESS, "No of services discovered: " + gatt.getServices().size()));
List<BluetoothGattService> services = gatt.getServices();
for (BluetoothGattService bluetoothGattService : services)
{
UUID uuid = bluetoothGattService.getUuid();
Log.e(TAG, ""+uuid.toString());
List<BluetoothGattCharacteristic> characteristics = bluetoothGattService.getCharacteristics();
for (BluetoothGattCharacteristic bluetoothGattCharacteristic : characteristics)
{
UUID uuidC = bluetoothGattCharacteristic.getUuid();
Log.i(TAG, "Gatt Properties : "+bluetoothGattCharacteristic.getProperties());
Log.i(TAG, ""+uuidC.toString());
CharacteristicHelper helper = new CharacteristicHelper(bluetoothGattCharacteristic);
Log.i(TAG, "isRead : "+helper.isRead());
Log.i(TAG, "isWrite : "+helper.isWrite());
Log.i(TAG, "isNotify : "+helper.isNotify());
Log.i(TAG, "isWriteNoResponse : "+helper.isWriteNoResponse());
}
}
}
// mHandler.sendMessage(Message.obtain(null, MSG_PROGRESS,
// "Enabling Sensors..."));
/*
* With services discovered, we are going to reset our state machine
* and start working through the sensors we need to enable
*/
reset();
enableNextSensor(gatt);
}
#Override
public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status)
{
Log.i(TAG, "onCharacteristicRead");
// For each read, pass the data up to the UI thread to update the
// display
/**methodToUpdateUI().*/
// After reading the initial value, next we enable notifications
setNotifyNextSensor(gatt);
}
#Override
public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status)
{
Log.i(TAG, "onCharacteristicWrite");
// After writing the enable flag, next we read the initial value
readNextSensor(gatt);
}
#Override
public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic)
{
Log.i(TAG, "onCharacteristicChanged");
/*
* After notifications are enabled, all updates from the device on
* characteristic value changes will be posted here. Similar to
* read, we hand these up to the UI thread to update the display.
*/
}
#Override
public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status)
{
Log.i(TAG, "onDescriptorWrite");
// Once notifications are enabled, we move to the next sensor and
// start over with enable
advance();
enableNextSensor(gatt);
}
#Override
public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status)
{
Log.i(TAG, "Remote RSSI: " + rssi);
}
private String connectionState(int status)
{
switch (status)
{
case BluetoothProfile.STATE_CONNECTED:
return "Connected";
case BluetoothProfile.STATE_DISCONNECTED:
return "Disconnected";
case BluetoothProfile.STATE_CONNECTING:
return "Connecting";
case BluetoothProfile.STATE_DISCONNECTING:
return "Disconnecting";
default:
return String.valueOf(status);
}
}
};
/*
* We have a Handler to process event results on the main thread
*/
private static final int MSG_PROGRESS = 201;
private static final int MSG_DISMISS = 202;
private static final int MSG_CLEAR = 301;
private Handler mHandler = new Handler()
{
#SuppressLint("NewApi")
#Override
public void handleMessage(Message msg)
{
BluetoothGattCharacteristic characteristic;
switch (msg.what)
{
case MSG_PROGRESS:
tvMessage.setText((String) msg.obj);
break;
case MSG_DISMISS:
tvMessage.setText("Service Enabled");
break;
case MSG_CLEAR:
tvMessage.setText("");
break;
}
}
};
}
In my activity, first of all I am scanning all the available devices & preparing ListView. On clicking on list item I connect to that particular device. When device's status become connected then I discover services. I have UUIDs of the device's service & its characteristics. But I am not sure how to write to any particular characteristics or enable or read data from it.
Although I have tried this thing but I don't see any success.
If any one has any idea about it, so please help me.
Had a device which required me to use
descriptor.setValue(BluetoothGattDescriptor.ENABLE_INDICATION_VALUE)
instead of
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE)
as explained in this question
Android BLE API: GATT Notification not received
Refer the source code of sample application "bluetoothlegatt" provided on developer portal.
Sample service:
http://developer.android.com/samples/BluetoothLeGatt/src/com.example.android.bluetoothlegatt/BluetoothLeService.html
Using service:
http://developer.android.com/samples/BluetoothLeGatt/src/com.example.android.bluetoothlegatt/DeviceControlActivity.html
This example contains characteristics with read and notify properties. So you definitely find your solution. Please go to section with following code:(You may figure it out)
public void readCharacteristic(BluetoothGattCharacteristic characteristic) {
if (mBluetoothAdapter == null || mBluetoothGatt == null) {
Log.w(TAG, "BluetoothAdapter not initialized");
return;
}
mBluetoothGatt.readCharacteristic(characteristic);
}
you have to differentiate the value is notification or indication, and set respective value using descriptor.setValue. if you set wrongly, you will not get the value.
This one is working for me:
to notify master device that some characteristic is change, call this function on your pheripheral:
private BluetoothGattServer server;
//init....
//on BluetoothGattServerCallback...
//call this after change the characteristic
server.notifyCharacteristicChanged(device, characteristic, false);
in your master device: enable setCharacteristicNotification after discover the service:
#Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
super.onServicesDiscovered(gatt, status);
services = mGatt.getServices();
for(BluetoothGattService service : services){
if( service.getUuid().equals(SERVICE_UUID)) {
characteristicData = service.getCharacteristic(CHAR_UUID);
for (BluetoothGattDescriptor descriptor : characteristicData.getDescriptors()) {
descriptor.setValue( BluetoothGattDescriptor.ENABLE_INDICATION_VALUE);
mGatt.writeDescriptor(descriptor);
}
gatt.setCharacteristicNotification(characteristicData, true);
}
}
if (dialog.isShowing()){
mHandler.post(new Runnable() {
#Override
public void run() {
dialog.hide();
}
});
}
}
now you can check your characteristic value is change, for example onCharacteristicRead function :
#Override
public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
Log.i("onCharacteristicRead", characteristic.toString());
byte[] value=characteristic.getValue();
String v = new String(value);
Log.i("onCharacteristicRead", "Value: " + v);
}