2015-10-09 16 views
21

Ich benutze einen LeScanCallback (kann nicht die neueren Scan-Methoden verwenden, weil ich für api 18 entwickle. Nicht dass es wichtig ist, da die Android 5.0+ Apis diese Funktionalität auch nicht bieten) um zu erkennen, wenn ein nahegelegenes BLE-Gerät ist festgestellt:Wie erkennt man, wenn ein BLE-Gerät nicht mehr in Reichweite ist?

private BluetoothAdapter.LeScanCallback bleCallback = new BluetoothAdapter.LeScanCallback() { 

    @Override 
    public void onLeScan(BluetoothDevice bluetoothDevice, int i, byte[] bytes) { 
     discoveredDevices.add(bluetoothDevice); 
    } 
}; 

mir nicht Paarung oder mit den Geräten verbinden, weil das nicht erforderlich ist, ich will einfach sehen, welche Geräte in der Nähe sind.

Ich versuche, einen Dienst zu machen, der alle 5 Minuten einen Webserver aufruft, um zu aktualisieren, welche Geräte gerade in der Nähe sind.

Tricky Teil ist, dass das Android-Gerät wird sich bewegen, so dass ein Bluetooth-Gerät, das in der Nähe ist jetzt, möglicherweise nicht in 5 Minuten sein. In diesem Fall muss ich es aus discoveredDevices entfernen.

Im Idealfall möchte ich einen Rückruf erhalten, wenn ein Bluetooth-Gerät in Reichweite war, aber nicht mehr. Dieser Rückruf existiert jedoch nicht.

(ich bin mir bewusst, die android.bluetooth.device.action.ACL_CONNECTED und android.bluetooth.device.action.ACL_DISCONNECTED Sendungen, aber die sind für, wenn Sie zu einem Bluetooth-Gerät anschließen, das will ich nicht.)

Eine Option ist eine neue Scan zu tun alle 5 Minuten, aber Sie können nicht feststellen, wenn alle in der Nähe befindlichen Geräte entdeckt wurden, so dass Sie einen zeitgesteuerten Scan, z Scannen Sie für 5 Sekunden und senden Sie die gesammelten Daten an den Webservice.
Das klingt dreckig und riskant, weil man nie sicher sein kann, dass alle Geräte in der Nähe innerhalb der vorgegebenen Zeit entdeckt wurden, also würde ich es gerne vermeiden, es so zu tun.

Gibt es eine andere Möglichkeit, dies zu tun?


bearbeiten
Einige Geräte kontinuierlich Entdeckung in der Nähe Bluetooth-Geräte melden, auch wenn sie bereits vor entdeckt wurden. Wenn diese Funktionalität universell wäre, könnte ich mein Problem lösen, dies ist jedoch gerätespezifisch.

Der Bluetooth-Adapter meines Telefons findet zum Beispiel nur in der Nähe befindliche Geräte. Einige andere Geräte, mit denen ich getestet habe, melden ständig dieselben Geräte in der Nähe, aber nicht alle Geräte, daher kann ich mich leider nicht darauf verlassen.

+0

Scannen Sie nie in einer Schleife, und legen Sie ein Zeitlimit für Ihren Scan fest. Ein Gerät, das zuvor verfügbar war, kann sich außerhalb des zulässigen Bereichs befinden, und das Scannen wird fortgesetzt. Der Benutzer muss sich manuell verbinden ... –

+0

@GopiKrishna Ich bin mir nicht sicher, ob ich verstehe, wovon Sie sprechen.Ich möchte nicht mit einem Zeitlimit scannen, also frage ich nach anderen Optionen –

+0

Ich hatte entwickelt, was Sie benötigen, nicht die sauberste Art vielleicht, aber nicht bessere Möglichkeiten finden. Es kann durchgeführt werden, wenn Sie Scans und entsprechende Gerätelisten paaren und berücksichtigen, ob das Gerät "a" in der Nähe des ersten und zweiten Scans ist. Fügen Sie es in die endgültige Liste ein, die an den Server gesendet wird von beiden Scans – user2450263

Antwort

13

Das klingt schmutzig und riskant, weil Sie können nie alle Geräte in der Nähe sicher wissen, wurden innerhalb der vorgegebenen Zeit entdeckt, so möchte ich sehr viel zu vermeiden, wie das zu tun.

Das klingt wie eine vernünftige Annahme, aber es ist falsch.

Bluetooth niedrige Energie funktioniert in einer bestimmten Weise und BLE-Geräte haben einige Grenzen. Zum Beispiel haben sie eine feste Bandbreite möglicher Werbefrequenzen von 20 Millisekunden bis 10,24 Sekunden in Schritten von 0,625 Millisekunden. Ausführliche Informationen finden Sie unter here und here.

Das bedeutet, dass es höchstens 10,24 Sekunden dauern kann, bevor ein Gerät eine neue Anzeige Paket ausgestrahlt wird. BLE-Geräte bieten im Allgemeinen, wenn auch nicht immer, ihren Besitzern die Möglichkeit, ihre Werbefrequenz anzupassen, sodass die Frequenz natürlich variieren kann.

In Fällen, in denen Sie regelmäßig Daten über Geräte in Ihrer Nähe sammeln, ist es in Ordnung, einen Scan mit einem festen Zeitlimit zu verwenden, diese Daten irgendwo zu speichern, den Scan neu zu starten, neue Daten zu sammeln, mit alten Daten zu vergleichen - -> Ergebnisse erhalten.

Wenn zum Beispiel ein Gerät in Scan 1, aber nicht in Scan 2 gefunden wurde, können Sie daraus schließen, dass das Gerät in Reichweite war, aber nicht mehr.
Gleiches gilt für die umgekehrte Situation: Wenn ein Gerät in Scan 4, aber nicht in Scan 3 gefunden wurde, handelt es sich um ein neu entdecktes Gerät.
Schließlich, wenn ein Gerät in Scan 5 gefunden wurde, nicht in Scan 6 gefunden wurde, aber in Scan 7 wieder gefunden wurde, wird es wiederentdeckt und kann als solches behandelt werden, falls erforderlich.

Da ich meine eigene Frage hier beantworte, werde ich den Code hinzufügen, den ich verwendet habe, um dies zu implementieren.

Ich habe das Scannen in einem Hintergrunddienst durchgeführt und kommunizieren mit anderen Teilen der App BroadcastReceivers verwenden. Asset ist eine benutzerdefinierte Klasse von mir, die einige Daten enthält. DataManager ist eine benutzerdefinierte Klasse von mir, die - wie hast du es vermutet - Daten verwaltet.

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); 
    } 
} 

Dieser Code ist nicht perfekt und könnte sogar fehlerhaft sein, aber es wird zumindest gibt Ihnen eine Idee oder ein Beispiel dafür, wie diese umgesetzt werden können.

+0

Vielen Dank dafür, schöne Infos zu meinem Wissensdatenbank hinzufügen. BLE hat viele Vorteile gegenüber Bluetooth –

2

kann ich diesen Ansatz empfehlen:

Verwenden Map<BluetoothDevice, Long> Struktur die erkannten Geräte zu speichern, wo Long die Zeit der Detektion des Gerätes ist (System.currentTimeMillis() zum Beispiel sein kann).

Dann in Ihrem Dienst (so weit ich aus der Frage verstehe, wird es eine Art von wiederholten Aufgabe implementiert) nur tatsächliche Geräte basierend auf dem Zeitpunkt ihrer Erkennung extrahieren.

Und Sie haben absolut Recht, es gibt keine Garantie, dass alle in der Nähe befindlichen Geräte innerhalb der vorgesehenen Zeit entdeckt wurden. Dies gilt insbesondere für Android-Geräte. iOS-Geräte in der Reihe haben ein anderes Problem - sie können die Adresse ihres BluetoothDevice in Runtime ohne offensichtliche externe Ursache ändern. Hoffe, dies wird Ihnen helfen, die Zeit während des Debuggens zu sparen.


bearbeiten

Als Ergebnis der Forschung zu diesem Thema fand diese Diskussion auf code.google.com

Ausgabe noch offen ist und scheint, dass es auf die Hardware-Funktionen verwendet und kann nicht sein, programmgesteuert behoben. Darüber hinaus scheint es, dass der Fehler auch nach einem Systemupdate auf Problemgeräten bleiben wird. Das periodische Neustarten des Scans könnte daher in diesem Fall eine akzeptable Problemumgehung sein.

+0

"Entnehmen Sie die tatsächlichen Geräte nur anhand des Zeitpunkts ihrer Erkennung" - wie würde das funktionieren? Sie können nicht sagen * dieses Gerät ist nicht mehr in der Nähe, weil es vor 5 Stunden gescannt wurde * weil es vielleicht immer noch in der Nähe ist –

+1

wenn das Gerät noch in der Nähe ist wird es von BluetoothAdapter.LeScanCallback abgefangen und die Zeit der Erkennung wird aktualisiert – DmitryArc

+0

Das ist gerätespezifisch. Der Bluetooth-Adapter meines Telefons meldet zum Beispiel nur Geräte in der Nähe, die sich in der Nähe befinden. Einige andere Geräte, mit denen ich getestet habe, melden ständig die gleichen Geräte in der Nähe und dies würde eine Lösung bieten, aber nicht alle Geräte, so dass wir uns leider nicht darauf verlassen können. –