I'm building an app that downlaod all characteristic from a BLE device. This device storage in your local memory 10 distinct double values, every 3ms.
So in 1 second the BLE device has storage 333 row (every row have 10 values).
My Android app, download this values but I can to downlaod one rows of second or little less. In this mode I can't never to download all the information of this BLE device.
Now there is any way to increase the frequency of scan o read this characteristic ?
this is my Service that downlaod the characteristic of BLE device:
public class BlePowerService extends Service {
public DbLayer db;
Setting settingApp;
List<ScanFilter> filters;
String[] stringSequence = new String[] {CHARACTERISTIC_FORZA_STRING, CHARACTERISTIC_TEMPERATURA_STRING};
BluetoothAdapter mBluetoothAdapter;
BluetoothGatt mGatt;
BluetoothDevice currDevice;
static final long SCAN_PERIOD = 500;
static String SPLIT_CHAR =";";
BluetoothLeScanner mLEScanner;
Handler mHandler;
int ReadQueueIndex;
List<BluetoothGattCharacteristic> ReadQueue;
BluetoothGattCharacteristic caratteristicaDaLeggere;
ScanSettings settings;
Integer ultimaForzaLetta,ultimaTemperaturaLetta;
boolean continuaLetturaForza, continuaLetturaTemperatura;
boolean isReading = true;
String LOG_CODE = "NOTIFSRV";
GattClientCallback gattClientCallback;
private static final int PERMISSION_REQUEST_COARSE_LOCATION = 1;
public BlePowerService() {
}
#Override
public void onCreate() {
super.onCreate();
mHandler = new Handler();
Context sharedContext = null;
try {
sharedContext = this.createPackageContext(
"com.care.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;
}
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
try {
//RECUPERO LE IMPOSTAZIONI SETTATE DALL'UTENTE
settingApp = db.fetchSetting();
if (settingApp != null && settingApp.getAddressBleSX()!=null) {
//POSSO FILTRARE DIRETTAMENTE PER L'UUID DEL DISPOSITIVO MEMORIZZATO
//DALL'UTENTE
ScanFilter.Builder scanFilterMac =
null;
scanFilterMac = new ScanFilter.Builder().setDeviceAddress(settingApp.getAddressBleSX());
if(filters==null)
filters = new ArrayList<ScanFilter>();
//if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
filters.add(scanFilterMac.build());
//}
//FILTRO ANCHE PER LA CARATTERISTICA DI FORZA
ScanFilter filter = null;
filter = new ScanFilter.Builder().setServiceUuid(ParcelUuid.fromString(CHARACTERISTIC_FORZA_STRING)).build();
filters.add(filter);
final BluetoothManager bluetoothManager =
(BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
mBluetoothAdapter = bluetoothManager.getAdapter();
//mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
mLEScanner = mBluetoothAdapter.getBluetoothLeScanner();
settings = new ScanSettings.Builder()
.setScanMode(ScanSettings.SCAN_MODE_LOW_POWER)
.build();
scanLeDevice(true);
mTimer = new Timer();
//ogni 2 ore
continuaLetturaForza = true;
continuaLetturaTemperatura = true;
int nSecondi = settingApp.getFrequenzaDownload()!= null ? settingApp.getFrequenzaDownload() : 1;
mTimer.schedule(timerTask,5000);
//mTimer.schedule(timerTask, 10000, 1000 * nSecondi);
//mTimer.scheduleAtFixedRate (timerTask, 10000, 1000 * nSecondi);
}
} 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
int counter = 0;
while(continuaLetturaForza || continuaLetturaTemperatura){
counter++;
//Log.v("CICLO WHILE", counter+"");
if (currDevice != null) {
if(ReadQueue!= null && ReadQueue.size()>0){
int index =0;
for(index=0; index < ReadQueue.size(); index++){
if(mGatt!=null){
isReading = mGatt.readCharacteristic(ReadQueue.get(index));
while(isReading){
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}else{
continuaLetturaForza = false;
continuaLetturaTemperatura = false;
}
}
}
}else{
//provo a ricollegarmi al dispositivo probabile, abbia perso la connessione con esso
scanLeDevice(true);
try {
Thread.sleep(2500);
} catch (InterruptedException e) {
e.printStackTrace();
}
if(currDevice == null && counter == 25){
//in questo caso non è riuscito a trovare il dispositivo BLE
//esco dal ciclo
continuaLetturaForza = false;
continuaLetturaTemperatura = false;
continue;
}
}
}
if(gattClientCallback!=null && mGatt != null)
gattClientCallback.disconnectGattServer();
mTimer.cancel();
stopSelf();
EventBus.getDefault().post(new MessageEvent("stopService"));
}
public void stopTask() {
if (timerTask != null) {
Log.d("TIMER", "timer canceled");
timerTask.cancel();
}
}
};
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 = 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
h.post(new Runnable() {
#Override
public void run() {
// Log.i("onLeScan", device.toString());
connectToDevice(device);
}
});
}
};
private void scanLeDevice(final boolean enable) {
if (enable) {
mHandler.postDelayed(new Runnable() {
#Override
public void run() {
if (Build.VERSION.SDK_INT < 21) {
mBluetoothAdapter.stopLeScan(mLeScanCallback);
} else {
mLEScanner.stopScan(mScanCallback);
}
}
}, SCAN_PERIOD);
if (Build.VERSION.SDK_INT < 21) {
mBluetoothAdapter.startLeScan(mLeScanCallback);
} else {
mLEScanner.startScan(filters, settings, mScanCallback);
}
} else {
if (Build.VERSION.SDK_INT < 21) {
mBluetoothAdapter.stopLeScan(mLeScanCallback);
} else {
mLEScanner.stopScan(mScanCallback);
}
}
}
private ScanCallback mScanCallback = new ScanCallback() {
#Override
public void onScanResult(int callbackType, ScanResult result) {
// Log.i("callbackType", String.valueOf(callbackType));
// Log.i("result", result.toString());
BluetoothDevice btDevice = null;
btDevice = result.getDevice();
connectToDevice(btDevice);
}
#Override
public void onBatchScanResults(List<ScanResult> results) {
for (ScanResult sr : results) {
Log.i("ScanResult - Results", sr.toString());
}
}
#Override
public void onScanFailed(int errorCode) {
Log.e("Scan Failed", "Error Code: " + errorCode);
}
};
private class GattClientCallback extends BluetoothGattCallback {
#Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
try{
super.onConnectionStateChange(gatt, status, newState);
//Log.i("tag", "onConnectionStateChange newState: " + newState);
if (status == BluetoothGatt.GATT_FAILURE) {
Log.e("ERROR_SERVICE", "Connection Gatt failure status " + status);
if(mGatt == null){
}else{
try {
gatt.close();
} catch (Exception e) {
Log.d("", "close ignoring: " + e);
}
mGatt = null;
}
return;
} else if (status != BluetoothGatt.GATT_SUCCESS) {
// handle anything not SUCCESS as failure
Log.e("ERROR_SERVICE", "Connection not GATT sucess status " + status);
if(mGatt == null){
}else{
try {
gatt.close();
} catch (Exception e) {
Log.d("", "close ignoring: " + e);
}
mGatt = null;
}
return;
}
if (newState == BluetoothProfile.STATE_CONNECTED) {
//Log.i("INFO", "Connected to device " + gatt.getDevice().getAddress());
gatt.discoverServices();
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
Log.i("INFO", "Disconnected from device");
if(mGatt == null){
}else{
try {
mGatt.close();
mGatt = null;
currDevice = null;
} catch (Exception e) {
Log.d("", "close ignoring: " + e);
}
mGatt = null;
}
}
}catch(Exception e){
Log.e("tag", e.getMessage());
continuaLetturaForza = true;
continuaLetturaTemperatura = true;
}
}
public void disconnectGattServer() {
continuaLetturaForza = true;
continuaLetturaTemperatura = true;
isReading = false;
if (mGatt == null) {
}
else{
try{
mGatt.disconnect();
// mGatt.close();
}catch(Exception e){
mGatt = null;
}
}
//mGatt = null;
//currDevice = null;
}
#Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
super.onServicesDiscovered(gatt, status);
if (status != BluetoothGatt.GATT_SUCCESS) {
// Log.i("INFO", "Device service discovery unsuccessful, status " + status);
return;
}
List<BluetoothGattCharacteristic> matchingCharacteristics =
BluetoothUtils.findCharacteristics(gatt,stringSequence);
if (matchingCharacteristics.isEmpty()) {
// Log.e("ERROR_SERVICE","Unable to find characteristics.");
return;
}else {
ReadQueue = new ArrayList<>();
for (BluetoothGattCharacteristic characterist : matchingCharacteristics) {
ReadQueue.add(characterist);
}
//ReadQueueIndex = 0;
//ReadCharacteristics(ReadQueueIndex);
}
}
#Override
public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
super.onCharacteristicRead(gatt, characteristic, status);
if (status == BluetoothGatt.GATT_SUCCESS) {
String valoreRestituito = characteristic.getStringValue(0);
if(valoreRestituito==null){
int i = 0;
i = i + 1;
return;
}
//SPLITTO PER IL CARATTERE ;
String[] arrayValori = valoreRestituito.split(SPLIT_CHAR);
//1 valore è l ID
try{
int id = new Integer(arrayValori[0]);
//VERIFICO SE STO LEGGENDO TEMPERATURA O FORZA
if(characteristic.getUuid().equals(CHARACTERISTIC_FORZA_UUID)){
/*Log.v("CARATTERISTICA", "*****************************************");
Log.v("FORZA", "*****************************************");*/
if(ultimaForzaLetta!= null && ultimaForzaLetta == id){
continuaLetturaForza = false;
isReading = false;
return;
}
ultimaForzaLetta = id;
}else if(characteristic.getUuid().equals( CHARACTERISTIC_TEMPERATURA_UUID)){
/*Log.v("CARATTERISTICA", "*****************************************");
Log.v("TEMPERATURA", "*****************************************");*/
if(ultimaTemperaturaLetta!= null && ultimaTemperaturaLetta == id){
continuaLetturaTemperatura = false;
isReading = false;
return;
}
ultimaTemperaturaLetta = id;
}
SensorData mSenData = new SensorData();
mSenData.setIdType(id);
mSenData.setValue1(arrayValori[1]);
mSenData.setValue2(arrayValori[2]);
mSenData.setValue3(arrayValori[3]);
mSenData.setValue4(arrayValori[4]);
mSenData.setValue5(arrayValori[5]);
mSenData.setValue6(arrayValori[6]);
mSenData.setValue7(arrayValori[7]);
mSenData.setValue8(arrayValori[8]);
//TO-DO HO COMMENTATO QUESTA RIGA DI CODICE
// mSenData.setIdType(st.getId());
mSenData.setCharacteristic(characteristic.getUuid().toString());
mSenData.setValueTimestamp(db.getDateTime(true));
db.insertSensorData(mSenData);
EventBus.getDefault().post(new MessageEvent("update"));
}catch (Exception e){
Log.e("ERROR", e.getMessage());
}
isReading = false;
} else {
isReading = false;
Log.e("ERROR_SERVICE", "Characteristic read unsuccessful, status: " + status);
disconnectGattServer();
return;
}
}
}
public void onDestroy() {
try {
db.close();
Log.d(LOG_CODE,"DB connection closed" );
mTimer.cancel();
timerTask.cancel();
} catch (Exception e) {
e.printStackTrace();
}
}
}
The service works correctly but is to slow.
You can't do more than one read request per connection interval. Since 3 ms is much less than the minimum allowed connection interval, what you're trying to do seems impossible. You will have to make your protocol more efficient, for example by letting the peripheral send a stream of notifications. This way you can achieve multiple peckets per connection event.
Related
I'm building an Android Service. This Service should to connect of BLE device and download all informatio that it have storage in yuor memory.
Now I want that this Service can running in background.
This is my BlePowerService that extends Service
public class BlePowerService extends Service {
public DbLayer db;
Setting settingApp;
List<ScanFilter> filters;
String[] stringSequence = new String[] {CHARACTERISTIC_FORZA_STRING, CHARACTERISTIC_TEMPERATURA_STRING};
BluetoothAdapter mBluetoothAdapter;
BluetoothGatt mGatt;
BluetoothDevice currDevice;
static final long SCAN_PERIOD = 500;
static String SPLIT_CHAR =";";
BluetoothLeScanner mLEScanner;
Handler mHandler;
int ReadQueueIndex;
List<BluetoothGattCharacteristic> ReadQueue;
BluetoothGattCharacteristic caratteristicaDaLeggere;
ScanSettings settings;
Integer ultimaForzaLetta,ultimaTemperaturaLetta;
boolean continuaLetturaForza, continuaLetturaTemperatura;
boolean isReading = true;
String LOG_CODE = "NOTIFSRV";
GattClientCallback gattClientCallback;
private static final int PERMISSION_REQUEST_COARSE_LOCATION = 1;
public BlePowerService() {
}
#Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
//throw new UnsupportedOperationException("Not yet implemented");
return null;
}
#Override
public void onCreate() {
super.onCreate();
mHandler = new Handler();
Context sharedContext = null;
try {
sharedContext = this.createPackageContext(
"com.care.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;
}
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
try {
//RECUPERO LE IMPOSTAZIONI SETTATE DALL'UTENTE
settingApp = db.fetchSetting();
if (settingApp != null && settingApp.getAddressBleSX()!=null) {
//POSSO FILTRARE DIRETTAMENTE PER L'UUID DEL DISPOSITIVO MEMORIZZATO
//DALL'UTENTE
ScanFilter.Builder scanFilterMac =
null;
scanFilterMac = new ScanFilter.Builder().setDeviceAddress(settingApp.getAddressBleSX());
if(filters==null)
filters = new ArrayList<ScanFilter>();
//if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
filters.add(scanFilterMac.build());
//}
//FILTRO ANCHE PER LA CARATTERISTICA DI FORZA
ScanFilter filter = null;
filter = new ScanFilter.Builder().setServiceUuid(ParcelUuid.fromString(CHARACTERISTIC_FORZA_STRING)).build();
filters.add(filter);
final BluetoothManager bluetoothManager =
(BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
mBluetoothAdapter = bluetoothManager.getAdapter();
//mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
mLEScanner = mBluetoothAdapter.getBluetoothLeScanner();
settings = new ScanSettings.Builder()
.setScanMode(ScanSettings.SCAN_MODE_LOW_POWER)
.build();
scanLeDevice(true);
mTimer = new Timer();
//ogni 2 ore
continuaLetturaForza = true;
continuaLetturaTemperatura = true;
int nSecondi = settingApp.getFrequenzaDownload()!= null ? settingApp.getFrequenzaDownload() : 1;
mTimer.schedule(timerTask, 10000, 1000 * nSecondi);
}
} 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
int counter = 0;
while(continuaLetturaForza || continuaLetturaTemperatura){
counter++;
Log.v("CICLO WHILE", counter+"");
if (currDevice != null) {
if(ReadQueue!= null && ReadQueue.size()>0){
int index =0;
for(index=0; index < ReadQueue.size(); index++){
isReading = mGatt.readCharacteristic(ReadQueue.get(index));
while(isReading){
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}else{
//provo a ricollegarmi al dispositivo probabile, abbia perso la connessione con esso
scanLeDevice(true);
try {
Thread.sleep(2500);
} catch (InterruptedException e) {
e.printStackTrace();
}
if(currDevice == null && counter == 25){
//in questo caso non è riuscito a trovare il dispositivo BLE
//esco dal ciclo
continuaLetturaForza = false;
continuaLetturaTemperatura = false;
continue;
}
}
}
if(gattClientCallback!=null && mGatt != null)
gattClientCallback.disconnectGattServer();
}
};
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 = 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
h.post(new Runnable() {
#Override
public void run() {
// Log.i("onLeScan", device.toString());
connectToDevice(device);
}
});
}
};
private void scanLeDevice(final boolean enable) {
if (enable) {
mHandler.postDelayed(new Runnable() {
#Override
public void run() {
if (Build.VERSION.SDK_INT < 21) {
mBluetoothAdapter.stopLeScan(mLeScanCallback);
} else {
mLEScanner.stopScan(mScanCallback);
}
}
}, SCAN_PERIOD);
if (Build.VERSION.SDK_INT < 21) {
mBluetoothAdapter.startLeScan(mLeScanCallback);
} else {
mLEScanner.startScan(filters, settings, mScanCallback);
}
} else {
if (Build.VERSION.SDK_INT < 21) {
mBluetoothAdapter.stopLeScan(mLeScanCallback);
} else {
mLEScanner.stopScan(mScanCallback);
}
}
}
private ScanCallback mScanCallback = new ScanCallback() {
#Override
public void onScanResult(int callbackType, ScanResult result) {
// Log.i("callbackType", String.valueOf(callbackType));
// Log.i("result", result.toString());
BluetoothDevice btDevice = null;
btDevice = result.getDevice();
connectToDevice(btDevice);
}
#Override
public void onBatchScanResults(List<ScanResult> results) {
for (ScanResult sr : results) {
Log.i("ScanResult - Results", sr.toString());
}
}
#Override
public void onScanFailed(int errorCode) {
Log.e("Scan Failed", "Error Code: " + errorCode);
}
};
private class GattClientCallback extends BluetoothGattCallback {
#Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
try{
super.onConnectionStateChange(gatt, status, newState);
Log.i("tag", "onConnectionStateChange newState: " + newState);
if (status == BluetoothGatt.GATT_FAILURE) {
Log.e("ERROR_SERVICE", "Connection Gatt failure status " + status);
if(mGatt == null){
}else{
try {
gatt.close();
} catch (Exception e) {
Log.d("", "close ignoring: " + e);
}
mGatt = null;
}
return;
} else if (status != BluetoothGatt.GATT_SUCCESS) {
// handle anything not SUCCESS as failure
Log.e("ERROR_SERVICE", "Connection not GATT sucess status " + status);
if(mGatt == null){
}else{
try {
gatt.close();
} catch (Exception e) {
Log.d("", "close ignoring: " + e);
}
mGatt = null;
}
return;
}
if (newState == BluetoothProfile.STATE_CONNECTED) {
//Log.i("INFO", "Connected to device " + gatt.getDevice().getAddress());
gatt.discoverServices();
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
Log.i("INFO", "Disconnected from device");
if(mGatt == null){
}else{
try {
gatt.close();
} catch (Exception e) {
Log.d("", "close ignoring: " + e);
}
mGatt = null;
}
}
}catch(Exception e){
Log.e("tag", e.getMessage());
continuaLetturaForza = true;
continuaLetturaTemperatura = true;
}
}
public void disconnectGattServer() {
//Log.i("INFo", "Closing Gatt connection");
// clearLogs();
//mConnected = false;
//mEchoInitialized = false;
continuaLetturaForza = false;
continuaLetturaTemperatura = false;
if (mGatt == null) {
}
else{
try{
mGatt.disconnect();
// mGatt.close();
}catch(Exception e){
mGatt = null;
}
}
//mGatt = null;
//currDevice = null;
}
#Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
super.onServicesDiscovered(gatt, status);
if (status != BluetoothGatt.GATT_SUCCESS) {
// Log.i("INFO", "Device service discovery unsuccessful, status " + status);
return;
}
List<BluetoothGattCharacteristic> matchingCharacteristics =
BluetoothUtils.findCharacteristics(gatt,stringSequence);
if (matchingCharacteristics.isEmpty()) {
// Log.e("ERROR_SERVICE","Unable to find characteristics.");
return;
}else {
//INIZIALIZZO LA LISTA DI CARATTERISTICHE CHE DEVO ANDARE A LEGGERE
ReadQueue = new ArrayList<>();
for (BluetoothGattCharacteristic characterist : matchingCharacteristics) {
ReadQueue.add(characterist);
}
}
}
#Override
public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
super.onCharacteristicRead(gatt, characteristic, status);
if (status == BluetoothGatt.GATT_SUCCESS) {
Log.v("INSERT_DB", "*****************************************");
Log.v("INSERT_DB", "*****************************************");
Log.v("INSERT_DB", "*****************************************");
Log.v("INSERT_DB", "INSERISCO LE CARATTERISTICHE LETTE SUL DB");
//readCharacteristic(characteristic);
String valoreRestituito = characteristic.getStringValue(0);
if(valoreRestituito==null){
int i = 0;
i = i + 1;
return;
}
//SPLITTO PER IL CARATTERE ;
String[] arrayValori = valoreRestituito.split(SPLIT_CHAR);
//1 valore è l ID
try{
int id = new Integer(arrayValori[0]);
//VERIFICO SE STO LEGGENDO TEMPERATURA O FORZA
if(characteristic.getUuid().equals(CHARACTERISTIC_FORZA_UUID)){
Log.v("CARATTERISTICA", "*****************************************");
Log.v("FORZA", "*****************************************");
if(ultimaForzaLetta!= null && ultimaForzaLetta == id){
continuaLetturaForza = false;
isReading = false;
return;
}
ultimaForzaLetta = id;
}else if(characteristic.getUuid().equals( CHARACTERISTIC_TEMPERATURA_UUID)){
Log.v("CARATTERISTICA", "*****************************************");
Log.v("TEMPERATURA", "*****************************************");
if(ultimaTemperaturaLetta!= null && ultimaTemperaturaLetta == id){
continuaLetturaTemperatura = false;
isReading = false;
return;
}
ultimaTemperaturaLetta = id;
}
SensorData mSenData = new SensorData();
mSenData.setIdType(id);
mSenData.setValue1(arrayValori[1]);
mSenData.setValue2(arrayValori[2]);
mSenData.setValue3(arrayValori[3]);
mSenData.setValue4(arrayValori[4]);
mSenData.setValue5(arrayValori[5]);
mSenData.setValue6(arrayValori[6]);
mSenData.setValue7(arrayValori[7]);
mSenData.setValue8(arrayValori[8]);
//TO-DO HO COMMENTATO QUESTA RIGA DI CODICE
// mSenData.setIdType(st.getId());
mSenData.setCharacteristic(characteristic.getUuid().toString());
mSenData.setValueTimestamp(db.getDateTime(true));
//inserisco i dati nel db
Log.v("INSERT_DB",valoreRestituito);
Log.v("INSERT_DB", "*****************************************");
Log.v("INSERT_DB", "*****************************************");
Log.v("INSERT_DB", "*****************************************");
Log.v("INSERT_DB", "*****************************************");
Log.v("INSERT_DB", "*****************************************");
Log.v("INSERT_DB", "*****************************************");
Log.v("INSERT_DB", "FINE LE CARATTERISTICHE LETTE SUL DB");
db.insertSensorData(mSenData);
EventBus.getDefault().post(new MessageEvent("update"));
//EventBus.getDefault().post(new UnfriendEvent(userId));
}catch (Exception e){
Log.e("ERROR", e.getMessage());
}
isReading = false;
} else {
isReading = false;
Log.e("ERROR_SERVICE", "Characteristic read unsuccessful, status: " + status);
Log.e("ERROR_SERVICE", "Characteristic read unsuccessful, status: " + status);
Log.e("ERROR_SERVICE", "Characteristic read unsuccessful, status: " + status);
Log.e("ERROR_SERVICE", "Characteristic read unsuccessful, status: " + status);
Log.e("ERROR_SERVICE", "Characteristic read unsuccessful, status: " + status);
Log.e("ERROR_SERVICE", "Characteristic read unsuccessful, status: " + status);
Log.e("ERROR_SERVICE", "Characteristic read unsuccessful, status: " + status);
Log.e("ERROR_SERVICE", "Characteristic read unsuccessful, status: " + status);
Log.e("ERROR_SERVICE", "Characteristic read unsuccessful, status: " + status);
// Trying to read from the Time Characteristic? It doesnt have the property or permissions
// set to allow this. Normally this would be an error and you would want to:
disconnectGattServer();
return;
}
}
}
public void onDestroy() {
try {
db.close();
Log.d(LOG_CODE,"DB connection closed" );
mTimer.cancel();
timerTask.cancel();
} catch (Exception e) {
e.printStackTrace();
}
}
}
In the onStartCommand method I create a Timer that run the service every X time.
The "cycle while" can download all information of BLE device, throught onCharacteristicRead method.
Now the question is, if unit I can to read a characteristic, I turn off the BLE device, I can rileve this status change connection by onConnectionStateChange method, but the Timer it is never performed again.
Why the Timer is not execute again?
I'm building a service that every times downlaod all the information of a BLE device and storage this data into a local database.
Now I'm building a service that do this jobs. If I try to start my application in debug from Android Studio it works never error. I'm try to do in background my application for 3 hours about, the service save all the information and the service is not locked.
Now, I'm building an APK file, I'm try to install it into the same Android device, but the service run only one times, after that it is not execute.
I'm using for test Samsung J3 with Android 7.0
What is my problem ?
This is my BlePowerService.java:
public class BlePowerService extends Service {
public DbLayer db;
Setting settingApp;
List<ScanFilter> filters;
String[] stringSequence = new String[] {CHARACTERISTIC_FORZA_STRING, CHARACTERISTIC_TEMPERATURA_STRING};
BluetoothAdapter mBluetoothAdapter;
BluetoothGatt mGatt;
BluetoothDevice currDevice;
static final long SCAN_PERIOD = 500;
static String SPLIT_CHAR =";";
BluetoothLeScanner mLEScanner;
Handler mHandler;
int ReadQueueIndex;
List<BluetoothGattCharacteristic> ReadQueue;
BluetoothGattCharacteristic caratteristicaDaLeggere;
ScanSettings settings;
Integer ultimaForzaLetta,ultimaTemperaturaLetta;
boolean continuaLetturaForza, continuaLetturaTemperatura;
boolean isReading = true;
String LOG_CODE = "NOTIFSRV";
GattClientCallback gattClientCallback;
private static final int PERMISSION_REQUEST_COARSE_LOCATION = 1;
public BlePowerService() {
}
#Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
//throw new UnsupportedOperationException("Not yet implemented");
return null;
}
#Override
public void onCreate() {
super.onCreate();
mHandler = new Handler();
Context sharedContext = null;
try {
sharedContext = this.createPackageContext(
"com.care.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;
}
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
try {
//RECUPERO LE IMPOSTAZIONI SETTATE DALL'UTENTE
settingApp = db.fetchSetting();
if (settingApp != null && settingApp.getAddressBleSX()!=null) {
//POSSO FILTRARE DIRETTAMENTE PER L'UUID DEL DISPOSITIVO MEMORIZZATO
//DALL'UTENTE
ScanFilter.Builder scanFilterMac =
null;
scanFilterMac = new ScanFilter.Builder().setDeviceAddress(settingApp.getAddressBleSX());
if(filters==null)
filters = new ArrayList<ScanFilter>();
//if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
filters.add(scanFilterMac.build());
//}
//FILTRO ANCHE PER LA CARATTERISTICA DI FORZA
ScanFilter filter = null;
filter = new ScanFilter.Builder().setServiceUuid(ParcelUuid.fromString(CHARACTERISTIC_FORZA_STRING)).build();
filters.add(filter);
final BluetoothManager bluetoothManager =
(BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
mBluetoothAdapter = bluetoothManager.getAdapter();
//mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
mLEScanner = mBluetoothAdapter.getBluetoothLeScanner();
settings = new ScanSettings.Builder()
.setScanMode(ScanSettings.SCAN_MODE_LOW_POWER)
.build();
scanLeDevice(true);
mTimer = new Timer();
//ogni 2 ore
continuaLetturaForza = true;
continuaLetturaTemperatura = true;
int nSecondi = settingApp.getFrequenzaDownload()!= null ? settingApp.getFrequenzaDownload() : 1;
//mTimer.schedule(timerTask, 10000, 1000 * nSecondi);
mTimer.scheduleAtFixedRate (timerTask, 10000, 1000 * nSecondi);
}
} 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
int counter = 0;
while(continuaLetturaForza || continuaLetturaTemperatura){
counter++;
//Log.v("CICLO WHILE", counter+"");
if (currDevice != null) {
if(ReadQueue!= null && ReadQueue.size()>0){
int index =0;
for(index=0; index < ReadQueue.size(); index++){
if(mGatt!=null){
isReading = mGatt.readCharacteristic(ReadQueue.get(index));
while(isReading){
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}else{
continuaLetturaForza = false;
continuaLetturaTemperatura = false;
}
}
}
}else{
//provo a ricollegarmi al dispositivo probabile, abbia perso la connessione con esso
scanLeDevice(true);
try {
Thread.sleep(2500);
} catch (InterruptedException e) {
e.printStackTrace();
}
if(currDevice == null && counter == 25){
//in questo caso non è riuscito a trovare il dispositivo BLE
//esco dal ciclo
continuaLetturaForza = false;
continuaLetturaTemperatura = false;
continue;
}
}
}
if(gattClientCallback!=null && mGatt != null)
gattClientCallback.disconnectGattServer();
}
public void stopTask() {
if (timerTask != null) {
Log.d("TIMER", "timer canceled");
timerTask.cancel();
}
}
};
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 = 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
h.post(new Runnable() {
#Override
public void run() {
// Log.i("onLeScan", device.toString());
connectToDevice(device);
}
});
}
};
private void scanLeDevice(final boolean enable) {
if (enable) {
mHandler.postDelayed(new Runnable() {
#Override
public void run() {
if (Build.VERSION.SDK_INT < 21) {
mBluetoothAdapter.stopLeScan(mLeScanCallback);
} else {
mLEScanner.stopScan(mScanCallback);
}
}
}, SCAN_PERIOD);
if (Build.VERSION.SDK_INT < 21) {
mBluetoothAdapter.startLeScan(mLeScanCallback);
} else {
mLEScanner.startScan(filters, settings, mScanCallback);
}
} else {
if (Build.VERSION.SDK_INT < 21) {
mBluetoothAdapter.stopLeScan(mLeScanCallback);
} else {
mLEScanner.stopScan(mScanCallback);
}
}
}
private ScanCallback mScanCallback = new ScanCallback() {
#Override
public void onScanResult(int callbackType, ScanResult result) {
// Log.i("callbackType", String.valueOf(callbackType));
// Log.i("result", result.toString());
BluetoothDevice btDevice = null;
btDevice = result.getDevice();
connectToDevice(btDevice);
}
#Override
public void onBatchScanResults(List<ScanResult> results) {
for (ScanResult sr : results) {
Log.i("ScanResult - Results", sr.toString());
}
}
#Override
public void onScanFailed(int errorCode) {
Log.e("Scan Failed", "Error Code: " + errorCode);
}
};
private class GattClientCallback extends BluetoothGattCallback {
#Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
try{
super.onConnectionStateChange(gatt, status, newState);
//Log.i("tag", "onConnectionStateChange newState: " + newState);
if (status == BluetoothGatt.GATT_FAILURE) {
Log.e("ERROR_SERVICE", "Connection Gatt failure status " + status);
if(mGatt == null){
}else{
try {
gatt.close();
} catch (Exception e) {
Log.d("", "close ignoring: " + e);
}
mGatt = null;
}
return;
} else if (status != BluetoothGatt.GATT_SUCCESS) {
// handle anything not SUCCESS as failure
Log.e("ERROR_SERVICE", "Connection not GATT sucess status " + status);
if(mGatt == null){
}else{
try {
gatt.close();
} catch (Exception e) {
Log.d("", "close ignoring: " + e);
}
mGatt = null;
}
return;
}
if (newState == BluetoothProfile.STATE_CONNECTED) {
//Log.i("INFO", "Connected to device " + gatt.getDevice().getAddress());
gatt.discoverServices();
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
Log.i("INFO", "Disconnected from device");
if(mGatt == null){
}else{
try {
mGatt.close();
mGatt = null;
currDevice = null;
} catch (Exception e) {
Log.d("", "close ignoring: " + e);
}
mGatt = null;
}
}
}catch(Exception e){
Log.e("tag", e.getMessage());
continuaLetturaForza = true;
continuaLetturaTemperatura = true;
}
}
public void disconnectGattServer() {
//Log.i("INFo", "Closing Gatt connection");
// clearLogs();
//mConnected = false;
//mEchoInitialized = false;
continuaLetturaForza = true;
continuaLetturaTemperatura = true;
isReading = false;
if (mGatt == null) {
}
else{
try{
mGatt.disconnect();
// mGatt.close();
}catch(Exception e){
mGatt = null;
}
}
//mGatt = null;
//currDevice = null;
}
#Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
super.onServicesDiscovered(gatt, status);
if (status != BluetoothGatt.GATT_SUCCESS) {
// Log.i("INFO", "Device service discovery unsuccessful, status " + status);
return;
}
List<BluetoothGattCharacteristic> matchingCharacteristics =
BluetoothUtils.findCharacteristics(gatt,stringSequence);
if (matchingCharacteristics.isEmpty()) {
// Log.e("ERROR_SERVICE","Unable to find characteristics.");
return;
}else {
//INIZIALIZZO LA LISTA DI CARATTERISTICHE CHE DEVO ANDARE A LEGGERE
ReadQueue = new ArrayList<>();
for (BluetoothGattCharacteristic characterist : matchingCharacteristics) {
ReadQueue.add(characterist);
}
//ReadQueueIndex = 0;
//ReadCharacteristics(ReadQueueIndex);
}
}
#Override
public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
super.onCharacteristicRead(gatt, characteristic, status);
if (status == BluetoothGatt.GATT_SUCCESS) {
String valoreRestituito = characteristic.getStringValue(0);
if(valoreRestituito==null){
int i = 0;
i = i + 1;
return;
}
//SPLITTO PER IL CARATTERE ;
String[] arrayValori = valoreRestituito.split(SPLIT_CHAR);
//1 valore è l ID
try{
int id = new Integer(arrayValori[0]);
//VERIFICO SE STO LEGGENDO TEMPERATURA O FORZA
if(characteristic.getUuid().equals(CHARACTERISTIC_FORZA_UUID)){
/*Log.v("CARATTERISTICA", "*****************************************");
Log.v("FORZA", "*****************************************");*/
if(ultimaForzaLetta!= null && ultimaForzaLetta == id){
continuaLetturaForza = false;
isReading = false;
return;
}
ultimaForzaLetta = id;
}else if(characteristic.getUuid().equals( CHARACTERISTIC_TEMPERATURA_UUID)){
/*Log.v("CARATTERISTICA", "*****************************************");
Log.v("TEMPERATURA", "*****************************************");*/
if(ultimaTemperaturaLetta!= null && ultimaTemperaturaLetta == id){
continuaLetturaTemperatura = false;
isReading = false;
return;
}
ultimaTemperaturaLetta = id;
}
SensorData mSenData = new SensorData();
mSenData.setIdType(id);
mSenData.setValue1(arrayValori[1]);
mSenData.setValue2(arrayValori[2]);
mSenData.setValue3(arrayValori[3]);
mSenData.setValue4(arrayValori[4]);
mSenData.setValue5(arrayValori[5]);
mSenData.setValue6(arrayValori[6]);
mSenData.setValue7(arrayValori[7]);
mSenData.setValue8(arrayValori[8]);
//TO-DO HO COMMENTATO QUESTA RIGA DI CODICE
// mSenData.setIdType(st.getId());
mSenData.setCharacteristic(characteristic.getUuid().toString());
mSenData.setValueTimestamp(db.getDateTime(true));
//inserisco i dati nel db
db.insertSensorData(mSenData);
EventBus.getDefault().post(new MessageEvent("update"));
//EventBus.getDefault().post(new UnfriendEvent(userId));
}catch (Exception e){
Log.e("ERROR", e.getMessage());
}
isReading = false;
} else {
isReading = false;
Log.e("ERROR_SERVICE", "Characteristic read unsuccessful, status: " + status);
disconnectGattServer();
return;
}
}
}
public void onDestroy() {
try {
db.close();
Log.d(LOG_CODE,"DB connection closed" );
mTimer.cancel();
timerTask.cancel();
} catch (Exception e) {
e.printStackTrace();
}
}
}
Use Job Scheduler instead of service. Modern apps can perform many of their tasks asynchronously, outside the direct flow of user interaction.
If your app targets Android 5.0 (API level 21), we recommend that you use the JobScheduler to execute background tasks..
For further details check android documentation below
https://developer.android.com/topic/performance/scheduling
I'm building an app that should to connect a BLE device and download all the information that this device has storage in your flash memory.
So I'm building this code:
This is the Timer that I use to download ALL the information every X minutes:
TimerTask timerTask = new TimerTask() {
#RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
#Override
public void run() {
valore = true;
int conta = 0;
while(valore){
conta++;
if (currDevice != null) {
mGatt = currDevice.connectGatt(getBaseContext(), false, gattClientCallback);
}else{
//provo a ricollegarmi al dispositivo probabile, abbia perso la connessione con esso
scanLeDevice(true);
}
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
This is the method that I use to connect at BLE device:
#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 = new GattClientCallback();
mGatt = currDevice.connectGatt(getBaseContext(), false, gattClientCallback);
scanLeDevice(false);// will stop after first device detection
}
}
This is the LeScanCallback():
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
h.post(new Runnable() {
#Override
public void run() {
// Log.i("onLeScan", device.toString());
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
connectToDevice(device);
}
}
});
}
};
This the last code to effectively download data from BLE device and storage it on SqlLite
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
h.post(new Runnable() {
#Override
public void run() {
// Log.i("onLeScan", device.toString());
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
connectToDevice(device);
}
}
});
}
};
#RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
private void scanLeDevice(final boolean enable) {
if (enable) {
mHandler.postDelayed(new Runnable() {
#Override
public void run() {
if (Build.VERSION.SDK_INT < 21) {
mBluetoothAdapter.stopLeScan(mLeScanCallback);
} else {
mLEScanner.stopScan(mScanCallback);
}
}
}, SCAN_PERIOD);
if (Build.VERSION.SDK_INT < 21) {
mBluetoothAdapter.startLeScan(mLeScanCallback);
} else {
mLEScanner.startScan(filters, settings, mScanCallback);
}
} else {
if (Build.VERSION.SDK_INT < 21) {
mBluetoothAdapter.stopLeScan(mLeScanCallback);
} else {
mLEScanner.stopScan(mScanCallback);
}
}
}
private ScanCallback mScanCallback = new ScanCallback() {
#RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
#Override
public void onScanResult(int callbackType, ScanResult result) {
// Log.i("callbackType", String.valueOf(callbackType));
// Log.i("result", result.toString());
BluetoothDevice btDevice = null;
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
btDevice = result.getDevice();
}
connectToDevice(btDevice);
}
#Override
public void onBatchScanResults(List<ScanResult> results) {
for (ScanResult sr : results) {
// Log.i("ScanResult - Results", sr.toString());
}
}
#Override
public void onScanFailed(int errorCode) {
// Log.e("Scan Failed", "Error Code: " + errorCode);
}
};
#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{
super.onConnectionStateChange(gatt, status, newState);
Log.i("tag", "onConnectionStateChange newState: " + newState);
if (status == BluetoothGatt.GATT_FAILURE) {
Log.e("ERROR_SERVICE", "Connection Gatt failure status " + status);
disconnectGattServer();
return;
} else if (status != BluetoothGatt.GATT_SUCCESS) {
// handle anything not SUCCESS as failure
Log.e("ERROR_SERVICE", "Connection not GATT sucess status " + status);
disconnectGattServer();
return;
}
if (newState == BluetoothProfile.STATE_CONNECTED) {
//Log.i("INFO", "Connected to device " + gatt.getDevice().getAddress());
gatt.discoverServices();
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
Log.i("INFO", "Disconnected from device");
disconnectGattServer();
}
}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);
if (status != BluetoothGatt.GATT_SUCCESS) {
// Log.i("INFO", "Device service discovery unsuccessful, status " + status);
return;
}
List<BluetoothGattCharacteristic> matchingCharacteristics =
BluetoothUtils.findCharacteristics(gatt,stringSequence);
if (matchingCharacteristics.isEmpty()) {
// Log.e("ERROR_SERVICE","Unable to find characteristics.");
return;
}else{
gatt.readCharacteristic(matchingCharacteristics.get(0));
}
}
#Override
public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
super.onCharacteristicRead(gatt, characteristic, status);
if (status == BluetoothGatt.GATT_SUCCESS) {
SensorData mSenData = new SensorData();
mSenData.setValue(characteristic.getStringValue(0));
mSenData.setIdType(++id);
mSenData.setCharacteristic(characteristic.getUuid().toString());
mSenData.setValueTimestamp(db.getDateTime());
db.insertSensorData(mSenData);
} else {
Log.e("ERROR_SERVICE", "Characteristic read unsuccessful, status: " + status);
}
}
Now, if I try to start my application, I can download some data from BLE device as 35, 36 values. After that I received this message from onConnectionStateChange method
E/ERROR_SERVICE: Connection not GATT sucess status 128
So after this error, the timer stopped. After X minutes the method restart but I'm not able to download never error from BLE device.
So how can I download all data from BLE device every X minutes and until the BLE device have data storage in yuor memory ?
I'm building an app that should to connect with a BLE device.
This device, storage the information that retreive from its sensors in a queque.
It is possibile that at the time X, the queque of this device have 100 items value.
Now my Android application must to connect of it, and download the data of the characteristic untile the queque of devis is empty.
This is the code that I use to read one value at time. How can I change it to get all data ?
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_ble);
mHandler = new Handler();
//mSensors = db.getAllSensorTypes();
//BLUETOOTH LOW ENERGY NON SUPPORTATO
if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
Toast.makeText(this, "BLE Not Supported",
Toast.LENGTH_SHORT).show();
finish();
}
final BluetoothManager bluetoothManager =
(BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
mBluetoothAdapter = bluetoothManager.getAdapter();
//VERIFICO SE IL BLUETOOTH DEL DISPOSITIVO E' ABILITATO
//OPPURE NO. SE NON è ABILITATO DEVO CHIEDERE ALL'UTENTE DI ATTIVARLO
// Ensures Bluetooth is available on the device and it is enabled. If not,
if (mBluetoothAdapter == null || !mBluetoothAdapter.isEnabled()) {
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
}
tvBLE = (TextView) findViewById(R.id.tvBLE);
ibDownload = (ImageButton) findViewById(R.id.ibDownload);
//ibDownload.setEnabled(false);
ibDownload.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if(currDevice!=null){
GattClientCallback gattClientCallback = new GattClientCallback();
mGatt = currDevice.connectGatt(getBaseContext(), false, gattClientCallback);
scanLeDevice(false);// will stop after first device detection
}
refreshListView();
}
});
// Bluetooth is supported?
if (mBluetoothAdapter == null) {
Toast.makeText(this, "Bluetooth non supportato", Toast.LENGTH_SHORT).show();
finish();
}
}
private void scanLeDevice(final boolean enable) {
if (enable) {
mHandler.postDelayed(new Runnable() {
#Override
public void run() {
if (Build.VERSION.SDK_INT < 21) {
mBluetoothAdapter.stopLeScan(mLeScanCallback);
} else {
mLEScanner.stopScan(mScanCallback);
}
}
}, SCAN_PERIOD);
if (Build.VERSION.SDK_INT < 21) {
mBluetoothAdapter.startLeScan(mLeScanCallback);
} else {
mLEScanner.startScan(filters, settings, mScanCallback);
}
} else {
if (Build.VERSION.SDK_INT < 21) {
mBluetoothAdapter.stopLeScan(mLeScanCallback);
} else {
mLEScanner.stopScan(mScanCallback);
}
}
}
private ScanCallback mScanCallback = new ScanCallback() {
#Override
public void onScanResult(int callbackType, ScanResult result) {
Log.i("callbackType", String.valueOf(callbackType));
Log.i("result", result.toString());
BluetoothDevice btDevice = result.getDevice();
connectToDevice(btDevice);
}
#Override
public void onBatchScanResults(List<ScanResult> results) {
for (ScanResult sr : results) {
Log.i("ScanResult - Results", sr.toString());
}
}
#Override
public void onScanFailed(int errorCode) {
Log.e("Scan Failed", "Error Code: " + errorCode);
}
};
private BluetoothAdapter.LeScanCallback mLeScanCallback =
new BluetoothAdapter.LeScanCallback() {
#Override
public void onLeScan(final BluetoothDevice device, int rssi,
byte[] scanRecord) {
runOnUiThread(new Runnable() {
#Override
public void run() {
Log.i("onLeScan", device.toString());
connectToDevice(device);
}
});
}
};
public void connectToDevice(BluetoothDevice device) {
String nomeDispositivo = device.getName();
if (mGatt == null && device.getName()!= null && device.getName().equals("Diabesity care")) {
currDevice = device;
ibDownload.setEnabled(true);
ibDownload.setImageResource(R.drawable.download_ok);
//mGatt = device.connectGatt(this, false, gattCallback);
//scanLeDevice(false);// will stop after first device detection
}
}
private class GattClientCallback extends BluetoothGattCallback {
#Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
super.onConnectionStateChange(gatt, status, newState);
Log.i("tag","onConnectionStateChange newState: " + newState);
if (status == BluetoothGatt.GATT_FAILURE) {
logError("Connection Gatt failure status " + status);
disconnectGattServer();
return;
} else if (status != BluetoothGatt.GATT_SUCCESS) {
// handle anything not SUCCESS as failure
logError("Connection not GATT sucess status " + status);
disconnectGattServer();
return;
}
if (newState == BluetoothProfile.STATE_CONNECTED) {
Log.i("INFO","Connected to device " + gatt.getDevice().getAddress());
setConnected(true);
gatt.discoverServices();
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
Log.i("INFO","Disconnected from device");
disconnectGattServer();
}
}
#Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
super.onServicesDiscovered(gatt, status);
if (status != BluetoothGatt.GATT_SUCCESS) {
Log.i("INFO","Device service discovery unsuccessful, status " + status);
return;
}
List<BluetoothGattCharacteristic> matchingCharacteristics = BluetoothUtils.findCharacteristics(gatt);
if (matchingCharacteristics.isEmpty()) {
logError("Unable to find characteristics.");
return;
}
ReadQueue = new ArrayList<>();
for (BluetoothGattCharacteristic characterist: matchingCharacteristics) {
ReadQueue.add(characterist);
}
ReadQueueIndex = 1;
ReadCharacteristics(ReadQueueIndex);
}
private void ReadCharacteristics(int index){
mGatt.readCharacteristic(ReadQueue.get(index));
}
#Override
public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
super.onCharacteristicRead(gatt, characteristic, status);
if (status == BluetoothGatt.GATT_SUCCESS) {
Log.i("INFO","Characteristic read successfully");
//readCharacteristic(characteristic);
SensorData mSenData = new SensorData();
mSenData.setValue(characteristic.getStringValue(0));
//TO-DO HO COMMENTATO QUESTA RIGA DI CODICE
// mSenData.setIdType(st.getId());
mSenData.setIdType(++id);
mSenData.setCharacteristic(characteristic.getUuid().toString());
mSenData.setValueTimestamp(db.getDateTime());
//inserisco i dati nel db
db.insertSensorData(mSenData);
ReadQueue.remove(ReadQueue.get(ReadQueueIndex));
if (ReadQueue.size() >= 0) {
ReadQueueIndex--;
if (ReadQueueIndex == -1) {
Log.i("Read Queue: ", "Complete");
}
else {
ReadCharacteristics(ReadQueueIndex);
}
}
//refreshListView();
} else {
logError("Characteristic read unsuccessful, status: " + status);
// Trying to read from the Time Characteristic? It doesnt have the property or permissions
// set to allow this. Normally this would be an error and you would want to:
// disconnectGattServer();
}
}
}
}
I currently have a method which writes to the BLE devices to beep it. My Bluetooth Callback goes as follows :
ReadCharacteristic rc = new ReadCharacteristic(context, ds.getMacAddress(), serviceUUID, UUID.fromString(myUUID), "") {
#Override
public void onRead() {
Log.w(TAG, "callDevice onRead");
try{Thread.sleep(1000);}catch(InterruptedException ex){}
WriteCharacteristic wc = new WriteCharacteristic(activity, context, getMacAddress(), serviceUUID, UUID.fromString(myUUID), ""){
#Override
public void onWrite(){
Log.w(TAG, "callDevice onWrite");
}
#Override
public void onError(){
Log.w(TAG, "callDevice onWrite-onError");
}
};
// Store data in writeBuffer
wc.writeCharacteristic(writeBuffer);
}
#Override
public void onError(){
Log.w(TAG, "callDevice onRead-onError");
}
};
rc.readCharacteristic();
My ReadCharacteristic implementation is as follows :
public class ReadCharacteristic extends BluetoothGattCallback {
public ReadCharacteristic(Context context, String macAddress, UUID service, UUID characteristic, Object tag) {
mMacAddress = macAddress;
mService = service;
mCharacteristic = characteristic;
mTag = tag;
mContext = context;
this.activity =activity;
final BluetoothManager bluetoothManager =
(BluetoothManager) mContext.getSystemService(Context.BLUETOOTH_SERVICE);
mBluetoothAdapter = bluetoothManager.getAdapter();
}
final private static String TAG = "ReadCharacteristic";
private Object mTag;
private String mMacAddress;
private UUID mService;
private UUID mCharacteristic;
private byte[] mValue;
private Activity activity;
private BluetoothAdapter mBluetoothAdapter;
private Context mContext;
private int retry = 5;
public String getMacAddress() {
return mMacAddress;
}
public UUID getService() {
return mService;
}
public UUID getCharacteristic() {
return mCharacteristic;
}
public byte[] getValue() { return mValue; }
public void onRead() {
Log.w(TAG, "onRead: " + getDataHex(getValue()));
}
public void onError() {
Log.w(TAG, "onError");
}
public void readCharacteristic(){
if (retry == 0)
{
onError();
return;
}
retry--;
final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(getMacAddress());
if (device != null) {
Log.w(TAG, "Starting Read [" + getService() + "|" + getCharacteristic() + "]");
final ReadCharacteristic rc = ReadCharacteristic.this;
device.connectGatt(mContext, false, rc);
}
}
#Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
Log.w(TAG,"onConnectionStateChange [" + status + "|" + newState + "]");
if ((newState == 2)&&(status ==0)) {
gatt.discoverServices();
}
else{
Log.w(TAG, "[" + status + "]");
// gatt.disconnect();
gatt.close();
try
{
Thread.sleep(2000);
}
catch(Exception e)
{
}
readCharacteristic();
}
}
#Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
Log.w(TAG,"onServicesDiscovered [" + status + "]");
BluetoothGattService bgs = gatt.getService(getService());
if (bgs != null) {
BluetoothGattCharacteristic bgc = bgs.getCharacteristic(getCharacteristic());
gatt.readCharacteristic(bgc);
}
}
#Override
public void onCharacteristicRead(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic,
int status) {
Log.w(TAG,"onCharacteristicRead [" + status + "]");
if (status == BluetoothGatt.GATT_SUCCESS) {
mValue = characteristic.getValue();
Log.w(TAG,"onCharacteristicRead [" + mValue + "]");
gatt.disconnect();
gatt.close();
onRead();
}
else {
gatt.disconnect();
gatt.close();
}
}
}
This current method works perfectly fine for devices running KitKat and below. But when I run the same function on Lollipop, it beeps the device a couple of times and then stops working. From then on wards, whenever I try to connect, it says the device is disconnected and gives me an error code of 257 in OnConnectionStateChanged method.
I also get this error whenever I call this method -
04-20 14:14:23.503 12329-12384/com.webble.xy W/BluetoothGatt﹕ Unhandled exception in callback
java.lang.NullPointerException: Attempt to invoke virtual method 'void android.bluetooth.BluetoothGattCallback.onConnectionStateChange(android.bluetooth.BluetoothGatt, int, int)' on a null object reference
at android.bluetooth.BluetoothGatt$1.onClientConnectionState(BluetoothGatt.java:181)
at android.bluetooth.IBluetoothGattCallback$Stub.onTransact(IBluetoothGattCallback.java:70)
at android.os.Binder.execTransact(Binder.java:446)
IS there anyone who has faced the same problem? I never encountered the object to be null when ever I tried debugging.
The problem has been reported to Google as Issue 183108: NullPointerException in BluetoothGatt.java when disconnecting and closing.
A workaround is to call disconnect() when you want to close BLE connection - and then only call close() in the onConnectionStateChange callback:
public void shutdown() {
try {
mBluetoothGatt.disconnect();
} catch (Exception e) {
Log.d(TAG, "disconnect ignoring: " + e);
}
}
private final BluetoothGattCallback mGattCallback =
new BluetoothGattCallback() {
#Override
public void onConnectionStateChange(BluetoothGatt gatt,
int status, int newState) {
if (newState == BluetoothProfile.STATE_CONNECTED) {
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
try {
gatt.close();
} catch (Exception e) {
Log.d(TAG, "close ignoring: " + e);
}
}
}
Here is my full source code (a class doing normal scan, directed scan - and discovering services):
public class BleObject {
public static final String ACTION_BLUETOOTH_ENABLED = "action.bluetooth.enabled";
public static final String ACTION_BLUETOOTH_DISABLED = "action.bluetooth.disabled";
public static final String ACTION_DEVICE_FOUND = "action.device.found";
public static final String ACTION_DEVICE_BONDED = "action.device.bonded";
public static final String ACTION_DEVICE_CONNECTED = "action.device.connected";
public static final String ACTION_DEVICE_DISCONNECTED = "action.device.disconnected";
public static final String ACTION_POSITION_READ = "action.position.read";
public static final String EXTRA_BLUETOOTH_DEVICE = "extra.bluetooth.device";
public static final String EXTRA_BLUETOOTH_RSSI = "extra.bluetooth.rssi";
private Context mContext;
private IntentFilter mIntentFilter;
private LocalBroadcastManager mBroadcastManager;
private BluetoothAdapter mBluetoothAdapter;
private BluetoothGatt mBluetoothGatt;
private BluetoothLeScanner mScanner;
private ScanSettings mSettings;
private List<ScanFilter> mScanFilters;
private Handler mConnectHandler;
public BleObject(Context context) {
mContext = context;
if (!context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
Log.d(TAG, "BLE not supported");
return;
}
BluetoothManager bluetoothManager = (BluetoothManager) context.getSystemService(Context.BLUETOOTH_SERVICE);
mBluetoothAdapter = bluetoothManager.getAdapter();
if (mBluetoothAdapter == null) {
Log.d(TAG, "BLE not accessible");
return;
}
mIntentFilter = new IntentFilter();
mIntentFilter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
mIntentFilter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
mSettings = new ScanSettings.Builder().setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY).build();
mScanFilters = new ArrayList<ScanFilter>();
mConnectHandler = new Handler();
mBroadcastManager = LocalBroadcastManager.getInstance(context);
}
public boolean isEnabled() {
return (mBluetoothAdapter != null && mBluetoothAdapter.isEnabled());
}
private ScanCallback mScanCallback = new ScanCallback() {
#Override
public void onScanResult(int callbackType, ScanResult result) {
processResult(result);
}
#Override
public void onBatchScanResults(List<ScanResult> results) {
for (ScanResult result: results) {
processResult(result);
}
}
private void processResult(ScanResult result) {
if (result == null)
return;
BluetoothDevice device = result.getDevice();
if (device == null)
return;
Intent i = new Intent(Utils.ACTION_DEVICE_FOUND);
i.putExtra(Utils.EXTRA_BLUETOOTH_DEVICE, device);
i.putExtra(Utils.EXTRA_BLUETOOTH_RSSI, result.getRssi());
mBroadcastManager.sendBroadcast(i);
}
};
private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
#Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
if (newState == BluetoothProfile.STATE_CONNECTED) {
if (gatt == null)
return;
BluetoothDevice device = gatt.getDevice();
if (device == null)
return;
Log.d(TAG, "BluetoothProfile.STATE_CONNECTED: " + device);
Intent i = new Intent(Utils.ACTION_DEVICE_CONNECTED);
i.putExtra(Utils.EXTRA_BLUETOOTH_DEVICE, device);
mBroadcastManager.sendBroadcast(i);
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
Log.d(TAG, "BluetoothProfile.STATE_DISCONNECTED");
Intent i = new Intent(Utils.ACTION_DEVICE_DISCONNECTED);
mBroadcastManager.sendBroadcast(i);
// Issue 183108: https://code.google.com/p/android/issues/detail?id=183108
try {
gatt.close();
} catch (Exception e) {
Log.d(TAG, "close ignoring: " + e);
}
}
}
#Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
if (gatt == null)
return;
for (BluetoothGattService service: gatt.getServices()) {
Log.d(TAG, "service: " + service.getUuid());
for (BluetoothGattCharacteristic chr: service.getCharacteristics()) {
Log.d(TAG, "char: " + chr.getUuid());
}
}
}
};
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
if (BluetoothAdapter.ACTION_STATE_CHANGED.equals(action)) {
final int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR);
switch (state) {
case BluetoothAdapter.STATE_TURNING_OFF: {
Log.d(TAG, "BluetoothAdapter.STATE_TURNING_OFF");
break;
}
case BluetoothAdapter.STATE_OFF: {
Log.d(TAG, "BluetoothAdapter.STATE_OFF");
Intent i = new Intent(Utils.ACTION_BLUETOOTH_DISABLED);
mBroadcastManager.sendBroadcast(i);
break;
}
case BluetoothAdapter.STATE_TURNING_ON: {
Log.d(TAG, "BluetoothAdapter.STATE_TURNING_ON");
break;
}
case BluetoothAdapter.STATE_ON: {
Log.d(TAG, "BluetoothAdapter.STATE_ON");
Intent i = new Intent(Utils.ACTION_BLUETOOTH_ENABLED);
mBroadcastManager.sendBroadcast(i);
break;
}
}
} else if (BluetoothDevice.ACTION_BOND_STATE_CHANGED.equals(action)) {
final int state = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.ERROR);
final int prevState = intent.getIntExtra(BluetoothDevice.EXTRA_PREVIOUS_BOND_STATE, BluetoothDevice.ERROR);
if (state == BluetoothDevice.BOND_BONDED &&
prevState == BluetoothDevice.BOND_BONDING) {
if (mBluetoothGatt != null) {
BluetoothDevice device = mBluetoothGatt.getDevice();
if (device == null)
return;
Intent i = new Intent(Utils.ACTION_DEVICE_BONDED);
i.putExtra(Utils.EXTRA_BLUETOOTH_DEVICE, device);
mBroadcastManager.sendBroadcast(i);
}
}
}
}
};
// scan for all BLE devices nearby
public void startScanning() {
Log.d(TAG, "startScanning");
mScanFilters.clear();
// create the scanner here, rather than in init() -
// because otherwise app crashes when Bluetooth is switched on
mScanner = mBluetoothAdapter.getBluetoothLeScanner();
mScanner.startScan(mScanFilters, mSettings, mScanCallback);
}
// scan for a certain BLE device and after delay
public void startScanning(final String address) {
Log.d(TAG, "startScanning for " + address);
mScanFilters.clear();
mScanFilters.add(new ScanFilter.Builder().setDeviceAddress(address).build());
// create the scanner here, rather than in init() -
// because otherwise app crashes when Bluetooth is switched on
mScanner = mBluetoothAdapter.getBluetoothLeScanner();
mScanner.startScan(mScanFilters, mSettings, mScanCallback);
}
public void stopScanning() {
Log.d(TAG, "stopScanning");
if (mScanner != null) {
mScanner.stopScan(mScanCallback);
mScanner = null;
}
mScanFilters.clear();
}
public void connect(final BluetoothDevice device) {
Log.d(TAG, "connect: " + device.getAddress() + ", mBluetoothGatt: " + mBluetoothGatt);
mConnectHandler.post(new Runnable() {
#Override
public void run() {
setPin(device, Utils.PIN);
mBluetoothGatt = device.connectGatt(mContext, true, mGattCallback);
}
});
}
private void setPin(BluetoothDevice device, String pin) {
if (device == null || pin == null || pin.length() < 4)
return;
try {
device.setPin(pin.getBytes("UTF8"));
} catch (Exception e) {
Utils.logw("setPin ignoring: " + e);
}
}
// called on successful device connection and will toggle reading coordinates
public void discoverServices() {
if (mBluetoothGatt != null)
mBluetoothGatt.discoverServices();
}
public boolean isBonded(BluetoothDevice device) {
Set<BluetoothDevice> bondedDevices = mBluetoothAdapter.getBondedDevices();
if (bondedDevices == null || bondedDevices.size() == 0)
return false;
for (BluetoothDevice bondedDevice: bondedDevices) {
Log.d(TAG, "isBonded bondedDevice: " + bondedDevice);
if (bondedDevice.equals(device)) {
Log.d(TAG, "Found bonded device: " + device);
return true;
}
}
return false;
}
public void startup() {
try {
mContext.registerReceiver(mReceiver, mIntentFilter);
} catch (Exception e) {
Log.d(TAG, "registerReceiver ignoring: " + e);
}
}
public void shutdown() {
Log.d(TAG, "BleObject shutdown");
try {
mContext.unregisterReceiver(mReceiver);
} catch (Exception e) {
Log.d(TAG, "unregisterReceiver ignoring: " + e);
}
try {
stopScanning();
} catch (Exception e) {
Log.d(TAG, "stopScanning ignoring: " + e);
}
try {
mBluetoothGatt.disconnect();
} catch (Exception e) {
Log.d(TAG, "disconnect ignoring: " + e);
}
mConnectHandler.removeCallbacksAndMessages(null);
}
}
On each BLE event it broadcasts intents via LocalBroadcastManager.
Thanks for pointing the problem Shashank.
I have looked at the Google example and followed what they recommend, it works like a charm with my device.
You should call the close() function in your onUnbind and the disconnect() when you need to, for example when you quit your application.