wake-up-neo.com

Windows UWP stellt nach der Erkennung eine Verbindung zum BLE-Gerät her

Ich verwende BluetoothLEAdvertisementWatcher, um in der Nähe befindliche BLE-Geräte zu finden, und es funktioniert gut. Nachdem ich sie gefunden habe, möchte ich Daten über GATT verbinden und lesen/schreiben. Ich kann jedoch nicht herausfinden, wie ich die API verwenden kann, nachdem ich die BluetoothLEAdvertisement erhalten habe ( https://msdn.Microsoft.com/de-de/library/windows/apps/windows.devices.bluetooth.genericattributeprofile ).

public class Adapter
{
    private readonly BluetoothLEAdvertisementWatcher _bleWatcher = new BluetoothLEAdvertisementWatcher();

    public Adapter()
    {
        _bleWatcher.Received += BleWatcherOnReceived;
    }

    private void BleWatcherOnReceived(BluetoothLEAdvertisementWatcher sender, BluetoothLEAdvertisementReceivedEventArgs args)
    {       
        // how to connect?
        // I know, it's the wrong place to to this, but this is just an example
    }

    public void StartScanningForDevices(Guid[] serviceUuids)
    {
        _blewatcher.advertisementfilter.advertisement.serviceuuids.clear();
        foreach (var uuid in serviceuuids)
        {
            _blewatcher.advertisementfilter.advertisement.serviceuuids.add(uuid);
        }
        _blewatcher.start();
    }
}

Ich habe Beispiele gefunden, die DeviceInformation.FindAllAsync anstelle von BluetoothLEAdvertisementWatcher verwenden, aber diese funktionieren nicht und finden kein Gerät.

UPDATE

Nachdem ich einige Zeit herumgegraben hatte, fand ich den folgenden Weg. Leider schlägt das Pairing fehl. Das Gerät ist nur ein Arduino mit einem BLE-Schild. Ich kann mich definitiv mit Android und iOS verbinden. Also muss es mit UWP irgendwie möglich sein. : /

private void BleWatcherOnReceived(BluetoothLEAdvertisementWatcher sender, BluetoothLEAdvertisementReceivedEventArgs args)
{       
    var dev = await BluetoothLEDevice.FromBluetoothAddressAsync(args.BluetoothAddress);
    // dev.DeviceInformation.Pairing.CanPair is true
    // dpr.Status is Failed
    DevicePairingResult dpr = await dev.DeviceInformation.Pairing.PairAsync(DevicePairingProtectionLevel.None);
    var service = await GattDeviceService.FromIdAsync(dev.DeviceInformation.Id);
}

UPDATE # 2

Ich kann jetzt herausfinden und paaren (instabil, aber für den Moment ok), aber 

var service = await GattDeviceService.FromIdAsync(args.Id);

wirft die folgende Ausnahme

System.IO.FileNotFoundException: Die angegebene Datei wurde vom System nicht gefunden. (Ausnahme von HRESULT: 0x80070002)

Ich habe keine Ahnung warum.

18

UPDATE 04/17 - CREATORS UPDATE

Microsoft hat gerade ihre Bluetooth-APIs aktualisiert. Wir haben jetzt eine ungepaarte BLE-Gerätekommunikation!

Sie haben im Moment sehr wenig Dokumentation, aber hier ist die stark vereinfachte neue Struktur:

BleWatcher = new BluetoothLEAdvertisementWatcher 
{ 
    ScanningMode = BluetoothLEScanningMode.Active
};
BleWatcher.Start();

BleWatcher.Received += async (w, btAdv) => {
    var device = await BluetoothLEDevice.FromBluetoothAddressAsync(btAdv.BluetoothAddress);
    Debug.WriteLine($"BLEWATCHER Found: {device.name}");

    // SERVICES!!
    var gatt = await device.GetGattServicesAsync();
    Debug.WriteLine($"{device.Name} Services: {gatt.Services.Count}, {gatt.Status}, {gatt.ProtocolError}");

    // CHARACTERISTICS!!
    var characs = await gatt.Services.Single(s => s.Uuid == SAMPLESERVICEUUID).GetCharacteristicsAsync();
    var charac = characs.Single(c => c.Uuid == SAMPLECHARACUUID);
    await charac.WriteValueAsync(SOMEDATA);
};

Viel besser jetzt. Wie gesagt, es gibt im Moment so gut wie keine Dokumentation. Ich habe ein seltsames Problem, bei dem mein Callback nach ValueChanged nach etwa 30 Sekunden nicht mehr aufgerufen wird. Dies scheint jedoch ein separates Problem zu sein.

UPDATE 2 - EINIGE WEIRDNESS

Nachdem Sie mit dem neuen Ersteller-Update noch ein wenig herumgespielt haben, sollten Sie beim Erstellen von BLE-Apps einige weitere Punkte beachten.

  • Sie müssen das Bluetooth-Material nicht mehr im UI-Thread ausführen. Es scheint keine Berechtigungsfenster für BLE ohne Pairing zu geben, so dass es nicht mehr erforderlich ist, um auf dem UI-Thread ausgeführt zu werden.
  • Möglicherweise stellen Sie fest, dass Ihre Anwendung nach einiger Zeit keine Updates mehr vom Gerät empfängt. Dies ist ein Problem, bei dem Objekte entsorgt werden, die nicht entfernt werden sollten. Wenn Sie im obigen Code ValueChanged der charac angehört haben, kann dieses Problem auftreten. Dies liegt daran, dass die Variable GattCharacteristic gelöscht wird, bevor sie sein sollte. Setzen Sie das Merkmal als global, anstatt sich darauf zu verlassen, dass es kopiert wird.
  • Das Trennen der Verbindung scheint ein bisschen kaputt zu sein. Das Beenden einer App beendet keine Verbindungen. Stellen Sie daher sicher, dass Sie den Verbindungscode App.xml.csOnSuspended verwenden, um Ihre Verbindungen zu beenden. Andernfalls geraten Sie in einen seltsamen Zustand, in dem Windows die BLE-Verbindung aufrechtzuerhalten (und weiterhin zu lesen !!) scheint.

Nun, es hat seine Macken, aber es funktioniert!

ALTE ANTWORT

Nach Jasons korrekter Antwort auf Geräte, die gepaart werden müssen, damit ihre Dienste entdeckt werden, gibt es hier ein Beispielcode, um dies zu beheben:

    private void SetupBluetooth()
    {
        Watcher = new BluetoothLEAdvertisementWatcher { ScanningMode = BluetoothLEScanningMode.Active };
        Watcher.Received += DeviceFound;

        DeviceWatcher = DeviceInformation.CreateWatcher();
        DeviceWatcher.Added += DeviceAdded;
        DeviceWatcher.Updated += DeviceUpdated;

        StartScanning();
    }

    private void StartScanning()
    {
        Watcher.Start();
        DeviceWatcher.Start();
    }

    private void StopScanning()
    {
        Watcher.Stop();
        DeviceWatcher.Stop();
    }

    private async void DeviceFound(BluetoothLEAdvertisementWatcher watcher, BluetoothLEAdvertisementReceivedEventArgs btAdv)
    {
        if (_devices.Contains(btAdv.Advertisement.LocalName))
        {
            await Dispatcher.RunAsync(CoreDispatcherPriority.Low, async () =>
            {
                Debug.WriteLine($"---------------------- {btAdv.Advertisement.LocalName} ----------------------");
                Debug.WriteLine($"Advertisement Data: {btAdv.Advertisement.ServiceUuids.Count}");
                var device = await BluetoothLEDevice.FromBluetoothAddressAsync(btAdv.BluetoothAddress);
                var result = await device.DeviceInformation.Pairing.PairAsync(DevicePairingProtectionLevel.None);
                Debug.WriteLine($"Pairing Result: {result.Status}");
                Debug.WriteLine($"Connected Data: {device.GattServices.Count}");
            });
        }
    }

    private async void DeviceAdded(DeviceWatcher watcher, DeviceInformation device)
    {
        if (_devices.Contains(device.Name))
        {
            try
            {
                var service = await GattDeviceService.FromIdAsync(device.Id);
                Debug.WriteLine("Opened Service!!");
            }
            catch
            {
                Debug.WriteLine("Failed to open service.");
            }
        }
    }

    private void DeviceUpdated(DeviceWatcher watcher, DeviceInformationUpdate update)
    {
        Debug.WriteLine($"Device updated: {update.Id}");
    }

Die wichtigsten Dinge, die hier zu beachten sind, sind:

  • DeviceWatcher benötigt sowohl die Eigenschaften "Hinzugefügt" als auch "Aktualisiert".
  • Sie müssen die Ausnahme FileNotFound abfangen, die auftritt, wenn Sie versuchen, einen Dienst abzufragen, der nicht gepaart oder noch nicht bereit ist.
17

UPDATE (5/5/16) : Das Fehlerproblem "Element nicht gefunden" scheint nur zu passieren, wenn der Bildschirm für Bluetooth-Einstellungen nicht geöffnet ist/das Scannen nicht funktioniert. Ich kann mich nicht erinnern, dass dies vor 10586.218 der Fall war, aber ich habe es nicht überprüft. Offensichtlich ist nicht jedes Problem im Update behoben.

UPDATE (4/29/16) : Das Fenster-Update 10586.218 scheint das Problem der Kopplung mit einem Gerät behoben zu haben, das noch nie mit dem Computer (oder dem Telefon) gekoppelt wurde. Der Prozess, den ich hier und den Beispielcode von Gerard Wilkinson in seiner Antwort beschrieben habe, sollte jetzt einheitlicher funktionieren.

Wenn Sie Glück haben, dass dies funktioniert, muss der Treiber eine beträchtliche Zeit warten, bis der Treiber installiert ist. Ich habe es geschafft, indem sowohl BluetoothLEAdvertisementWatcher als auch ein DeviceWatcher gleichzeitig ausgeführt werden.

Speichern Sie die DeviceInformation des BluetoothLED-Geräts, das Sie von FromBluetoothAddressAsync () erhalten, und entsorgen Sie () das BluetoothLEDevice, bevor Sie das Pairing starten. Das ist wichtig. Wenn Sie dies nicht tun, werden die Gatt-Services nach dem Pairing nicht angezeigt.

Warten Sie dann, bis der DeviceWatcher das gekoppelte Gerät sieht. Es kann Minuten dauern, aber Sie erhalten es normalerweise, bevor die Fortschrittsanzeige für die Geräteinstallation (in der Bluetooth-Systemsteuerung) 100% erreicht. Wenn FromIdAsync weiterhin fehlschlägt, bedeutet dies normalerweise, dass ein Treiberinstallationsfehler aufgetreten ist. Sie können das Pairing aufheben und das Pairing erneut durchführen. Das funktioniert normalerweise für mich.

Es ist jedoch sehr instabil und es scheint abhängig zu sein, welchen Bluetooth-Chipsatz und -Treiber der Rechner hat. Ich erhalte häufig einen Fehler "Element nicht gefunden" mit FromBluetoothAddress, aber wenn es dort vorbeikommt, funktioniert das Pairing normalerweise beim ersten oder zweiten Versuch.

PairAsync und UnpairAsync müssen auch im UI-Thread bereitgestellt werden. Wenn es nicht in der Lage ist, ein blaues Dialogfeld anzuzeigen, in dem Sie zur Autorisierung aufgefordert werden, werden Ausnahmen angezeigt. Sie können Post () von einem gespeicherten UI SynchronizationContext oder Windows.ApplicationModel.Core.CoreApplication.MainView.Dispatcher.RunAsync () mit einem asynchronen Delegierten verwenden, um dies auszuführen.

Ich habe mehrere Beiträge von MS-Angestellten in den Foren gesehen, die besagen, dass FromBluetoothAddressAsync () nur für gekoppelte Geräte funktioniert. Dies ist nicht der Fall, aber es ist fehlerhaft und scheint am besten zu funktionieren, wenn das Gerät in der Vergangenheit mindestens einmal manuell gepaart wurde.

5
Jason S. Clary

Gerard Wilkinsons Antwort ist richtig. Um das Leben einfacher zu machen, habe ich es mit Reactive Extensions () zu einer erwarteten Methode gemacht. Alle Kommentare sind willkommen.

Sobald Sie das Gerät mit dem BluetoothLEAdvertisementWatcher gefunden und mit ihm gekoppelt haben, können Sie damit GATTServices aktivieren.

private async Task<GattDeviceService> GetGATTServiceAsync(string deviceName)
{
  //devicewatcher is abused to trigger connection
  var deviceWatcher = DeviceInformation.CreateWatcher(); //trick to enable GATT

  var addedSource = Observable.FromEventPattern(deviceWatcher, nameof(deviceWatcher.Added))
                              .Select(pattern => ((DeviceInformation)pattern.EventArgs));

  var updatedSource = Observable.FromEventPattern(deviceWatcher, nameof(deviceWatcher.Updated))
                                .Select(pattern =>
                                {
                                  var update = ((DeviceInformationUpdate)pattern.EventArgs);
                                  return Observable.FromAsync(() => DeviceInformation.CreateFromIdAsync(update.Id).AsTask());
                                }).Concat();

  var source = addedSource.Merge(updatedSource);
  source.Publish().Connect(); //make sure the event handlers are attached before starting the device watcher

  deviceWatcher.Start();

  var result = await source.Where(di =>  di.Name == deviceName)                                       //find the relevant device
                           .Select(di => Observable.FromAsync(() => GattDeviceService.FromIdAsync(di.Id).AsTask()))       //get all services from the device
                           .Concat()                                                                                      //necessary because of the async method in the previous statement
                           .Where(service => service.Uuid == SERVICE_UUID)                                                //get the service with the right UUID
                           .Retry()                                                                                       //GattDeviceService.FromIdAsync can throw exceptions
                           .FirstAsync();

  deviceWatcher.Stop();

  return result;
}
3
LanderV

Grundsätzlich haben Sie die Antwort teilweise in die Fragen aufgenommen. Im Wesentlichen verwenden Sie den BluetoothLEAdvertisementWatcher, um nur die Geräte zu finden. Sie funktionieren im Grunde wie Baken.

Es wird nicht angenommen, dass Sie diese Geräte nur über diese API anschließen. Um die Geräte anzuschließen, müssen Sie DeviceInformation.FindAllAsync () verwenden. Damit Sie alle Geräte anzeigen können, müssen Sie sie zuerst koppeln. 

Wenn Sie Interesse haben, Daten von bestimmten BLE-Merkmalen zu erhalten, können Sie versuchen, GattCharacteristicNotificationTrigger zu verwenden, um ein vollständiges Beispiel zu erhalten und ein paar zusätzliche Erklärungen siehe mein Blog .

0
Dr.Jukka