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.
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.
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.App.xml.cs
OnSuspended
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:
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.
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;
}
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 .