BLE Device Name irretrievable - android

I have a project that is scanning for beacons that run on BLE.
I can scan for the beacon and list them in a nice custom ListView just fine. However retrieving their names seems to not work.
In my OnLeScan callback I use device.getName() which appears to always be returning null?
Furthermore, when I attempt to parse the ScanRecord byte[] array for the data in concordance with this post - I still am not having much luck. Any ideas/tips?
Should I be retrieving the local name from the BluetoothDevice class? Should I retrieve it from parsing the ScanRecord/ScanResult class?
Here is what my onLeScan looks like:
public void onLeScan(final BluetoothDevice device, int rssi, final byte[] scanRecord) {
runOnUiThread(new Runnable() {
#Override
public void run() {
Log.v(device.getName(),device.getName());
mLeDeviceListAdapter.addDevice(device);
mLeDeviceListAdapter.notifyDataSetChanged();
}
}
});
}
Edit:
I attempted some modification of the code from the linked post. Adding a case 0x08: as suggested in the comments. However my ListView still lists all found devices as "unknown device" by default and will not retrieve the friendly name.

See this answer for a breakdown of an iBeacon advertisement packet. There is no name.
The official spec is only available via Apple's MFi program.

Related

How to get my own endpointId with Nearby Connections?

With Nearby Connections, each device has an endpointId, something similar to zkHk.
Getting the endpointId of others is trivial since it is returned by the API when scanning or connecting to other devices.
I must miss something, but I cannot find a way to get my own endpointId (apart implementing a mechanism where a connected peer echoes my id). It can be useful for some protocols where I want to follow what is sent to who.
The only thing I found is getLocalEndpointName but it returns my name, not my id. Even though it seems the C++ version of Nearby have it!
Do you have some ideas for Java/Kotlin? I specifically seek to get the endpointId, and not use alternatives like using a kind of GUID in the localendpoint name as a replacement.
Edit: Some example of usage
1) For instance, it can be interesting to implement some network mesh protocols. Several devices are interconnected making a global network, and each device add its endpointId in the incoming payload before sending it again, so others can check if they should send the payload to a device that already has it.
2) I may also want to specifically send a packet from device A to C through B acting as a relay, and add some "from: A" and "to: C" field in the payload so the network would know how to route the data and avoid some retransmission cycles. It is simpler to do that with endpointId since each device has a list of endpointId to which it is connected.
3) It can also be interesting for debug purpose. If I do some tests with a phone connected to several others (e.g. star network), it is easier to know from which phone a new piece of data is coming, all the more if I want to use name for another purpose.
Note: all of that could be done differently (e.g. use some unique identifier for the "name" of the devices and check that instead of the endpointId) but it seems a little cumbersome. All the more since endpointId guarantee a kind of unicity, whereas I must enforce it for the name. Moreover there isn't lots of information I can have on another device before exchanging data (only endpointId and name), so I feel I remove my last metadata slot if I use name as a substitute for endpointId.
As of today, you can't get your own endpoint id. We didn't see a reason you'd need it. Can you give a more detailed example of an algorithm where you need to know your own id?
i think you want to get your endpointId and sent its to other devices to know you again ?
if yes
let's think like that :
other devices will get your EndpointID and save it every time you connect to them
1)you have an Arrylist<EndPointObject> listOfUsers where EndPointObject it's an Object contain informations about Connected Endpoint Device (you create this class).
we w'ill use this Arry list to save recieved Endpoint informations
2)you need to make EndPointObject class Serializable by implements Serializable,you are doing that to make it able to be converted to Byte[] and send it in payload
public class EndPointObject implements Serializable
{
String endpointId ;
.
.
.
}
3)this is the Converting class add it to your project
public class SerializeHelperForPayLoad {
public static byte[] serialize(Object object) throws IOException {
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
// transform object to stream and then to a byte array
objectOutputStream.writeObject(object);
objectOutputStream.flush();
objectOutputStream.close();
return byteArrayOutputStream.toByteArray();
}
public static Object deserialize(byte[] bytes) throws IOException, ClassNotFoundException{
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
return objectInputStream.readObject();
}
}
4) now the strategy is every time you connect to an endpoint Device you will exchange yours EndpointObject informations,so in payloadcallback
PayloadCallback mPayloadCallback =
new PayloadCallback() {
#Override
public void onPayloadReceived(String endpointId, Payload payload) {
if (payload.getType() == Payload.Type.BYTES) {
try{
onDataReceived(endpointId, SerializeHelperForPayLoad.deserialize(payload.asBytes()));
} catch (IOException | ClassNotFoundException e) { e.getMessage(); }
}
}
// onData recieved void
void onDataReceived(String endpointId, Object object) {
// do something with your Object
EndPointObject recieved_user_info = new EndPointObject();
if (object.getClass() == EndPointObject.class){
//casting
recieved_user_info = (EndPointObject) object;
//now add his end pointid to his information
recieved_user_info.setEndpointId(endpointId);
listOfUsers.add(recieved_user_info);
}
}
i'm very new in nearby technology ,but i hope that's helpful ,
by this way you can ask other end endpoint to send you your own endpointid every time

How to detect when a BLE device is not in range anymore?

I use a LeScanCallback (can not use the newer scan methods because I'm developing for api 18. Not that it matters, since the android 5.0+ apis don't offer this functionality either) to detect when a nearby BLE device is detected:
private BluetoothAdapter.LeScanCallback bleCallback = new BluetoothAdapter.LeScanCallback() {
#Override
public void onLeScan(BluetoothDevice bluetoothDevice, int i, byte[] bytes) {
discoveredDevices.add(bluetoothDevice);
}
};
I am not pairing or connecting with the devices because that's not required, I simply want to see which devices are nearby.
I'm trying to make a service that, every 5 mins or so, calls a webserver to update which devices are nearby at that moment.
Tricky part is that the android device will be moving, so a bluetooth device that is nearby right now, might not be in 5 mins. In that case I need to remove it from discoveredDevices.
Ideally, I would like to receive a callback when a bluetooth device was in range before, but is not anymore. This callback doesn't exist though.
(I'm aware of the android.bluetooth.device.action.ACL_CONNECTED and android.bluetooth.device.action.ACL_DISCONNECTED broadcasts, but those are for when you connect to a bluetooth device, which I don't want.)
An option is to do a fresh scan every 5 mins, but you can't tell when all nearby devices have been discovered, so you would have to do a timed scan, e.g. scan for 5 seconds and then send the collected data to the webservice.
This sounds dirty and risky because you can never know for sure all nearby devices were discovered within the allotted time, so I would very much like to avoid doing it like that.
Is there another way to do this?
Edit
Some devices continuously report discovery of nearby bluetooth devices, even if they were already discovered before. If that functionality was universal I could solve my problem, however this is device specific.
My phone's bluetooth adapter for example only discovers nearby devices once. Some other devices I have tested with do continuously report the same nearby devices, but not all devices do, so I can't rely on that unfortunately.
This sounds dirty and risky because you can never know for sure all nearby devices were discovered within the allotted time, so I would very much like to avoid doing it like that.
That sounds like a reasonable assumption, but it's wrong.
Bluetooth low energy works in a particular way and BLE devices have some limits. For instance, they have a fixed range of possible advertising frequencies, ranging from 20 milliseconds to 10.24 seconds, in steps of 0.625 milliseconds. See here and here for more detailed information.
This means that it can take at most 10.24 seconds before a device will broadcast a new advertisement package. BLE devices generally, if not always, provide a way for their owner to adjust their advertising frequency, so the frequency can of course vary.
In cases where you are periodically collecting data about nearby devices, like yours, it is fine to use a scan with a fixed time limit, save that data somewhere, restart the scan, collect new data, compare with old data --> get results.
For example, if a device was found in scan 1 but not in scan 2, you can conclude that the device was in range, but is not anymore.
Same goes for the other way around: if a device was found in scan 4 but not in scan 3, it is a newly discovered device.
Finally, if a device was found in scan 5, was not found in scan 6, but was again found in scan 7, it is rediscovered and can be handled as such if need be.
Because I'm answering my own question here, I'll add the code that I used to implement this.
I have the scanning done in a background service, and communicate to other parts of the app using BroadcastReceivers. Asset is a custom class of mine that holds some data. DataManager is a custom class of mine that - how did you guess it - manages data.
public class BLEDiscoveryService extends Service {
// Broadcast identifiers.
public static final String EVENT_NEW_ASSET = "EVENT_NEW_ASSET ";
public static final String EVENT_LOST_ASSET = "EVENT_LOST_ASSET ";
private static Handler handler;
private static final int BLE_SCAN_TIMEOUT = 11000; // 11 seconds
// Lists to keep track of current and previous detected devices.
// Used to determine which are in range and which are not anymore.
private List<Asset> previouslyDiscoveredAssets;
private List<Asset> currentlyDiscoveredAssets;
private BluetoothAdapter bluetoothAdapter;
private BluetoothAdapter.LeScanCallback BLECallback = new BluetoothAdapter.LeScanCallback() {
#Override
public void onLeScan(BluetoothDevice bluetoothDevice, int i, byte[] bytes) {
Asset asset = DataManager.getAssetForMACAddress(bluetoothDevice.getAddress());
handleDiscoveredAsset(asset);
}
};
#Override
public void onCreate() {
super.onCreate();
BluetoothManager manager = (BluetoothManager) getSystemService(BLUETOOTH_SERVICE);
bluetoothAdapter = manager.getAdapter();
previouslyDiscoveredAssets = new ArrayList<>();
currentlyDiscoveredAssets = new ArrayList<>();
handler = new Handler();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
// Start scanning.
startBLEScan();
// After a period of time, stop the current scan and start a new one.
// This is used to detect when assets are not in range anymore.
handler.postDelayed(new Runnable() {
#Override
public void run() {
performRepeatingTask();
// Repeat.
handler.postDelayed(this, BLE_SCAN_TIMEOUT);
}
}, BLE_SCAN_TIMEOUT);
// Service is not restarted if it gets terminated.
return Service.START_NOT_STICKY;
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onDestroy() {
handler.removeCallbacksAndMessages(null);
stopBLEScan();
super.onDestroy();
}
private void startBLEScan() {
bluetoothAdapter.startLeScan(BLECallback);
}
private void stopBLEScan() {
bluetoothAdapter.stopLeScan(BLECallback);
}
private void handleDiscoveredAsset(Asset asset) {
currentlyDiscoveredAssets.add(asset);
// Notify observers that we have a new asset discovered, but only if it was not
// discovered previously.
if (currentlyDiscoveredAssets.contains(asset) &&
!previouslyDiscoveredAssets.contains(asset)) {
notifyObserversOfNewAsset(asset);
}
}
private void performRepeatingTask() {
// Check if a previously discovered asset is not discovered this scan round,
// meaning it's not in range anymore.
for (Asset asset : previouslyDiscoveredAssets) {
if (!currentlyDiscoveredAssets.contains(asset)) {
notifyObserversOfLostAsset(asset);
}
}
// Update lists for a new round of scanning.
previouslyDiscoveredAssets.clear();
previouslyDiscoveredAssets.addAll(currentlyDiscoveredAssets);
currentlyDiscoveredAssets.clear();
// Reset the scan.
stopBLEScan();
startBLEScan();
}
private void notifyObserversOfNewAsset(Asset asset) {
Intent intent = new Intent();
intent.putExtra("macAddress", asset.MAC_address);
intent.setAction(EVENT_NEW_ASSET);
sendBroadcast(intent);
}
private void notifyObserversOfLostAsset(Asset asset) {
Intent intent = new Intent();
intent.putExtra("macAddress", asset.MAC_address);
intent.setAction(EVENT_LOST_ASSET);
sendBroadcast(intent);
}
}
This code is not perfect and might even be buggy, but it will at least give you an idea or example of how this can be implemented.
I can recommend this approach:
Use Map<BluetoothDevice, Long> structure to store the discovered devices, where Long is the time of detection of the device (can be System.currentTimeMillis() for example).
Then in your service (as far as I understand from the question there will be implemented some kind of repeated task) just extract actual devices based on the time of their detection.
And you are absolutely right, there are no guarantee that all nearby devices were discovered within the allotted time. Especially this is actual for the Android devices.
iOS devices in it's turn have another issue - they can change their BluetoothDevice's adress in runtime without apparent external cause.
Hope this will help you to save the time during debugging.
Edit
As a result of research of this topic found this discussion on code.google.com
Issue is still open and seems that it is related to the hardware features and can't be fixed programmatically. Moreover, it seems that bug will remains on problem devices even after a system updates.
So restarting the scan periodically might be acceptable workaround for this case.

In Android, how to get the profile of a connected bluetooth device?

Following a lot of answers here, I am able to build the list of connected bluetooth devices with the help of a BroadcastReceiver. Now my question is how do I know which device supports which profile. I want to be able to pick the devices based on the profile, for example, get a list of currently connected devices and their profile, and pick one of them. I don't see how I can get such info if I have the instance of BluetoothDevice.
On this page there are some codes illustrating how to work with a bluetooth headset profile: http://developer.android.com/guide/topics/connectivity/bluetooth.html#Profiles. But it doesn't solve my problem. If you think I am missing anything, please help me and point it out.
Thanks a lot in advance.
I've run into the same problem. It doesn't appear that you can get the available profiles from the BluetoothDevice class.
But there is a long way around by getting a List of BluetoothDevices from the getDevicesMatchingConnectionStates method in the BluetoothProfile class.
For example if you want to find which BluetoothDevices support A2DP, first create a custom BluetoothProfile.ServiceListener
public class cServiceListener implements BluetoothProfile.ServiceListener {
private static final int[] states={ BluetoothProfile.STATE_DISCONNECTING,
BluetoothProfile.STATE_DISCONNECTED,
BluetoothProfile.STATE_CONNECTED,
BluetoothProfile.STATE_CONNECTING};
#Override
public void onServiceConnected(int profile, BluetoothProfile bluetoothProfile) {
List<BluetoothDevice> Devices=bluetoothProfile.getDevicesMatchingConnectionStates(states);
for (BluetoothDevice loop:Devices){
Log.i("myTag",loop.getName());
}
}
#Override
public void onServiceDisconnected(int profile) {
}
}
Then attach it to the profile you want to check, in this example A2DP
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
cServiceListener mServiceListener=new cServiceListener();
mBluetoothAdapter.getProfileProxy(thisContext,mServiceListener, BluetoothProfile.A2DP);
This will logcat all the bluetooth devices that support A2DP which are in the requested states. In this example it includes all devices which are currently connected and previously paired devices which are disconnected.
Looking at the Android source code, you can guess which profiles are available for a device by looking at its UUIDs, and then connect each profile one by one.
Step 0 : Copy the _PROFILE_UUIDS constants from there : https://android.googlesource.com/platform/packages/apps/Settings/+/9ad703cdb9a8d0972c123b041d18aa7bbeb391a4/src/com/android/settings/bluetooth/LocalBluetoothProfileManager.java
Step 1 : get your BluetoothDevice, via scanning for instance. Assure that it's properly bonded.
Step 2 : register a BroadcastReceiver for the android.bluetooth.BluetoothDevice.ACTION_UUID action intent
Step 3 : on your device, call the fetchUuidsWithSdp method
Step 4 : you will recieve a ACTION_UUID broadcast : in the onReceive method you can unregister the receiver, and get the list of profiles like so :
ArrayList<Integer> profiles = new ArrayList<>();
ParcelUuid[] uuids = device.getUuids();
if (BluetoothUuid.containsAnyUuid(uuids, HEADSET_PROFILE_UUIDS))
{
profiles.add(BluetoothProfile.HEADSET);
}
if (BluetoothUuid.containsAnyUuid(uuids, A2DP_PROFILE_UUIDS))
{
profiles.add(BluetoothProfile.A2DP);
}
if (BluetoothUuid.containsAnyUuid(uuids, OPP_PROFILE_UUIDS))
{
//OPP doesn't have any BluetoothProfile value
}
if (BluetoothUuid.containsAnyUuid(uuids, HID_PROFILE_UUIDS))
{
//You will need system privileges in order to use this one
profiles.add(BluetoothProfile.INPUT_DEVICE);
}
if (BluetoothUuid.containsAnyUuid(uuids, PANU_PROFILE_UUIDS))
{
profiles.add(BluetoothProfile.PAN);
}
Step 5 : get the proxies for the profiles, one by one :
for (int profile : profiles)
{
if (!adapter.getProfileProxy(context, listener, profile))
{
//Do something
}
}
Step 6 : do anything with each proxy retrieved in the onServiceConnected method of your listener. You can access the connect method using relfection.

Android 4.3: How to connect to multiple Bluetooth Low Energy devices

My Question is: Can Android 4.3 (client) have active connections with multiple BLE devices (servers)? If so, how can I achieve it?
What I did so far
I try to evaluate what throughput you can achieve using BLE and Android 4.3 BLE API. In addition I also try to find out how many devices can be connected and active at the same time. I use a Nexus 7 (2013), Android 4.4 as master and TI CC2540 Keyfob as slaves.
I wrote a simple server software for the slaves, which transmits 10000 20Byte packets through BLE notifications. I based my Android App on the Application Accelerator from the Bluetooth SIG.
It works well for one device and I can achieve around 56 kBits payload throughput at a Connection Interval of 7.5 ms. To connect to multiple slaves I followed the advice of a Nordic Employee who wrote in the Nordic Developer Zone:
Yes it's possible to handle multiple slaves with a single app. You would need to handle each slave with one BluetoothGatt instance. You would also need specific BluetoothGattCallback for each slave you connect to.
So I tried that and it partly works. I can connect to multiple slaves. I can also register for notifications on multiple slaves. The problem begins when I start the test. I receive at first notifications from all slaves, but after a couple Connection Intervals just the notifications from one device come trough. After about 10 seconds the other slaves disconnect, because they seem to reach the connection time-out. Sometimes I receive right from the start of the test just notifications from one slave.
I also tried accessing the attribute over a read operation with the same result. After a couple of reads just the answers from one device came trough.
I am aware that there are a few similar questions on this forum: Does Android 4.3 support multiple BLE device connections?, Has native Android BLE GATT implementation synchronous nature? or Ble multiple connection. But none of this answers made it clear for me, if it is possible and how to do it.
I would be very grateful for advice.
I suspect everyone adding delays is just allowing the BLE system to complete the action you have asked before you submit another one. Android's BLE system has no form of queueing. If you do
BluetoothGatt g;
g.writeDescriptor(a);
g.writeDescriptor(b);
then the first write operation will immediately be overwritten with the second one. Yes it's really stupid and the documentation should probably actually mention this.
If you insert a wait, it allows the first operation to complete before doing the second. That is a huge ugly hack though. A better solution is to implement your own queue (like Google should have). Fortunately Nordic have released one for us.
https://github.com/NordicSemiconductor/puck-central-android/tree/master/PuckCentral/app/src/main/java/no/nordicsemi/puckcentral/bluetooth/gatt
Edit: By the way this is the universal behaviour for BLE APIs. WebBluetooth behaves the same way (but Javascript does make it easier to use), and I believe iOS's BLE API also behaves the same.
Re visting the bluetooth-lowenergy problem on android: I am still using delays.
The concept: after every major action that provokes the BluetoothGattCallback (e.g. conenction, service discovery, write, read) a dealy is needed. P.S. have a look at Google example on BLE API level 19 sample for connectivity to understand how Broadcasts should be sent and get some general understanding etc...
Firstly, scan (or scan) for BluetoothDevices, populate the connectionQueue with desired devices and call initConnection().
Have a look on the following example.
private Queue<BluetoothDevice> connectionQueue = new LinkedList<BluetoothDevice>();
public void initConnection(){
if(connectionThread == null){
connectionThread = new Thread(new Runnable() {
#Override
public void run() {
connectionLoop();
connectionThread.interrupt();
connectionThread = null;
}
});
connectionThread.start();
}
}
private void connectionLoop(){
while(!connectionQueue.isEmpty()){
connectionQueue.poll().connectGatt(context, false, bleInterface.mGattCallback);
try {
Thread.sleep(250);
} catch (InterruptedException e) {}
}
}
Now if all is good, you have made connections and BluetoothGattCallback.onConnectionStateChange(BluetoothGatt gatt, int status, int newState) has been called.
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
switch(status){
case BluetoothGatt.GATT_SUCCESS:
if (newState == BluetoothProfile.STATE_CONNECTED) {
broadcastUpdate(BluetoothConstants.ACTION_GATT_CONNECTED, gatt);
}else if(newState == BluetoothProfile.STATE_DISCONNECTED){
broadcastUpdate(BluetoothConstants.ACTION_GATT_DISCONNECTED, gatt);
}
break;
}
}
protected void broadcastUpdate(String action, BluetoothGatt gatt) {
final Intent intent = new Intent(action);
intent.putExtra(BluetoothConstants.EXTRA_MAC, gatt.getDevice().getAddress());
sendBroadcast(intent);
}
P.S. sendBroadcast(intent) might need to be done like this:
Context context = activity.getBaseContext();
context.sendBroadcast(intent);
Then the broadcast is received by BroadcastReceiver.onReceive(...)
public BroadcastReceiver myUpdateReceiver = new BroadcastReceiver(){
#Override
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
if(BluetoothConstants.ACTION_GATT_CONNECTED.equals(action)){
//Connection made, here you can make a decision: do you want to initiate service discovery.
// P.S. If you are working with multiple devices,
// make sure that you start the service discovery
// after all desired connections are made
}
....
}
}
After doing whatever you want in the broadcast receiver, here is how I continue:
private Queue<BluetoothGatt> serviceDiscoveryQueue = new LinkedList<BluetoothGatt>();
private void initServiceDiscovery(){
if(serviceDiscoveryThread == null){
serviceDiscoveryThread = new Thread(new Runnable() {
#Override
public void run() {
serviceDiscovery();
serviceDiscoveryThread.interrupt();
serviceDiscoveryThread = null;
}
});
serviceDiscoveryThread.start();
}
}
private void serviceDiscovery(){
while(!serviceDiscoveryQueue.isEmpty()){
serviceDiscoveryQueue.poll().discoverServices();
try {
Thread.sleep(250);
} catch (InterruptedException e){}
}
}
Again, after a successful service discovery, BluetoothGattCallback.onServicesDiscovered(...) is called. Again, I send an intent to the BroadcastReceiver (this time with different action String) and it is now that you can start reading, writing and enabling notifications/indications...
P.S. If you are working with multiple devices, make sure that you start the reading, writing etc... stuff after all devices have reported that their services have been discovered.
private Queue<BluetoothGattCharacteristic> characteristicReadQueue = new LinkedList<BluetoothGattCharacteristic>();
private void startThread(){
if(initialisationThread == null){
initialisationThread = new Thread(new Runnable() {
#Override
public void run() {
loopQueues();
initialisationThread.interrupt();
initialisationThread = null;
}
});
initialisationThread.start();
}
}
private void loopQueues() {
while(!characteristicReadQueue.isEmpty()){
readCharacteristic(characteristicReadQueue.poll());
try {
Thread.sleep(BluetoothConstants.DELAY);
} catch (InterruptedException e) {}
}
// A loop for starting indications and all other stuff goes here!
}
BluetoothGattCallback will have all your incoming data from the BLE sensor. A good practice is to send a broadcast with the data to your BroadcastReceiver and handle it over there.
I am developing an app with BLE features myself. The way I managed to connect to multiple devices and turn on notifications was to implement delays.
So I make a new thread (in order not to block UI thread) and in the new thread connect and turn on notifications.
For example, after BluetoothDevice.connectGatt(); call Thread.sleep();
And add the same delay for read/write and enable/dissable notifications.
EDIT
Use wait like this so that Android dindn't reaise ANR
public static boolean waitIdle() {
int i = 300;
i /= 10;
while (--i > 0) {
if (true)
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return i > 0;
}
Unfortunately notifications in the current Android BLE stack are a bit buggy. There are some hardcoded limits and I've found some stability issues even with a single device. (I read at one point that you could only have 4 notifications... not sure if that's across all devices or per device. Trying to find the source for that info now.)
I would try switching to a polling loop (say, poll the items in question 1/sec) and seeing if you find your stability increases. I would also consider switching to a different slave device (say a HRM or the TI SensorTag) to see if there is perhaps an issue with the slave-side code (unless you can test that against iOS or another platform and confirm it isn't part of the issue).
Edit: Reference for notification limitation
Rain is right in his answer, you need delays for pretty much everything when you work with BLE in Android. I developed several apps with it and it is really necessary. By using them you avoid a lot of crashes.
In my case, I use delays after every read/write command. Doing so, you ensure you receive the response from the BLE device almost always. I do something like this: (of course everything is done in a separate thread to avoid to much work on the main thread)
readCharacteristic(myChar);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
myChar.getValue();
or:
myChar.setValue(myByte);
writeCharacteristic(myChar);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
This is really useful when you read/write several characteristics in a row... As Android is enough fast to execute the commands almost instantly, if you don't use a delay between them you may get errors or incoherent values...
Hope it helps even if it is not exactly the answer to your question.

How to use LeScanCallback when trying to find BLE-devices?

I'm working on an application in Android and I want it to find and list BLE-devices.
There aren't very many topics on it here and I don't get it. I've tried to use the
startLeScan(BluetoothAdapter.LeScanCallback callback);
But Eclipse says that LeScanCallback can't be resolved or is not a field (I have the same problem with BluetoothManager and LeDeviceListAdapter). I have all the BT permissions, imported BluetoothGatt, BluetoothGattCallback and have the Samsung SDk jar-file in the right place. What have I missed?
I've used this codesuggestion from the Android developers page (only a part of my code), but I can't get it to work:
public class DeviceScanActivity extends ListActivity {
// Stops scanning after 10 seconds.
private static final long SCAN_PERIOD = 10000;
public boolean scanning;
public BluetoothAdapter aBTAdapter;
public Handler aHandler;
//------------------------------------------------
private void scanLeDevice(final boolean enable) {
if (enable) {
// Stops scanning after a pre-defined scan period.
aHandler.postDelayed(new Runnable() {
#Override
public void run() {
scanning = false;
aBTAdapter.stopLeScan(mLeScanCallback);
}
}, SCAN_PERIOD);
scanning = true;
aBTAdapter.startLeScan(mLeScanCallback);
} else {
scanning = false;
aBTAdapter.stopLeScan(mLeScanCallback);
}
};
//------------------------------------------
}
What do I have to do for Eclipse to recognize BluetoothManager, LeDeviceListAdapter and LeScanCallback?
How do I introduce the mLeScanCallback-variable?
Any help would be greatly appreciated! I'm sort of new to programming so pedagogical answers please.
Edit:
I have tried this code to declare the callback but it doesn't work, gives me the same error as mentioned above (BluetoothAdapter.LeScanCallback can't be resolved or is not a field):
private LeDeviceListAdapter mLeDeviceListAdapter;
// Device scan callback.
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() {
mLeDeviceListAdapter.addDevice(device);
mLeDeviceListAdapter.notifyDataSetChanged();
}
});
}
};
Also, errors --> can't run it --> no log
The Bluetooth SIG has a application accelerator kit for BLE which includes complete working code for Android. Great resource to get started, free download at https://developer.bluetooth.org/Pages/Bluetooth-Smart-Developers.aspx
I had the same issue, briefly, and resolved it by:
First make sure you have API level 18+ downloaded. Right click on your project, go to Properties -> Android. Make sure Android 4.3 or above is selected as the Project Build Target. Apply the changes, and you should be good to go.
There's even a code sample below the one you used:
http://developer.android.com/guide/topics/connectivity/bluetooth-le.html#find
You need to introduce a field variable like this:
// Device scan callback.
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() {
mLeDeviceListAdapter.addDevice(device);
mLeDeviceListAdapter.notifyDataSetChanged();
}
});
}
};
I just resolved the same ( / similar) problem..
In my case I miss-added another android.jar (api 4) in addition to the Reference Library. Eclipse was using the added one so the LeScanCallback interface wasn't existing (in my case Context.BLUETOOTH_SERVICE wasn't nether..)
I had to remove the unexpected jar :
In eclipse package explorer, right click on the project
Build Path > Configure build path
Go to Librairies tab
Select the bad android.jar
delete
Best regards,
Léni
You can try Shift+ Crtl + O, to organize imports. If you use API 18, then it should automatically add appropriate imports;
import android.bluetooth.BluetoothAdapter;
If not - make sure you have API 18 installed in Android SDK Manager.
Well I think you have to first understand a few concept before working on.
1.Understand what is a class, what is an object, what is a variable, even better to understand variable scope, etc.
If the code you post is the full code it is definitely not working, as you just copy some a part of code without declaring the variable the code uses. For example, you have to declare and init mLeScanCallback like Boni2k does before using the variable.
2.Do not mix up Samsung BLE SDK and the Android BLE code. They are similar, but what you are doing have nothing to do with Samsung BLE SDK,so including the Samsung JAR definitely not helping the situation.
Although BLE development in Android is not hard by means of code complexity (high level remote IO API !), I think you will suffer if you dig into it with no programming foundation. Better go through some basic Java programming (at least to understand the example code) and Android development concepts.

Categories

Resources