Example #1
0
        /// <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>();
            }
        }
Example #3
0
        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
            {
            }
        }
Example #4
0
        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;
        }
Example #6
0
        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);
        }
Example #8
0
        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);
            }
        }
Example #9
0
        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);
        }
Example #10
0
        //---------------------------------------------------------------------
        // 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;
            }
        }
Example #11
0
        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;
        }
Example #12
0
        /// <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();
            }
        }
Example #14
0
        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);
        }
Example #15
0
        /// <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;
        }