Android: Update bluetooth rssi every second - android

I'm trying to display the bluetooth signal strength (rssi) evry second (Timer()) from detected device but i couldn't call onRecive() multiple times because Receiver Lifecycle.
I need a way(idea) to refresh the RSSI, or some other way to measure a signal every second?
Is it easier if the device is connected?
App gives a constant value always:
DeviceName 2b:3c:d5:6c:3x:0X 40 db
Part of the app stored in Timer() method:
BroadcastReceiver mReceiver = new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
// When discovery finds a device
if (BluetoothDevice.ACTION_FOUND.equals(action)) {
// Get the BluetoothDevice object from the Intent
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
short rssi = intent.getShortExtra(BluetoothDevice.EXTRA_RSSI,Short.MIN_VALUE);
// Add the name and address to an array adapter to show in a ListView
msg = device.getName() + " " + device.getAddress() + " " +rssi+ " db";
}
}};

You can only get the RSSI value during a device discovery scan. (Bluetooth Discovery vs Connection)
"Is it easier if the device is connected?" Accordingly with android docs:
"if you already hold a connection with a device, then performing discovery can significantly reduce the bandwidth available for the connection, so you should not perform discovery while connected."
Do you really need to perform an inquiry every 1 second? Will consume a lot of battery...
Since you need to perform the measure periodically, why not use AlarmManager or CountDownTimer?
In your specific case, I believe you should be using AlarmManager, since it can repeat indefinitely. If you want something to execute every second for 10 seconds, for example, use CountDownTimer.

Related

Can we get wifiscanresults repeatedly every second?

I am trying to scan all visible wifi APs using wifimanger class. My question is that my code is working good when the (wifiscan-results) called repeatedly every 5 sec using recursive (Runnable) service. But when the time is reduced to be 1 sec or less, such that I register (wifimanger.statrscan) every 1 sec to receive the (wifiscan-results) every 1 sec, the output give null and gives values only every 4 second at least. Could you tell me why this happens. My aim to scan all visible wifi APs every one second or less, repeatedly, Is this possible?
Runnable function code:
public void Running() {
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
tv1.setText("counter : " + time1);
time1++;
Scan_number++;
mwifiManager.startScan();
periodic_recieving_wifi_signals();
Running();
}
}, 1000);}
broadcastreciever code:
public void periodic_recieving_wifi_signals() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && checkSelfPermission(android.Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
requestPermissions(new String[]{Manifest.permission.ACCESS_COARSE_LOCATION}, PERMISSIONS_REQUEST_CODE_ACCESS_COARSE_LOCATION);
} else {
//flag1 = false;
registerReceiver(new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
//tv2.setText("Each scan period is : " + results);
results = mwifiManager.getScanResults();
size = results.size();// number of the elements in the list
}
}, new IntentFilter(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION));
}}
You almost certainly cannot get a WiFi scan every second ... the process simply takes too long.
I don't know all the technical details, and while some of the specific details may vary according to which version of WiFi you are using, looking at Wi-Fi / WLAN Channels, Frequencies, Bands & Bandwidths we can see that basic 2.4GHz 802.11 WiFi has 14 channels defined and 5GHz WiFi has 25 channels (not all channels are permitted in all locations).
To perform a scan, the radio in your phone has to tune to each channel in turn to see "what's out there". Seeing what (if anything) is on a particular channel will involve an exchange of messages, and will take a finite amount of time1 (it will have to wait "long enough" to tell the difference between a slow-to-respond device and "nothing there").
As you noted in a comment, if all of this is taking four seconds or so, you won't be able to perform a scan faster than about once every five seconds.
1 Thanks to John Hanley for supplying some numbers. By default, access points transmit their "I am here" beacons every 102.4ms. Thus some minimum scan-times are:
2.4GHz: 14 x 102.4ms = 1,433ms
5GHz: 25 x 102.4ms = 2,560ms
(Both figures may be slightly less in some locations, depending on the number or channels allowed to be used).
In practice, you would want to listen on each channel for longer than 102.4ms, otherwise you run the risk of not-quite-getting-a-beacon as you switch to a channel, and then switching from that channel just as (or before) the next beacon is sent. There may also be a small delay for the radio to stabilise to each new frequency.
This article: "SSID Overhead Calculator" from the Revolution WiFi website (as well as confirming the 102.4ms figure) also shows the dangers of having too many access-points (APs) and/or too many SSIDs... it doesn't take many of each before the time spent sending out these "beacon" frames takes a significant chunk out of your WiFi throughput. For example: 4 SSIDs on one AP (or 4 single-SSID APs on the same channel, or 2 double-SSID APs on the same channel) will each use about one-eighth of that channel's airtime just with "I am here" beacons!

How to discover devices within a certain distance

As explained in the following thread:
How to find the devices in the range by using bluetooth?
An android app can get a list of devices in the range (bluetooth enabled)
My question is that if there is any way to limit the discovery range to a certain radius e.g. 1 meter?
Thanks,
Ben
You could use BluetoothDevice.EXTRA_RSSI to get the signal strength.
Nevertheless, it won't be very accurate because it's variable.
private final BroadcastReceiver receiver = new BroadcastReceiver(){
#Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if(BluetoothDevice.ACTION_FOUND.equals(action)) {
short rssi = intent.getShortExtra(BluetoothDevice.EXTRA_RSSI, Short.MIN_VALUE);
System.out.println("RSSI: " + rssi + "dBm");
}
}
};
The value you obtain is in dBm. A really close device would have a rssi between around -20 dBm and -40 dBm depending of the device (built-in Bluetooth device, antenna, actual orientation of device). You can test the values you get to define an average 'dBm-range' for 1 meter. The closer the value is to 0, the stronger the received signal has been.

changing bluetooth low energy gatt timeout or flushing read stream to detect disconnect event quicker

I am looking for an android way to flush the characteristics the app receives from a Ble device, or at least know from the data that the connection has been lost as soon as it actually is except around 15 seconds after it disconnected. If there is a way to change the gatt connection timeout, that would be significantly better.
To repeat in a different form, I would like a solution (or a link that can explain) to detect a disconnect of the BLE device faster than whatever the timeout value currently is, by a means of seeing if the value I am getting is fresh by flushing the characteristic, or changing the disconnect timeout on the gatt side, so I can see within a second of it disconnecting to trigger other code.
Other answers in here may be better than this one, but this was my solution to the problem. Be sure to attempt Emil's answer before using this.
What I did since the time was too slow to wait for it was to check the rssi since it always is changing. If there is a period of time, lets say 3 seconds, where the value stays the same, it disconnects from the device. This goes around the 15 second timeout and adds our own timeout.
This would be what would be needed to check signal strength. This was written a couple of years ago, so some things may need to be changed.
private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
#Override
public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status){
//check for signal strength changes. It will stay the same if we
//are getting no updates
if(mLastRssi == rssi){
disconnectCounter++;
if(disconnectCounter> 140) {
//disconnect logic as if we are done with connection
//maybe start listening for device again to reconnect
disconnectCounter = 0;
}
}
else{
//we are connected. reset counter
disconnectCounter = 0;
}
//store last value as global for comparison
mLastRssi= rssi;
}
}
Somewhere in a loop, call
mBluetoothGatt.readRemoteRssi()
Don't know if this would be of help, but you can take advantage (if you have it) of a periodic data that is transmitted in the gatt. So if for example you have a measurement of 1 second of periodicity you can do something like:
// runnable to detect the lack of activity:
private final Runnable watchDog = new Runnable() {
#Override
public void run() {
measurement_timeout--;
if(measurement_timeout==0) {
Log.d("BLE_CONTROLLER", "PROBE WITH NO ACTIVITY");
}
}
};
// this should be in the reception of the periodic data:
measurement_timeout++;
mHandler.postDelayed(watchDog, 3000);
So the "measurement_timeout" will work as an actual timeout that when it reaches the 0 means that you don't have data received in the period of 3000 ms.
Notice that you must have a watch time > 2*data period.
The only way I have managed to achieve a fast gatt disconnect has been to ensure that the peripheral device sends a BLE disconnect instruction before it powers down or severs the connection.
Once android receives the disconnect instruction, the gatt tidies up immediately instead of taking 15 seconds to realise the peripheral is missing.
It would seem that most peripherals do not bother and just disappear.
Clearly this approach is only possible if you are able to modify the peripheral.
The proper way is to use the Connection Parameter Update Request from the peripheral side to change the timeout to a lower value.
There is a callback in Android :
BluetoothGattCallback btleGattCallback = new BluetoothGattCallback() {
#Override
public void onConnectionStateChange( BluetoothGatt gatt,int status,int newState){
if(newState == BluetoothProfile.STATE_DISCONNECTED){
//your code here
}
}
}

WifiManager.getScanResults returns old results if no APs are in range

Code in my current project periodically calls the WifiManager.startScan method and fetches the result via BroadcastReceiver:
void setupWifiScanner() {
final WifiManager wifiManager = (WifiManager) getSystemService(Context.WIFI_SERVICE);
final IntentFilter filter = new IntentFilter();
filter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
registerReceiver(new BroadcastReceiver(){
public void onReceive(Context c, Intent i){
scanResultHandler(wifiManager.getScanResults());
wifiManager.startScan();
}
}, filter);
wifiManager.startScan();
}
The BroadcastReceiver gets called every 2-3 seconds when scanning 2,5Ghz and 5Ghz bands and every ~800ms when scanning the 2,5Ghz band only - this is fine. However, I'm facing an annoying problem. In a nutshell:
As long as there are APs in range, WifiManager.getScanResults returns up-to-date data
After leaving that range (e.g. leaving an urban area), the BroadcastReceiver still gets called, but from now on the getScanResults method will always return the last APs that were fetched - even if I am 20 miles away
As soon as there is at least one new AP in range, I suddenly get valid data again
In other words: getScanResults never returns an emtpy list (except perhabs for some seconds after the app was started). Either there are APs in range and the returned list is up-to-date, or there are no APs in range and the list is out-of-date (and contains the last seen APs).
I'm testing on a Nexus 5 with stock Android (4.4.4), but I'm quite sure the same code worked on a Galaxy Nexus six months ago.
I've got an idea regarding a workaround - just hashing the result and if the same hash occurs x-times in a row, I think it's safe to say that the data is not valid anymore (at least the signal strength of an AP should change between multiple scans). But maybe I'm doing something wrong and there is a simple solution. Any help is appreciated, thanks.

Reading multiple characteristics from a BLE device synchronously (Recommended Method for Android)

I am working on an android application which reads data from a BLE device. I came across plenty of solutions here on how to read multiple characteristics and most of them suggested Queues.
I did implement the Queue method and everything is working fine in my code as expected. The reason why i started this thread is to find the best possible and most efficient solution and also to clear some of my doubts regarding how certain BLE service characteristics work.
I have taken the below two links as reference which helped me in making my code work.
Source 1:
Android: BLE how to read multiple Characteristics?
Source 2:
Android BLE API: GATT Notification not received
My requirement was to read the heart rate measurement & battery level. Initially i tried adding the heart rate & battery characteristics into a Queue and then call read/set methods for each of the added elements.
MainActivity:
private void displayGattServices(List<BluetoothGattService> gattServices)
{
// get the required service & characteristics
................
................
// add the characteristics via Queue
hRM_characteristicReadQueue.add(characteristics);
// Initiate read/set methods
read_Characteristic();
};
private void read_Characteristic()
{
bluetoothHDPService.read(hRM_characteristicReadQueue.element());
bluetoothHDPService.set(hRM_characteristicReadQueue.element(),true);
hRM_characteristicReadQueue.remove();
};
bluetoothHDPService:
public void read(BluetoothGattCharacteristic characteristic)
{
if (bluetoothAdapter == null || bluetoothGatt == null)
{
Log.w(TAG, "BluetoothAdapter not initialized");
return;
};
bluetoothGatt.readCharacteristic(characteristic);
};
public void set(BluetoothGattCharacteristic characteristic, boolean enabled)
{
if(bluetoothAdapter == null || bluetoothGatt == null)
{
Log.w(TAG, "BluetoothAdapter not initialized");
return;
};
bluetoothGatt.setCharacteristicNotification(characteristic, enabled);
BluetoothGattDescriptor descriptor = characteristic.getDescriptor(CLIENT_UUID);
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
bluetoothGatt.writeDescriptor(descriptor);
};
Back at MainActivity: (Once the read characteristic BLE callback operation is triggered)
I used the Broadcast Receiver to read/set the next Queue element.
private final BroadcastReceiver gattUpdateReceiver = new BroadcastReceiver()
{
#Override
public void onReceive(Context context, Intent intent)
{
// TODO Auto-generated method stub
final String action = intent.getAction();
if (Service_HeartRateX_HDP.ACTION_GATT_CONNECTED.equals(action))
{
// Connection with the BLE device successful
................
................
}
else if (Service_HeartRateX_HDP.ACTION_GATT_DISCONNECTED.equals(action))
{
// BLE device is disconnected
................
................
}
else if (Service_HeartRateX_HDP.ACTION_GATT_SERVICES_DISCOVERED.equals(action))
{
displayGattServices(bluetoothHDPService.getSupportedGattServices());
}
else if (Service_HeartRateX_HDP.ACTION_DATA_AVAILABLE.equals(action))
{
Log.i(TAG, "Collecting data");
// Collecting the incoming data
displayData(intent.getStringExtra(Service_HeartRateX_HDP.HEART_DATA),
intent.getStringExtra(Service_HeartRateX_HDP.BATTERY_DATA));
if(hRM_characteristicReadQueue.size() > 0)
{
read_Characteristic();
};
};
};
};
The above code snippet worked correctly only for one characteristic (heart rate) BLE device continued sending the heart rate measurement data, while for the other characteristic (battery percentage) BLE device sent the battery percentage data only once. Please note that, the order of the Queue element was such that heart rate characteristic was read/set and removed from the Queue first followed by the battery characteristic.
Initially i thought the queue didn't work as expected and tried interchanging the characteristics order in the queue with battery percentage being the first element to be read/set and removed followed by the heart rate characteristic to see if the issue was indeed related to incorrect programming.
But it didn't turn out to be the case, as the BLE device did the same thing as before (continued sending the heart rate measurement data, while the battery percentage was sent only once).
So taking the above scenarios into consideration, i came to the conclusion that battery level percentage characteristic needs to be read/set every once in a while to force the BLE device to send its data. This was further aided by the below post where one developer had to use a timer thread to get the battery percentage updates regularly from the BLE device.
how to update the battery level for every 5seconds in ble in android
I was reluctant to use a timer thread in my code, since this was making my already complex code into complex infinity. I then added the below condition at read_Characteristic() method to overcome this problem.
# MainActivity
// where hrmBattery_Characteristics is a temporary variable which holds the
// battery characteristics
if(hRM_characteristicReadQueue.element() != hrmBattery_Characteristics)
{
hRM_characteristicReadQueue.remove();
};
By doing so, battery characteristics is never removed from the Queue and read_Characteristic() method will be called every once in a while via the broadcast receiver (synchronous pattern is maintained). This currently works perfectly in my code, but i need the experts advice on whether this is correct.
Is this problem related only to the battery or other characteristics as well. Fortunately as of now i need data for only these two characteristics (heart rate measurement data & battery percentage).
I haven't tried for more than two characteristics since my BLE device has only limited set of features and these are the only two which are currently present in it.
Is this because of the BLE device inability to send large packets of data to the android device at a given stretch? The reason being, even though the above code is working fine there was never one instance where both the data (heart rate & battery percentage) were sent in the same stretch.
If someone can throw some light into this i would be greatly indebted.
Thanks in advance!

Categories

Resources