I am developing a code for heart rate detection using BLE. I am able to connect to the gatt server, and even broadcast updates. But I can't seem to detect any heart rate. Am I missing something?
Here'y my codes.
public class BleActivity extends Activity {
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";
private BluetoothDevicesAdapter bluetoothDevicesAdapter;
private BluetoothAdapter mBluetoothAdapter;
private Handler mHandler = new Handler();
private static final long SCAN_PERIOD = 5000;
private ProgressDialog progressDialog;
private BluetoothGatt mGatt;
#Bind(R.id.bleDeviceListView)
ListView listView;
#TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_ble);
ButterKnife.bind(this);
bluetoothDevicesAdapter = new BluetoothDevicesAdapter(getApplicationContext());
listView.setAdapter(bluetoothDevicesAdapter);
final BluetoothManager bluetoothManager =
(BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
mBluetoothAdapter = bluetoothManager.getAdapter();
}
#OnClick(R.id.scanButton)
public void onScanClicked() {
scanLeDevice();
}
#OnItemClick(R.id.bleDeviceListView)
public void onItemClicked(int position) {
connectToDevice((BluetoothDevice) bluetoothDevicesAdapter.getItem(position));
}
#Override
protected void onPause() {
super.onPause();
unregisterReceiver(mGattUpdateReceiver);
}
#Override
protected void onResume() {
super.onResume();
registerReceiver(mGattUpdateReceiver, makeGattUpdateIntentFilter());
//CONFIRMS IF BLUETOOTH IS ENABLED
if (!mBluetoothAdapter.isEnabled()) {
if (!mBluetoothAdapter.isEnabled()) {
GenericDialogBox.EnableBluetoothBox(BleActivity.this, "Notice", "This app requires bluetooth. Enable?", mBluetoothAdapter);
}
}
}
private void scanLeDevice() {
bluetoothDevicesAdapter.clear();
bluetoothDevicesAdapter.notifyDataSetChanged();
progressDialog = new ProgressDialog(BleActivity.this);
progressDialog.setCancelable(false);
progressDialog.setMessage("Scanning Devices");
progressDialog.show();
mHandler.postDelayed(new Runnable() {
#Override
public void run() {
mBluetoothAdapter.stopLeScan(leScanCallback);
progressDialog.hide();
mBluetoothAdapter.stopLeScan(leScanCallback);
progressDialog.dismiss();
progressDialog = null;
}
}, SCAN_PERIOD);
mBluetoothAdapter.startLeScan(leScanCallback);
}
private BluetoothAdapter.LeScanCallback leScanCallback = new BluetoothAdapter.LeScanCallback() {
#Override
public void onLeScan(final BluetoothDevice device, final int rssi, final byte[] scanRecord) {
runOnUiThread(new Runnable() {
#Override
public void run() {
bluetoothDevicesAdapter.addDevice(device);
bluetoothDevicesAdapter.notifyDataSetChanged();
}
});
}
};
public void connectToDevice(BluetoothDevice device) {
if (mGatt == null) {
mGatt = device.connectGatt(this, false, btleGattCallback);
progressDialog = new ProgressDialog(BleActivity.this);
progressDialog.setMessage("Connecting to Gatt Server.");
progressDialog.setCancelable(false);
progressDialog.show();
}
}
private final BluetoothGattCallback btleGattCallback = new BluetoothGattCallback() {
#Override
public void onCharacteristicChanged(BluetoothGatt gatt, final BluetoothGattCharacteristic characteristic) {
broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
}
#Override
public void onConnectionStateChange(final BluetoothGatt gatt, final int status, final int newState) {
String intentAction;
Log.e("onConnectionStateChange", "Status: " + status);
switch (newState) {
case BluetoothProfile.STATE_CONNECTED:
intentAction = ACTION_GATT_CONNECTED;
Log.e("gattCallback", "STATE_CONNECTED");
progressDialog.dismiss();
progressDialog = null;
broadcastUpdate(intentAction);
mGatt.discoverServices();
break;
case BluetoothProfile.STATE_DISCONNECTED:
intentAction = ACTION_GATT_DISCONNECTED;
Log.e("gattCallback", "STATE_DISCONNECTED");
BluetoothDevice mDevice = gatt.getDevice();
mGatt = null;
broadcastUpdate(intentAction);
break;
default:
Log.e("gattCallback", "STATE_OTHER");
}
}
#Override
public void onServicesDiscovered(final BluetoothGatt gatt, final int status) {
if (status == BluetoothGatt.GATT_SUCCESS) {
List<BluetoothGattService> services = gatt.getServices();
BluetoothGattCharacteristic therm_char;
for (int i = 0; i < services.size(); i++) {
therm_char = services.get(i).getCharacteristics().get(0);
if(therm_char.getUuid().equals(SampleGattAttributes.HEART_RATE_MEASUREMENT)){
gatt.setCharacteristicNotification(therm_char, true);
BluetoothGattDescriptor descriptor = therm_char.getDescriptor(
UUID.fromString(SampleGattAttributes.CLIENT_CHARACTERISTIC_CONFIG));
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
mGatt.writeDescriptor(descriptor);
}
}
broadcastUpdate(ACTION_GATT_SERVICES_DISCOVERED);
Log.e("gattCallback", "Service discovered" + gatt.getServices() + " Status: " + status);
} else {
Log.e("BluetoothServices: ", "onServicesDiscovered received: " + status);
}
}
#Override
public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
if (status == BluetoothGatt.GATT_SUCCESS) {
broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
}
}
};
private void broadcastUpdate(final String action) {
final Intent intent = new Intent(action);
sendBroadcast(intent);
}
private void broadcastUpdate(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.
if (UUID.fromString(SampleGattAttributes.HEART_RATE_MEASUREMENT).equals(characteristic.getUuid())) {
int flag = characteristic.getProperties();
int format = -1;
if ((flag & 0x01) != 0) {
format = BluetoothGattCharacteristic.FORMAT_UINT16;
Log.d("Bluetooth Service: ", "Heart rate format UINT16.");
} else {
format = BluetoothGattCharacteristic.FORMAT_UINT8;
Log.d("Bluetooth Service: ", "Heart rate format UINT8.");
}
final int heartRate = characteristic.getIntValue(format, 1);
Log.d("Bluetooth Service: ", 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());
}
}
sendBroadcast(intent);
}
private final BroadcastReceiver mGattUpdateReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
if (ACTION_GATT_CONNECTED.equals(action)) {
Toast.makeText(getApplicationContext(), "Connected to Service.", Toast.LENGTH_SHORT).show();
invalidateOptionsMenu();
} else if (ACTION_GATT_DISCONNECTED.equals(action)) {
Toast.makeText(getApplicationContext(), "Connected to Service.", Toast.LENGTH_SHORT).show();
} else if (ACTION_GATT_SERVICES_DISCOVERED.equals(action)) {
//Update Connection State
} else if (ACTION_DATA_AVAILABLE.equals(action)) {
Toast.makeText(getApplicationContext(), intent.getStringExtra(EXTRA_DATA), Toast.LENGTH_SHORT).show();
}
}
};
private static IntentFilter makeGattUpdateIntentFilter() {
final IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(ACTION_GATT_CONNECTED);
intentFilter.addAction(ACTION_GATT_DISCONNECTED);
intentFilter.addAction(ACTION_GATT_SERVICES_DISCOVERED);
intentFilter.addAction(ACTION_DATA_AVAILABLE);
return intentFilter;
}
And this is the sampleAttributes Class.
public class SampleGattAttributes {
private static HashMap<String, String> attributes = new HashMap();
public static String HEART_RATE_MEASUREMENT = "00002a37-0000-1000-8000-00805f9b34fb";
public static String CLIENT_CHARACTERISTIC_CONFIG = "00002902-0000-1000-8000-00805f9b34fb";
static {
// Sample Services.
attributes.put("0000180d-0000-1000-8000-00805f9b34fb", "Heart Rate Service");
attributes.put("0000180a-0000-1000-8000-00805f9b34fb", "Device Information Service");
// Sample Characteristics.
attributes.put(HEART_RATE_MEASUREMENT, "Heart Rate Measurement");
attributes.put("00002a29-0000-1000-8000-00805f9b34fb", "Manufacturer Name String");
}
public static String lookup(String uuid, String defaultName) {
String name = attributes.get(uuid);
return name == null ? defaultName : name;
}
}
And also is there a way to log if the device is searching for any changes from time to time?
Thank you!
Related
This is my first app working with Bluetooth in Androind and I'm having a kinda unique problem: the bluetooth receiver isn't working on the device that is advertising the bluetooth service.
I have tested the application in 2 phones at the same time (I'll call them phone A and B to explain better). At first I start the advertising with the phone A, then I start the discovery with the phone B and finally I press the button in phone B to send data. This button should first start the Gatt connection and if it's working it should then broadcast a message that confirms the connection. To see it I have used a Log in the Broadcast receiver but the result I get is this messsagge appearing only in the logcat of the phone B but not on the one of phone A.
I have looked at a lot of examples and post on Stackoverflow but I can't seem to find the solution to this problem.
So I really can't find what the real problem here is. Maybe I'm just using badly the Bluetooth classes or I just lack knowledge. In any case, here there is all the code of the MainActivity as it is the only class of this simple project.
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private TextView mText;
private Button mAdvertiseButton;
private Button mDiscoverButton;
private Button mSendButton;
private String TAG = "INFOBLERESULTS";
private BluetoothLeScanner mBluetoothLeScanner;
private BluetoothDevice bluetoothDevice;
private Handler mHandler = new Handler();
private ScanCallback mScanCallback = new ScanCallback() {
#Override
public void onScanResult(int callbackType, ScanResult result) {
Log.d(TAG, result.getDevice().getAddress());
super.onScanResult(callbackType, result);
if( result == null
|| result.getDevice() == null
|| TextUtils.isEmpty(result.getDevice().getAddress()) )
return;
StringBuilder builder = new StringBuilder( result.getDevice().getAddress() );
builder.append("\n").append(result.getDevice().getName());
//builder.append("\n").append(new String(result.getScanRecord().getServiceData(result.getScanRecord().getServiceUuids().get(0)), Charset.forName("UTF-8")));
mText.setText(builder.toString());
bluetoothDevice = result.getDevice();
bluetoothLeService = new BluetoothLeService();
bluetoothLeService.setAddress(result.getDevice().getAddress());
}
#Override
public void onBatchScanResults(List<ScanResult> results) {
super.onBatchScanResults(results);
}
#Override
public void onScanFailed(int errorCode) {
Log.e( TAG, "Discovery onScanFailed: " + errorCode );
super.onScanFailed(errorCode);
}
};
private BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
private boolean connected = false;
//Inner classes
class BluetoothLeService extends Service {
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";
private static final int STATE_DISCONNECTED = 0;
private static final int STATE_CONNECTED = 2;
private int connectionState;
public Context ctx;
protected BluetoothGatt bluetoothGatt;
protected final BluetoothGattCallback gattCallback = new BluetoothGattCallback() {
#Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
Log.i(TAG, "GATT status = "+ status + " newState = " + newState);
if(status == BluetoothGatt.GATT_SUCCESS){
if (newState == BluetoothProfile.STATE_CONNECTED) {
// successfully connected to the GATT Server
connectionState = STATE_CONNECTED;
broadcastUpdate(ACTION_GATT_CONNECTED);
bluetoothGatt = gatt;
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
// disconnected from the GATT Server
connectionState = STATE_DISCONNECTED;
broadcastUpdate(ACTION_GATT_DISCONNECTED);
gatt.close();
}
}else{
gatt.close();
}
}
#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);
}
};
private Binder binder = new LocalBinder();
private String address = "";
public void setAddress(String address) {
this.address = address;
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
return binder;
}
#Override
public boolean onUnbind(Intent intent) {
close();
return super.onUnbind(intent);
}
private void close() {
if (bluetoothGatt == null) {
return;
}
bluetoothGatt.close();
bluetoothGatt = null;
}
public boolean connect() {
if (bluetoothAdapter == null || this.address == null || this.address.equals("")) {
Log.w(TAG, "BluetoothAdapter not initialized or unspecified address.");
return false;
}
try {
final BluetoothDevice device = bluetoothAdapter.getRemoteDevice(address);
bluetoothGatt = device.connectGatt(MainActivity.this.getApplicationContext(), false, gattCallback);
Log.d(TAG,"GATT "+ bluetoothGatt);
return true;
} catch (IllegalArgumentException exception) {
Log.w(TAG, "Device not found with provided address.");
return false;
}
}
private void broadcastUpdate(final String action) {
final Intent intent = new Intent(action);
Log.i(TAG, intent + "");
MainActivity.this.getApplicationContext().sendBroadcast(intent);
}
private void broadcastUpdate(final String action, BluetoothGattCharacteristic characteristic) {
final Intent intent = new Intent(action);
sendBroadcast(intent);
}
public List<BluetoothGattService> getSupportedGattServices() {
if (bluetoothGatt == null) return null;
return bluetoothGatt.getServices();
}
public void readCharacteristic(BluetoothGattCharacteristic characteristic) {
if (bluetoothGatt == null) {
Log.w(TAG, "BluetoothGatt not initialized");
return;
}
bluetoothGatt.readCharacteristic(characteristic);
}
public void setCharacteristicNotification(BluetoothGattCharacteristic characteristic,boolean enabled) {
if (bluetoothGatt == null) {
Log.w(TAG, "BluetoothGatt not initialized");
return;
}
bluetoothGatt.setCharacteristicNotification(characteristic, enabled);
}
class LocalBinder extends Binder {
public BluetoothLeService getService() {
return BluetoothLeService.this;
}
}
}
private BluetoothLeService bluetoothLeService = new BluetoothLeService();
private final ServiceConnection serviceConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName name, IBinder service) {
bluetoothLeService = ((BluetoothLeService.LocalBinder) service).getService();
if (bluetoothLeService != null) {
if (!bluetoothAdapter.isEnabled()) {
Log.e(TAG, "Unable to initialize Bluetooth");
}
else{
bluetoothLeService.connect();
Log.i(TAG, "Service connected");
}
}
}
#Override
public void onServiceDisconnected(ComponentName name) {
bluetoothLeService = null;
}
};
private final BroadcastReceiver gattUpdateReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
Log.d(TAG, "RECEIVED " + action);
if (BluetoothLeService.ACTION_GATT_CONNECTED.equals(action)) {
connected = true;
//Log.d(TAG, "CONNECTED");
} else if (BluetoothLeService.ACTION_GATT_DISCONNECTED.equals(action)) {
connected = false;
//Log.d(TAG, "DISCONNECTED");
}
}
};
private final ActivityResultLauncher<Intent> someActivityResultLauncher = registerForActivityResult(
new ActivityResultContracts.StartActivityForResult(),
new ActivityResultCallback<ActivityResult>() {
#Override
public void onActivityResult(ActivityResult result) {
Log.i("RESULT", result.getResultCode() + "");
setup();
}
});
private void setup() {
bluetoothLeService.ctx = this.getApplicationContext();
Log.d("APPLICATIONCONTEXT", bluetoothLeService.ctx + "");
mDiscoverButton.setOnClickListener(this);
mAdvertiseButton.setOnClickListener(this);
mSendButton.setOnClickListener(this);
mBluetoothLeScanner = BluetoothAdapter.getDefaultAdapter().getBluetoothLeScanner();
if (!BluetoothAdapter.getDefaultAdapter().isMultipleAdvertisementSupported()) {
Toast.makeText(this, "Multiple advertisement not supported", Toast.LENGTH_SHORT).show();
mAdvertiseButton.setEnabled(false);
mDiscoverButton.setEnabled(false);
}
}
#Override
public void onRequestPermissionsResult(int requestCode, #NonNull #org.jetbrains.annotations.NotNull String[] permissions, #NonNull #org.jetbrains.annotations.NotNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == 2) {
final LocationManager manager = (LocationManager) getSystemService( Context.LOCATION_SERVICE );
if(!manager.isProviderEnabled(LocationManager.GPS_PROVIDER)){
Intent enableLocationIntent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
someActivityResultLauncher.launch(enableLocationIntent);
}
else{
setup();
}
}
}
#RequiresApi(api = Build.VERSION_CODES.M)
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mText = (TextView) findViewById( R.id.text );
mDiscoverButton = (Button) findViewById( R.id.discover_btn );
mAdvertiseButton = (Button) findViewById( R.id.advertise_btn );
mSendButton = (Button) findViewById( R.id.send_btn );
this.requestPermissions(new String[]{Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION}, 2);
}
#Override
protected void onResume() {
super.onResume();
MainActivity.this.getApplicationContext().registerReceiver(gattUpdateReceiver, makeGattUpdateIntentFilter());
}
#Override
protected void onPause() {
super.onPause();
MainActivity.this.getApplicationContext().unregisterReceiver(gattUpdateReceiver);
}
#Override
public void onClick(View v) {
Log.d(TAG, getString( R.string.ble_uuid ));
if( v.getId() == R.id.discover_btn ) {
discover();
} else if( v.getId() == R.id.advertise_btn ) {
advertise();
//MainActivity.this.getApplicationContext().registerReceiver(gattUpdateReceiver, makeGattUpdateIntentFilter());
} else if (v.getId() == R.id.send_btn){
//MainActivity.this.getApplicationContext().unregisterReceiver(gattUpdateReceiver);
send();
}
}
public void advertise(){
BluetoothLeAdvertiser advertiser = BluetoothAdapter.getDefaultAdapter().getBluetoothLeAdvertiser();
AdvertiseSettings settings = new AdvertiseSettings.Builder().setTimeout(0)
.setAdvertiseMode( AdvertiseSettings.ADVERTISE_MODE_BALANCED )
.setTxPowerLevel( AdvertiseSettings.ADVERTISE_TX_POWER_HIGH )
.setConnectable( true )
.build();
ParcelUuid pUuid = ParcelUuid.fromString( getString( R.string.ble_uuid ) ) ;
AdvertiseData data = new AdvertiseData.Builder()
.addServiceUuid( pUuid ).setIncludeDeviceName(false)
.build();
AdvertiseCallback advertisingCallback = new AdvertiseCallback() {
#Override
public void onStartSuccess(AdvertiseSettings settingsInEffect) {
Log.d(TAG, "START ADVERTISING");
super.onStartSuccess(settingsInEffect);
}
#Override
public void onStartFailure(int errorCode) {
Log.e( TAG, "Advertising onStartFailure: " + errorCode );
super.onStartFailure(errorCode);
}
};
advertiser.startAdvertising( settings, data, advertisingCallback );
}
public void discover(){
ScanFilter filter = new ScanFilter.Builder()
.setServiceUuid( ParcelUuid.fromString( getString(R.string.ble_uuid ) ) )
.build();
List<ScanFilter> filters = new ArrayList<>();
filters.add( filter );
ScanSettings settings = new ScanSettings.Builder()
.setScanMode( ScanSettings.SCAN_MODE_BALANCED )
.build();
mBluetoothLeScanner.startScan(filters, settings, mScanCallback);
Log.d(TAG, "Discovery started");
mHandler.postDelayed(new Runnable() {
#Override
public void run() {
Log.d(TAG, "Discovery stopped");
mBluetoothLeScanner.stopScan(mScanCallback);
}
}, 10000);
}
public void send(){
Log.d(TAG, "START CONNECTIONG GATT");
mBluetoothLeScanner.stopScan(mScanCallback);
//boundGatt();
connectGatt();
}
public void boundGatt(){
Intent gattServiceIntent = new Intent(MainActivity.this.getApplicationContext(), BluetoothLeService.class);
bindService(gattServiceIntent, serviceConnection, Context.BIND_AUTO_CREATE);
}
public void connectGatt(){
bluetoothLeService.connect();
}
private static IntentFilter makeGattUpdateIntentFilter() {
final IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(BluetoothLeService.ACTION_GATT_CONNECTED);
intentFilter.addAction(BluetoothLeService.ACTION_GATT_DISCONNECTED);
intentFilter.addAction(BluetoothLeService.ACTION_GATT_SERVICES_DISCOVERED);
intentFilter.addAction(BluetoothLeService.ACTION_DATA_AVAILABLE);
return intentFilter;
}
}
Your Peripheral (phone A) does only advertisement, but BluetoothGattServer is not set up. That could be a reason of the described behavior - advertisement & scanning work, but connection doesn't.
BluetoothGatt + BluetoothGattCallback are for Central role (you call it phone B).
BluetoothGattServer + BluetoothGattServerCallback are for Peripheral (phone A).
Notes:
Connection from Central side (phone B) looks good, because when advertised device found, you get it using bluetoothAdapter.getRemoteDevice(address), then you call device.connectGatt
To transfer some data, you will need to add BluetoothGattService with BluetoothGattCharacteristic to your BluetoothGattServer - example of setup in Kotlin
Example project on github: BLEProof - it's in Kotlin, 2 apps communicate with each other: Central and Peripheral, all code in MainActivity.kt
I want to build an application that start a Service. This Service can run also if the APP is closed.
So I'm building this code on the OnCreate method of my BLEActivity.java:
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_ble);
startService(new Intent(this, BlePowerService.class));
}
Now I want that this service shoudl to start every 5 minutes also if the APP is closed.
The correct way to do this, is to create a Thread into BlePowerService ?
So I'm building this:
public class BlePowerService extends Service {
public DbLayer db;
Setting settingApp;
List<ScanFilter> filters;
String[] stringSequence = new String[] {CHARACTERISTIC_FORZA_STRING};
BluetoothAdapter mBluetoothAdapter;
BluetoothGatt mGatt;
BluetoothDevice currDevice;
static final long SCAN_PERIOD = 1000;
BluetoothLeScanner mLEScanner;
Handler mHandler;
int ReadQueueIndex;
List<BluetoothGattCharacteristic> ReadQueue;
ScanSettings settings;
int id = 1000;
boolean valore;
//String UuID = "f0001132-0451-4000-b000-000000000000";
String LOG_CODE = "NOTIFSRV";
int NOTIFICATION_GREEN = 1;
int NOTIFICATION_RED = 2;
int START_HOUR = 7;
int RANGE_HOURS_WEIGHT = 13;
int START_NOTIFY = 9;
int END_NOTIFY = 21;
public static String PAR_NOTIFICATION_EXERCISEID = "parNotificationExerciseId";
String NEW_ACTIVITY_START = "NAS";
String OPEN_ACTIVITY_START = "OAS";
public BlePowerService() {
}
#Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
//throw new UnsupportedOperationException("Not yet implemented");
return null;
}
#RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
#Override
public void onCreate() {
super.onCreate();
mHandler = new Handler();
Context sharedContext = null;
try {
sharedContext = this.createPackageContext(
"com.eresult.diabesitycare.devicesensor",
Context.CONTEXT_INCLUDE_CODE);
if (sharedContext == null) {
return;
}
db=new DbLayer(sharedContext);
db.open();
} catch (Exception e) {
String error = e.getMessage();
// Log.d(LOG_CODE,"DB error : " + error);
return;
}
}
#RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
try {
//RECUPERO LE IMPOSTAZIONI SETTATE DALL'UTENTE
settingApp = db.fetchSetting();
mTimer = new Timer();
mTimer.schedule(timerTask, 2500, 1 * 60 * 1000);
} catch (Exception e) {
// Log.e("POWER_SERVICE", e.getMessage());
}
return super.onStartCommand(intent, flags, startId);
}
private Timer mTimer;
TimerTask timerTask = new TimerTask() {
#RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
#Override
public void run() {
//LEGGO TUTTI I DATI DAL SENSORE FINCHE CI SONO VALORI
valore = true;
int conta = 0;
while(valore){
conta++;
Log.v("CICLO WHILE", conta+"");
if (currDevice != null) {
GattClientCallback gattClientCallback = new GattClientCallback();
mGatt = currDevice.connectGatt(getBaseContext(), false, gattClientCallback);
scanLeDevice(false);// will stop after first device detection
}else{
//provo a ricollegarmi al dispositivo probabile, abbia perso la connessione con esso
scanLeDevice(true);
}
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
#RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
public void connectToDevice(BluetoothDevice device) {
//VERIFICO SE IL DEVICE รจ QUELLO CHE VOGLIO IO
if (mGatt == null && settingApp != null
&& device.getAddress().equals(settingApp.getAddressBleSX())) {
currDevice = device;
GattClientCallback gattClientCallback = new GattClientCallback();
mGatt = currDevice.connectGatt(getBaseContext(), false, gattClientCallback);
scanLeDevice(false);// will stop after first device detection
}
}
private BluetoothAdapter.LeScanCallback mLeScanCallback =
new BluetoothAdapter.LeScanCallback() {
#Override
public void onLeScan(final BluetoothDevice device, int rssi,
byte[] scanRecord) {
Handler h = new Handler(getApplicationContext().getMainLooper());
// Although you need to pass an appropriate context
//TO DO
}
};
#RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
private void scanLeDevice(final boolean enable) {
//TO DO
}
private ScanCallback mScanCallback = new ScanCallback() {
#RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
#Override
public void onScanResult(int callbackType, ScanResult result) {
//TODO
}
};
#RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
private class GattClientCallback extends BluetoothGattCallback {
#SuppressLint("LongLogTag")
#Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
try{
//TODO
}catch(Exception e){
Log.e("ON_CONNECTION_STATE_CHANGE", e.getMessage());
}
}
public void disconnectGattServer() {
valore = false;
if (mGatt != null) {
mGatt.disconnect();
mGatt.close();
}
}
#Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
super.onServicesDiscovered(gatt, status);
//TODO
}
private void ReadCharacteristics(int index) {
mGatt.readCharacteristic(ReadQueue.get(index));
}
#Override
public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
super.onCharacteristicChanged(gatt, characteristic);
// Log.i("INFO", "Characteristic changed, " + characteristic.getUuid().toString());
readCharacteristic(characteristic);
}
private void enableCharacteristicNotification(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
//TODO
}
#Override
public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
super.onCharacteristicRead(gatt, characteristic, status);
//TODO
}
}
}
I'd like to connect the Android application and sensors with bluetooth and display sensor data on the application, but I do not know where to change.
Is the data of the sensor stored in the character stick?
If so, how do you display the stored data?
The equipment you are using is the SONY MESH button.
Android ver 5.0.2
Android Studio ver 2.3.1
MainActivity.java
private final static int SDKVER_LOLLIPOP = 21;
private final static int MESSAGE_NEW_RECEIVEDNUM = 0;
private final static int MESSAGE_NEW_SENDNUM = 1;
private final static int REQUEST_ENABLE_BT = 123456;
private BluetoothManager mBleManager;
private BluetoothAdapter mBleAdapter;
private boolean mIsBluetoothEnable = false;
private BluetoothLeScanner mBleScanner;
private BluetoothGatt mBleGatt;
private BluetoothGattCharacteristic mBleCharacteristic;
private TextView mTxtReceivedNum;
private TextView mTxtSendNum;
private String meshdata = String.valueOf(0x00020103);
private String mStrReceivedNum = "";
private String mStrSendNum = "";
private static final String SERVICE_UUID = "72C90001-57A9-4D40-B746-534E22EC9F9E";
private static final String CHARACTERISTIC_UUID = "72C90003-57A9-4D40-B746-534E22EC9F9E";
private static final String CHARACTERISTIC_CONFIG_UUID = "00002902-0000-1000-8000-00805f9b34fb";
private Random mRandom = new Random();
private Timer mTimer;
private SendDataTimer mSendDataTimer;
private final LeScanCallback mScanCallback = new BluetoothAdapter.LeScanCallback() {
#Override
public void onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord) {
runOnUiThread(new Runnable() {
#Override
public void run() {
mBleGatt = device.connectGatt(getApplicationContext(), false, mGattCallback);
}
});
}
};
private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
#Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState)
{
if (newState == BluetoothProfile.STATE_CONNECTED) {
gatt.discoverServices();
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
if (mBleGatt != null)
{
mBleGatt.close();
mBleGatt = null;
}
mIsBluetoothEnable = false;
}
}
#Override
public void onServicesDiscovered(BluetoothGatt gatt, int status)
{
if (status == BluetoothGatt.GATT_SUCCESS) {
BluetoothGattService service = gatt.getService(UUID.fromString(SERVICE_UUID));
if (service != null)
{
mBleCharacteristic = service.getCharacteristic(UUID.fromString(CHARACTERISTIC_UUID));
if (mBleCharacteristic != null) {
mBleGatt = gatt;
boolean registered = mBleGatt.setCharacteristicNotification(mBleCharacteristic, true);
BluetoothGattDescriptor descriptor = mBleCharacteristic.getDescriptor(
UUID.fromString(CHARACTERISTIC_CONFIG_UUID));
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
mBleGatt.writeDescriptor(descriptor);
mIsBluetoothEnable = true;
}
}
}
}
#Override
public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic)
{
if (CHARACTERISTIC_UUID.equals(characteristic.getUuid().toString().toUpperCase()))
{
mStrReceivedNum = characteristic.getStringValue(0);
mBleHandler.sendEmptyMessage(MESSAGE_NEW_RECEIVEDNUM);
}
}
};
private Handler mBleHandler = new Handler()
{
public void handleMessage(Message msg)
{
switch (msg.what)
{
case MESSAGE_NEW_RECEIVEDNUM:
mTxtReceivedNum.setText(mStrReceivedNum);
break;
case MESSAGE_NEW_SENDNUM:
mTxtSendNum.setText(mStrSendNum);
break;
}
}
};
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mIsBluetoothEnable = false;
mTxtReceivedNum = (TextView) findViewById(R.id.received_num);
mTxtSendNum = (TextView) findViewById(R.id.send_num);
mBleManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
mBleAdapter = mBleManager.getAdapter();
mTimer = new Timer();
mSendDataTimer = new SendDataTimer();
mTimer.schedule(mSendDataTimer, 500, 1000);
if ((mBleAdapter == null)
|| (! mBleAdapter.isEnabled())) {
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
}
else
{
this.scanNewDevice();
}
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
switch (requestCode) {
case REQUEST_ENABLE_BT:
if ((mBleAdapter != null)
|| (mBleAdapter.isEnabled())) {
this.scanNewDevice();
}
break;
}
}
private void scanNewDevice()
{
if (Build.VERSION.SDK_INT >= SDKVER_LOLLIPOP)
{
this.startScanByBleScanner();
}
else
{
mBleAdapter.startLeScan(mScanCallback);
}
}
#TargetApi(SDKVER_LOLLIPOP)
private void startScanByBleScanner()
{
mBleScanner = mBleAdapter.getBluetoothLeScanner();
mBleScanner.startScan(new ScanCallback() {
#Override
public void onScanResult(int callbackType, ScanResult result) {
super.onScanResult(callbackType, result);
result.getDevice().connectGatt(getApplicationContext(), false, mGattCallback);
}
#Override
public void onScanFailed(int intErrorCode)
{
super.onScanFailed(intErrorCode);
}
});
}
public class SendDataTimer extends TimerTask{
#Override
public void run() {
if(mIsBluetoothEnable)
{
mStrSendNum = String.valueOf(mRandom.nextInt(1000));
mBleHandler.sendEmptyMessage(MESSAGE_NEW_SENDNUM);
mBleCharacteristic.setValue(meshdata);
mBleGatt.writeCharacteristic(mBleCharacteristic);
}
}
}
#Override
protected void onDestroy()
{
mIsBluetoothEnable = false;
if(mBleGatt != null) {
mBleGatt.close();
mBleGatt = null;
}
super.onDestroy();
}
Related places
#Override
public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic)
{
if (CHARACTERISTIC_UUID.equals(characteristic.getUuid().toString().toUpperCase()))
{
mStrReceivedNum = characteristic.getStringValue(0);
mBleHandler.sendEmptyMessage(MESSAGE_NEW_RECEIVEDNUM);
}
}
Related places
private Handler mBleHandler = new Handler()
{
public void handleMessage(Message msg)
{
switch (msg.what)
{
case MESSAGE_NEW_RECEIVEDNUM:
mTxtReceivedNum.setText(mStrReceivedNum);
break;
case MESSAGE_NEW_SENDNUM:
mTxtSendNum.setText(mStrSendNum);
break;
}
}
};
Related places
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mIsBluetoothEnable = false;
mTxtReceivedNum = (TextView) findViewById(R.id.received_num);
mTxtSendNum = (TextView) findViewById(R.id.send_num);
Related places
public class SendDataTimer extends TimerTask{
#Override
public void run() {
if(mIsBluetoothEnable)
{
mStrSendNum = String.valueOf(mRandom.nextInt(1000));
mBleHandler.sendEmptyMessage(MESSAGE_NEW_SENDNUM);
mBleCharacteristic.setValue(mStrSendNum);
mBleGatt.writeCharacteristic(mBleCharacteristic);
}
}
}
activity_main.xml
<TextView
android:id="#+id/received_num"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="40sp"
android:gravity="right"
/>
<TextView
android:id="#+id/send_num"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="40sp"
/>
I made an app that communicates with an device via BLE. I wrote everything that was required for it's communication. The device receives the sent value via characteristic.setValue() , but doesn't return feedback in the method onCharacteristicChanged as is supposed to, in despite that has the descriptor set for notification. For instance i send "GTN" and should get "GTN deviceId" (like on iOS), but it calls just onCharacteristicWrite(). Checked also what properties the characteristic does support. It supports PROPERTY_NOTIFY & PROPERTY_WRITE_NO_RESPONSE. I ran out of ideas why it doesn't want to get that callback message. Can someone check and try to tell me if I made some big mistakes while sending/subscribing ??
MainActivity.java
public class MainActivity extends AppCompatActivity {
private static final int REQUEST_LOCATION_PERMISSION = 1;
private static final int REQUEST_ENABLE_BLUETOOTH = 2;
private static final int REQUEST_ENABLE_LOCATION = 3;
private static final int BLUETOOTH_ENABLED = -1;
private static final int LOCATION_ENABLED = -1;
private static final int MANUFACTURER_ID = xxxxx;
private static final UUID SERVICE =xxxxxxxxxxxxx;
private static final UUID CHARACTERISTIC =xxxxxxxxxxxxxxxxx;
private static final UUID DESCRIPTOR = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb");
private final static String TAG = "BLE";
private byte[] mfrData;
private BluetoothManager bluetoothManager;
private BluetoothAdapter bluetoothAdapter;
private BluetoothGatt bluetoothGatt;
private BluetoothLeScanner bluetoothLeScanner;
private BluetoothGattService customService;
private BluetoothGattCallback bluetoothGattCallback = new BluetoothGattCallback() {
#Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
super.onConnectionStateChange(gatt, status, newState);
if (newState == BluetoothProfile.STATE_CONNECTED) {
gatt.discoverServices();
}
}
#Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
super.onServicesDiscovered(gatt, status);
BluetoothGattService service = gatt.getService(SERVICE);
BluetoothGattCharacteristic characteristic = service.getCharacteristic(CHARACTERISTIC);
gatt.setCharacteristicNotification(characteristic, true);
enableDataNotifications(gatt, characteristic);
}
#Override
public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
super.onCharacteristicRead(gatt, characteristic, status);
Log.d(TAG, "onCharacteristicRead: ");
}
#Override
public void onCharacteristicWrite(final BluetoothGatt gatt, final BluetoothGattCharacteristic characteristic, int status) {
super.onCharacteristicWrite(gatt, characteristic, status);
Log.d(TAG, "onCharacteristicWrite: ");
}
#Override
public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
super.onCharacteristicChanged(gatt, characteristic);
Log.d(TAG, "onCharacteristicChanged: ");
}
#Override
public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
super.onDescriptorRead(gatt, descriptor, status);
Log.d(TAG, "onDescriptorRead: ");
}
#Override
public void onDescriptorWrite(final BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
super.onDescriptorWrite(gatt, descriptor, status);
Log.d(TAG, "onDescriptorWrite: ");
checkCharacteristicProperties(gatt.getService(SERVICE).getCharacteristic(CHARACTERISTIC));
new Thread(new Runnable() {
#Override
public void run() {
writeDataToCharCommand("GTN", gatt);
}
}).start();
}
#Override
public void onReliableWriteCompleted(BluetoothGatt gatt, int status) {
super.onReliableWriteCompleted(gatt, status);
Log.d(TAG, "onReliableWriteCompleted: ");
}
#Override
public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) {
super.onReadRemoteRssi(gatt, rssi, status);
Log.d(TAG, "onReadRemoteRssi: ");
}
#Override
public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) {
super.onMtuChanged(gatt, mtu, status);
Log.d(TAG, "onMtuChanged: ");
}
};
private ScanCallback scanCallback = new ScanCallback() {
#Override
public void onScanResult(int callbackType, ScanResult result) {
super.onScanResult(callbackType, result);
if (result.getScanRecord() != null && result.getScanRecord().getManufacturerSpecificData(MANUFACTURER_ID) != null) {
Log.d(TAG, "onScanResult: found device");
bluetoothLeScanner.stopScan(scanCallback);
boolean isBonded = result.getDevice().createBond();
if (isBonded) {
bluetoothGatt = result.getDevice().connectGatt(MainActivity.this, false, bluetoothGattCallback, BluetoothDevice.TRANSPORT_LE);
}
}
}
#Override
public void onBatchScanResults(List<ScanResult> results) {
super.onBatchScanResults(results);
Log.d(TAG, "onBatchScanResults: ");
}
#Override
public void onScanFailed(int errorCode) {
super.onScanFailed(errorCode);
Log.d(TAG, "onScanFailed: ");
}
};
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
setupBluetoothData();
checkIfSupportedBluetooth();
checkLocationPermission();
}
private void checkCharacteristicProperties(BluetoothGattCharacteristic pChar) {
Log.d(TAG, "checkCharacteristicProperties: PROPERTY_READ " + ((pChar.getProperties() & BluetoothGattCharacteristic.PROPERTY_READ) != 0));
Log.d(TAG, "checkCharacteristicProperties: PROPERTY_NOTIFY " + ((pChar.getProperties() & BluetoothGattCharacteristic.PROPERTY_NOTIFY) != 0));
Log.d(TAG, "checkCharacteristicProperties: PROPERTY_BROADCAST " + ((pChar.getProperties() & BluetoothGattCharacteristic.PROPERTY_BROADCAST) != 0));
Log.d(TAG, "checkCharacteristicProperties: PROPERTY_EXTENDED_PROPS " + ((pChar.getProperties() & BluetoothGattCharacteristic.PROPERTY_EXTENDED_PROPS) != 0));
Log.d(TAG, "checkCharacteristicProperties: PROPERTY_INDICATE " + ((pChar.getProperties() & BluetoothGattCharacteristic.PROPERTY_INDICATE) != 0));
Log.d(TAG, "checkCharacteristicProperties: PROPERTY_SIGNED_WRITE " + ((pChar.getProperties() & BluetoothGattCharacteristic.PROPERTY_SIGNED_WRITE) != 0));
Log.d(TAG, "checkCharacteristicProperties: PROPERTY_WRITE " + ((pChar.getProperties() & BluetoothGattCharacteristic.PROPERTY_WRITE) != 0));
Log.d(TAG, "checkCharacteristicProperties: PROPERTY_WRITE_NO_RESPONSE " + ((pChar.getProperties() & BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE) != 0));
}
private void setupBluetoothData() {
bluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
bluetoothAdapter = bluetoothManager.getAdapter();
}
private void checkIfSupportedBluetooth() {
if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
Toast.makeText(this, R.string.ble_not_supported, Toast.LENGTH_SHORT).show();
finish();
}
if (bluetoothAdapter == null) {
Toast.makeText(this, R.string.error_bluetooth_not_supported, Toast.LENGTH_SHORT).show();
finish();
}
}
private void checkLocationPermission() {
if (!hasGrantedLocationPermission()) {
requestLocationPermission();
} else {
checkIfBluetoothEnabled();
}
}
private boolean hasGrantedLocationPermission() {
int result = ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION);
return result == PackageManager.PERMISSION_GRANTED;
}
private void requestLocationPermission() {
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, REQUEST_LOCATION_PERMISSION);
}
private void checkIfBluetoothEnabled() {
if (!bluetoothAdapter.isEnabled()) {
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent, REQUEST_ENABLE_BLUETOOTH);
} else {
displayLocationSettingsRequest();
}
}
private void displayLocationSettingsRequest() {
GoogleApiClient googleApiClient = new GoogleApiClient.Builder(this)
.addApi(LocationServices.API).build();
googleApiClient.connect();
LocationRequest locationRequest = LocationRequest.create();
locationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
locationRequest.setInterval(10000);
locationRequest.setFastestInterval(10000 / 2);
LocationSettingsRequest.Builder builder = new LocationSettingsRequest.Builder().addLocationRequest(locationRequest);
builder.setAlwaysShow(true);
PendingResult<LocationSettingsResult> result = LocationServices.SettingsApi.checkLocationSettings(googleApiClient, builder.build());
result.setResultCallback(new ResultCallback<LocationSettingsResult>() {
#Override
public void onResult(#NonNull LocationSettingsResult locationSettingsResult) {
final Status status = locationSettingsResult.getStatus();
switch (status.getStatusCode()) {
case LocationSettingsStatusCodes.SUCCESS: {
scanForDevices();
break;
}
case LocationSettingsStatusCodes.RESOLUTION_REQUIRED: {
try {
status.startResolutionForResult(MainActivity.this, REQUEST_ENABLE_LOCATION);
} catch (IntentSender.SendIntentException e) {
e.printStackTrace();
}
break;
}
case LocationSettingsStatusCodes.SETTINGS_CHANGE_UNAVAILABLE: {
break;
}
}
}
});
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
switch (requestCode) {
case REQUEST_ENABLE_BLUETOOTH: {
if (resultCode == BLUETOOTH_ENABLED) {
displayLocationSettingsRequest();
} else {
Toast.makeText(this, "Restart app", Toast.LENGTH_SHORT).show();
}
break;
}
case REQUEST_ENABLE_LOCATION: {
if (resultCode == LOCATION_ENABLED) {
scanForDevices();
} else {
Toast.makeText(this, "Restart app", Toast.LENGTH_SHORT).show();
}
break;
}
default: {
break;
}
}
}
private void scanForDevices() {
Log.d(TAG, "scanForDevices: ");
Toast.makeText(this, "Scanning", Toast.LENGTH_SHORT).show();
ScanSettings scanSettings = new ScanSettings.Builder()
.setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
.build();
ScanFilter scanFilter = new ScanFilter.Builder()
.setManufacturerData(0x0361, mfrData)
.build();
bluetoothLeScanner = bluetoothAdapter.getBluetoothLeScanner();
bluetoothLeScanner.startScan(Collections.singletonList(scanFilter),
scanSettings, scanCallback);
}
private String translate(byte[] value) throws UnsupportedEncodingException {
return new String(value, StandardCharsets.UTF_8);
}
#Override
public void onRequestPermissionsResult(int requestCode, #NonNull String[] permissions, #NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
switch (requestCode) {
case REQUEST_LOCATION_PERMISSION: {
if (grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
checkIfBluetoothEnabled();
} else {
Toast.makeText(this, "Restart app", Toast.LENGTH_SHORT).show();
}
break;
}
default: {
break;
}
}
}
private void writeDataToCharCommand(String data, BluetoothGatt gatt) {
Log.d(TAG, "writeDataToCharCommand: ");
gatt.getService(SERVICE).getCharacteristic(CHARACTERISTIC).setValue(data);
gatt.writeCharacteristic(gatt.getService(SERVICE).getCharacteristic(CHARACTERISTIC));
}
private void enableDataNotifications(BluetoothGatt gatt, BluetoothGattCharacteristic gattCharacteristic) {
Log.d(TAG, "enableDataNotifications: ");
boolean enabled = gatt.setCharacteristicNotification(gattCharacteristic, true);
if (enabled) {
BluetoothGattDescriptor descriptor = gattCharacteristic.getDescriptor(DESCRIPTOR);
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
gatt.writeDescriptor(descriptor);
}
}
}
i have notification work flawlessly with BLE.
i created this method to mark a characteristics notification enabled.
public void markCharForNotification(BluetoothGattCharacteristic readableChar) {
mBluetoothGatt.setCharacteristicNotification(readableChar, true);
List<BluetoothGattDescriptor> listDescr = readableChar.getDescriptors();
BluetoothGattDescriptor descriptor = listDescr.get(0);
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
mBluetoothGatt.writeDescriptor(descriptor);
}
hope it helps
You might want to run the checkCharacteristicProperties on the notify characteristic also.
Anyway, the correct way to enable notifications is to check the properties first and enable notify or indicate, based on those properties:
BluetoothGattDescriptor descriptor = characteristic.getDescriptor(CLIENT_CHARACTERISTIC_CONFIG_DESCRIPTOR_UUID);
int properties = characteristic.getProperties();
if (descriptor != null) {
if ((properties & BluetoothGattCharacteristic.PROPERTY_NOTIFY) > 0) {
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
} else if ((properties & BluetoothGattCharacteristic.PROPERTY_INDICATE) > 0) {
descriptor.setValue(BluetoothGattDescriptor.ENABLE_INDICATION_VALUE);
}
gatt.writeDescriptor(descriptor);
Hope this helps.
I am developing mobile app which working with sensor, that use UART. Example from official github page works perfect, but code extracted from doesnt work. Only thing that i have changed, is a device search, but this code works perfect with Polar H7 device(only discovering device name and device adress and sending to next activity). On this activity i am trying to connect to UART device, here is code :
public class UartRecordingActivity extends AppCompatActivity {
private final static String TAG = UartRecordingActivity.class.getSimpleName();
// BLE stuff
public static final String EXTRAS_DEVICE_NAME = "DEVICE_NAME";
public static final String EXTRAS_DEVICE_ADDRESS = "DEVICE_ADDRESS";
//private BleService bleService;
private String deviceName;
private String deviceAddress;
//private BleServicesAdapter gattServiceAdapter;
private boolean isConnected = false;
private static final int REQUEST_SELECT_DEVICE = 1;
private static final int REQUEST_ENABLE_BT = 2;
private static final int UART_PROFILE_READY = 10;
private static final int UART_PROFILE_CONNECTED = 20;
private static final int UART_PROFILE_DISCONNECTED = 21;
private static final int STATE_OFF = 10;
private int mState = UART_PROFILE_DISCONNECTED;
private UartService mService = null;
private BluetoothDevice mDevice = null;
private BluetoothAdapter mBtAdapter = null;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_recording_uart);
Log.i(TAG, "onCreate");
final Intent intent = getIntent();
deviceName = intent.getStringExtra(EXTRAS_DEVICE_NAME);
deviceAddress = intent.getStringExtra(EXTRAS_DEVICE_ADDRESS);
final Intent gattServiceIntent = new Intent(this, UartService.class);
bindService(gattServiceIntent, mServiceConnection, BIND_AUTO_CREATE);
}
#Override
protected void onStart() {
super.onStart();
mBtAdapter = BluetoothAdapter.getDefaultAdapter();
service_init();
mDevice = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(deviceAddress);
mService.connect(deviceAddress);
}
private void service_init() {
Intent bindIntent = new Intent(this, UartService.class);
bindService(bindIntent, mServiceConnection, BIND_AUTO_CREATE);
LocalBroadcastManager.getInstance(this).registerReceiver(UARTStatusChangeReceiver,
makeGattUpdateIntentFilter());
}
//UART service connected/disconnected
private ServiceConnection mServiceConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder rawBinder) {
mService = ((UartService.LocalBinder) rawBinder).getService();
Log.d(TAG, "onServiceConnected mService= " + mService);
if (!mService.initialize()) {
Log.e(TAG, "Unable to initialize Bluetooth");
finish();
}
}
public void onServiceDisconnected(ComponentName classname) {
mService = null;
}
};
#Override
public boolean onOptionsItemSelected(MenuItem menuItem) {
if (menuItem.getItemId() == android.R.id.home) {
Intent intent = new Intent(this, HomeActivity.class);
startActivity(intent);
}
return super.onOptionsItemSelected(menuItem);}
private final BroadcastReceiver UARTStatusChangeReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
final Intent mIntent = intent;
//*********************//
if (action.equals(UartService.ACTION_GATT_CONNECTED)) {
runOnUiThread(new Runnable() {
public void run() {
String currentDateTimeString = DateFormat.getTimeInstance().format(new Date());
Log.d(TAG, "UART_CONNECT_MSG");
mState = UART_PROFILE_CONNECTED;
}
});
}
//*********************//
if (action.equals(UartService.ACTION_GATT_DISCONNECTED)) {
runOnUiThread(new Runnable() {
public void run() {
String currentDateTimeString = DateFormat.getTimeInstance().format(new Date());
Log.d(TAG, "UART_DISCONNECT_MSG");
mState = UART_PROFILE_DISCONNECTED;
mService.close();
}
});
}
//*********************//
if (action.equals(UartService.ACTION_GATT_SERVICES_DISCOVERED)) {
mService.enableTXNotification();
}
//*********************//
if (action.equals(UartService.ACTION_DATA_AVAILABLE)) {
Log.i("data","received");
final byte[] txValue = intent.getByteArrayExtra(UartService.EXTRA_DATA);
//handle data
}
//*********************//
if (action.equals(UartService.DEVICE_DOES_NOT_SUPPORT_UART)) {
}
}
};
private static IntentFilter makeGattUpdateIntentFilter() {
final IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(UartService.ACTION_GATT_CONNECTED);
intentFilter.addAction(UartService.ACTION_GATT_DISCONNECTED);
intentFilter.addAction(UartService.ACTION_GATT_SERVICES_DISCOVERED);
intentFilter.addAction(UartService.ACTION_DATA_AVAILABLE);
intentFilter.addAction(UartService.DEVICE_DOES_NOT_SUPPORT_UART);
return intentFilter;
}
}
Error is caused by this line(mService causing NullPointerException):
mService.connect(deviceAddress);
Method OnServiceConnected is guarantees that you have been connected to service by binding. In OnStart callback you have NullPointer because when you call bindService, it needs a little time to make a connection. Just make your code something like that:
public void onServiceConnected(ComponentName className, IBinder rawBinder) {
mService = ((UartService.LocalBinder) rawBinder).getService();
Log.d(TAG, "onServiceConnected mService= " + mService);
if (!mService.initialize()) {
Log.e(TAG, "Unable to initialize Bluetooth");
finish();
}
mDevice = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(deviceAddress);
mService.connect(deviceAddress);
}