How to Bind manually to a BlueTooth Low Energy Device in a WinForm using C#?

Michael Frederick picture Michael Frederick · Jan 2, 2017 · Viewed 12.1k times · Source

This question is mostly answered by: Windows UWP connect to BLE device after discovery

I am writing a custom service and testing,for now, using a C#.NET WinForm on Windows 10 to connect to a Bluetooth Low Energy (BLE) device. I am using Framework 4.6.1. We are using a TI SmartRF06 Evaluation Board with a TI CC2650 BLE daughter card. Another developer is handling the Firmware of the Board.

Currently using methods similar to the reference answer above I am able to connect to an already bound BLE device. This device was manually bound and Windows did require me to enter a PIN. Since the device has no PIN simply entering "0" allowed the device to connect. Once connected, in this manner, I can get to all the GATT services and do what I need to do. So I have no issues with finding and getting a hold of a Advertising BLE device.

The issue is that how do I connect to BLE device that has not already been paired? I have gone through the net and found many examples of BLE code but nothing specific to showing how the pairing in code is done. Not sure I even need it to pair but Windows seems to only show my the GATT services on paired devices.

When I do this with unpaired device:

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

The result of dpr is always failed when device has not been manually paired. Which results in the GattDeviceServices being empty. But I am able to get the advertisement and the properties of the BLE device.

There is also this type of method to connect but I can't figure out how to use it:

var prslt = await device.DeviceInformation.Pairing.Custom.PairAsync(DevicePairingKinds.ProvidePin, DevicePairingProtectionLevel.None,IDevicePairingSettings);

IdeviceParingSettings is an Interface. Not sure what Class to use with it. I am thinking this is where I can set the PIN of "O" that I might need?

Has anyone had any luck pairing to a BLE device in Windows using C# where the BLE device has no security. Basically it should be wide open. I feel like I am missing something simple or this is simply not possible (which I have seen some posts claiming that is the case. Most of those were many years old).

I did try the methods described in the mentioned post without any difference in result.

Any help is appreciated. If you need more of the code please look at the link I provided at top as that is what I started with. I will be happy to provide all of my actual code if there is, perhaps, a sequence that I am doing out of place.

Answer

Michael Frederick picture Michael Frederick · Jan 2, 2017

I figured it out. I was on the right track.

After you connect using:

var dev = await BluetoothLEDevice.FromBluetoothAddressAsync(args.BluetoothAddress);

You need to do a custom Pairing:

var prslt = await device.DeviceInformation.Pairing.Custom.PairAsync(DevicePairingKinds.ProvidePin, DevicePairingProtectionLevel.None);

But that would simply give you an error. You must also create a device.DeviceInformation.Pairing.Custom.PairingRequested event handler.

So I created this handler:

private void handlerPairingReq(DeviceInformationCustomPairing CP, DevicePairingRequestedEventArgs DPR)
        {
            //so we get here for custom pairing request.
            //this is the magic place where your pin goes.
            //my device actually does not require a pin but
            //windows requires at least a "0".  So this solved 
            //it.  This does not pull up the Windows UI either.
            DPR.Accept("0");


}

Hooked it up just before the PairAsync call Like:

device.DeviceInformation.Pairing.Custom.PairingRequested += handlerPairingRequested;

Example code for the BlueToothAdvertisementWatcher Code that does my connection:

    private BluetoothLEAdvertisementWatcher BTWatch = new BluetoothLEAdvertisementWatcher();

    private void Inits() 
        {
           BTWatch.Received += new TypedEventHandler<BluetoothLEAdvertisementWatcher, BluetoothLEAdvertisementReceivedEventArgs>(BtAddRx);
           BTWatch.Start();
        }

    private async void BtAddRx(BluetoothLEAdvertisementWatcher bw, BluetoothLEAdvertisementReceivedEventArgs args)
        {
            GattCommunicationStatus srslt;
            GattReadResult rslt;
            bw.Stop();//Stop this while inside.

            device = await BluetoothLEDevice.FromBluetoothAddressAsync(args.BluetoothAddress);
                if (device.DeviceInformation.Pairing.IsPaired == false)
                {   

                    /* Optional Below - Some examples say use FromIdAsync
                    to get the device. I don't think that it matters.   */            
                    var did = device.DeviceInformation.Id; //I reuse did to reload later.
                    device.Dispose();
                    device = null;
                    device = await BluetoothLEDevice.FromIdAsync(did);
                    /* end optional */
                    var handlerPairingRequested = new TypedEventHandler<DeviceInformationCustomPairing, DevicePairingRequestedEventArgs>(handlerPairingReq);
                    device.DeviceInformation.Pairing.Custom.PairingRequested += handlerPairingRequested;
                    log("Pairing to device now...."); 

                    var prslt = await device.DeviceInformation.Pairing.Custom.PairAsync(DevicePairingKinds.ProvidePin, DevicePairingProtectionLevel.None);                  
                    log("Custom PAIR complete status: " + prslt.Status.ToString() + " Connection Status: " + device.ConnectionStatus.ToString());

                    device.DeviceInformation.Pairing.Custom.PairingRequested -= handlerPairingRequested; //Don't need it anymore once paired.


                    if (prslt.Status != DevicePairingResultStatus.Paired)
                    { //This should not happen. If so we exit to try again.
                        log("prslt exiting.  prslt.status=" + prslt.Status.ToString());// so the status may have updated.  lets drop out of here and get the device again.  should be paired the 2nd time around?
                        bw.Start();//restart this watcher.
                        return;
                    }
                    else
                    {
                        // The pairing takes some time to complete. If you don't wait you may have issues. 5 seconds seems to do the trick.

                        System.Threading.Thread.Sleep(5000); //try 5 second lay.
                        device.Dispose();
                       //Reload device so that the GATT services are there. This is why we wait.                     
                       device = await BluetoothLEDevice.FromIdAsync(did);

                    }
 var services = device.GattServices;
//then more code to finish it up.
}

If you wish to disconnect just use:

await device.DeviceInformation.Pairing.UnpairAsync();

Sorry for the messy Code. If there is anyone that finds is useful or has question let me know. I could not find any WinForm examples of this code anywhere. Actually I could not find any code to show how to pair with PIN without the UI. So I hope this helps anyone that might get stuck.