Hi there,
I'm new to Android BLE development and simply trying to read out a BLE temperature sensor that encodes the temp. in the service data from the advertising packets.
This works, however the update rate is very slow (~1min), while the two sensors send out an advertising packet every second. So it seems like a great deal of packets get dropped/scanner misbehaves.
When I look at the sensors with NRF connect, the update rate is perfect, so I must be doing something terribly wrong here?
I tried many different types of filtering (in the callback, before the callback,...) and scanning modes. Very little change..
Thanks a lot in advance, I'm learning a lot!
package com.portacapena.ble.grinn_ble;
import android.Manifest;
import android.app.AlertDialog;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothManager;
import android.bluetooth.le.BluetoothLeScanner;
import android.bluetooth.le.ScanCallback;
import android.bluetooth.le.ScanFilter;
import android.bluetooth.le.ScanRecord;
import android.bluetooth.le.ScanResult;
import android.bluetooth.le.ScanSettings;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.location.Location;
import android.location.LocationManager;
import android.os.AsyncTask;
import android.os.ParcelUuid;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.text.TextUtils;
import android.text.method.ScrollingMovementMethod;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import java.io.UnsupportedEncodingException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.BitSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.UUID;
import java.util.Vector;
import static java.util.Locale.US;
public class MainActivity extends AppCompatActivity {
BluetoothManager btManager;
BluetoothAdapter btAdapter;
BluetoothLeScanner btScanner;
Button startScanningButton;
Button stopScanningButton;
TextView peripheralTextView;
private final static int REQUEST_ENABLE_BT = 1;
private static final int PERMISSION_REQUEST_COARSE_LOCATION = 1;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
peripheralTextView = (TextView) findViewById(R.id.PeripheralTextView);
peripheralTextView.setMovementMethod(new ScrollingMovementMethod());
startScanningButton = (Button) findViewById(R.id.StartScanButton);
startScanningButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
startScanning();
}
});
stopScanningButton = (Button) findViewById(R.id.StopScanButton);
stopScanningButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
stopScanning();
}
});
stopScanningButton.setVisibility(View.INVISIBLE);
btManager = (BluetoothManager)getSystemService(Context.BLUETOOTH_SERVICE);
btAdapter = btManager.getAdapter();
btScanner = btAdapter.getBluetoothLeScanner();
if (btAdapter != null && !btAdapter.isEnabled()) {
Intent enableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableIntent,REQUEST_ENABLE_BT);
}
// Make sure we have access coarse location enabled, if not, prompt the user to enable it
if (this.checkSelfPermission(Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
final AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("This app needs location access");
builder.setMessage("Please grant location access so this app can detect peripherals.");
builder.setPositiveButton(android.R.string.ok, null);
builder.setOnDismissListener(new DialogInterface.OnDismissListener() {
@Override
public void onDismiss(DialogInterface dialog) {
requestPermissions(new String[]{Manifest.permission.ACCESS_COARSE_LOCATION}, PERMISSION_REQUEST_COARSE_LOCATION);
}
});
builder.show();
}
startScanning();
}
// Device scan callback.
private ScanCallback leScanCallback = new ScanCallback() {
@Override
public void onScanResult(int callbackType, ScanResult result) {
//peripheralTextView.append("Device Name: " + result.getDevice().getName() + " rssi: " + result.getRssi() + "\n");
ScanRecord scanRecord = result.getScanRecord();
String mac = result.getDevice().getAddress();
//String deviceName = scanRecord.getDeviceName();
byte[] serviceData = scanRecord.getServiceData(ParcelUuid.fromString("00001809-0000-1000-8000-00805f9b34fb"));
Log.d("serviceData", serviceData.toString());
StringBuilder hexOut = new StringBuilder();
Log.d("Hex", hexOut.toString());
long exponent = unsignedToSigned(serviceData[3],8);
Log.d("exponent",Long.toString(exponent));
long[] mant = new long[3];
mant[0] = (serviceData[2] << 16) & 0xFFFFFF;
mant[1] = (serviceData[1] << 8) & 0xFFFF;
mant[2] = (serviceData[0]) & 0xFF;
long mantissa = unsignedToSigned(mant[0] + mant[1] + mant[2],24);
Log.d("mantissa", Long.toString(mantissa));
double temp = mantissa * Math.pow(10,exponent);
Log.d("Temperatuur",Double.toString(temp));
peripheralTextView.append("Adres: " + mac + " Temperatuur: " + String.format(Locale.US,"%.2f",temp) + "\n");
// auto scroll for text view
final int scrollAmount = peripheralTextView.getLayout().getLineTop(peripheralTextView.getLineCount()) - peripheralTextView.getHeight();
// if there is no need to scroll, scrollAmount will be <=0
if (scrollAmount > 0)
peripheralTextView.scrollTo(0, scrollAmount);
}
public long unsignedToSigned(long unsigned, int size){
long signed = 0;
if ((unsigned & (1 << (size-1))) != 0)
{
signed = (-1) * (1 << (size - 1)) + unsigned ^ (1 << (size - 1)) ;
}
else {
signed = unsigned;
}
return signed;
}
};
@Override
public void onRequestPermissionsResult(int requestCode,
String permissions[], int[] grantResults) {
switch (requestCode) {
case PERMISSION_REQUEST_COARSE_LOCATION: {
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
System.out.println("coarse location permission granted");
} else {
final AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("Functionality limited");
builder.setMessage("Since location access has not been granted, this app will not be able to discover beacons when in the background.");
builder.setPositiveButton(android.R.string.ok, null);
builder.setOnDismissListener(new DialogInterface.OnDismissListener() {
@Override
public void onDismiss(DialogInterface dialog) {
}
});
builder.show();
}
return;
}
}
}
public void startScanning() {
System.out.println("start scanning");
//peripheralTextView.setText("");
startScanningButton.setVisibility(View.INVISIBLE);
stopScanningButton.setVisibility(View.VISIBLE);
byte[] emptyData = new byte[0];
byte[] mask = new byte[] {
(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00
};
final Vector<ScanFilter> filter = new Vector<ScanFilter>();
ScanFilter.Builder builder = new ScanFilter.Builder().setServiceData(ParcelUuid.fromString("00001809-0000-1000-8000-00805f9b34fb"),emptyData,mask);//.setDeviceName("BTTEMP(MCP)");
//builder.setServiceData(ParcelUuid.fromString("00001809-0000-1000-8000-00805f9b34fb"),emptyData);
filter.add(builder.build());
final ScanSettings.Builder scanSettingsBuilder = new ScanSettings.Builder();
scanSettingsBuilder.setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY);
scanSettingsBuilder.setMatchMode(ScanSettings.MATCH_MODE_AGGRESSIVE);
scanSettingsBuilder.setCallbackType(ScanSettings.CALLBACK_TYPE_ALL_MATCHES);
scanSettingsBuilder.setNumOfMatches(ScanSettings.MATCH_NUM_ONE_ADVERTISEMENT);
AsyncTask.execute(new Runnable() {
@Override
public void run() {
btScanner.startScan(filter, scanSettingsBuilder.build(), leScanCallback);
Log.d("scan","Scan gestart");
}
});
}
public void stopScanning() {
System.out.println("stopping scanning");
peripheralTextView.append("Stopped Scanning");
startScanningButton.setVisibility(View.VISIBLE);
stopScanningButton.setVisibility(View.INVISIBLE);
AsyncTask.execute(new Runnable() {
@Override
public void run() {
btScanner.stopScan(leScanCallback);
}
});
}
}