I have create BLE app which includes client and server app. The code is running successfully. Now i want to run server code as a service so that Bluetooth is open every time and it can be searched by client app whenever required. The code used for server is as below. Please guide me to use this code as a service.
public class ServerActivity extends AppCompatActivity {
private static final String TAG = "ServerActivity";
private ActivityServerBinding mBinding;
private Handler mHandler;
private Handler mLogHandler;
private List<BluetoothDevice> mDevices;
private Map<String, byte[]> mClientConfigurations;
private BluetoothGattServer mGattServer;
private BluetoothManager mBluetoothManager;
private BluetoothAdapter mBluetoothAdapter;
private BluetoothLeAdvertiser mBluetoothLeAdvertiser;
// Lifecycle
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mHandler = new Handler();
mLogHandler = new Handler(Looper.getMainLooper());
mDevices = new ArrayList<>();
mClientConfigurations = new HashMap<>();
mBluetoothManager = (BluetoothManager) getSystemService(BLUETOOTH_SERVICE);
mBluetoothAdapter = mBluetoothManager.getAdapter();
mBinding = DataBindingUtil.setContentView(this, R.layout.activity_server);
mBinding.restartServerButton.setOnClickListener(v -> restartServer());
}
#Override
protected void onResume() {
super.onResume();
// Check if bluetooth is enabled
if (mBluetoothAdapter == null || !mBluetoothAdapter.isEnabled()) {
// Request user to enable it
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivity(enableBtIntent);
finish();
return;
}
// Check low energy support
if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
// Get a newer device
log("No LE Support.");
finish();
return;
}
// Check advertising
if (!mBluetoothAdapter.isMultipleAdvertisementSupported()) {
// Unable to run the server on this device, get a better device
log("No Advertising Support.");
finish();
return;
}
mBluetoothLeAdvertiser = mBluetoothAdapter.getBluetoothLeAdvertiser();
GattServerCallback gattServerCallback = new GattServerCallback();
mGattServer = mBluetoothManager.openGattServer(this, gattServerCallback);
Bundle bundle = getIntent().getExtras();
String plate_name = bundle.getString("PLATE_NAME");
mBluetoothAdapter.setName(plate_name);
Toast.makeText(getApplicationContext(),"PLATE NAME "+plate_name,Toast.LENGTH_LONG).show();
#SuppressLint("HardwareIds")
String deviceInfo = "Device Info"
+ "\nName: " + mBluetoothAdapter.getName()
+ "\nAddress: " + mBluetoothAdapter.getAddress();
mBinding.serverDeviceInfoTextView.setText(deviceInfo);
setupServer();
startAdvertising();
}
#Override
protected void onPause() {
super.onPause();
stopAdvertising();
stopServer();
}
// GattServer
private void setupServer() {
BluetoothGattService service = new BluetoothGattService(SERVICE_UUID, BluetoothGattService.SERVICE_TYPE_PRIMARY);
// Write characteristic
BluetoothGattCharacteristic writeCharacteristic = new BluetoothGattCharacteristic(CHARACTERISTIC_ECHO_UUID,
BluetoothGattCharacteristic.PROPERTY_WRITE,
// Somehow this is not necessary, the client can still enable notifications
// | BluetoothGattCharacteristic.PROPERTY_NOTIFY,
BluetoothGattCharacteristic.PERMISSION_WRITE);
// Characteristic with Descriptor
BluetoothGattCharacteristic notifyCharacteristic = new BluetoothGattCharacteristic(CHARACTERISTIC_TIME_UUID,
// Somehow this is not necessary, the client can still enable notifications
// BluetoothGattCharacteristic.PROPERTY_NOTIFY,
0, 0);
BluetoothGattDescriptor clientConfigurationDescriptor = new BluetoothGattDescriptor(CLIENT_CONFIGURATION_DESCRIPTOR_UUID,
BluetoothGattDescriptor.PERMISSION_READ | BluetoothGattDescriptor.PERMISSION_WRITE);
clientConfigurationDescriptor.setValue(BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE);
notifyCharacteristic.addDescriptor(clientConfigurationDescriptor);
service.addCharacteristic(writeCharacteristic);
service.addCharacteristic(notifyCharacteristic);
mGattServer.addService(service);
}
private void stopServer() {
if (mGattServer != null) {
mGattServer.close();
}
}
private void restartServer() {
stopAdvertising();
stopServer();
setupServer();
startAdvertising();
}
// Advertising
private void startAdvertising() {
if (mBluetoothLeAdvertiser == null) {
return;
}
AdvertiseSettings settings = new AdvertiseSettings.Builder()
.setAdvertiseMode(AdvertiseSettings.ADVERTISE_MODE_BALANCED)
.setConnectable(true)
.setTimeout(0)
.setTxPowerLevel(AdvertiseSettings.ADVERTISE_TX_POWER_LOW)
.build();
ParcelUuid parcelUuid = new ParcelUuid(SERVICE_UUID);
AdvertiseData data = new AdvertiseData.Builder()
.setIncludeDeviceName(true)
.addServiceUuid(parcelUuid)
.build();
mBluetoothLeAdvertiser.startAdvertising(settings, data, mAdvertiseCallback);
}
private void stopAdvertising() {
if (mBluetoothLeAdvertiser != null) {
mBluetoothLeAdvertiser.stopAdvertising(mAdvertiseCallback);
}
}
private AdvertiseCallback mAdvertiseCallback = new AdvertiseCallback() {
#Override
public void onStartSuccess(AdvertiseSettings settingsInEffect) {
log("Peripheral advertising started.");
}
#Override
public void onStartFailure(int errorCode) {
log("Peripheral advertising failed: " + errorCode);
}
};
// Notifications
private void notifyCharacteristicTime(byte[] value) {
notifyCharacteristic(value, CHARACTERISTIC_TIME_UUID);
}
private void notifyCharacteristic(byte[] value, UUID uuid) {
mHandler.post(() -> {
BluetoothGattService service = mGattServer.getService(SERVICE_UUID);
BluetoothGattCharacteristic characteristic = service.getCharacteristic(uuid);
log("Notifying characteristic " + characteristic.getUuid().toString()
+ ", new value: " + StringUtils.byteArrayInHexFormat(value));
characteristic.setValue(value);
// Indications require confirmation, notifications do not
boolean confirm = BluetoothUtils.requiresConfirmation(characteristic);
for (BluetoothDevice device : mDevices) {
if (clientEnabledNotifications(device, characteristic)) {
mGattServer.notifyCharacteristicChanged(device, characteristic, confirm);
}
}
});
}
private boolean clientEnabledNotifications(BluetoothDevice device, BluetoothGattCharacteristic characteristic) {
List<BluetoothGattDescriptor> descriptorList = characteristic.getDescriptors();
BluetoothGattDescriptor descriptor = BluetoothUtils.findClientConfigurationDescriptor(descriptorList);
if (descriptor == null) {
// There is no client configuration descriptor, treat as true
return true;
}
String deviceAddress = device.getAddress();
byte[] clientConfiguration = mClientConfigurations.get(deviceAddress);
if (clientConfiguration == null) {
// Descriptor has not been set
return false;
}
byte[] notificationEnabled = BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE;
return clientConfiguration.length == notificationEnabled.length
&& (clientConfiguration[0] & notificationEnabled[0]) == notificationEnabled[0]
&& (clientConfiguration[1] & notificationEnabled[1]) == notificationEnabled[1];
}
// Gatt Server Actions
public void log(String msg) {
Log.d(TAG, msg);
mLogHandler.post(() -> {
mBinding.viewServerLog.logTextView.append(msg + "\n");
mBinding.viewServerLog.logScrollView.post(() -> mBinding.viewServerLog.logScrollView.fullScroll(View.FOCUS_DOWN));
if (msg.contains("Open")) {
ToneGenerator toneGen1 = new ToneGenerator(AudioManager.STREAM_MUSIC, 100);
toneGen1.startTone(ToneGenerator.TONE_CDMA_PIP, 150);
} else if (msg.contains("Close")) {
ToneGenerator toneGen1 = new ToneGenerator(AudioManager.STREAM_MUSIC, 100);
toneGen1.startTone(ToneGenerator.TONE_CDMA_PIP, 150);
toneGen1.startTone(ToneGenerator.TONE_CDMA_PIP, 300);
}
});
}
public void addDevice(BluetoothDevice device) {
log("Device added: " + device.getAddress());
mHandler.post(() -> mDevices.add(device));
}
public void removeDevice(BluetoothDevice device) {
log("Device removed: " + device.getAddress());
mHandler.post(() -> {
mDevices.remove(device);
String deviceAddress = device.getAddress();
mClientConfigurations.remove(deviceAddress);
});
}
public void addClientConfiguration(BluetoothDevice device, byte[] value) {
String deviceAddress = device.getAddress();
mClientConfigurations.put(deviceAddress, value);
}
public void sendResponse(BluetoothDevice device, int requestId, int status, int offset, byte[] value) {
mGattServer.sendResponse(device, requestId, status, 0, null);
}
public void notifyCharacteristicEcho(byte[] value) {
notifyCharacteristic(value, CHARACTERISTIC_ECHO_UUID);
}
// Gatt Callback
private class GattServerCallback extends BluetoothGattServerCallback {
#Override
public void onConnectionStateChange(BluetoothDevice device, int status, int newState) {
super.onConnectionStateChange(device, status, newState);
log("onConnectionStateChange " + device.getAddress()
+ "\nstatus " + status
+ "\nnewState " + newState);
if (newState == BluetoothProfile.STATE_CONNECTED) {
addDevice(device);
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
removeDevice(device);
}
}
// The Gatt will reject Characteristic Read requests that do not have the permission set,
// so there is no need to check inside the callback
#Override
public void onCharacteristicReadRequest(BluetoothDevice device,
int requestId,
int offset,
BluetoothGattCharacteristic characteristic) {
super.onCharacteristicReadRequest(device, requestId, offset, characteristic);
log("onCharacteristicReadRequest " + characteristic.getUuid().toString());
if (BluetoothUtils.requiresResponse(characteristic)) {
// Unknown read characteristic requiring response, send failure
sendResponse(device, requestId, BluetoothGatt.GATT_FAILURE, 0, null);
}
// Not one of our characteristics or has NO_RESPONSE property set
}
// The Gatt will reject Characteristic Write requests that do not have the permission set,
// so there is no need to check inside the callback
#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("onCharacteristicWriteRequest" + characteristic.getUuid().toString()
+ "\nReceived: " + StringUtils.stringFromBytes(value));
if (CHARACTERISTIC_ECHO_UUID.equals(characteristic.getUuid())) {
sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, 0, null);
// Reverse message to differentiate original message & response
byte[] response = ByteUtils.reverse(value);
characteristic.setValue(response);
log("Sending: " + StringUtils.byteArrayInHexFormat(response));
notifyCharacteristicEcho(response);
}
}
// The Gatt will reject Descriptor Read requests that do not have the permission set,
// so there is no need to check inside the callback
#Override
public void onDescriptorReadRequest(BluetoothDevice device,
int requestId,
int offset,
BluetoothGattDescriptor descriptor) {
super.onDescriptorReadRequest(device, requestId, offset, descriptor);
log("onDescriptorReadRequest" + descriptor.getUuid().toString());
}
// The Gatt will reject Descriptor Write requests that do not have the permission set,
// so there is no need to check inside the callback
#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);
log("onDescriptorWriteRequest: " + descriptor.getUuid().toString()
+ "\nvalue: " + StringUtils.stringFromBytes(value));
if (CLIENT_CONFIGURATION_DESCRIPTOR_UUID.equals(descriptor.getUuid())) {
addClientConfiguration(device, value);
sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, 0, null);
}
}
#Override
public void onNotificationSent(BluetoothDevice device, int status) {
super.onNotificationSent(device, status);
log("onNotificationSent");
}
}
}
Use Foreground service with persistent notification. Your service will be keep on running.
Related
android 10 for BLE Bluetooth connection is not connected , i will set all permission also but i get connection status 133 issues ,how to solve this issues ,here i declare the scanning and callback code ,please check and give your idea
but below android 10 version is working fine,only issues on android 10 device ,give an idea to solve the issues
private void scanLeDevice(final boolean enable) {
if (scanner == null) {
scanner = btAdapter.getBluetoothLeScanner();
}
if (scanCallback == null) setScanCallback(null);
scanner.startScan(createScanFilters(),createScanSettings(),scanCallback);
isScanning = true;
}
ScanCallback scanCallback = new ScanCallback() {
#RequiresApi(api = Build.VERSION_CODES.M)
#Override
public void onScanResult(int callbackType, #NonNull ScanResult scanResult) {
if (connectedDevices.containsKey(scanResult.getDevice().getAddress())) {
return;
}
if (connectingDevices.contains(scanResult.getDevice().getAddress())) {
// If we're already connected, forget it
//Timber.d("Denied connection. Already connecting to " + scanResult.getDevice().getAddress());
return;
}
if (connectionGovernor != null && !connectionGovernor.shouldConnectToAddress(scanResult.getDevice().getAddress())) {
// If the BLEConnectionGovernor says we should not bother connecting to this peer, don't
return;
}
final BluetoothDevice device = btAdapter.getRemoteDevice(scanResult.getDevice().getAddress());
if (device == null) {
Log.e(TAG, "Device not found. Unable to connect.");
}
connectingDevices.add(scanResult.getDevice().getAddress());
// connectToDevice(device);
Timber.d("Initiating connection to " + scanResult.getDevice().getAddress());
device.connectGatt(context, false, mGattCallback, BluetoothDevice.TRANSPORT_LE );
}
#Override
public void onScanFailed(int i) {
Timber.e("Scan failed with code " + i);
}
};
#NonNull
BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
#Override
public void onConnectionStateChange(#NonNull BluetoothGatt gatt, int status, int newState) {
synchronized (connectedDevices) {
// It appears that certain events (like disconnection) won't have a GATT_SUCCESS status
// even when they proceed as expected, at least with the Motorola bluetooth stack
if (status != BluetoothGatt.GATT_SUCCESS)
Timber.w("onConnectionStateChange with newState %d and non-success status %s", newState, gatt.getDevice().getAddress());
Set<BluetoothGattCharacteristic> characteristicSet;
switch (newState) {
case BluetoothProfile.STATE_DISCONNECTING:
Timber.d("Disconnecting from " + gatt.getDevice().getAddress());
characteristicSet = discoveredCharacteristics.get(gatt.getDevice().getAddress());
for (BluetoothGattCharacteristic characteristic : characteristicSet) {
if (notifyUUIDs.contains(characteristic.getUuid())) {
Timber.d("Attempting to unsubscribe on disconneting");
setIndictaionSubscription(gatt, characteristic, false);
}
}
discoveredCharacteristics.remove(gatt.getDevice().getAddress());
break;
case BluetoothProfile.STATE_DISCONNECTED:
Timber.d("Disconnected from " + gatt.getDevice().getAddress());
connectedDevices.remove(gatt.getDevice().getAddress());
connectingDevices.remove(gatt.getDevice().getAddress());
if (transportCallback != null)
transportCallback.identifierUpdated(BLETransportCallback.DeviceType.CENTRAL,
gatt.getDevice().getAddress(),
Transport.ConnectionStatus.DISCONNECTED,
null);
characteristicSet = discoveredCharacteristics.get(gatt.getDevice().getAddress());
if (characteristicSet != null) { // Have we handled unsubscription on DISCONNECTING?
for (BluetoothGattCharacteristic characteristic : characteristicSet) {
if (notifyUUIDs.contains(characteristic.getUuid())) {
Timber.d("Attempting to unsubscribe before disconnet");
setIndictaionSubscription(gatt, characteristic, false);
}
}
} else
if ( status != BluetoothGatt.GATT_SUCCESS ) {
if ( status == 133) {
refreshDeviceCache(gatt);
}
}
gatt.close(); discoveredCharacteristics.remove(gatt.getDevice().getAddress());
break;
case BluetoothProfile.STATE_CONNECTED:
boolean mtuSuccess = gatt.requestMtu(BLETransport.DEFAULT_MTU_BYTES);
Timber.d("Connected to %s. Requested MTU success %b", gatt.getDevice().getAddress(),
mtuSuccess);
break;
}
super.onConnectionStateChange(gatt, status, newState);
}
}
#Override
public void onMtuChanged(#NonNull BluetoothGatt gatt, int mtu, int status) {
Timber.d("Got MTU (%d bytes) for device %s. Was changed successfully: %b",
mtu,
gatt.getDevice().getAddress(),
status == BluetoothGatt.GATT_SUCCESS);
mtus.put(gatt.getDevice().getAddress(), mtu);
// TODO: Can we craft characteristics and avoid discovery step?
boolean discovering = gatt.discoverServices();
Timber.d("Discovering services : " + discovering);
}
#Override
public void onServicesDiscovered(#NonNull BluetoothGatt gatt, int status) {
if (status == BluetoothGatt.GATT_SUCCESS)
Timber.d("Discovered services");
else
Timber.d("Discovered services appears unsuccessful with code " + status);
// TODO: Keep this here to examine characteristics
// eventually we should get rid of the discoverServices step
boolean foundService = false;
try {
List<BluetoothGattService> serviceList = gatt.getServices();
for (BluetoothGattService service : serviceList) {
if (service.getUuid().equals(serviceUUID)) {
Timber.d("Discovered Service");
foundService = true;
HashSet<BluetoothGattCharacteristic> characteristicSet = new HashSet<>();
characteristicSet.addAll(service.getCharacteristics());
discoveredCharacteristics.put(gatt.getDevice().getAddress(), characteristicSet);
for (BluetoothGattCharacteristic characteristic : characteristicSet) {
if (notifyUUIDs.contains(characteristic.getUuid())) {
setIndictaionSubscription(gatt, characteristic, true);
}
}
}
}
if (foundService) {
synchronized (connectedDevices) {
connectedDevices.put(gatt.getDevice().getAddress(), gatt);
}
connectingDevices.remove(gatt.getDevice().getAddress());
}
} catch (Exception e) {
Timber.d("Exception analyzing discovered services " + e.getLocalizedMessage());
e.printStackTrace();
}
if (!foundService)
Timber.d("Could not discover chat service!");
super.onServicesDiscovered(gatt, status);
}
/**
* Subscribe or Unsubscribe to/from indication of a peripheral's characteristic.
*
* After calling this method you must await the result via
* {#link #onDescriptorWrite(BluetoothGatt, BluetoothGattDescriptor, int)}
* before performing any other peripheral actions.
*/
private void setIndictaionSubscription(#NonNull BluetoothGatt peripheral,
#NonNull BluetoothGattCharacteristic characteristic,
boolean enable) {
boolean success = peripheral.setCharacteristicNotification(characteristic, enable);
Timber.d("Request notification %s %s with sucess %b", enable ? "set" : "unset", characteristic.getUuid().toString(), success);
BluetoothGattDescriptor desc = characteristic.getDescriptor(CLIENT_CHARACTERISTIC_CONFIG);
desc.setValue(enable ? BluetoothGattDescriptor.ENABLE_INDICATION_VALUE : BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE);
boolean desSuccess = peripheral.writeDescriptor(desc);
Timber.d("Wrote descriptor with success %b", desSuccess);
}
#Override
public void onDescriptorWrite(#NonNull BluetoothGatt gatt, #NonNull BluetoothGattDescriptor descriptor,
int status) {
Timber.d("onDescriptorWrite");
if (status == BluetoothGatt.GATT_SUCCESS && transportCallback != null) {
if (Arrays.equals(descriptor.getValue(), BluetoothGattDescriptor.ENABLE_INDICATION_VALUE)) {
transportCallback.identifierUpdated(BLETransportCallback.DeviceType.CENTRAL,
gatt.getDevice().getAddress(),
Transport.ConnectionStatus.CONNECTED,
null);
} else if (Arrays.equals(descriptor.getValue(), BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE)) {
Timber.d("disabled indications successfully. Closing gatt");
gatt.close();
}
}
}
#Override
public void onCharacteristicChanged(#NonNull BluetoothGatt gatt, #NonNull BluetoothGattCharacteristic characteristic) {
Timber.d("onCharacteristicChanged %s with %d bytes", characteristic.getUuid().toString().substring(0,5),
characteristic.getValue().length);
if (transportCallback != null)
transportCallback.dataReceivedFromIdentifier(BLETransportCallback.DeviceType.CENTRAL,
characteristic.getValue(),
gatt.getDevice().getAddress());
super.onCharacteristicChanged(gatt, characteristic);
}
#Override
public void onCharacteristicWrite(#NonNull BluetoothGatt gatt,
#NonNull BluetoothGattCharacteristic characteristic, int status) {
Timber.d("onCharacteristicWrite with %d bytes", characteristic.getValue().length);
Exception exception = null;
if (status != BluetoothGatt.GATT_SUCCESS) {
String msg = "Write was not successful with code " + status;
Timber.w(msg);
exception = new UnknownServiceException(msg);
}
if (transportCallback != null)
transportCallback.dataSentToIdentifier(BLETransportCallback.DeviceType.CENTRAL,
characteristic.getValue(),
gatt.getDevice().getAddress(),
exception);
}
#Override
public void onReadRemoteRssi(#NonNull BluetoothGatt gatt, int rssi, int status) {
super.onReadRemoteRssi(gatt, rssi, status);
}
};
now the app is working for change the bluetooth device support type
device.connectGatt(context, false, mGattCallback, BluetoothDevice.DEVICE_TYPE_LE);
but now the app is not working on huawei device , if any have idea for the device huawei for this issue
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 have a problem trying to connect with 2 Bluetooth LE modules (RN4020) throught native Android Bluetooth LE API and based on the Bluetooth Gatt sample of Android developers (https://developer.android.com/samples/BluetoothLeGatt/index.html). I modified BluetoothLeService.java to hold many connections instead of only one.
This is my process:
1- Start a new service which handle connections
2- Loop to connect devices:
for(String address: addresses) {
mBluetoothLeService.connect(address)
}
3- Find characteristics I need
4- Send data:
mBluetoothLeService.writeCharacteristic(address, mCharacteristic, dataArray);
The thing is that when I send data to the first module, it receive it perfect (onCharacteristicWrite() in BluetoothGattCallback() is called), then I send data to the second module and everything OK! But when I try to send data to the first module again, it doesn't receive it (tested in the board and also onCharacteristicWrite() is not called).
I also try with Reliable Write:
btGatt.beginReliableWrite();
characteristic.setValue(bytearray);
btGatt.writeCharacteristic(characteristic);
btGatt.executeReliableWrite();
but the same problem. The callback:
onReliableWriteCompleted(BluetoothGatt gatt, int status)
Status code is 6 (BluetoothGatt.GATT_REQUEST_NOT_SUPPORTED)
I checked connection status to both modules and there were connected every time (no disconnection problem).
Here is the modified class BluetoothLeService.java:
public class BluetoothLeService extends Service {
private final static String TAG = BluetoothLeService.class.getSimpleName();
private BluetoothManager mBluetoothManager;
private BluetoothAdapter mBluetoothAdapter;
// private String mBluetoothDeviceAddress;
private Map<String,BluetoothGatt> mBluetoothGatts = new HashMap<>();
private int mConnectionState = STATE_DISCONNECTED;
private static final int STATE_DISCONNECTED = 0;
private static final int STATE_CONNECTING = 1;
private static final int STATE_CONNECTED = 2;
public final static String ACTION_GATT_CONNECTED =
"com.example.bluetooth.le.ACTION_GATT_CONNECTED";
public final static String ACTION_GATT_DISCONNECTED =
"com.example.bluetooth.le.ACTION_GATT_DISCONNECTED";
public final static String ACTION_GATT_SERVICES_DISCOVERED =
"com.example.bluetooth.le.ACTION_GATT_SERVICES_DISCOVERED";
public final static String ACTION_DATA_AVAILABLE =
"com.example.bluetooth.le.ACTION_DATA_AVAILABLE";
public final static String EXTRA_DATA =
"com.example.bluetooth.le.EXTRA_DATA";
public final static String EXTRA_ADDRESS =
"com.example.bluetooth.le.EXTRA_ADDRESS";
public final static UUID UUID_HEART_RATE_MEASUREMENT =
UUID.fromString(SampleGattAttributes.HEART_RATE_MEASUREMENT);
// Implements callback methods for GATT events that the app cares about. For example,
// connection change and services discovered.
private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
#Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
String intentAction;
if (newState == BluetoothProfile.STATE_CONNECTED) {
intentAction = ACTION_GATT_CONNECTED;
mConnectionState = STATE_CONNECTED;
broadcastUpdate(gatt.getDevice().getAddress(), intentAction);
Log.i(TAG, "Connected to GATT server. address: " + gatt.getDevice().getAddress());
// Attempts to discover services after successful connection.
Log.i(TAG, "Attempting to start service discovery:" +
gatt.discoverServices());
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
intentAction = ACTION_GATT_DISCONNECTED;
mConnectionState = STATE_DISCONNECTED;
Log.i(TAG, "Disconnected from GATT server.");
broadcastUpdate(gatt.getDevice().getAddress(), intentAction);
}
}
#Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
Log.i(TAG, "--onServicesDiscovered. status: " + status);
if (status == BluetoothGatt.GATT_SUCCESS) {
broadcastUpdate(gatt.getDevice().getAddress(), ACTION_GATT_SERVICES_DISCOVERED);
} else {
Log.w(TAG, "onServicesDiscovered received: " + status);
}
}
#Override
public void onCharacteristicRead(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic,
int status) {
if (status == BluetoothGatt.GATT_SUCCESS) {
broadcastUpdate(gatt.getDevice().getAddress(), ACTION_DATA_AVAILABLE, characteristic);
}
}
#Override
public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
super.onCharacteristicWrite(gatt, characteristic, status);
Log.i(TAG, "--onCharacteristicWrite: value: " + Arrays.toString(characteristic.getValue()));
}
#Override
public void onReliableWriteCompleted(BluetoothGatt gatt, int status) {
super.onReliableWriteCompleted(gatt, status);
Log.i(TAG, "--onReliableWriteCompleted: status: " + status);
}
#Override
public void onCharacteristicChanged(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic) {
broadcastUpdate(gatt.getDevice().getAddress(), ACTION_DATA_AVAILABLE, characteristic);
}
};
private void broadcastUpdate(String address, final String action) {
final Intent intent = new Intent(action);
intent.putExtra(EXTRA_ADDRESS, address);
sendBroadcast(intent);
}
private void broadcastUpdate(final String address, final String action,
final BluetoothGattCharacteristic characteristic) {
final Intent intent = new Intent(action);
// This is special handling for the Heart Rate Measurement profile. Data parsing is
// carried out as per profile specifications:
// http://developer.bluetooth.org/gatt/characteristics/Pages/CharacteristicViewer.aspx?u=org.bluetooth.characteristic.heart_rate_measurement.xml
if (UUID_HEART_RATE_MEASUREMENT.equals(characteristic.getUuid())) {
int flag = characteristic.getProperties();
int format = -1;
if ((flag & 0x01) != 0) {
format = BluetoothGattCharacteristic.FORMAT_UINT16;
Log.d(TAG, "Heart rate format UINT16.");
} else {
format = BluetoothGattCharacteristic.FORMAT_UINT8;
Log.d(TAG, "Heart rate format UINT8.");
}
final int heartRate = characteristic.getIntValue(format, 1);
Log.d(TAG, String.format("Received heart rate: %d", heartRate));
intent.putExtra(EXTRA_DATA, String.valueOf(heartRate));
} else {
// For all other profiles, writes the data formatted in HEX.
final byte[] data = characteristic.getValue();
if (data != null && data.length > 0) {
final StringBuilder stringBuilder = new StringBuilder(data.length);
for (byte byteChar : data)
stringBuilder.append(String.format("%02X ", byteChar));
intent.putExtra(EXTRA_DATA, new String(data) + "\n" + stringBuilder.toString());
}
}
intent.putExtra(EXTRA_ADDRESS, address);
sendBroadcast(intent);
}
public class LocalBinder extends Binder {
public BluetoothLeService getService() {
return BluetoothLeService.this;
}
}
#Override
public IBinder onBind(Intent intent) {
return mBinder;
}
#Override
public boolean onUnbind(Intent intent) {
// After using a given device, you should make sure that BluetoothGatt.close() is called
// such that resources are cleaned up properly. In this particular example, close() is
// invoked when the UI is disconnected from the Service.
disconnectAll();
closeAll();
return super.onUnbind(intent);
}
private final IBinder mBinder = new LocalBinder();
/**
* Initializes a reference to the local Bluetooth adapter.
*
* #return Return true if the initialization is successful.
*/
public boolean initialize() {
// For API level 18 and above, get a reference to BluetoothAdapter through
// BluetoothManager.
if (mBluetoothManager == null) {
mBluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
if (mBluetoothManager == null) {
Log.e(TAG, "Unable to initialize BluetoothManager.");
return false;
}
}
mBluetoothAdapter = mBluetoothManager.getAdapter();
if (mBluetoothAdapter == null) {
Log.e(TAG, "Unable to obtain a BluetoothAdapter.");
return false;
}
return true;
}
/**
* Connects to the GATT server hosted on the Bluetooth LE device.
*
* #param address The device address of the destination device.
* #return Return true if the connection is initiated successfully. The connection result
* is reported asynchronously through the
* {#code BluetoothGattCallback#onConnectionStateChange(android.bluetooth.BluetoothGatt, int, int)}
* callback.
*/
public boolean connect(final String address) {
if (mBluetoothAdapter == null || address == null) {
Log.w(TAG, "BluetoothAdapter not initialized or unspecified address.");
return false;
}
BluetoothGatt btGatt = mBluetoothGatts.get(address);
// Previously connected device. Try to reconnect.
if ( btGatt != null) {
Log.d(TAG, "---Trying to use an existing mBluetoothGatt for connection.");
if (btGatt.connect()) {
mConnectionState = STATE_CONNECTING;
return true;
} else {
return false;
}
}
final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address);
if (device == null) {
Log.w(TAG, "---Device not found. Unable to connect.");
return false;
}
// We want to directly connect to the device, so we are setting the autoConnect
// parameter to false.
BluetoothGatt newBtGatt = device.connectGatt(this, true, mGattCallback);
Log.d(TAG, "---Trying to create a new connection. address: " + address);
mBluetoothGatts.put(address, newBtGatt);
mConnectionState = STATE_CONNECTING;
return true;
}
/**
* Disconnects an existing connection or cancel a pending connection. The disconnection result
* is reported asynchronously through the
* {#code BluetoothGattCallback#onConnectionStateChange(android.bluetooth.BluetoothGatt, int, int)}
* callback.
*/
public void disconnect(String address) {
BluetoothGatt btGatt = mBluetoothGatts.get(address);
if (mBluetoothAdapter == null || btGatt == null) {
Log.w(TAG, "BluetoothAdapter not initialized");
return;
}
Log.i(TAG, "disconnecting gatt");
btGatt.disconnect();
}
public void disconnectAll() {
// Connecting all devices
Iterator it = mBluetoothGatts.entrySet().iterator();
while (it.hasNext()) {
Map.Entry pair = (Map.Entry) it.next();
String address = (String) pair.getKey();
disconnect(address);
}
}
public int getConnectionState() {
return mConnectionState;
}
/**
* After using a given BLE device, the app must call this method to ensure resources are
* released properly.
*/
public void close(String address, boolean removeFromMap) {
BluetoothGatt btGatt = mBluetoothGatts.get(address);
if (btGatt == null) {
return;
}
Log.i(TAG, "closing gatt");
btGatt.close();
if (removeFromMap) {
mBluetoothGatts.remove(address);
}
}
public void closeAll() {
// Connecting all devices
Iterator it = mBluetoothGatts.entrySet().iterator();
while (it.hasNext()) {
Map.Entry pair = (Map.Entry) it.next();
String address = (String) pair.getKey();
close(address, false);
}
}
/**
* Request a read on a given {#code BluetoothGattCharacteristic}. The read result is reported
* asynchronously through the {#code BluetoothGattCallback#onCharacteristicRead(android.bluetooth.BluetoothGatt, android.bluetooth.BluetoothGattCharacteristic, int)}
* callback.
*
* #param characteristic The characteristic to read from.
*/
public void readCharacteristic(String address, BluetoothGattCharacteristic characteristic) {
BluetoothGatt btGatt = mBluetoothGatts.get(address);
if (mBluetoothAdapter == null || btGatt == null) {
Log.w(TAG, "BluetoothAdapter not initialized");
return;
}
btGatt.readCharacteristic(characteristic);
}
public void writeCharacteristic(String address, BluetoothGattCharacteristic characteristic,
byte[] bytearray) {
BluetoothGatt btGatt = mBluetoothGatts.get(address);
if (mBluetoothAdapter == null || btGatt == null) {
Log.w(TAG, "BluetoothAdapter not initialized");
return;
}
if (characteristic == null) {
Log.w(TAG, "Characteristic == null");
}
Log.i(TAG, "--> Writing characacteristic: " + Arrays.toString(bytearray) + ", device address: " + address);
// btGatt.beginReliableWrite();
characteristic.setValue(bytearray);
// BluetoothManager bluetoothManager =
// (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
// Log.i(TAG, "--connection state: " + bluetoothManager.getConnectionState(btGatt.getDevice(), BluetoothGatt.GATT_SERVER));
btGatt.writeCharacteristic(characteristic);
// btGatt.executeReliableWrite();
}
/**
* Enables or disables notification on a give characteristic.
*
* #param characteristic Characteristic to act on.
* #param enabled If true, enable notification. False otherwise.
*/
public void setCharacteristicNotification(String address, BluetoothGattCharacteristic characteristic,
boolean enabled) {
BluetoothGatt btGatt = mBluetoothGatts.get(address);
if (mBluetoothAdapter == null || btGatt == null) {
Log.w(TAG, "BluetoothAdapter not initialized");
return;
}
btGatt.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);
btGatt.writeDescriptor(descriptor);
}
}
/**
* Retrieves a list of supported GATT services on the connected device. This should be
* invoked only after {#code BluetoothGatt#discoverServices()} completes successfully.
*
* #return A {#code List} of supported services.
*/
public List<BluetoothGattService> getSupportedGattServices(String address) {
BluetoothGatt btGatt = mBluetoothGatts.get(address);
if (btGatt == null) return null;
return btGatt.getServices();
}
}
Any ideas or libraries to handle multiple BLE connections? Thanks!
My goal in this question is push the rssilevel from one .java file to the other.
I tried to push from class 1 to class 2, as well as pulling data dfrom class 1 to class 2 and i have always had my app crash.
I tried
ProximityManager temp = new ProximityManager();
int temp = temp.rssilevel;
The above code is in a class in the same package, but different .java file and class. They are linked.
and it would not work, any other ideas please!
EDIT: Also how would i call the function playSound() in another class?
i would normaly do something like Public ProximityManager mProximityManager;
then call mProximityManager.playSound(); but i get an app crash when i do this.
public class ProximityManager implements BleManager<ProximityManagerCallbacks> {
private final String TAG = "ProximityManager";
private ProximityManagerCallbacks mCallbacks;
private BluetoothGattServer mBluetoothGattServer;
private BluetoothGatt mBluetoothGatt;
private BluetoothDevice mDeviceToConnect;
private Context mContext;
private Handler mHandler;
private LogSession mLogSession;
private Ringtone mRingtoneNotification;
private Ringtone mRingtoneAlarm;
public int rssilevel;
public final static UUID IMMEIDIATE_ALERT_SERVICE_UUID = UUID.fromString("00001802-0000-1000-8000-00805f9b34fb");
public final static UUID LINKLOSS_SERVICE_UUID = UUID.fromString("00001803-0000-1000-8000-00805f9b34fb");
private static final UUID ALERT_LEVEL_CHARACTERISTIC_UUID = UUID.fromString("00002A06-0000-1000-8000-00805f9b34fb");
private final static UUID BATTERY_SERVICE_UUID = UUID.fromString("0000180F-0000-1000-8000-00805f9b34fb");
private final static UUID BATTERY_LEVEL_CHARACTERISTIC_UUID = UUID.fromString("00002A19-0000-1000-8000-00805f9b34fb");
private final static UUID TX_POWER_UUID = UUID.fromString("00001804-0000-1000-8000-00805f9b34fb");
private final static String ERROR_CONNECTION_STATE_CHANGE = "Error on connection state change";
private final static String ERROR_DISCOVERY_SERVICE = "Error on discovering services";
private final static String ERROR_AUTH_ERROR_WHILE_BONDED = "Phone has lost bonding information";
private final static String ERROR_WRITE_CHARACTERISTIC = "Error on writing characteristic";
private final static String ERROR_READ_CHARACTERISTIC = "Error on reading characteristic";
private final static int HIGH_ALERT = 2;
private final static int MID_ALERT = 1;
private final static int NO_ALERT = 0;
private BluetoothGattCharacteristic mAlertLevelCharacteristic, mLinklossCharacteristic, mBatteryCharacteristic;
private boolean userDisconnectedFlag = false;
public void getrssi( int rssilevel) {
this.rssilevel = rssilevel;
}
public ProximityManager(Context context) {
initializeAlarm(context);
mHandler = new Handler();
// Register bonding broadcast receiver
final IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
context.registerReceiver(mBondingBroadcastReceiver, filter);
}
private void openGattServer(Context context, BluetoothManager manager) {
mBluetoothGattServer = manager.openGattServer(context, mGattServerCallbacks);
}
private void closeGattServer() {
if (mBluetoothGattServer != null) {
// mBluetoothGattServer.cancelConnection(mBluetoothGatt.getDevice()); // FIXME this method does not cancel the connection
mBluetoothGattServer.close(); // FIXME This method does not cause BluetoothGattServerCallback#onConnectionStateChange(newState=DISCONNECTED) to be called on Nexus phones.
mBluetoothGattServer = null;
}
}
private void addImmediateAlertService() {
/*
* This method must be called in UI thread. It works fine on Nexus devices but if called from other thread (f.e. from onServiceAdded in gatt server callback) it hangs the app.
*/
BluetoothGattCharacteristic alertLevel = new BluetoothGattCharacteristic(ALERT_LEVEL_CHARACTERISTIC_UUID, BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE,
BluetoothGattCharacteristic.PERMISSION_WRITE);
alertLevel.setValue(HIGH_ALERT, BluetoothGattCharacteristic.FORMAT_UINT8, 0);
BluetoothGattService immediateAlertService = new BluetoothGattService(IMMEIDIATE_ALERT_SERVICE_UUID, BluetoothGattService.SERVICE_TYPE_PRIMARY);
immediateAlertService.addCharacteristic(alertLevel);
mBluetoothGattServer.addService(immediateAlertService);
}
private void addLinklossService() {
/*
* This method must be called in UI thread. It works fine on Nexus devices but if called from other thread (f.e. from onServiceAdded in gatt server callback) it hangs the app.
*/
BluetoothGattCharacteristic linklossAlertLevel = new BluetoothGattCharacteristic(ALERT_LEVEL_CHARACTERISTIC_UUID, BluetoothGattCharacteristic.PROPERTY_WRITE
| BluetoothGattCharacteristic.PROPERTY_READ, BluetoothGattCharacteristic.PERMISSION_WRITE);
linklossAlertLevel.setValue(HIGH_ALERT, BluetoothGattCharacteristic.FORMAT_UINT8, 0);
BluetoothGattService linklossService = new BluetoothGattService(LINKLOSS_SERVICE_UUID, BluetoothGattService.SERVICE_TYPE_PRIMARY);
linklossService.addCharacteristic(linklossAlertLevel);
mBluetoothGattServer.addService(linklossService);
}
private BluetoothGattServerCallback mGattServerCallbacks = new BluetoothGattServerCallback() {
#Override
public void onCharacteristicReadRequest(BluetoothDevice device, int requestId, int offset, BluetoothGattCharacteristic characteristic) {
DebugLogger.d(TAG, "[Proximity Server] onCharacteristicReadRequest " + device.getName());
}
#Override
public void onCharacteristicWriteRequest(BluetoothDevice device, int requestId, BluetoothGattCharacteristic characteristic, boolean preparedWrite, boolean responseNeeded, int offset,
byte[] value) {
DebugLogger.d(TAG, "[Proximity Server] onCharacteristicWriteRequest " + device.getName());
final int receivedValue = value[0];
if (receivedValue != NO_ALERT) {
Logger.i(mLogSession, "[Proximity Server] Immediate alarm request received: ON");
playAlarm();
} else {
Logger.i(mLogSession, "[Proximity Server] Immediate alarm request received: OFF");
stopAlarm();
}
}
#Override
public void onConnectionStateChange(BluetoothDevice device, int status, int newState) {
DebugLogger.d(TAG, "[Proximity Server] onConnectionStateChange " + device.getName() + " status: " + status + " new state: " + newState);
}
#Override
public void onDescriptorReadRequest(BluetoothDevice device, int requestId, int offset, BluetoothGattDescriptor descriptor) {
DebugLogger.d(TAG, "[Proximity Server] onDescriptorReadRequest " + device.getName());
}
#Override
public void onDescriptorWriteRequest(BluetoothDevice device, int requestId, BluetoothGattDescriptor descriptor, boolean preparedWrite, boolean responseNeeded, int offset, byte[] value) {
DebugLogger.d(TAG, "[Proximity Server] onDescriptorWriteRequest " + device.getName());
}
#Override
public void onExecuteWrite(BluetoothDevice device, int requestId, boolean execute) {
DebugLogger.d(TAG, "[Proximity Server] onExecuteWrite " + device.getName());
}
#Override
public void onServiceAdded(final int status, final BluetoothGattService service) {
DebugLogger.d(TAG, "[Proximity Server] onServiceAdded " + service.getUuid());
mHandler.post(new Runnable() {
#Override
public void run() {
// adding another service from callback thread fails on Samsung S4 with Android 4.3
if (IMMEIDIATE_ALERT_SERVICE_UUID.equals(service.getUuid()))
addLinklossService();
else {
DebugLogger.d(TAG, "[Proximity Server] Gatt server started");
Logger.i(mLogSession, "[Proximity Server] Gatt server started");
if (mBluetoothGatt == null) {
mBluetoothGatt = mDeviceToConnect.connectGatt(mContext, false, mGattCallback);
mDeviceToConnect = null;
} else {
mBluetoothGatt.connect();
}
}
}
});
}
};
/**
* Callbacks for activity {HTSActivity} that implements HTSManagerCallbacks interface activity use this method to register itself for receiving callbacks
*/
#Override
public void setGattCallbacks(ProximityManagerCallbacks callbacks) {
mCallbacks = callbacks;
}
/**
* Sets the log session that can be used to log events
*
* #param logSession
*/
public void setLogger(LogSession logSession) {
mLogSession = logSession;
}
#Override
public void connect(Context context, BluetoothDevice device) {
mContext = context;
// save the device that we want to connect to
mDeviceToConnect = device;
final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
if (preferences.getBoolean(ProximityActivity.PREFS_GATT_SERVER_ENABLED, true)) {
final BluetoothManager bluetoothManager = (BluetoothManager) context.getSystemService(Context.BLUETOOTH_SERVICE);
try {
DebugLogger.d(TAG, "[Proximity Server] Starting Gatt server...");
Logger.v(mLogSession, "[Proximity Server] Starting Gatt server...");
openGattServer(context, bluetoothManager);
addImmediateAlertService();
// the BluetoothGattServerCallback#onServiceAdded callback will proceed further operations
} catch (final Exception e) {
// On Nexus 4&7 with Android 4.4 (build KRT16S) sometimes creating Gatt Server fails. There is a Null Pointer Exception thrown from addCharacteristic method.
Logger.e(mLogSession, "[Proximity Server] Gatt server failed to start");
Log.e(TAG, "Creating Gatt Server failed", e);
}
} else {
if (mBluetoothGatt == null) {
mBluetoothGatt = mDeviceToConnect.connectGatt(context, false, mGattCallback);
mDeviceToConnect = null;
} else {
mBluetoothGatt.connect();
}
}
}
#Override
public void disconnect() {
if (mBluetoothGatt != null) {
userDisconnectedFlag = true;
mBluetoothGatt.disconnect();
stopAlarm();
closeGattServer();
}
}
private void initializeAlarm(Context context) {
final Uri alarmUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_ALARM);
mRingtoneAlarm = RingtoneManager.getRingtone(context, alarmUri);
mRingtoneAlarm.setStreamType(AudioManager.STREAM_ALARM);
final Uri notification = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
mRingtoneNotification = RingtoneManager.getRingtone(context, notification);
}
private void playNotification() {
DebugLogger.d(TAG, "playNotification");
mRingtoneNotification.play();
}
private void playAlarm() {
DebugLogger.d(TAG, "playAlarm");
final AudioManager am = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
am.setStreamVolume(AudioManager.STREAM_ALARM, am.getStreamMaxVolume(AudioManager.STREAM_ALARM), AudioManager.FLAG_REMOVE_SOUND_AND_VIBRATE);
mRingtoneAlarm.play();
}
private void stopAlarm() {
DebugLogger.d(TAG, "stopAlarm");
mRingtoneAlarm.stop();
}
/**
* BluetoothGatt callbacks for connection/disconnection, service discovery, receiving indication, etc
*/
private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
#Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
if (status == BluetoothGatt.GATT_SUCCESS) {
if (newState == BluetoothProfile.STATE_CONNECTED) {
DebugLogger.d(TAG, "Device connected");
mBluetoothGatt.discoverServices();
//This will send callback to ProximityActivity when device get connected
mCallbacks.onDeviceConnected();
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
DebugLogger.d(TAG, "Device disconnected");
if (userDisconnectedFlag) {
mCallbacks.onDeviceDisconnected();
userDisconnectedFlag = false;
} else {
playNotification();
mCallbacks.onLinklossOccur();
}
}
} else {
mCallbacks.onError(ERROR_CONNECTION_STATE_CHANGE, status);
}
}
#Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
if (status == BluetoothGatt.GATT_SUCCESS) {
final List<BluetoothGattService> services = gatt.getServices();
for (BluetoothGattService service : services) {
if (service.getUuid().equals(IMMEIDIATE_ALERT_SERVICE_UUID)) {
DebugLogger.d(TAG, "Immediate Alert service is found");
mAlertLevelCharacteristic = service.getCharacteristic(ALERT_LEVEL_CHARACTERISTIC_UUID);
} else if (service.getUuid().equals(LINKLOSS_SERVICE_UUID)) {
DebugLogger.d(TAG, "Linkloss service is found");
mLinklossCharacteristic = service.getCharacteristic(ALERT_LEVEL_CHARACTERISTIC_UUID);
} else if (service.getUuid().equals(BATTERY_SERVICE_UUID)) {
DebugLogger.d(TAG, "Battery service is found");
mBatteryCharacteristic = service.getCharacteristic(BATTERY_LEVEL_CHARACTERISTIC_UUID);
}
}
if (mLinklossCharacteristic == null) {
mCallbacks.onDeviceNotSupported();
gatt.disconnect();
} else {
mCallbacks.onServicesDiscovered(mAlertLevelCharacteristic != null);
writeLinklossAlertLevel(HIGH_ALERT);
}
} else {
mCallbacks.onError(ERROR_DISCOVERY_SERVICE, status);
}
mBluetoothGatt.readRemoteRssi();
DebugLogger.d(TAG, "Finished READING THE RSSI");
}
#Override
public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) {
if (rssi != 0) {
rssilevel = rssi; }
else {
rssilevel = 0; }
DebugLogger.d(TAG, "READING THE RSSI");
}
public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
if (status == BluetoothGatt.GATT_SUCCESS) {
if (characteristic.getUuid().equals(BATTERY_LEVEL_CHARACTERISTIC_UUID)) {
int batteryValue = characteristic.getValue()[0];
mCallbacks.onBatteryValueReceived(batteryValue);
}
} else if (status == BluetoothGatt.GATT_INSUFFICIENT_AUTHENTICATION) {
if (gatt.getDevice().getBondState() != BluetoothDevice.BOND_NONE) {
DebugLogger.w(TAG, ERROR_AUTH_ERROR_WHILE_BONDED);
mCallbacks.onError(ERROR_AUTH_ERROR_WHILE_BONDED, status);
}
} else {
mCallbacks.onError(ERROR_READ_CHARACTERISTIC, status);
}
}
#Override
public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
if (status == BluetoothGatt.GATT_SUCCESS) {
if (characteristic.getUuid().equals(ALERT_LEVEL_CHARACTERISTIC_UUID)) {
if (mBatteryCharacteristic != null) {
readBatteryLevel();
}
}
} else if (status == BluetoothGatt.GATT_INSUFFICIENT_AUTHENTICATION) {
if (gatt.getDevice().getBondState() != BluetoothDevice.BOND_NONE) {
DebugLogger.w(TAG, ERROR_AUTH_ERROR_WHILE_BONDED);
mCallbacks.onError(ERROR_AUTH_ERROR_WHILE_BONDED, status);
}
} else {
mCallbacks.onError(ERROR_WRITE_CHARACTERISTIC, status);
}
}
};
private BroadcastReceiver mBondingBroadcastReceiver = new BroadcastReceiver() {
#Override
public void onReceive(final Context context, final Intent intent) {
final BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
final int bondState = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, -1);
final int previousBondState = intent.getIntExtra(BluetoothDevice.EXTRA_PREVIOUS_BOND_STATE, -1);
// skip other devices
if (!device.getAddress().equals(mBluetoothGatt.getDevice().getAddress()))
return;
DebugLogger.i(TAG, "Bond state changed for: " + device.getName() + " new state: " + bondState + " previous: " + previousBondState);
if (bondState == BluetoothDevice.BOND_BONDING) {
mCallbacks.onBondingRequired();
return;
}
if (bondState == BluetoothDevice.BOND_BONDED) {
if (mLinklossCharacteristic != null) {
writeLinklossAlertLevel(HIGH_ALERT);
}
mCallbacks.onBonded();
}
}
};
private void readBatteryLevel() {
if (mBatteryCharacteristic != null) {
DebugLogger.d(TAG, "reading battery characteristic");
mBluetoothGatt.readCharacteristic(mBatteryCharacteristic);
} else {
DebugLogger.w(TAG, "Battery Level Characteristic is null");
}
}
#SuppressWarnings("unused")
private void readLinklossAlertLevel() {
if (mLinklossCharacteristic != null) {
DebugLogger.d(TAG, "reading linkloss alert level characteristic");
mBluetoothGatt.readCharacteristic(mLinklossCharacteristic);
} else {
DebugLogger.w(TAG, "Linkloss Alert Level Characteristic is null");
}
}
private void writeLinklossAlertLevel(int alertLevel) {
if (mLinklossCharacteristic != null) {
DebugLogger.d(TAG, "writing linkloss alert level characteristic");
mLinklossCharacteristic.setValue(alertLevel, BluetoothGattCharacteristic.FORMAT_UINT8, 0);
mBluetoothGatt.writeCharacteristic(mLinklossCharacteristic);
} else {
DebugLogger.w(TAG, "Linkloss Alert Level Characteristic is not found");
}
}
public void writeImmediateAlertOn() {
if (mAlertLevelCharacteristic != null) {
DebugLogger.d(TAG, "writing Immediate alert characteristic On");
mAlertLevelCharacteristic.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE);
mAlertLevelCharacteristic.setValue(HIGH_ALERT, BluetoothGattCharacteristic.FORMAT_UINT8, 0);
mBluetoothGatt.writeCharacteristic(mAlertLevelCharacteristic);
} else {
DebugLogger.w(TAG, "Immediate Alert Level Characteristic is not found");
}
}
public void writeImmediateAlertOff() {
if (mAlertLevelCharacteristic != null) {
DebugLogger.d(TAG, "writing Immediate alert characteristic Off");
mAlertLevelCharacteristic.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE);
mAlertLevelCharacteristic.setValue(NO_ALERT, BluetoothGattCharacteristic.FORMAT_UINT8, 0);
mBluetoothGatt.writeCharacteristic(mAlertLevelCharacteristic);
} else {
DebugLogger.w(TAG, "Immediate Alert Level Characteristic is not found");
}
}
#Override
public void closeBluetoothGatt() {
try {
mContext.unregisterReceiver(mBondingBroadcastReceiver);
} catch (Exception e) {
// the receiver must have been not registered or unregistered before
}
if (mBluetoothGatt != null) {
mBluetoothGatt.close();
mBluetoothGatt = null;
}
if (mBluetoothGattServer != null) {
mBluetoothGattServer.close();
mBluetoothGattServer = null;
}
mCallbacks = null;
mLogSession = null;
mRingtoneAlarm = mRingtoneNotification = null;
}
}
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);
}