What's the purpose of setReportDelay for BluetoothLeScanner in Android? I can't imagine why one would get reports with delay...
From Android Official Site:
setReportDelay() takes reportDelayMillis parameter where reportDelayMillis is:
Delay of report in milliseconds. Set to 0 to be notified of results
immediately. Values > 0 causes the scan results to be queued up and
delivered after the requested delay or when the internal buffers fill
up
coming to your question:
What's the purpose of setReportDelay for BluetoothLeScanner in Android? I can't imagine why one would get reports with delay...|
As i understood the purpose of this is that sometimes when you scan for a very short period of time, not all the devices/beacons can be found, but on the second scan another one can be cought while scanning.
Ex:
Suppose you have three beacons and you made two different scans for 1s. On the first scan only single beacon is found, but on the second other beacons found too. Setting a delay will queue all of them and you will be able to show all of them. Otherwise sometimes single beacon, sometimes all of them and sometimes two of them will be shown.(This is related mostly with the frequency they transmit)
You can use also Lists for this purpose, so when a beacon is found you add them to the list and show them after some delay(using Handler/Timer). But this will require more work. So it is probably simplified with this function
setReportDelay() > 0 causes the scanner to queue up results and then fire the onBatchScanResults() from ScanCallback rather than the normal onScanResult().
This is quite useful if you need to do a UI update every few seconds: rather than refreshing your UI every time an announcement packet arrives, you can simply delay the results. Also, you get a more complete result set.
Note that your device need to be able to support this. See
http://developer.android.com/reference/android/bluetooth/BluetoothAdapter.html#isOffloadedScanBatchingSupported()
Different devices scan slightly differently. When scanning some will return the same found device multiple times per scan.
If you set a delay, Android will create a queue, and all duplicates are filtered out before that list of devices in the queue is returned.
Related
I'm currently developing an app that uses the activity recogition api.
I have a main activity and an intent service which is called using requestActivityUpdates() and returns data for the activity using the broadcast api.
According to the documentation, requestActivityUpdates' first parameter, detectionIntervalMillis, stands for "the desired time between activity detections. Larger values will result in fewer activity detections while improving battery life. A value of 0 will result in activity detections at the fastest possible rate.".
I'm using a values as low as 100ms and still I get updates once every 3 minutes on average. What could be the problem here? I tested on 2 different fully charged devices already, so I believe this has nothing to do with power saving configurations.
edit: took the devices for a walk and it seems that the rate of updates go up to ~8s. Still way more than expected. :/
I would like to understand when can we encounter ADVERTISE_FAILED_INTERNAL_ERROR on startAdvertising failure callback. I am using AndroidBeaconLibrary to transmit multiple beacons.
I have a map which stores current transmissions. The Map structure is
It was working smoothly and emitting beacons. Somehow when I changed the structure to which would be more precise as names are not unique, it started throwing ADVERTISE_FAILED_INTERNAL_ERROR.
Also, this runs sometimes and works perfectly, most of the times it throws ADVERTISE_FAILED_INTERNAL_ERROR. I am clueless as to what is happening.
Would like to know when can the API throw ADVERTISE_FAILED_INTERNAL_ERROR?
On some devices ADVERTISE_FAILED_INTERNAL_ERROR is returned when the bluetooth stack has gotten into a bad state an is not recoverable without a power cycle. This is actually such a good predictor of this state, that I have built an experimental class for use with the library that periodically starts advertising, and if this error code is returned, it turns bluetooth off, waits 1000 milliseconds, then turns it back on again. This typically clears the condition.
I'm playing around with the AltBeacon and these parameters. My goal is to have the fastest (as fast as possible) callback didRangeBeaconsInRegion().
I understand that ranging uses running average to calculate the distance and make the callback. I'm not interested in the distance, but the rssi. With that said, if the rssi is varied by 1 that's ok.
In my current code, I currently use:
RangedBeacon.setSampleExpirationMilliseconds(1000);
try
{
mBeaconManager.setForegroundScanPeriod(700l);
mBeaconManager.setForegroundBetweenScanPeriod(0l);
mBeaconManager.updateScanPeriods();
}
catch(RemoteException ex)
{
...
}
My app is in foreground all the time. Running on Nexus 5X.
I notice that the smaller the value for setSampleExpirationMilliseconds(), the more frequent I get the didRangeBeaconsInRegion() callback, which is good. The setForegroundBetweenScanPeriod is set to 0 which means the service always scans all the time.
In my venue, I have about 30 beacons deployed. With the above code setup, I get callbacks every second, each time a different set of beacons.
The problem is even when I stand right next to a beacon, that beacon is not heard every 1 or less second. When I get the callback, it's usually for other far away beacons. There are times that it takes a good 30 seconds for me to hear that particular beacon to which I'm standing next again.
I know that the beacon we setup chirps every 20ms, so during that 700 ms, I should see them.
I notice that if I raise the setForegroundScanPeriod to 5000 (I hope the scan period to be longer so I can get the nearby beacons), I get less callbacks. The delay between callbacks is about 10 seconds. So I think a smaller value means faster callback.
My questions:
Why don't I get all the beacons in the callback (they all chirp at 20ms)? How is the callback called? When it has enough info, or it has some kind of interval? What controls it?
Is there any relationship between setSampleExpirationMilliseconds, setForegroundScanPeriod, and setForegroundScanPeriod? How to make them work well together?
My app requires that I should hear a nearby beacon (3ft or less) within less than a second, how to best setup the parameters to achieve this?
Thanks for reading such a long question. Appreciate any insights.
#davidgyoung maybe you could shed some light?
The Android Beacon Library isn't designed to give you a callback for every beacon packet detected, but rather to give you regular callbacks at some configured interval to let you know beacons are still around. By default, this interval is 1100 ms in the foreground, which is configured by
setForegroundScanPeriod(1100l);
setForegroundBetweenScanPeriod(0l);
As soon as the scan period ends, a list of beacon detected in the scan period are returned in a list via the didRangeBeaconsInRegion callback.
You can get faster callbacks by setting a shorter scan period. To get callbacks every 500ms, setForegroundScanPeriod(500l); The disadvantage of this is that this stops and restarts BLE scans at the end of each scan period. (Stopping and restarting is necessary for some Android phone models that can only detect one packet per unique bluetooth MAC address in a single scan cycle.) But whenever you stop and restart scanning, you will miss any packets that are being transmitted at that exact time -- it's akin to shutting off the bluetooth radio in the middle of the packet. This leads to a higher percentage of missed packet detections the shorter the scan period.
This may be OK for your use case, provided that the beacon is transmitting every 20ms -- with a 500ms scan interval, you have plenty of samples to ensure a detection.
The setSampleExpirationMilliseconds parameter is largely unrelated to what you are trying to do. It is used for distance estimates when using the default RunningAverageRssiFilter. This setting decides how long to average RSSI measurements for distance estimating purposes. By default, it retains 20 seconds worth of RSSI samples, which affects the results of getDistance() method on Beacon.
I am trying to write a Bluetooth LE app that accesses a Zephyr HxM Smart heart monitor. This monitor has several Bluetooth services, but I am interested in the Battery Service, Heart Rate Service, and a Custom Service that has Activity and Peak Acceleration. There is one characteristic for each, Battery Level, (BAT), Heart Rate Measurement (HR), and Custom Measurement (CUS). The HxM updates about once per sec.
I am doing this with a Galaxy S4 with Android 4.4.
It is not working as expected from the documentation.
My initial approach was to do:
Read BAT
Set notification for HR
Set notification for CUS.
Then wait for the callbacks. Setting notification means calling
BluetoothGatt.setCharacteristicNotification(Characteristic char , boolean enabled)
(One could also do notification for BAT, however, the spec does not require this to be supported. The HxM, however, does support it.)
This didn't work. I got BAT and notifications for HR, but not CUS. If I eliminated the second step, I got notifications for CUS. I couldn't get both. (This indicates I am reading the characteristics correctly, so that is [probably] not the problem.)
I found some indications there were problems with the Bluetooth stack for Android being synchronous, but no hard documentation. I then tried the following:
Read BAT.
Wait for the BAT reading, then set notification for HR,
Get HR, then disable notification for HR, and start notification for CUS.
Get CUS, then disable notification for CUS, and start notification for HR.
And continue to loop.
I got BAT and that is all.
By trial and error, I discovered the following works:
Read BAT.
Wait for the BAT reading, then set notification for HR,
Get HR, then start notification for CUS.
Get CUS, then start notification for HR.
And continue to loop.
(Same as above but without disabling notifications.) Typically, I get a HR reading, then the CUS one within 200 ms. One can assume they are from the same update. (There are no timestamps in the data, which have to be kept short to be LE.) In reality the logic is more complicated, as timers are necessary in case expected readings don't come in. This logic is FAR more complicated (and more prone to error) than my first try, which is what the documentation seems to say is what is appropriate.
I have contacted Zephyr, and they say the HxM Smart has been extensively tested on Windows, and will do simultaneous notifications as it should. There are also indications it works as it should on iOS.
There is a further problem that I do not understand. In order to get notifications, you have to enable the Characteristics locally for notification with something like:
BluetoothGattDescriptor descriptor = characteristic
.getDescriptor(UUID_CLIENT_CHARACTERISTIC_CONFIG);
resSet = descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
resWrite = mBluetoothGatt.writeDescriptor(descriptor);
This is a per characteristic setting, and should only need to be done once, when the characteristic is first received. Instead, I find I have to do it every time I set the notifications. It is possible this just causes a sufficient time delay for things to work. I don't know. This trial and error is taking a lot of my time. It would be nice to have a definitive statement of how it works.
I should note that for all calls that return a result, the result is true (success).
I apologize for the long statement. My question is:
I find no documentation that I have to do the things described. All indications are that you set up notifications and wait for the callbacks. Is there documentation, or is this a bug, or just a bad implementation? (Or is it my error?) I would especially like to know where is the documentation for what I have had to do.
Second, there is a further complication. I have tried to debug into the routines to see what the code is actually doing. When I get to BluetoothGatt.class, the source lines don't match what the debug stack says. I thus assume that the S4 is not using standard Android. I don't know where to go from there. It has been frustrating, and while I have something that appears to work, it is kludgy and almost certainly less robust.
Thanks for any help.
I had the same problem with writing multiple values in sequence, putting a Thread.sleep(200) in between them solved the problem (alas i should say). Maybe this helps as well with getting notifications.
Tested this on android 4.4.2 on Nexus 5. And no, 4.4 does not solve all problems...
Im probably very late to this but you should implement a queuing architecture for setting up your characteristics to send notifications. I used a similar technique to miznick in the post https://stackoverflow.com/a/18207869/3314615 and ended up just writing wrapper code for the native BLE stack since there are several apps i use BLE for. Ever since then I have had no issues receiving notifications. I agree with you that the Android BLE documentation should have some sort of information or warning about the BLE Stack not being synchronous. Frankly, i believe it should be re-written to handle synchronous write calls and just queue them.
First, you set Notification(i.e. setCharacteristicNotification and set Descriptor) for first characteristic.
And, Set notification for second characteristic in onDescriptorWrite callback function.
I am doing my Master thesis at the moment on WiFi positioning and in order to test my algorithms I needed to collect some data.
To do this I have written a short and very simple program for Android which simply collects the RSSI for all availible access points found by each scan and saves them to file. I have set up a BroadcastReceiver that listens on the event WifiManager.SCAN_RESULTS_AVAILABLE_ACTION and I use a Timer, here called tim, to initiate scans with a WifiManager, called wifi as follows:
tim.schedule(new TimerTask(){
#Override
public void run(){
wifi.startScan();
}
}, 0, 1000);
The problem I am having now is that the initiated scans don't seem to happen every second even if I succeed in initiating them and every now and then there are other scans initiated from some other app that gets recorded as well.
Is there any easy way to scan on a set interval and not receive the scans initiated by some other app?
The whole app can be found on https://github.com/while/RSSIMiner if it helps in any way.
Is there any easy way to scan on a set interval?
If this doesn't work well, I'm afraid not. From my experience, "hardware related" methods may not work exactly like their definition says. For example, I once created a small app which records your position every X minutes. So I call requestLocationUpdates with some minTime parameter. But my phone simply ignores the minTime value, and I get updates from the GPS as soon as they're available, whcih is not what I wanted. I posted a question about it here, and got this answer, from which we learn that prior to jelly bean, devices may simply ignore this value...
So it may be something similar now. I'd try to run this code on the latest Android version. And I don't understand that much in Wifi, but isn't 1 second a too frequent interval for scans? Perhaps the system doesn't ignore the scan request (So it returns true) but the hardware does?
Can we ignore the scans initiated by some other app?
As far as I know, it's negative here too. There are no extras contained in the SCAN_RESULTS_AVAILABLE_ACTION broadcast so you can't know which app initiated the scan.
The best solution will be to defnie your requirements. You can use the ScanResult.timestamp to determine if you should use this result or not. For example, if you're trying to get the RSSI for each access point each second, you can compare the current BSSID to previous BSSIDs. If the current BSSID was included in a scan result from the last second, you can simply ignore it. Then, it doesn't matter how many results you get.
Another, much more simple soltuion will be to create a boolean called scanInitiated and set it to true when starting a scan. When receiving the broacast, use the data only if scanInitiated is true, and then set it to false. This isn't so reliable when the intervals are short, but for long intervals it will work great.