/// <summary> /// Retrieves the characteristics of the given service. /// The error logging is handled in the function. /// </summary> /// <param name="service"></param> /// <returns>The list of characteristic if it succeed, null otherwise</returns> public static async Task <IReadOnlyList <GattCharacteristic> > GetCharacteristics(GattDeviceService service) { try { DeviceAccessStatus status = await service.RequestAccessAsync(); if (status == DeviceAccessStatus.Allowed) { GattCharacteristicsResult result = await service.GetCharacteristicsAsync(BluetoothCacheMode.Uncached); if (result.Status == GattCommunicationStatus.Success) { return(result.Characteristics); } else { LogHelper.Error("Error accessing device"); return(null); } } else { LogHelper.Error("Error accessing device: access not granted"); return(null); } } catch (Exception e) { LogHelper.Error("Restricted service. Can't read characteristics: " + e.Message); return(null); } }
private async void GetCharachteristics(GattDeviceService service) { List <GattCharacteristic> characteristics = null; try { // Ensure we have access to the device. var accessStatus = await service.RequestAccessAsync(); if (accessStatus == DeviceAccessStatus.Allowed) { var result1 = await service.GetCharacteristicsForUuidAsync(TX_CHAR_UUID); if (result1.Status == GattCommunicationStatus.Success) { characteristics = new List <GattCharacteristic>(result1.Characteristics); GattCharacteristic txChar = characteristics[0]; } } } catch (Exception ex) { Debug.WriteLine(ex); // On error, act as if there are no characteristics. characteristics = new List <GattCharacteristic>(); } }
public async Task FindAllServices(GattDeviceService service) { try { IReadOnlyList <GattCharacteristic> characteristics = null; var accessStatus = await service.RequestAccessAsync(); if (accessStatus == DeviceAccessStatus.Allowed) { var result = await service.GetCharacteristicsAsync(BluetoothCacheMode.Uncached); if (result.Status == GattCommunicationStatus.Success) { characteristics = result.Characteristics; } else { // On error, act as if there are no characteristics. characteristics = new List <GattCharacteristic>(); } foreach (GattCharacteristic c in characteristics) { // add to a list } } } catch { } }
private static async void GetCharacteristics(GattDeviceService service) { IReadOnlyList <GattCharacteristic> characteristics = null; string device = service.Device.DeviceInformation.Id.Substring(41); try { DeviceAccessStatus accessStatus = await service.RequestAccessAsync(); if (accessStatus == DeviceAccessStatus.Allowed) { GattCharacteristicsResult result = await service.GetCharacteristicsAsync(BluetoothCacheMode.Uncached); if (result.Status == GattCommunicationStatus.Success) { characteristics = result.Characteristics; Console.WriteLine("Found Characteristics"); } else { blePowermates[device].isSubscribing = false; Console.WriteLine("Error accessing service."); characteristics = new List <GattCharacteristic>(); } } else { blePowermates[device].isSubscribing = false; Console.WriteLine("Error accessing service."); } } catch (Exception ex) { blePowermates[device].isSubscribing = false; Console.WriteLine("Error: Restricted service. Can't read characteristics: " + ex.ToString()); } if (characteristics != null) { foreach (GattCharacteristic characteristic in characteristics) { Console.WriteLine("└Characteristic uuid: " + characteristic.Uuid.ToString()); if (UuidEquals(uuidRead, characteristic.Uuid)) { SubscribeToValueChange(characteristic); Console.WriteLine(" └Subscribing to Read Characteristic"); } } } }
private async void EnumeratingChara(GattDeviceService service) { string charaName; characteristics = null; try { var accessStatus = await service.RequestAccessAsync(); if (accessStatus == DeviceAccessStatus.Allowed) { var result = await service.GetCharacteristicsAsync(BluetoothCacheMode.Uncached); if (result.Status == GattCommunicationStatus.Success) { characteristics = result.Characteristics; } else { NotifyUser("Error accessing service.", NotifyType.ErrorMessage); characteristics = new List <GattCharacteristic>(); } } else { NotifyUser("Error accessing service.", NotifyType.ErrorMessage); characteristics = new List <GattCharacteristic>(); } } catch (Exception ex) { NotifyUser("Restricted service. Can't read charcteristics:" + ex.Message, NotifyType.ErrorMessage); characteristics = new List <GattCharacteristic>(); } var chara = characteristics[0]; charaName = DisplayHelpers.GetCharacteristicName(chara); CharaReadUUID.Text = charaName; CharaReadUUIDBorder.Visibility = Visibility.Visible; EnumerationReadDescriptor(chara); chara = characteristics[1]; charaName = DisplayHelpers.GetCharacteristicName(chara); CharaWriteUUID.Text = charaName; CharaWriteUUIDBorder.Visibility = Visibility.Visible; EnumerationWriteDescriptor(chara); CharaReadUUIDBorder.Visibility = Visibility.Visible; }
public static async Task <IReadOnlyList <GattCharacteristic> > GetCharacteristics(this GattDeviceService service) { var accessStatus = await service.RequestAccessAsync(); if (accessStatus == DeviceAccessStatus.Allowed) { // BT_Code: Get all the child characteristics of a service. Use the cache mode to specify uncached characterstics only // and the new Async functions to get the characteristics of unpaired devices as well. var result = await service.GetCharacteristicsAsync(BluetoothCacheMode.Uncached); if (result.Status == GattCommunicationStatus.Success) { return(result.Characteristics); } } return(null); }
private bool BluetoothSetup() { BluetoothLEDevice device = this.Waitfor( "connection to Bluetooth device", BluetoothLEDevice.FromBluetoothAddressAsync(System.Convert.ToUInt64("001580912553", 16))); if (device == null) { this.logEol("Device wasn't found"); return(false); } GattDeviceServicesResult deviceServices = this.Waitfor( "device services", device.GetGattServicesAsync()); DeviceAccessStatus deviceAccessStatus = this.Waitfor( "device access", device.RequestAccessAsync()); this.logEol($"Device access status: {deviceAccessStatus}"); System.Guid gyroServiceGuid = System.Guid.Parse("0000ffe0-0000-1000-8000-00805f9b34fb"); GattDeviceService gyroService = deviceServices.Services.Single(x => x.Uuid.Equals(gyroServiceGuid)); var gyroServiceAccessStatus = this.Waitfor( "gro data service access", gyroService.RequestAccessAsync()); this.logEol($"Gyro service access status: {gyroServiceAccessStatus}"); GattCharacteristicsResult characteristics = this.Waitfor( "gyro data service", gyroService.GetCharacteristicsAsync()); this.txRxChannel = characteristics .Characteristics .SingleOrDefault(x => x.UserDescription.Replace(" ", "") == "TX&RX"); if (this.txRxChannel == default(GattCharacteristic)) { this.logEol("Couldn't find TXRX channel...disconnected?"); return(false); } this.txRxChannel.ValueChanged += this.TxRx_ValueChanged; return(true); }
private static async Task <GattCharacteristic> GetCharacteristicAsync(GattDeviceService service, Guid characteristicId) { if (service == null) { throw new InvalidOperationException("Failed to connect to service."); } IReadOnlyList <GattCharacteristic> characteristics = new List <GattCharacteristic>(); try { // Ensure we have access to the device. var accessStatus = await service.RequestAccessAsync(); if (accessStatus != DeviceAccessStatus.Allowed) { // Not granted access throw new InvalidOperationException($"Error accessing service: {accessStatus}"); } // Get all the child characteristics of a service. Use the cache mode to specify uncached characteristics only // and the new Async functions to get the characteristics of unpaired devices as well. var result = await service.GetCharacteristicsAsync(BluetoothCacheMode.Uncached); if (result.Status != GattCommunicationStatus.Success) { throw new InvalidOperationException($"Error accessing service: {result.Status}"); } GattCharacteristic characteristic = result.Characteristics.FirstOrDefault(c => c.Uuid == characteristicId); if (characteristic == null) { throw new InvalidOperationException($"Requested characteristic ID was not found."); } return(characteristic); } catch (Exception ex) { throw new InvalidOperationException($"Restricted service. Can't read characteristics.", ex); } }
public async Task <bool> Initialize(IReadOnlyList <GattDeviceService> services, TypedEventHandler <GattCharacteristic, GattValueChangedEventArgs> eventHandler) { service = services.FirstOrDefault(item => item.Uuid == serviceUuid); valueChanged = eventHandler; if (service == null) { return(false); } if (await service.RequestAccessAsync() != DeviceAccessStatus.Allowed) { return(false); } GattCharacteristicsResult result = await service.GetCharacteristicsAsync(BluetoothCacheMode.Uncached); if (result.Status != GattCommunicationStatus.Success) { return(false); } foreach (var item in result.Characteristics) { GattReadResult readResult = await item.ReadValueAsync(); if (readResult.Status != GattCommunicationStatus.Success) { return(false); } if (item.CharacteristicProperties.HasFlag(GattCharacteristicProperties.Notify)) { await item.WriteClientCharacteristicConfigurationDescriptorAsync(GattClientCharacteristicConfigurationDescriptorValue.Notify); item.ValueChanged += valueChanged; item.ValueChanged += localValueChanged; } characteristics.Add(item); values.Add(item.Uuid, FormatToString(readResult.Value)); } return(true); }
//--------------------------------------------------------------------- // Event handlers for advertisements //--------------------------------------------------------------------- // // Specifies behavior for when an advertisement is received by the // watcher. Because the IPv6 Over Bluetooth Low Energy project is based // on 6LoWPAN principles, routers actively watch for possible // recipients when they have a packet to deliver. Receiving an // advertisement means that there is a suitable node (or other router) // within range that can receive the packet, so we transmit it here. // private async void OnAdvertisementReceived( BluetoothLEAdvertisementWatcher watcher, BluetoothLEAdvertisementReceivedEventArgs eventArgs ) { BluetoothError status = BluetoothError.Success; // Variables for the remote device, the IPv6ToBle packet processing // service, IPSS, and the device's characteristics BluetoothLEDevice device = null; GattDeviceService internetProtocolSupportService = null; GattDeviceService ipv6ToBlePacketProcessingService = null; IReadOnlyList <GattCharacteristic> deviceCharacteristics = null; // Variables to hold the characteristics with which we need to // interact GattCharacteristic ipv6PacketWriteCharacteristic = null; GattCharacteristic ipv6AddressCharacteristic = null; IPAddress deviceAddress = null; // // Step 1 // Verify we have recevied a proper advertisement from the server // by checking its manufacturer data. This is kind of redundant // because we filter advertisements based on this, but it's not a // bad idea. // IList <BluetoothLEManufacturerData> manufacturerDataList = eventArgs.Advertisement.ManufacturerData; if (manufacturerDataList.Count == 0) { status = BluetoothError.OtherError; Debug.WriteLine("No manufacturer data in the advertisement."); goto Exit; } else { // There should only be one if it's one of our advertisements BluetoothLEManufacturerData manufacturerData = manufacturerDataList[0]; // Verify it's the IPv6ToBle manufacturer name string manufacturerDataCompanyId = string.Format("0x{0}", manufacturerData.CompanyId.ToString("X") ); if (manufacturerDataCompanyId != "0xDEDE") { status = BluetoothError.OtherError; Debug.WriteLine("Manufacturer Company ID did not match " + "IPv6ToBle." ); goto Exit; } } // // Step 2 // Connect to the device // try { // Connect based on the device's Bluetooth address device = await BluetoothLEDevice.FromBluetoothAddressAsync(eventArgs.BluetoothAddress); if (device == null) { status = BluetoothError.DeviceNotConnected; Debug.WriteLine("Error connecting to device."); goto Exit; } } catch (Exception e) when(e.HResult == Constants.E_DEVICE_NOT_AVAILABLE) { status = BluetoothError.RadioNotAvailable; Debug.WriteLine("Bluetooth radio is not on."); goto Exit; } // // Step 3 // Enumerate the GATT services to get the // IPv6ToBlePacketProcessingService and Internet Protocol Support // Service (IPSS) // if (device != null) { // Retrieve the list of services from the device (uncached) GattDeviceServicesResult servicesResult = await device.GetGattServicesAsync(BluetoothCacheMode.Uncached); if (servicesResult.Status == GattCommunicationStatus.Success) { var services = servicesResult.Services; Debug.WriteLine($"Found {services.Count} services"); // Iterate through the list of services and check if // both services we require are there foreach (GattDeviceService service in services) { Guid uuid = service.Uuid; // Check for IPv6ToBle Packet Write Service if (uuid == Constants.IPv6ToBlePacketProcessingServiceUuid) { ipv6ToBlePacketProcessingService = service; continue; } // Check for IPSS ushort shortId = GattHelpers.ConvertUuidToShortId(uuid); if (shortId == (ushort)GattHelpers.SigAssignedGattNativeUuid.InternetProtocolSupport) { internetProtocolSupportService = service; continue; } } } } // Report error if the device was not running our packet processing // service for some reason if (ipv6ToBlePacketProcessingService == null || internetProtocolSupportService == null) { status = BluetoothError.OtherError; Debug.WriteLine("Device did not have the " + "IPv6ToBlePacketProcessingService running or" + " available, or did not have the Internet" + " Protocol Support Service running or " + "available." ); goto Exit; } // // Step 4 // Enumerate the GATT characteristics // try { // Verify we can access the service DeviceAccessStatus accessStatus = await ipv6ToBlePacketProcessingService.RequestAccessAsync(); if (accessStatus == DeviceAccessStatus.Allowed) { // Enumerate the characteristics GattCharacteristicsResult characteristicsResult = await ipv6ToBlePacketProcessingService.GetCharacteristicsAsync(BluetoothCacheMode.Uncached); if (characteristicsResult.Status == GattCommunicationStatus.Success) { deviceCharacteristics = characteristicsResult.Characteristics; } else { status = BluetoothError.OtherError; Debug.WriteLine("Could not access the packet " + "processing service." ); goto Exit; } } else { // Not granted access status = BluetoothError.NotSupported; Debug.WriteLine("Could not access the packet " + "processing service." ); goto Exit; } } catch (Exception e) { status = BluetoothError.DeviceNotConnected; Debug.WriteLine("Could not read characteristics due to " + "permissions issues. " + e.Message ); goto Exit; } if (deviceCharacteristics != null) { // Find the IPv6 Address and packet write characteristics foreach (GattCharacteristic characteristic in deviceCharacteristics) { if (characteristic.Uuid == Constants.IPv6ToBleIPv6AddressCharacteristicUuid) { ipv6AddressCharacteristic = characteristic; } if (characteristic.Uuid == Constants.IPv6ToBlePacketWriteCharacteristicUuid) { ipv6PacketWriteCharacteristic = characteristic; } } } if (ipv6PacketWriteCharacteristic == null || ipv6AddressCharacteristic == null) { status = BluetoothError.OtherError; Debug.WriteLine("Could not access the IPv6 address" + " characteristic and the packet write" + " characteristic."); goto Exit; } // Get the device's IPv6 address from the characteristic if (ipv6AddressCharacteristic != null) { GattReadResult readResult = await ipv6AddressCharacteristic.ReadValueAsync(); if (readResult.Status == GattCommunicationStatus.Success) { deviceAddress = new IPAddress(GattHelpers.ConvertBufferToByteArray(readResult.Value)); } else { status = BluetoothError.OtherError; Debug.WriteLine("Could not read the device's IPv6 address" + " from the remote characteristic." ); goto Exit; } } // // Step 5 // Write the packet now that we have verified that the device is // supported and is either the destination or in the path to the // destination // GattCommunicationStatus writeStatus = await ipv6PacketWriteCharacteristic.WriteValueAsync(GattHelpers.ConvertByteArrayToBuffer(Packet)); if (writeStatus != GattCommunicationStatus.Success) { status = BluetoothError.OtherError; Debug.WriteLine("Could not write the IPv6 packet to the" + " remote device"); } Exit: if (status != BluetoothError.Success) { TransmittedSuccessfully = false; } else { TransmittedSuccessfully = true; } }
private async void ConnectButton_Click() { ConnectButton.IsEnabled = false; if (!await ClearBluetoothLEDeviceAsync()) { rootPage.NotifyUser("Error: Unable to reset state, try again.", NotifyType.ErrorMessage); ConnectButton.IsEnabled = true; return; } try { // BT_Code: BluetoothLEDevice.FromIdAsync must be called from a UI thread because it may prompt for consent. //bluetoothLeDevice = await BluetoothLEDevice.FromIdAsync(rootPage.SelectedBleDeviceId); "BluetoothLE#BluetoothLE74:40:bb:fe:e8:16-c1:81:6b:98:16:1f" bluetoothLeDevice = await BluetoothLEDevice.FromIdAsync("BluetoothLE#BluetoothLE74:40:bb:fe:e8:16-c1:81:6b:98:16:1f"); if (bluetoothLeDevice == null) { rootPage.NotifyUser("Failed to connect to device.", NotifyType.ErrorMessage); } } catch (Exception ex) when(ex.HResult == E_DEVICE_NOT_AVAILABLE) { rootPage.NotifyUser("Bluetooth radio is not on.", NotifyType.ErrorMessage); } if (bluetoothLeDevice != null) { // Note: BluetoothLEDevice.GattServices property will return an empty list for unpaired devices. For all uses we recommend using the GetGattServicesAsync method. // BT_Code: GetGattServicesAsync returns a list of all the supported services of the device (even if it's not paired to the system). // If the services supported by the device are expected to change during BT usage, subscribe to the GattServicesChanged event. GattDeviceServicesResult result = await bluetoothLeDevice.GetGattServicesAsync(BluetoothCacheMode.Uncached); if (result.Status == GattCommunicationStatus.Success) { var services = result.Services; rootPage.NotifyUser("Successfully connected", NotifyType.StatusMessage); foreach (var service in services) { ServiceList.Items.Add(new ComboBoxItem { Content = DisplayHelpers.GetServiceName(service), Tag = service }); var zzz = service.Uuid; if (service.Uuid == Constants.RightFootSensorServiceUuid) { rightFootService = service; break; } } ConnectButton.Visibility = Visibility.Collapsed; ServiceList.Visibility = Visibility.Collapsed; // From characteristics listing: IReadOnlyList <GattCharacteristic> characteristics = null; var accessStatus = await rightFootService.RequestAccessAsync(); if (accessStatus == DeviceAccessStatus.Allowed) { // BT_Code: Get all the child characteristics of a service. Use the cache mode to specify uncached characterstics only // and the new Async functions to get the characteristics of unpaired devices as well. var result2 = await rightFootService.GetCharacteristicsAsync(BluetoothCacheMode.Uncached); if (result2.Status == GattCommunicationStatus.Success) { characteristics = result2.Characteristics; foreach (GattCharacteristic c in characteristics) { CharacteristicList.Items.Add(new ComboBoxItem { Content = DisplayHelpers.GetCharacteristicName(c), Tag = c }); if (c.Uuid == Constants.RightFootSensorCharacteristicUuid) { rightFootCharacteristic = c; break; } } ValueChangedSubscribeToggle.IsEnabled = true; ValueChangedSubscribeToggle.Visibility = Visibility.Visible; ValueChangedSubscribeToggle.Content = "Start"; selectedCharacteristic = rightFootCharacteristic; SelectedDeviceRun.Text = "Connected"; } else { rootPage.NotifyUser("Error accessing service.", NotifyType.ErrorMessage); } } } else { rootPage.NotifyUser("Device unreachable", NotifyType.ErrorMessage); } } ConnectButton.IsEnabled = true; }
/// <summary> /// Writes a packet to a given device. /// </summary> /// <param name="targetDevice">The target device.</param> /// <param name="packet">The packet, compressed or not.</param> /// <param name="compressedHeaderLength">Optional. The length of the compressed header if the caller is sending a compressed packet.</param> /// <param name="payloadLength">Optional. The paylaod length of the packet. Only needed if the caller is sending a compressed packet.</param> /// <returns></returns> public static async Task <bool> WritePacketAsync( DeviceInformation targetDevice, byte[] packet, int compressedHeaderLength, int payloadLength ) { BluetoothError status = BluetoothError.Success; // Variables for the remote device, the IPv6ToBle packet processing // service, IPSS, and the device's characteristics BluetoothLEDevice device = null; GattDeviceService ipv6ToBlePacketProcessingService = null; IReadOnlyList <GattCharacteristic> deviceCharacteristics = null; // Variables for the remote packet write characteristic GattCharacteristic ipv6PacketWriteCharacteristic = null; GattCharacteristic compressedHeaderLengthCharacteristic = null; GattCharacteristic payloadLengthCharacteristic = null; // Variables for timing GATT communication latency and transmission time Stopwatch gattLatencyTimer = new Stopwatch(); Stopwatch gattTransmissionTimer = new Stopwatch(); // // Step 1 // Connect to the device // // Start timing how long it takes to perform pre-transmission GATT // communications gattLatencyTimer.Start(); try { // Connect based on the device's Bluetooth address device = await BluetoothLEDevice.FromIdAsync(targetDevice.Id); if (device == null) { status = BluetoothError.DeviceNotConnected; Debug.WriteLine("Error connecting to device."); goto Exit; } } catch (Exception e) when(e.HResult == Constants.E_DEVICE_NOT_AVAILABLE) { status = BluetoothError.RadioNotAvailable; Debug.WriteLine("Bluetooth radio is not on."); goto Exit; } // // Step 2 // Enumerate the GATT services to get the // IPv6ToBlePacketProcessingService // if (device != null) { // Retrieve the list of services from the device (uncached) GattDeviceServicesResult servicesResult = await device.GetGattServicesAsync(BluetoothCacheMode.Cached); if (servicesResult.Status == GattCommunicationStatus.Success) { var services = servicesResult.Services; Debug.WriteLine($"Found {services.Count} services when " + "querying services for packet writing." ); // Iterate through the list of services and check if // both services we require are there foreach (GattDeviceService service in services) { Guid uuid = service.Uuid; // Check for IPv6ToBle Packet Write Service if (uuid == Constants.IPv6ToBlePacketProcessingServiceUuid) { ipv6ToBlePacketProcessingService = service; break; } } } } // Report error if the device was not running our packet processing // service for some reason if (ipv6ToBlePacketProcessingService == null) { status = BluetoothError.OtherError; Debug.WriteLine("Device did not have the " + "IPv6ToBlePacketProcessingService running or" + " available." ); goto Exit; } // // Step 3 // Enumerate the GATT characteristics // try { // Verify we can access the service DeviceAccessStatus accessStatus = await ipv6ToBlePacketProcessingService.RequestAccessAsync(); if (accessStatus == DeviceAccessStatus.Allowed) { // Enumerate the characteristics GattCharacteristicsResult characteristicsResult = await ipv6ToBlePacketProcessingService.GetCharacteristicsAsync(BluetoothCacheMode.Cached); if (characteristicsResult.Status == GattCommunicationStatus.Success) { deviceCharacteristics = characteristicsResult.Characteristics; } else { status = BluetoothError.OtherError; Debug.WriteLine("Could not access the packet " + "processing service." ); goto Exit; } } else { // Not granted access status = BluetoothError.NotSupported; Debug.WriteLine("Could not access the packet " + "processing service." ); goto Exit; } } catch (Exception e) { status = BluetoothError.DeviceNotConnected; Debug.WriteLine("Could not read characteristics due to " + " permissions issues. " + e.Message ); goto Exit; } // Find the required characteristics for packet writing if (deviceCharacteristics != null) { foreach (GattCharacteristic characteristic in deviceCharacteristics) { if (characteristic.Uuid == Constants.IPv6ToBlePacketWriteCharacteristicUuid) { ipv6PacketWriteCharacteristic = characteristic; } if (characteristic.Uuid == Constants.IPv6ToBleCompressedHeaderLengthCharacteristicUuid) { compressedHeaderLengthCharacteristic = characteristic; } if (characteristic.Uuid == Constants.IPv6ToBlePayloadLengthCharacteristicUuid) { payloadLengthCharacteristic = characteristic; } } } if (ipv6PacketWriteCharacteristic == null || compressedHeaderLengthCharacteristic == null || payloadLengthCharacteristic == null) { status = BluetoothError.OtherError; Debug.WriteLine("Could not access all three characteristics " + "required for packet writing." ); goto Exit; } // Stop the GATT latency timer now that we are ready to do the actual // transmission gattLatencyTimer.Stop(); Debug.WriteLine("GATT communications completed successfully. Total " + $"time: {gattLatencyTimer.ElapsedMilliseconds} milliseconds." ); // // Step 5 // Write the packet now that we have verified that the device is // supported and is either the destination or in the path to the // destination // // Start the GATT transmission timer gattTransmissionTimer.Start(); //DataWriter writer = new DataWriter(); //// Write the compressed header length //writer.WriteInt32(compressedHeaderLength); //GattCommunicationStatus writeStatus = await compressedHeaderLengthCharacteristic.WriteValueAsync(writer.DetachBuffer()); //if (writeStatus != GattCommunicationStatus.Success) //{ // status = BluetoothError.OtherError; // Debug.WriteLine("Could not write compressed header length."); // goto Exit; //} //// Write the payload length //writer = new DataWriter(); //writer.WriteInt32(payloadLength); //writeStatus = await payloadLengthCharacteristic.WriteValueAsync(writer.DetachBuffer()); //if (writeStatus != GattCommunicationStatus.Success) //{ // status = BluetoothError.OtherError; // Debug.WriteLine("Could not write payload length."); // goto Exit; //} // Write the packet itself last so the receiver knows that it has // all needed data when this characteristic is written to GattCommunicationStatus writeStatus = await ipv6PacketWriteCharacteristic.WriteValueAsync(GattHelpers.ConvertByteArrayToBuffer(packet), GattWriteOption.WriteWithoutResponse ); if (writeStatus != GattCommunicationStatus.Success) { status = BluetoothError.OtherError; Debug.WriteLine("Could not write the IPv6 packet to the" + " remote device"); goto Exit; } // Stop the GATT transmission timer and record the time if successful gattTransmissionTimer.Stop(); long microseconds = gattTransmissionTimer.ElapsedTicks / (Stopwatch.Frequency / (1000L * 1000L)); Debug.WriteLine("GATT transmission completed successfully. Total " + $"time: {microseconds} microseconds." ); Exit: // Dispose of the service and device that we accessed, then force // a garbage collection to destroy the objects and fully disconnect // from the remote GATT server and device. This is as a workaround // for a current Windows bug that doesn't properly disconnect // devices, as well as a workaround for the Broadcomm Bluetooth LE // driver on the Raspberry Pi 3 that can't handle multiple connects // and reconnects if it thinks it's still occupied. // // Additionally, at this step, if you had connected any events // to the services or characteristics, you'd have to do that first. // But we didn't do that here, so no need. ipv6ToBlePacketProcessingService?.Dispose(); device?.Dispose(); device = null; GC.Collect(); if (status != BluetoothError.Success) { // Stop the timers if we failed if (gattLatencyTimer.IsRunning) { gattLatencyTimer.Stop(); } if (gattTransmissionTimer.IsRunning) { gattTransmissionTimer.Stop(); } return(false); } return(true); }
//--------------------------------------------------------------------- // Methods for device connection after discovery. Enumerate services // on each discovered device and add supported devices to a dictionary // that maps devices to their link-local IPv6 addresses. //--------------------------------------------------------------------- // Connects to each found device and enumerates available GATT // services, then only adds devices to the list that support both the // IPSSS and our IPv6ToBle packet writing service. This method is // called after the initial device discovery phase. public async Task PopulateSupportedDevices() { // // Step 1 // Check for empty list in case we couldn't find anything // if (foundDevices.Count == 0) { return; } // // Step 2 // Connect to each previously found device and enumerate its // services. If it supports both IPSS and IPv6ToBle Packet Writing // Service, add it to the list. // foreach (DeviceInformation deviceInfo in foundDevices) { BluetoothLEDevice currentDevice = null; GattDeviceService ipv6ToBlePacketProcessingService = null; GattCharacteristic ipv6AddressCharacteristic = null; IPAddress ipv6Address = null; bool hasInternetProtocolSupportService = false; bool hasIPv6ToBlePacketWriteService = false; try { // Connect. This is recommended to do on a UI thread // normally because it may prompt for consent, but for our // purposes in this application it will auto-accept and we // don't have to use a UI thread. currentDevice = await BluetoothLEDevice.FromIdAsync(deviceInfo.Id); if (currentDevice == null) { Debug.WriteLine($"Failed to connect to device {deviceInfo.Id}"); } } catch (Exception e) when(e.HResult == Constants.E_DEVICE_NOT_AVAILABLE) { Debug.WriteLine("Bluetooth radio is not on."); } // Enumerate the GATT services with GetGattServicesAsync if (currentDevice != null) { // Retrieve the list of services from the device (uncached) GattDeviceServicesResult servicesResult = await currentDevice.GetGattServicesAsync(BluetoothCacheMode.Uncached); if (servicesResult.Status == GattCommunicationStatus.Success) { var services = servicesResult.Services; Debug.WriteLine($"Found {services.Count} services for" + $" device {deviceInfo.Id}" ); // Iterate through the list of services and check if // both services we require are there foreach (GattDeviceService service in services) { Guid uuid = service.Uuid; // Check for IPSS ushort shortId = GattHelpers.ConvertUuidToShortId(uuid); if (shortId == (ushort)GattHelpers.SigAssignedGattNativeUuid.InternetProtocolSupport) { hasInternetProtocolSupportService = true; continue; } // Check for IPv6ToBle Packet Write Service if (uuid == Constants.IPv6ToBlePacketProcessingServiceUuid) { hasIPv6ToBlePacketWriteService = true; ipv6ToBlePacketProcessingService = service; continue; } } } } // Query the device's IP address - enumerate characteristics // for the IPv6ToBlePacketProcessingService and read from the // IPv6 address characteristic to map devices to their // addresses if (hasInternetProtocolSupportService && hasIPv6ToBlePacketWriteService && ipv6ToBlePacketProcessingService != null) { IReadOnlyList <GattCharacteristic> characteristics = null; try { // Verify we can access the device's packet processing service DeviceAccessStatus accessStatus = await ipv6ToBlePacketProcessingService.RequestAccessAsync(); if (accessStatus == DeviceAccessStatus.Allowed) { // Enumerate the characteristics GattCharacteristicsResult characteristicsResult = await ipv6ToBlePacketProcessingService.GetCharacteristicsAsync(BluetoothCacheMode.Uncached); if (characteristicsResult.Status == GattCommunicationStatus.Success) { characteristics = characteristicsResult.Characteristics; } else { Debug.WriteLine("Could not access the packet" + " processing service." ); // On error, act as if there were no characteristics characteristics = new List <GattCharacteristic>(); } } else { // Not granted access Debug.WriteLine("Could not access the packet" + "processing service." ); // On error, act as if there were no characteristics characteristics = new List <GattCharacteristic>(); } } catch (Exception e) { Debug.WriteLine("Could not read characteristics due to" + " permissions issues. " + e.Message ); // On error, act as if there were no characteristics characteristics = new List <GattCharacteristic>(); } // Find the IPv6 address characteristic foreach (GattCharacteristic characteristic in characteristics) { if (characteristic.Uuid == Constants.IPv6ToBleIPv6AddressCharacteristicUuid) { ipv6AddressCharacteristic = characteristic; break; } } // Get the IPv6 address from the characteristic if (ipv6AddressCharacteristic != null) { GattReadResult readResult = await ipv6AddressCharacteristic.ReadValueAsync(); if (readResult.Status == GattCommunicationStatus.Success) { ipv6Address = new IPAddress(GattHelpers.ConvertBufferToByteArray(readResult.Value)); } } // Finally, add the deviceInfo/IP address pair to the // dictionary if (ipv6Address != null) { supportedBleDevices.Add(ipv6Address, deviceInfo); } } // Dispose of the service and device that we accessed, then force // a garbage collection to destroy the objects and fully disconnect // from the remote GATT server and device. This is as a workaround // for a current Windows bug that doesn't properly disconnect // devices, as well as a workaround for the Broadcomm Bluetooth LE // driver on the Raspberry Pi 3 that can't handle multiple connects // and reconnects if it thinks it's still occupied. // // Additionally, at this step, if you had connected any events // to the services or characteristics, you'd have to disconnect // them first. But we didn't do that here, so no need. ipv6ToBlePacketProcessingService?.Dispose(); currentDevice?.Dispose(); currentDevice = null; GC.Collect(); } }
public async Task <bool> Connect(BluetoothLEDevice bleDevice, Guid serviceUuid) { if (bleDevice == null || serviceUuid == Guid.Empty) { return(false); } // Clean old history Disconnect(); BleDevice = bleDevice; ServiceUuid = serviceUuid; try { GattDeviceServicesResult gattDeviceServicesesult = await BleDevice.GetGattServicesForUuidAsync(serviceUuid, BluetoothCacheMode.Uncached); if (gattDeviceServicesesult.Status == GattCommunicationStatus.Success) { IReadOnlyList <GattDeviceService> gattServices = gattDeviceServicesesult.Services; if (gattServices.Count > 0) { GattService = gattServices[0]; } } } catch (Exception) { Disconnect(); return(false); } if (GattService == null) { Disconnect(); return(false); } try { // Ensure we have access to the device. var accessStatus = await GattService.RequestAccessAsync(); if (accessStatus == DeviceAccessStatus.Allowed) { // BT_Code: Get all the child characteristics of a service. Use the cache mode to specify uncached characterstics only // and the new Async functions to get the characteristics of unpaired devices as well. var result = await GattService.GetCharacteristicsAsync(BluetoothCacheMode.Uncached); if (result.Status == GattCommunicationStatus.Success) { Characteristics = result.Characteristics; } } } catch (Exception) { Disconnect(); return(false); } return(true); }
/// <summary> /// Setup and connect to services for the discovered device. /// </summary> async Task SetupDevice() { State = BlueState.Connecting; try { Debug.WriteLineIf(sw.TraceVerbose, "++> Getting Bluetooth LE device"); // BluetoothLEDevice.FromIdAsync must be called from a UI thread because it may prompt for consent. bluetoothLeDevice = await BluetoothLEDevice.FromIdAsync(DeviceInfo.Id); } catch (Exception ex) when((uint)ex.HResult == 0x800710df) { ErrorMessage = "ERROR_DEVICE_NOT_AVAILABLE because the Bluetooth radio is not on."; } catch (Exception ex) { ErrorMessage = "FromIdAsync ERROR: " + ex.Message; } if (bluetoothLeDevice == null) { Disconnect(); return; } var sr = await bluetoothLeDevice.GetGattServicesForUuidAsync(uuidService, BluetoothCacheMode.Uncached); if (sr.Status != GattCommunicationStatus.Success || !sr.Services.Any()) { ErrorMessage = "Can't find service: " + sr.Status.ToString(); Disconnect(); return; } Service = sr.Services.First(); Debug.WriteLineIf(sw.TraceVerbose, "++> GattService found"); var accessStatus = await Service.RequestAccessAsync(); if (accessStatus != DeviceAccessStatus.Allowed) { // Not granted access ErrorMessage = "Error accessing service:" + accessStatus.ToString(); Disconnect(); return; } var charResult = await Service.GetCharacteristicsForUuidAsync(uuidTX, BluetoothCacheMode.Uncached); if (charResult.Status != GattCommunicationStatus.Success || !charResult.Characteristics.Any()) { ErrorMessage = "Error getting TX characteristic: " + charResult.Status.ToString(); Disconnect(); return; } _TX = charResult.Characteristics.First(); charResult = await Service.GetCharacteristicsForUuidAsync(uuidRX, BluetoothCacheMode.Uncached); if (charResult.Status != GattCommunicationStatus.Success || !charResult.Characteristics.Any()) { ErrorMessage = "Error getting RX characteristic: " + charResult.Status.ToString(); Disconnect(); return; } _RX = charResult.Characteristics.First(); try { Debug.WriteLineIf(sw.TraceVerbose, "++> Setting Notify descriptor"); var res = await _RX.WriteClientCharacteristicConfigurationDescriptorAsync( GattClientCharacteristicConfigurationDescriptorValue.Notify); if (res != GattCommunicationStatus.Success) { ErrorMessage = "Error setting RX notify: " + charResult.Status.ToString(); Disconnect(); return; } } catch (Exception ex) { ErrorMessage = "Exception setting RX notify: " + ex.Message; Disconnect(); return; } _RX.ValueChanged += Receive_ValueChanged; bluetoothLeDevice.ConnectionStatusChanged += BluetoothLeDevice_ConnectionStatusChanged; // now that services are connected, we're ready to go State = BlueState.Connected; }