I want to create android app, which can connect with bluetooth low energy module and can receive data. In my system microcontroller stm32f1 send data of measurements over UART to the BT LE module.
My question is how to start? I read a lot about GATT and UART Service, but still don't know how to start. Give me some information please.
You need:
UUIDs of your Service, Characteristic and Descriptor;
Permissions' Bluetooth Low Energy
The program must:
Scan BLE devices
Connect a device
Set callbacks' BLE
AndroidManifest.xml
<uses-feature android:name="android.hardware.bluetooth_le" android:required="true"/>
<uses-permission android:name="android.permission.BLUETOOTH"/>
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
Activity.java
// Bluetooth's variables
BluetoothAdapter bluetoothAdapter;
BluetoothLeScanner bluetoothLeScanner;
BluetoothManager bluetoothManager;
BluetoothScanCallback bluetoothScanCallback;
BluetoothGatt gattClient;
BluetoothGattCharacteristic characteristicID; // To get Value
// UUID's (set yours)
final UUID SERVICE_UUID = UUID.fromString("ab0828b1-198e-4351-b779-901fa0e0371e");
final UUID CHARACTERISTIC_UUID_ID = UUID.fromString("1a220d0a-6b06-4767-8692-243153d94d85");
final UUID DESCRIPTOR_UUID_ID = UUID.fromString("ec6e1003-884b-4a1c-850f-1cfce9cf6567");
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Bluetooth
bluetoothManager = (BluetoothManager) getSystemService(BLUETOOTH_SERVICE);
bluetoothAdapter = bluetoothManager.getAdapter();
startScan();
}
// BLUETOOTH SCAN
private void startScan(){
Log.i(TAG,"startScan()");
bluetoothScanCallback = new BluetoothScanCallback();
bluetoothLeScanner = bluetoothAdapter.getBluetoothLeScanner();
bluetoothLeScanner.startScan(bluetoothScanCallback);
}
// BLUETOOTH CONNECTION
private void connectDevice(BluetoothDevice device) {
if (device == null) Log.i(TAG,"Device is null");
GattClientCallback gattClientCallback = new GattClientCallback();
gattClient = device.connectGatt(this,false,gattClientCallback);
}
// BLE Scan Callbacks
private class BluetoothScanCallback extends ScanCallback {
#Override
public void onScanResult(int callbackType, ScanResult result) {
Log.i(TAG, "onScanResult");
if (result.getDevice().getName() != null){
if (result.getDevice().getName().equals(YOUR_DEVICE_NAME)) {
// When find your device, connect.
connectDevice(result.getDevice());
bluetoothLeScanner.stopScan(bluetoothScanCallback); // stop scan
}
}
}
#Override
public void onBatchScanResults(List<ScanResult> results) {
Log.i(TAG, "onBathScanResults");
}
#Override
public void onScanFailed(int errorCode) {
Log.i(TAG, "ErrorCode: " + errorCode);
}
}
// Bluetooth GATT Client Callback
private class GattClientCallback extends BluetoothGattCallback {
#Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
super.onConnectionStateChange(gatt, status, newState);
Log.i(TAG,"onConnectionStateChange");
if (status == BluetoothGatt.GATT_FAILURE) {
Log.i(TAG, "onConnectionStateChange GATT FAILURE");
return;
} else if (status != BluetoothGatt.GATT_SUCCESS) {
Log.i(TAG, "onConnectionStateChange != GATT_SUCCESS");
return;
}
if (newState == BluetoothProfile.STATE_CONNECTED) {
Log.i(TAG, "onConnectionStateChange CONNECTED");
gatt.discoverServices();
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
Log.i(TAG, "onConnectionStateChange DISCONNECTED");
}
}
#Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
super.onServicesDiscovered(gatt, status);
Log.i(TAG,"onServicesDiscovered");
if (status != BluetoothGatt.GATT_SUCCESS) return;
// Reference your UUIDs
characteristicID = gatt.getService(SERVICE_UUID).getCharacteristic(CHARACTERISTIC_UUID_ID);
gatt.setCharacteristicNotification(characteristicID,true);
BluetoothGattDescriptor descriptor = characteristicID.getDescriptor(DESCRIPTOR_UUID_ID);
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
gatt.writeDescriptor(descriptor);
}
#Override
public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
super.onCharacteristicRead(gatt, characteristic, status);
Log.i(TAG,"onCharacteristicRead");
}
#Override
public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
super.onCharacteristicWrite(gatt, characteristic, status);
Log.i(TAG,"onCharacteristicWrite");
}
#Override
public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
super.onCharacteristicChanged(gatt, characteristic);
Log.i(TAG,"onCharacteristicChanged");
// Here you can read the characteristc's value
// new String(characteristic.getValue();
}
#Override
public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
super.onDescriptorRead(gatt, descriptor, status);
Log.i(TAG,"onDescriptorRead");
}
#Override
public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
super.onDescriptorWrite(gatt, descriptor, status);
Log.i(TAG,"onDescriptorWrite");
}
}
Libs:
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCallback;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattDescriptor;
import android.bluetooth.BluetoothManager;
import android.bluetooth.BluetoothProfile;
import android.bluetooth.le.BluetoothLeScanner;
import android.bluetooth.le.ScanCallback;
import android.bluetooth.le.ScanResult;
Notes:
The UUIDs must be same seted on the module.
In your program you must have two callbacks: ScanCallback and GattCallback. Scan callback is to manage the scan results and the GattCallback you can manage the data input/output.
This code the basic to show how use BLE on Android, works fine for me.
You can generate UUID here: https://www.uuidgenerator.net/
Related
I'm developing an Android app that discovers and connects to a GATT service that is being advertised by my rPi 3B+. The iOS app that I've finished developing works without any issue. However, almost every time (95%) my Android app connects to the GATT server and tries to discover the services, the GATT connection times out with the status response code: 8.
Here's my android code:
package com.example.myapplication;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCallback;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattDescriptor;
import android.bluetooth.BluetoothManager;
import android.bluetooth.BluetoothProfile;
import android.bluetooth.le.BluetoothLeScanner;
import android.bluetooth.le.ScanCallback;
import android.bluetooth.le.ScanFilter;
import android.bluetooth.le.ScanResult;
import android.bluetooth.le.ScanSettings;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.ParcelUuid;
import android.util.Log;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
public class MainActivity extends AppCompatActivity implements AdapterView.OnItemClickListener {
BluetoothAdapter bluetoothAdapter;
BluetoothLeScanner bluetoothLeScanner;
BluetoothManager bluetoothManager;
BluetoothScanCallback bluetoothScanCallback;
BluetoothGatt gattClient;
BluetoothGattCharacteristic characteristicID;
TextView scanningText;
final UUID SERVICE_UUID = UUID.fromString("6E400001-B5A3-F393-E0A9-E50E24DCCA9E");
final UUID CHARACTERISTIC_UUID_ID = UUID.fromString("6E400002-B5A3-F393-E0A9-E50E24DCCA9E");
final UUID DESCRIPTOR_UUID_ID = UUID.fromString("00002902-0000-1000-8000-00805F9B34FB");
ArrayList<String> wifiSSIDs = new ArrayList<>() ;
ListView listViewSSID;
ArrayAdapter<String> SSIDadapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
getSupportActionBar().setDefaultDisplayHomeAsUpEnabled(true);
listViewSSID = findViewById(R.id.lv_SSID);
scanningText = findViewById(R.id.scanningText);
SSIDadapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, wifiSSIDs);
listViewSSID.setAdapter(SSIDadapter);
listViewSSID.setOnItemClickListener(this);
bluetoothManager = (BluetoothManager) getSystemService(BLUETOOTH_SERVICE);
bluetoothAdapter = bluetoothManager.getAdapter();
startScan();
}
#Override
protected void onDestroy() {
super.onDestroy();
Log.i("App","App closing...");
gattClient.disconnect();
gattClient.close();
}
private void startScan(){
ScanFilter scanFilter = new ScanFilter.Builder().setServiceUuid(new ParcelUuid(SERVICE_UUID)).build();
ArrayList<ScanFilter> filters = new ArrayList<>();
filters.add(scanFilter);
ScanSettings scanSettings = new ScanSettings.Builder().setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY).build();
Log.i("Bluetooth Scan","startScan()");
bluetoothScanCallback = new BluetoothScanCallback();
bluetoothLeScanner = bluetoothAdapter.getBluetoothLeScanner();
bluetoothLeScanner.startScan(filters, scanSettings, bluetoothScanCallback);
}
private void connectDevice(BluetoothDevice device) {
if (device == null) Log.i("Bluetooth Connection","Device is null");
GattClientCallback gattClientCallback = new GattClientCallback();
gattClient = device.connectGatt(this,false, gattClientCallback, BluetoothDevice.TRANSPORT_LE);
}
private class BluetoothScanCallback extends ScanCallback {
#Override
public void onScanResult(int callbackType,final ScanResult result) {
Log.i("Bluetooth Scan Result", "onScanResult");
bluetoothLeScanner.stopScan(bluetoothScanCallback); // stop scan
connectDevice(result.getDevice());
}
#Override
public void onBatchScanResults(List<ScanResult> results) {
Log.i("Bluetooth Scan Result", "onBathScanResults");
}
#Override
public void onScanFailed(int errorCode) {
Log.i("Bluetooth Scan Result", "ErrorCode: " + errorCode);
}
}
private class GattClientCallback extends BluetoothGattCallback {
#Override
public void onConnectionStateChange(final BluetoothGatt gatt, int status, int newState) {
super.onConnectionStateChange(gatt, status, newState);
Log.i("Bluetooth Connection","onConnectionStateChange");
if (status == BluetoothGatt.GATT_FAILURE) {
Log.i("Bluetooth Connection", "onConnectionStateChange GATT FAILURE");
return;
} else if (status != BluetoothGatt.GATT_SUCCESS) {
Log.i("Bluetooth Connection", "onConnectionStateChange != GATT_SUCCESS");
Log.i("Bluetooth Connection", String.valueOf(status));
startScan();
return;
}
else {
final BluetoothDevice device = gatt.getDevice();
if (newState == BluetoothProfile.STATE_CONNECTED) {
Log.i("Bluetooth Connection", "onConnectionStateChange CONNECTED");
new Handler(Looper.getMainLooper()).postDelayed(new Runnable() {
#Override
public void run() {
gatt.discoverServices(); }
}, 500);
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
Log.i("Bluetooth Connection", "onConnectionStateChange DISCONNECTED");
}
}
}
#Override
public void onServicesDiscovered(final BluetoothGatt gatt, int status) {
super.onServicesDiscovered(gatt, status);
Log.i("Bluetooth Services","onServicesDiscovered");
new Handler(Looper.getMainLooper()).postDelayed(new Runnable() {
#Override
public void run() {
characteristicID = gatt.getService(SERVICE_UUID).getCharacteristic(CHARACTERISTIC_UUID_ID);
gatt.setCharacteristicNotification(characteristicID,true); }
}, 500);
new Handler(Looper.getMainLooper()).postDelayed(new Runnable() {
#Override
public void run() {
BluetoothGattDescriptor descriptor = characteristicID.getDescriptor(DESCRIPTOR_UUID_ID);
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
gatt.writeDescriptor(descriptor); }
}, 500);
}
#Override
public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
super.onCharacteristicRead(gatt, characteristic, status);
Log.i("BT Characteristics","onCharacteristicRead");
}
#Override
public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
super.onCharacteristicWrite(gatt, characteristic, status);
Log.i("BT Characteristics","onCharacteristicWrite");
}
#Override
public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
super.onCharacteristicChanged(gatt, characteristic);
Log.i("BT Characteristics","onCharacteristicChanged");
Log.i("Characteristic Change",new String(characteristic.getValue()));
final String characteristicValue = new String(characteristic.getValue());
runOnUiThread(new Runnable() {
#Override
public void run() {
wifiSSIDs.add(characteristicValue);
SSIDadapter.notifyDataSetChanged();
listViewSSID.setVisibility(View.VISIBLE);
scanningText.setVisibility(View.INVISIBLE);
}
});
}
#Override
public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
super.onDescriptorRead(gatt, descriptor, status);
Log.i("BT Characteristics","onDescriptorRead");
}
#Override
public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
super.onDescriptorWrite(gatt, descriptor, status);
Log.i("BT Characteristics","onDescriptorWrite");
characteristicID.setValue("RW");
gatt.writeCharacteristic(characteristicID);
}
}
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
String ssid = ((TextView) view).getText().toString();
Log.i("Item Click","SSID: " + ssid);
characteristicID.setValue("SSID: " + ssid);
gattClient.writeCharacteristic(characteristicID);
Intent intent = new Intent(this, PasswordActivity.class);
startActivity(intent);
}
}
I can provide the python script code that's running on rPi but I believe that the problem stems from the android code. The log returns:
2020-10-05 14:05:53.710 19952-19952/com.example.myapplication I/Bluetooth Scan: startScan()
2020-10-05 14:05:53.711 19952-19952/com.example.myapplication I/chatty: uid=10299(com.example.myapplication) identical 1 line
2020-10-05 14:05:53.852 19952-19952/com.example.myapplication I/InputMethodManager: startInputInner - mService.startInputOrWindowGainedFocus
2020-10-05 14:05:54.937 19952-19952/com.example.myapplication I/Bluetooth Scan Result: onScanResult
2020-10-05 14:05:56.688 19952-19966/com.example.myapplication I/Bluetooth Connection: onConnectionStateChange
2020-10-05 14:05:56.688 19952-19966/com.example.myapplication I/Bluetooth Connection: onConnectionStateChange CONNECTED
2020-10-05 14:06:02.208 19952-19966/com.example.myapplication I/Bluetooth Connection: onConnectionStateChange
2020-10-05 14:06:02.208 19952-19966/com.example.myapplication I/Bluetooth Connection: onConnectionStateChange != GATT_SUCCESS
2020-10-05 14:06:02.208 19952-19966/com.example.myapplication I/Bluetooth Connection: 8
I am trying to scan BLE devices continuously for a research project. I use LOW_LATENCY mode of BLE scanning. However, after 20 mins or so my UI freezes.
Basically I press button it should start scanning for BLE devices, and when I press the button again, it should stop scanning. I want to scan for at least 2 hours continuously. The following is my code.
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
toggle = (ToggleButton) findViewById(R.id.toggleButton); // initiate a toggle button
tv = (TextView) findViewById(R.id.tv);
EasyPermissions.requestPermissions(this, "need permission", 1001, Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.WRITE_EXTERNAL_STORAGE,Manifest.permission.ACCESS_COARSE_LOCATION,Manifest.permission.BLUETOOTH,Manifest.permission.BLUETOOTH_ADMIN);
// Check BLE support for the device
if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
Toast.makeText(getApplicationContext(),"no bluetooth LE functionality", Toast.LENGTH_SHORT).show();
finish();
}
btManager = (BluetoothManager)getSystemService(Context.BLUETOOTH_SERVICE);
btAdapter = btManager.getAdapter();
bLEScanner = btAdapter.getBluetoothLeScanner();
settings = new ScanSettings.Builder()
.setScanMode(SCAN_MODE_LOW_LATENCY)
.setCallbackType(ScanSettings.CALLBACK_TYPE_ALL_MATCHES)
.build();
toggle.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
#TargetApi(Build.VERSION_CODES.N)
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if (isChecked) {
Toast.makeText(getApplicationContext(), "Scanning started",
Toast.LENGTH_SHORT).show();
// The toggle is enabled
bFileName = getBluetoothFileName("Bluetooth");
bLEScanner.startScan(null,settings,bluetoothLeScanCallback);
} else {
// The toggle is disabled
Toast.makeText(getApplicationContext(), "Scanning stopped",
Toast.LENGTH_SHORT).show();
bLEScanner.stopScan(bluetoothLeScanCallback);
}
}
});
}
Scan Callback code
private ScanCallback bluetoothLeScanCallback = new ScanCallback() {
#Override
public void onScanResult(int callbackType, android.bluetooth.le.ScanResult result) {
byte[] scanRecord = result.getScanRecord().getBytes();
int startByte = 2;
boolean patternFound = false;
while (startByte <= 5)
{
if ( ((int) scanRecord[startByte + 2] & 0xff) == 0x02 && //Identifies an iBeacon
((int) scanRecord[startByte + 3] & 0xff) == 0x15)
{ //Identifies correct data length
patternFound = true;
break;
}
startByte++;
}
if (patternFound)
{
//Convert to hex String
byte[] uuidBytes = new byte[16];
System.arraycopy(scanRecord, startByte + 4, uuidBytes, 0, 16);
String hexString = bytesToHex(uuidBytes);
//UUID detection
String uuid = hexString.substring(0,8) + "-" +
hexString.substring(8,12) + "-" +
hexString.substring(12,16) + "-" +
hexString.substring(16,20) + "-" +
hexString.substring(20,32);
// major
final int major = (scanRecord[startByte + 20] & 0xff) * 0x100 + (scanRecord[startByte + 21] & 0xff);
// minor
final int minor = (scanRecord[startByte + 22] & 0xff) * 0x100 + (scanRecord[startByte + 23] & 0xff);
Log.i("BLE","UUID: " +uuid + " nmajor : " +major +" nminor " +minor+" time "+getCompleteDate(System.currentTimeMillis())+" rssi "+result.getRssi());
StringBuilder bStringBuilder = new StringBuilder();
long sysTime = System.currentTimeMillis();
bStringBuilder.append(imei+","+sysTime+","+uuid+","+major+","+minor+","+result.getRssi()+","+getCompleteDate(sysTime)+","+"\n");
String finalString = bStringBuilder.toString();
exportBluetoothData(finalString, finalString.length(), "Bluetooth");
}
}
#Override
public void onScanFailed(int errorCode) {
super.onScanFailed(errorCode);
Log.i("BLE", "error");
}
};
Will it be helpful to use AsyncTask for this kind of long operation? Any guide would be highly appreciated. Thanks.
It won't be helpful to use AsyncTask for this kind of long operation. Android 7.0 introduced a BLE scan timeout, where any scan running for 30 minutes or more is effectively stopped automatically: https://stackoverflow.com/a/44488991/2068732
Furthermore, BLE scan drains the battery, so you should avoid running BLE scan for so long.
If your app does require scanning frequently, you might consider stopping and re-starting the BLE scan upon a user action. But bare in mind that Android 7 prevents scan start-stops more than 5 times in 30 seconds.
you should use background thread for this process,because when you are scanning, your ui thread may be blocked that's the reason for ui freezing.make use of task scheduler or any other background process for that.
package com.myapp.services;
import java.util.List;
import android.annotation.TargetApi;
import android.app.Service;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCallback;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattService;
import android.bluetooth.BluetoothManager;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.preference.PreferenceManager;
import android.support.v4.content.LocalBroadcastManager;
import android.text.TextUtils;
import android.widget.Toast;
import com.myapp.R;
/**Both RX and RSSI (Received Signal Strength Indication) are indications of the power level being received
* by an antenna
* The difference between RX and RSSI is that RX is measured in milliWatts (mW) or decibel-milliwatts (dBm)
* whereas RSSI is a signal strength percentage—the higher the RSSI number, the stronger the signal
*
*/
#TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
public class BeaconService extends Service implements BluetoothAdapter.LeScanCallback{
private static final String TAG = BeaconService.class.getSimpleName();
private BluetoothGatt btGatt;
private BluetoothAdapter mBluetoothAdapter;
#Override
public void onCreate() {
super.onCreate();
writeLine("Automate service created...");
getBTService();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
writeLine("Automate service start...");
if (!isBluetoothSupported()) {
Toast.makeText(this, R.string.ble_not_supported, Toast.LENGTH_SHORT).show();
stopSelf();
}else{
if(mBluetoothAdapter!=null && mBluetoothAdapter.isEnabled()){
startBLEscan();
}else{
stopSelf();
}
}
return START_STICKY;
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onDestroy() {
writeLine("Automate service destroyed...");
stopBLEscan();
super.onDestroy();
if(btGatt!=null){
btGatt.disconnect();
btGatt.close();
btGatt = null;
}
}
#Override
public boolean stopService(Intent name) {
writeLine("Automate service stop...");
stopSelf();
return super.stopService(name);
}
// Initializes a Bluetooth adapter. For API level 18 and above, get a reference to
// BluetoothAdapter through BluetoothManager.
public BluetoothAdapter getBTService(){
BluetoothManager btManager = (BluetoothManager) getApplicationContext().getSystemService(Context.BLUETOOTH_SERVICE);
mBluetoothAdapter = (BluetoothAdapter) btManager.getAdapter();
return mBluetoothAdapter;
}
public boolean isBluetoothSupported() {
return this.getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE);
}
public void startBLEscan(){
mBluetoothAdapter.startLeScan(this);
}
public void stopBLEscan(){
mBluetoothAdapter.stopLeScan(this);
}
/**
*
* #param enable
*/
public void scanLeDevice(final boolean enable) {
if (enable) {
startBLEscan();
} else {
stopBLEscan();
}
}
public static void enableDisableBluetooth(boolean enable){
BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
if (bluetoothAdapter != null) {
if(enable) {
bluetoothAdapter.enable();
}else{
bluetoothAdapter.disable();
}
}
}
#Override
public void onLeScan(final BluetoothDevice device, final int rssi, byte[] scanRecord) {
if(device!=null && device.getName()!=null){
//Log.d(TAG + " onLeScan: ", "Name: "+device.getName() + "Address: "+device.getAddress()+ "RSSI: "+rssi);
if(rssi > -90 && rssi <-1){
writeLine("Automate service BLE device in range: "+ device.getName()+ " "+rssi);
if (device.getName().equalsIgnoreCase("NCS_Beacon") || device.getName().equalsIgnoreCase("estimote")) {
//This Main looper thread is main for connect gatt, don't remove it
// Although you need to pass an appropriate context getApplicationContext(),
//Here if you use Looper.getMainLooper() it will stop getting callback and give internal exception fail to register //callback
new Handler(getApplicationContext().getMainLooper()).post(new Runnable() {
#Override
public void run() {
btGatt = device.connectGatt(getApplicationContext(), false, bleGattCallback);
Log.e(TAG, "onLeScan btGatt value returning from connectGatt "+btGatt);
}
});
}
stopBLEscan();
}else{
//Log.v("Device Scan Activity", device.getAddress()+" "+"BT device is still too far - not connecting");
}
}
}
BluetoothGattCallback bleGattCallback = new BluetoothGattCallback() {
#Override
public void onConnectionStateChange(final BluetoothGatt gatt, int status, int newState) {
super.onConnectionStateChange(gatt, status, newState);
writeLine("Automate service connection state: "+ newState);
if (newState == android.bluetooth.BluetoothProfile.STATE_CONNECTED){
writeLine("Automate service connection state: STATE_CONNECTED");
Log.v("BLEService", "BLE Connected now discover services");
Log.v("BLEService", "BLE Connected");
new Thread(new Runnable() {
#Override
public void run() {
writeLine("Automate service go for discover services");
gatt.discoverServices();
}
}).start();
}else if (newState == android.bluetooth.BluetoothProfile.STATE_DISCONNECTED){
writeLine("Automate service connection state: STATE_DISCONNECTED");
Log.v("BLEService", "BLE Disconnected");
}
}
#Override
public void onServicesDiscovered(final BluetoothGatt gatt, int status) {
super.onServicesDiscovered(gatt, status);
if (status == BluetoothGatt.GATT_SUCCESS) {
writeLine("Automate service discover service: GATT_SUCCESS");
Log.v("BLEService", "BLE Services onServicesDiscovered");
//Get service
List<BluetoothGattService> services = gatt.getServices();
writeLine("Automate service discover service imei: " +imei);
}
}
#Override
public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
super.onCharacteristicRead(gatt, characteristic, status);
}
#Override
public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
super.onCharacteristicChanged(gatt, characteristic);
}
};
private void writeLine(final String message) {
Handler h = new Handler(getApplicationContext().getMainLooper());
// Although you need to pass an appropriate context
h.post(new Runnable() {
#Override
public void run() {
Toast.makeText(getApplicationContext(),message,Toast.LENGTH_SHORT).show();
}
});
}
}
In manifest.xml
<uses-feature
android:name="android.hardware.bluetooth_le"
android:required="true" />
<!-- Permission for bluetooth -->
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<service
android:name="com.myapp.services.BeaconService"
android:enabled="true"
android:exported="false" />
I used SCAN_MODE_BALANCED instead of SCAN_MODE_LOW_LATENCY Now it doesn't freeze the thread. Thank you for all your answers.
I'm trying to read the heart rate from a BLE device (It's a smartwatch and the model is CURREN R5 Pro), but when I get the Heart Rate Service, it returns null.
This is the code:
class Constants {
final static UUID HEART_RATE_SERVICE_UUID = convertFromInteger(0x180D);
final static UUID HEART_RATE_MEASUREMENT_UUID = convertFromInteger(0x2A37);
final static UUID HEART_RATE_CONTROL_POINT_UUID = convertFromInteger(0x2A39);
final static UUID CLIENT_CHARACTERISTIC_CONFIG_UUID = convertFromInteger(0x2902);
private static UUID convertFromInteger(int i) {
final long MSB = 0x0000000000001000L;
final long LSB = 0x800000805f9b34fbL;
long value = i & 0xFFFFFFFF;
return new UUID(MSB | (value << 32), LSB);
}
}
private final BluetoothGattCallback bluetoothGattCallback = new BluetoothGattCallback() {
#Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
if (newState == BluetoothProfile.STATE_CONNECTED) {
bluetoothGatt.discoverServices();
Log.d(TAG, "Connected");
}
if (newState == BluetoothProfile.STATE_DISCONNECTED) {
Log.d(TAG, "Disconnected");
}
}
#Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
if (status == BluetoothGatt.GATT_SUCCESS) {
//getCharacteristic(HEART_RATE_MEASUREMENT_UUID) returns null
BluetoothGattCharacteristic characteristic = gatt.getService(HEART_RATE_SERVICE_UUID)
.getCharacteristic(HEART_RATE_MEASUREMENT_UUID);
gatt.setCharacteristicNotification(characteristic, true);
BluetoothGattDescriptor descriptor = characteristic.getDescriptor(CLIENT_CHARACTERISTIC_CONFIG_UUID);
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
gatt.writeDescriptor(descriptor);
}
}
#Override
public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
BluetoothGattCharacteristic characteristic = gatt.getService(HEART_RATE_SERVICE_UUID)
.getCharacteristic(HEART_RATE_CONTROL_POINT_UUID);
characteristic.setValue(DATA_STREAMING_COMMAND);
gatt.writeCharacteristic(characteristic);
}
#Override
public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
if (HEART_RATE_MEASUREMENT_UUID.equals(characteristic.getUuid())) {
Log.d(TAG, "HEART_RATE_MEASUREMENT_UUID");
//PROCESS DATA
}
if (HEART_RATE_CONTROL_POINT_UUID.equals(characteristic.getUuid())) {
Log.d(TAG, "HEART_RATE_CONTROL_POINT_UUID");
//PROCESS DATA
}
}
};
However, the only app that is able to read the heart rate from the device is Wearfit. Other applications are not able to read the heart rate from this device.
The services that I'm able to get are these:
00001800-0000-1000-8000-00805f9b34fb
00001801-0000-1000-8000-00805f9b34fb
6e400001-b5a3-f393-e0a9-e50e24dcca9e
00001530-1212-efde-1523-785feabcd123
0000fee7-0000-1000-8000-00805f9b34fb
Has anyone experienced this situation? Is there anything wrong with my code? Thanks for your help.
I need some response data from Ble. When I am writing something on ble I need to read response data from Ble. I am able to successfully enable and disable my ble device but only missing response data from ble. I also need to convert decimal time into Integer hex format like for 60 min into 0x3c.
private BluetoothGattCallback gattCallback= new BluetoothGattCallback() {
#RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
#Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
super.onServicesDiscovered(gatt,status);
clientGatt =gatt;
if (status == BluetoothGatt.GATT_SUCCESS) {
BluetoothGattService service = gatt.getServices().get(2);
List<BluetoothGattCharacteristic> gattCharacteristics = service.getCharacteristics();
for (BluetoothGattCharacteristic gattCharacteristic : gattCharacteristics) {
if (gattCharacteristic.getUuid().toString().equalsIgnoreCase(AppConstant.RECEIVE_UUID)) {
readCharacteristic=gattCharacteristic;
}
if (gattCharacteristic.getUuid().toString().equalsIgnoreCase(AppConstant.SEND_UUID_STR)) {
writeCharacteristic = gattCharacteristic;
}
}
}
}
#RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
#Override
public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
super.onCharacteristicRead(gatt,characteristic,status);
LogUtils.errorLog("onCharacteristicRead", "##: "+characteristic.getValue()[0]);
}
#RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
#Override
public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
super.onCharacteristicChanged(gatt, characteristic)
}
#RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
#Override
public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
super.onCharacteristicWrite(gatt, characteristic, status);
setCharacteristicNotification(characteristic,true);
}
#TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
#Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
super.onConnectionStateChange(gatt, status, newState);
clientGatt = gatt;
switch (newState) {
case BluetoothProfile.STATE_CONNECTED:
isBLE_Connected=true;
gatt.discoverServices();
break;
case BluetoothProfile.STATE_DISCONNECTED:
isBLE_Connected=false;
if(status==133 || status==22 || status==62){
refreshDeviceCache();
clientGatt.discoverServices();
}else{
clientGatt.disconnect();
clientGatt.close();
}
break;
}
}
};
Before onCharacteristicChanged is called you had to enable notification.
Someting like:
//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);
should help.
You when you write something on characteristic with the help of BluetoothGattDescriptor as
Mike's answer
You need to override the following method to listen to the changes has been completed then you can read the characteristics:
#Override
public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
super.onDescriptorWrite(gatt, descriptor, status);
Log.d(TAG, "onDescriptorWrite :" + ((status == BluetoothGatt.GATT_SUCCESS) ? "Sucess" : "false"));
}
I can scan for Bluetooth LE devices through startLeScan or via the new getBluetoothLeScanner and that works fine. However even though it keeps on scanning it never detects the same device twice. That's unfortunate because I would like to receive events when the rssi of a beacon changes. Does Android support that?
Implement a BluetoothGattCallback and override the onReadRemoteRssi method.
private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
#Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
String intentAction;
if (newState == BluetoothProfile.STATE_CONNECTED) {
broadcastUpdate(intentAction);
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
broadcastUpdate(intentAction);
}
}
#Override
public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) {
super.onReadRemoteRssi(gatt, rssi, status);
// use rssi value here
}
#Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
if (status == BluetoothGatt.GATT_SUCCESS) {
broadcastUpdate(ACTION_GATT_SERVICES_DISCOVERED);
} else {
Log.w(TAG, "onServicesDiscovered received: " + status);
}
}
#Override
public void onCharacteristicRead(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic,
int status) {
if (status == BluetoothGatt.GATT_SUCCESS) {
broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
}
}
#Override
public void onCharacteristicChanged(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic) {
broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
}
};
Two solutions which I used.
1) Make a connection, call readRemoteRssi(); of gatt and override onReadRemoteRssi, and have your own check to see if the RSSI is changed.
2) StrartLEScan every 20 Sec(20 sec is enough time to receive new advertisement packets) and have a check on onLeScan callback to see if the device is found, if yes then compare RSSI.
RSSI is the value which your device(scanner) derives.
Also if you have code for GATT STACK, you will get onLEScan called many times for single device, it is GattService which truncate duplicate results.